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

unresolved external symbol __imp__fprintf and __imp____iob_func, SDL2

本文关键字:imp iob func SDL2 fprintf 外部 符号      更新时间:2023-10-16

谁能解释一下

__imp__fprintf

__imp____iob_func

未解析的外部含义?

因为当我试图编译时,我得到了这些错误:

1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _ShowError
1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp____iob_func referenced in function _ShowError
1>E:DocumentsVisual Studio 2015ProjectsSDL2_TestDebugSDL2_Test.exe : fatal error LNK1120: 2 unresolved externals

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

我正在尝试使用SDL2。

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

我已经链接到SDL2。lib和SDL2main。lib在链接器->输入->额外的依赖关系,我已经确保vc++目录是正确的。

我终于明白为什么会这样了!

在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 到您的链接器选项。

To Milan Babuškov, IMO,这正是替换功能应该看起来的样子:-)

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

微软对此有一个特别的说明(https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT):

printf和scanf函数族现在是内联定义的。

所有的printf和scanf函数的定义都已给出内联移动到stdio.hconio.h和其他CRT头文件中。这是一个导致链接器错误(LNK2019,未解析的外部符号)函数,而不包括适当的CRT标头。如果可能的话,您应该更新代码以包含CRT标头(即是,添加#include)和内联函数,但是如果你这样做不希望修改代码以包含这些头文件另一种解决方案是在您的链接器中添加一个额外的库输入, legacy_stdio_definitions.lib

要将此库添加到IDE中的链接器输入中,请打开上下文菜单中,选择"属性",然后选择"项目"属性对话框中,选择链接器,并编辑要添加的链接器输入legacy_stdio_definitions。

如果您的项目链接到使用在2015年之前发布的Visual c++版本中,链接器可能会报告一个错误未解析的外部符号。这些错误可能引用内部stdio定义_iob_iob_func,或为某些stdio函数以__imp_*的形式。微软建议您重新编译所有的静态库版本的Visual c++编译器和库项目。如果库是第三方库,则source为不可用,您应该请求更新的二进制文件第三方或将您对该库的使用封装到一个单独的使用旧版本的Visual c++编译器编译的DLL和库。

如上所述,正确的答案是使用VS2015编译所有内容,但出于兴趣,以下是我对问题的分析。

这个符号似乎没有在微软提供的任何静态库中定义,作为VS2015的一部分,这是相当奇怪的,因为所有其他的都是。要找出原因,我们需要看看该函数的声明,更重要的是,看看它是如何使用的。

下面是Visual Studio 2008头文件的一个片段:

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

所以我们可以看到这个函数的工作是返回一个FILE对象数组的开始(不是句柄,"FILE *"是句柄,FILE是存储重要状态数据的底层不透明数据结构)。此函数的用户是用于各种fscanf, fprintf样式调用的三个宏stdin, stdout和stderr。

现在让我们来看看Visual Studio 2015是如何定义相同的东西的:

_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))

因此,替换函数的方法已经改变,现在返回文件句柄而不是文件对象数组的地址,并且宏已经改变为简单地调用函数并传递一个标识数字。

那么为什么他们/我们不能提供一个兼容的API?在通过__iob_func:

的原始实现方面,微软不能违反两个关键规则。
  1. 必须有一个包含三个FILE结构的数组,可以像以前一样被索引。
  2. FILE的结构布局不能改变

上述任何一个的任何更改都意味着,如果调用该API,与此链接的现有编译代码将出现严重错误。

让我们来看看FILE是如何定义的。

首先是VS2008文件定义:

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

现在VS2015文件定义:

typedef struct _iobuf
{
    void* _Placeholder;
} FILE;

所以这就是关键所在:结构改变了形状。引用__iob_func的现有编译代码依赖于这样一个事实:返回的数据既是一个可索引的数组,而且该数组中元素之间的距离相同。

上述答案中提到的可能的解决方案(如果调用)由于以下几个原因无法工作:

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

FILE数组_iob将在VS2015中编译,因此它将被布局为包含void*的结构块。假设采用32位对齐,这些元素之间的间隔为4个字节。所以_iob[0]在偏移量0处,_iob[1]在偏移量4处,_iob[2]在偏移量8处。调用代码将期望FILE更长,在我的系统上对齐为32字节,因此它将获取返回数组的地址并添加0字节以获得元素零(这是可以的),但对于_iob[1],它将推断它需要添加32字节,对于_iob[2],它将推断它需要添加64字节(因为这就是它在VS2008头文件中的样子)。实际上VS2008的反汇编代码证明了这一点。

上述解决方案的第二个问题是,它复制 FILE结构(*stdin)的内容,而不是FILE *句柄。因此,任何VS2008代码都将查看与VS2015不同的底层结构。如果结构体只包含指针,这可能有效,但这是一个很大的风险。无论如何,第一个问题使这无关紧要。

我能想到的唯一hack是__iob_func遍历调用堆栈以找出他们正在寻找的实际文件句柄(基于添加到返回地址的偏移量)并返回一个计算值,以便它给出正确的答案。这听起来很疯狂,但下面列出了仅用于x86(而不是x64)的原型,供您参考。它在我的实验中工作得很好,但您的里程可能会有所不同-不建议用于生产使用!

#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,%02Xn", 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;
}

我在VS2015中有同样的问题。我通过在VS2015中编译SDL2源代码解决了这个问题。

  1. 到http://libsdl.org/download-2.0.php下载SDL 2源代码。
  2. SDL_VS2013开放。在VS2015中。您将被要求转换项目。做。
  3. 编译SDL2项目编译SDL2main项目
  4. 使用新生成的输出文件SDL2main。自由,SDL2。
  5. 在VS2015的SDL2项目中添加sdl2 . lib和SDL2.dll。

我不知道为什么但是:

#ifdef main
#undef main
#endif

根据我的经验,在include之后但在main之前应该修复它。

这个问题的最新解决方案:在

上使用最新的sdl库

" https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/?C=M; O = D"

他们似乎已经解决了这个问题,虽然它只是32位库(我认为)。

链接意味着不能正常工作。深入研究VS2012和VS2015的studio .h,以下内容对我有用。唉,您必须决定它是否应该为{stdin, stdout, stderr}中的一个工作,而不是多于一个。

extern "C" FILE* __cdecl __iob_func()
{
    struct _iobuf_VS2012 { // ...Microsoft Visual Studio 11.0VCincludestdio.h #56
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname; };
    // VS2015 has only 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);
}

我的建议是不要(尝试)实现__iob_func。

libpngd.v110.lib(pngrutil.obj) : error LNK2001: unresolved external symbol ___iob_func curllib.v110.lib(mprintf.obj) : error LNK2001: unresolved external symbol ___iob_func

我尝试了其他答案的解决方案,但最终,返回FILE* c -数组与Windows内部IOB结构体数组不匹配。@Volker是对的,它永远不会在多个stdin, stdoutstderr中工作。

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

结论:不可能混合VS2012(平台工具集v110/v110_xp)和VS2015+库,如果他们使用stdin, stdout和/或stderr。

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

我用下面的函数解决这个问题。我用的是Visual Studio 2019。

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

因为stdin宏定义的函数调用,"*stdin"表达式不能使用全局数组初始化器。但是本地数组初始化是可能的。对不起,我的英语很差。

使用预编译的SDL2main。lib和SDL。用于VS2015项目的库:https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225.zip

对于任何仍在寻找上述技巧不起作用的答案的人。静态链接是解决这个问题的方法。更改运行时库设置,如下所示

Project properties --> C/C++ --> Code generation --> Runtime Library --> Multi-threaded Debug (/MTd) instead of /MDd

这里是对这个解决方案的讨论:https://social.msdn.microsoft.com/Forums/vstudio/en-US/4a1c9610-fa41-45f6-ad39-c9f6795be6f2/msvcrt-iob-disappeared?forum=vclanguage

当您链接到msvcr10.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)
#endif

这个片段对于Visual Studio 6及其编译器来说是不需要的。因此#ifdef。

为了给这个已经很丰富的线程带来更多的混乱,我碰巧在fprintf

上碰到了相同的未解决的外部
main.obj : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _GenerateInfoFile

即使在我的情况下,它是在一个相当不同的上下文中:在Visual Studio 2005 (Visual Studio 8.0)下,错误发生在我自己的代码中(我正在编译相同的代码),而不是第三方。

碰巧这个错误是由我的编译器标志中的/MD选项触发的。切换到/MT解决了这个问题。这很奇怪,因为通常情况下,静态链接(MT)比动态链接(MD)引发的问题更多……但为了以防万一,我把它放在那里了。

在我的情况下,这个错误来自于我尝试删除依赖于msvc版本的运行时库DLL (msvcr10.dll或左右)和/或删除静态运行时库,以从我的可执行文件中删除多余的脂肪。

所以我使用/NODEFAULTLIB链接器开关,我自制的"msvcrt-light"。(当你需要的时候谷歌搜索),mainCRTStartup()/WinMainCRTStartup()条目。

从Visual Studio 2015开始,所以我坚持使用旧的编译器。

然而,定义符号_NO_CRT_STDIO_INLINE消除了所有的麻烦,一个简单的"Hello World"应用程序同样只有3kb小,并且不依赖于不寻常的dll。在Visual Studio 2017中测试。

也许这对你有帮助。我添加了Shell32。给我的链接器——>输入——比;附加依赖项,它停止了这个错误。我从这篇文章中发现了它:https://discourse.libsdl.org/t/windows-build-fails-with-missing-symbol-imp-commandlinetoargvw/27256/3

我得到了同样的问题(未解决的外部符号__impl_printf)当试图链接。lib到我的vs2015项目

对我来说唯一有效的解决方案是project->properties->link ->legacy_stdio_definitions。lib

这适用于visual studio 2017:

#include<corecrt_wstdio.h>
FILE* __cdecl __iob_func(unsigned const id)
{
   return(__acrt_iob_func(id));
}

我设法解决了这个问题。

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

fprintf(stderr, "%s: %sn", title, message);

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

fprintf("%s: %sn", title, message);

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

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

我不知道这是否会伤到我,但一切都很顺利。

将此代码粘贴到任何源文件中并重新构建。

 #include <stdio.h>
FILE _iob[3];
FILE* __cdecl __iob_func(void)
{
   _iob[0] = *stdin;
   _iob[0] = *stdout;
   _iob[0] = *stderr;
   return _iob;
}