当前位置 : 首页 » 互动问答 » 正文

未解析的外部符号__imp__fprintf和__imp____iob_func,SDL2

分类 : 互动问答 | 发布时间 : 2015-05-23 20:58:16 | 评论 : 12 | 浏览 : 81240 | 喜欢 : 84

有人可以解释什么

__ imp__fprintf

and

__ imp ____ iob_func

未解析的外部手段?

因为我在尝试编译时遇到这些错误:

 1> SDL2main.lib(SDL_windows_main.obj):错误LNK2019:函数_ShowError中引用的未解析的外部符号__imp__fprintf
1> SDL2main.lib(SDL_windows_main.obj):错误LNK2019:函数_ShowError中引用的未解析的外部符号__imp____iob_func
1> E:\ Documents \ Visual Studio 2015 \ Projects \ SDL2_Test \ Debug \ SDL2_Test.exe:致命错误LNK1120:2个未解析的外部

我已经可以说问题不是来自链接错误。我已正确链接所有内容,但由于某种原因它无法编译。

我正在尝试使用SDL2。

我使用Visual Studio 2015作为编译器。

我已链接到SDL2。链接器中的lib和SDL2main.lib - >输入 - >附加依赖项,我确保VC ++目录是正确的。

回答(12)

  • 1楼
  • 我终于想出了为什么会发生这种情况!

    在visual studio 2015中,stdin,stderr,stdout定义如下:

     #define stdin(__ acrt_iob_func(0))
    #define stdout(__ acrt_iob_func(1))
    #define stderr(__ acrt_iob_func(2))
    

    但之前,它们被定义为:

     #define stdin(&__ iob_func()[0])
    #define stdout(&__ iob_func()[1])
    #define stderr(&__ iob_func()[2])
    

    所以现在__iob_func不再被定义,当使用与以前版本的visual studio一起编译的.lib文件时,会导致链接错误。

    为了解决这个问题,你可以尝试自己定义的数组__ iob_func(),这应该返回一个包含 {* stdin,* stdout,* stderr} .

    关于stdio函数的其他链接错误(在我的例子中是sprintf()),你可以添加 legacy_stdio_definitions.lib到您的链接器选项。

  • 2楼
  • 对于IMO的米兰巴布什科夫来说,这正是替换函数的样子: - )

     FILE _iob [] = {* stdin,* stdout,* stderr};
    
    extern“C”FILE * __cdecl __iob_func(void)
    {
        return _iob;
    }
  • 3楼
  • Microsoft对此有一个特别说明( https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT ):

    printf和scanf系列函数现在已内联定义。

    所有printf和scanf函数的定义都已内联移入stdio.h, conio.h和其他CRT头。这是一个重大更改,导致链接器错误(LNK2019,未解析的外部符号),用于在本地声明这些函数的任何程序如果可能,您应该更新代码以包含CRT标头(即添加#include)和内联函数,但如果您不想修改代码以包含这些头文件,替代解决方案是向链接器输入添加其他库, legacy_stdio_definitions.lib .

    要将此库添加到IDE中的链接器输入,请打开项目节点的上下文菜单,选择“属性”,然后在“项目”中属性对话框bo x,选择链接器,然后编辑链接器输入以将legacy_stdio_definitions.lib添加到以分号分隔的列表。

    如果您的项目链接到使用早于2015年的Visual C ++版本编译的静态库,则链接器可能会报告未解析的外部符号。这些错误可能引用_iob, _iob_func的内部stdio定义,或以__imp_*形式引用某些stdio函数的相关导入。 Microsoft建议您在升级项目时使用最新版本的Visual C ++编译器和库重新编译所有静态库。如果库是源不可用的第三方库,则应该从第三方请求更新的二进制文件,或者将您对该库的使用封装到使用旧版Visual C ++编译器编译的单独DLL中和图书馆。

  • 4楼
  • 我在VS2015中遇到了同样的问题。我通过在VS2015中编译SDL2源来解决它。

    1. Go to http://libsdl.org/download-2.0.php 并下载SDL 2源代码。
    2. VS2015中打开SDL_VS2013.sln。您将被要求转换项目。执行。
    3. 编译SDL2项目。
    4. 编译SDL2main项目。
    5. 在VS2015的SDL 2项目中使用新生成的输出文件SDL2main.lib,SDL2.lib和SDL2.dll。
  • 5楼
  • As answered above, the right answer is to compile everything with VS2015, but for interest the following is my analysis of the problem.

    This symbol does not appear to be defined in any static library provided by Microsoft as part of VS2015, which is rather peculiar since all others are. To discover why, we need to look at the declaration of that function and, more importantly, how it's used.

    Here's a snippet from the Visual Studio 2008 headers:

    _CRTIMP FILE * __cdecl __iob_func(void);
    #define stdin (&__iob_func()[0])
    #define stdout (&__iob_func()[1])
    #define stderr (&__iob_func()[2])
    

    So we can see that the job of the function is to return the start of an array of FILE objects (not handles, the "FILE *" is the handle, FILE is the underlying opaque data structure storing the important state goodies). The users of this function are the three macros stdin, stdout and stderr which are used for various fscanf, fprintf style calls.

    Now let's take a look at how Visual Studio 2015 defines the same things:

    _ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
    #define stdin (__acrt_iob_func(0))
    #define stdout (__acrt_iob_func(1))
    #define stderr (__acrt_iob_func(2))
    

    So the approach has changed for the replacement function to now return the file handle rather than the address of the array of file objects, and the macros have changed to simply call the function passing in an identifying number.

    So why can't they/we provide a compatible API? There are two key rules which Microsoft can't contravene in terms of their original implementation via __iob_func:

    1. There must be an array of three FILE structures which can be indexed in the same manner as before.
    2. The structural layout of FILE cannot change.

    Any change in either of the above would mean existing compiled code linked against that would go badly wrong if that API is called.

    Let's take a look at how FILE was/is defined.

    First the VS2008 FILE definition:

    struct _iobuf {
            char *_ptr;
            int   _cnt;
            char *_base;
            int   _flag;
            int   _file;
            int   _charbuf;
            int   _bufsiz;
            char *_tmpfname;
            };
    typedef struct _iobuf FILE;
    

    And now the VS2015 FILE definition:

    typedef struct _iobuf
    {
        void* _Placeholder;
    } FILE;
    

    So there is the crux of it: the structure has changed shape. Existing compiled code referring to __iob_func relies upon the fact that the data returned is both an array that can be indexed and that in that array the elements are the same distance apart.

    The possible solutions mentioned in the answers above along these lines would not work (if called) for a few reasons:

    FILE _iob[] = {*stdin, *stdout, *stderr};
    
    extern "C" FILE * __cdecl __iob_func(void)
    {
        return _iob;
    }
    

    The FILE array _iob would be compiled with VS2015 and so it would be laid out as a block of structures containing a void*. Assuming 32-bit alignment, these elements would be 4 bytes apart. So _iob[0] is at offset 0, _iob[1] is at offset 4 and _iob[2] is at offset 8. The calling code will instead expect FILE to be much longer, aligned at 32 bytes on my system, and so it will take the address of the returned array and add 0 bytes to get to element zero (that one is okay), but for _iob[1] it will deduce that it needs to add 32 bytes and for _iob[2] it will deduce that it needs to add 64-bytes (because that's how it looked in the VS2008 headers). And indeed the disassembled code for VS2008 demonstrates this.

    A secondary issue with the above solution is that it copies the content of the FILE structure (*stdin), not the FILE * handle. So any VS2008 code would be looking at a different underlying structure to VS2015. This might work if the structure only contained pointers, but that's a big risk. In any case the first issue renders this irrelevant.

    The only hack I've been able to dream up is one in which __iob_func walks the call stack to work out which actual file handle they are looking for (based on the offset added to the returned address) and returns a computed value such that it gives the right answer. This is every bit as insane as it sounds, but the prototype for x86 only (not x64) is listed below for your amusement. It worked okay in my experiments, but your mileage may vary - not recommended for production use!

    #include <windows.h>
    #include <stdio.h>
    #include <dbghelp.h>
    
    /* #define LOG */
    
    #if defined(_M_IX86)
    
    #define GET_CURRENT_CONTEXT(c, contextFlags) \
      do { \
        c.ContextFlags = contextFlags; \
        __asm    call x \
        __asm x: pop eax \
        __asm    mov c.Eip, eax \
        __asm    mov c.Ebp, ebp \
        __asm    mov c.Esp, esp \
      } while(0);
    
    #else
    
    /* This should work for 64-bit apps, but doesn't */
    #define GET_CURRENT_CONTEXT(c, contextFlags) \
      do { \
        c.ContextFlags = contextFlags; \
        RtlCaptureContext(&c); \
    } while(0);
    
    #endif
    
    FILE * __cdecl __iob_func(void)
    {
        CONTEXT c = { 0 };
        STACKFRAME64 s = { 0 };
        DWORD imageType;
        HANDLE hThread = GetCurrentThread();
        HANDLE hProcess = GetCurrentProcess();
    
        GET_CURRENT_CONTEXT(c, CONTEXT_FULL);
    
    #ifdef _M_IX86
        imageType = IMAGE_FILE_MACHINE_I386;
        s.AddrPC.Offset = c.Eip;
        s.AddrPC.Mode = AddrModeFlat;
        s.AddrFrame.Offset = c.Ebp;
        s.AddrFrame.Mode = AddrModeFlat;
        s.AddrStack.Offset = c.Esp;
        s.AddrStack.Mode = AddrModeFlat;
    #elif _M_X64
        imageType = IMAGE_FILE_MACHINE_AMD64;
        s.AddrPC.Offset = c.Rip;
        s.AddrPC.Mode = AddrModeFlat;
        s.AddrFrame.Offset = c.Rsp;
        s.AddrFrame.Mode = AddrModeFlat;
        s.AddrStack.Offset = c.Rsp;
        s.AddrStack.Mode = AddrModeFlat;
    #elif _M_IA64
        imageType = IMAGE_FILE_MACHINE_IA64;
        s.AddrPC.Offset = c.StIIP;
        s.AddrPC.Mode = AddrModeFlat;
        s.AddrFrame.Offset = c.IntSp;
        s.AddrFrame.Mode = AddrModeFlat;
        s.AddrBStore.Offset = c.RsBSP;
        s.AddrBStore.Mode = AddrModeFlat;
        s.AddrStack.Offset = c.IntSp;
        s.AddrStack.Mode = AddrModeFlat;
    #else
    #error "Platform not supported!"
    #endif
    
        if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
        {
    #ifdef LOG
            printf("Error: 0x%08X (Address: %p)\n", GetLastError(), (LPVOID)s.AddrPC.Offset);
    #endif
            return NULL;
        }
    
        if (s.AddrReturn.Offset == 0)
        {
            return NULL;
        }
    
        {
            unsigned char const * assembly = (unsigned char const *)(s.AddrReturn.Offset);
    #ifdef LOG
            printf("Code bytes proceeding call to __iob_func: %p: %02X,%02X,%02X\n", assembly, *assembly, *(assembly + 1), *(assembly + 2));
    #endif
            if (*assembly == 0x83 && *(assembly + 1) == 0xC0 && (*(assembly + 2) == 0x20 || *(assembly + 2) == 0x40))
            {
                if (*(assembly + 2) == 32)
                {
                    return (FILE*)((unsigned char *)stdout - 32);
                }
                if (*(assembly + 2) == 64)
                {
                    return (FILE*)((unsigned char *)stderr - 64);
                }
    
            }
            else
            {
                return stdin;
            }
        }
        return NULL;
    }
    
  • 6楼
  • 我不知道为什么但是:

     #ifdef main
    #undef main
    #万一
    

    在包括之后,但在主要应该从我的经验中修复它之前。

  • 8楼
  • 链接意味着不能正常工作。深入研究VS2012和VS2015的stdio.h,以下内容对我有用。唉,你必须决定它是否适用于{stdin,stdout,stderr}之一,永远不会超过一个。

     extern“C”FILE * __cdecl __iob_func()
    {
        struct _iobuf_VS2012 {// ... \ Microsoft Visual Studio 11.0 \ VC \ include \ stdio.h#56
            char * _ptr;
            int _cnt;
            char * _base;
            int _flag;
            int _file;
            int _charbuf;
            int _bufsiz;
            char * _tmpfname; };
        // VS2015只有FILE = struct {void *}
    
        int const count = sizeof(_iobuf_VS2012)/ sizeof(FILE);
    
        //// stdout
        // return(FILE *)(&(__ acrt_iob_func(1) - > _ Placeholder) -  count);
    
        // stderr
        return(FILE *)(&(__ acrt_iob_func(2) - > _ Placeholder) -  2 * count);
    }
  • 9楼
  • 我的建议是不要(尝试)实现__iob_func。

    修复这些错误:

    libpngd.v110.lib(pngrutil.obj):错误LNK2001:未解析的外部符号___iob_func curllib.v110.lib(mprintf.obj):错误LNK2001:未解析的外部符号___ iob_func

    我尝试了其他答案的解决方案,但最后,返回FILE* C数组与数组不匹配Windows的内部IOB结构。 @Volker是正确的,它将永远不会用于stdin, stdout or stderr.

    如果库实际使用其中一个流,它将崩溃。只要您的程序不会导致lib使用它们,你永远不会知道。例如,当CRC在PNG的元数据中不匹配时, png_default_error writes to stderr。 (通常不是崩溃问题)

    结论:不可能混合使用VS2012(Platform Toolset v110) / v110_xp)和VS2015 +库,如果它们使用stdin,stdout和/或stderr。

    解决方案:使用当前版本的VS和匹配的平台工具集重新编译具有__iob_func未解析符号的库。

  • 11楼
  • 我设法解决了这个问题。

    错误的来源是这行代码,可以在SDLmain源代码中找到。

     fprintf(stderr,“%s:%s \ n”,title,message);
    

    所以我所做的就是在该行的SDLmain中编辑源代码:

     fprintf(“%s:%s \ n”,title,message);
    

    然后我构建了SDLmain并使用新构建和编辑的方式复制并替换了SDL2库目录中的旧SDLmain.lib。

    然后当我用SDL2运行我的程序时,没有出现任何错误消息,并且代码运行顺利。

    我不知道这是否会在以后咬我,但所以一切都很顺利。

  • 12楼
  • 当你链接到msvcrt.dll而不是msvcr10.dll(或类似的)时会发生这种情况,这是一个很好的计划。因为它可以让你自由地在你的最终软件包中重新发布你的Visual Studio的运行时库。

    这个解决方法帮助我(在Visual Studio 2008上):

     #if _MSC_VER> = 1400
    #undef stdin
    #undef stdout
    #undef stderr
    extern“C”_CRTIMP extern FILE _iob [];
    #define stdin _iob
    #define stdout(_iob + 1)
    #define stderr(_iob + 2)
    #万一
    

    Visual Studio 6及其编译器不需要此代码段。因此#ifdef。

相关阅读:

How to convert string to char array in C++?

Why is it faster to process a sorted array than an unsorted array?

What does the C++ standard state the size of int, long type to be?

Best C++ IDE or Editor for Windows

Convert char to int in C and C++

usr/bin/ld: cannot find -l<nameOfTheLibrary>

Calling C++ class methods via a function pointer

What is the best way to use a HashMap in C++?

What does the symbol \0 mean in a string-literal?

Where does Visual Studio look for C++ header files?