неразрешенный внешний символ imp fprintf и imp IOB func, SDL2

может кто-нибудь объяснить, что

__бесенок__fprintf

и

__бесенок____работа_по 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++ верны.

12 ответов


я наконец понял почему это происходит !

в 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 больше не определен, что приводит к ошибке ссылки при использовании.lib-файл, скомпилированный с предыдущими версиями visual studio.

чтобы решить проблему, вы можете попробовать определить __iob_func() yourself, который должен возвращать массив, содержащий {*stdin,*stdout,*stderr}.

относительно других ошибок ссылки о функциях stdio (в моем случае это было sprintf()), вы можете добавить legacy_stdio_definitions.Либ!--17--> в параметрах линкера.


Милану Бабушкову, ИМО, это именно то, как должна выглядеть функция замены : -)

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

Microsoft имеет специальную заметку об этом (https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT):

семейство функций printf и scanf теперь определено inline.

определения всех функций printf и scanf были перемещены вС stdio.h, попечительством.h и другие заголовки ЭЛТ. Это разрывное изменение, которое приводит к ошибке компоновщика (LNK2019, неразрешенный внешний символ) для любые программы, которые объявили эти функции локально без включения соответствующих заголовков CRT. Если возможно, вы должны обновить код, чтобы включить заголовки CRT (то есть добавить #include ) и встроенные функции, но если вы не хотите изменять свой код, чтобы включить эти файлы заголовков, альтернативным решением является добавление дополнительной библиотеки к входу компоновщика,legacy_stdio_definitions.Либ!--6-->.

чтобы добавить эту библиотеку к входу компоновщика в IDE, откройте контекстное меню узла проекта выберите Свойства, затем в диалоговом окне Свойства проекта выберите Компоновщик и измените входные данные компоновщика, чтобы добавить legacy_stdio_definitions.Либ с запятой-запятыми.

Если проект связан со статическими библиотеками, скомпилированными с выпуском Visual C++ ранее 2015 года, компоновщик может сообщить о неразрешенном внешнем символе. Эти ошибки могут ссылаться на внутренние определения stdio для _iob, _iob_func, или связанный импорт для некоторых функций stdio в виде __imp_*. Корпорация Майкрософт рекомендует перекомпилировать все статические библиотеки с последней версией компилятора Visual C++ и библиотек при обновлении проекта. Если библиотека является сторонней библиотекой, для которой источник недоступен, следует либо запросить обновленный двоичный файл у третьей стороны, либо инкапсулировать использование этой библиотеки в отдельную библиотеку DLL, компилируемую с помощью более старая версия компилятора и библиотек Visual C++.


У меня была такая же проблема в VS2015. Я решил это путем компиляции источников SDL2 в VS2015.

  1. на http://libsdl.org/download-2.0.php скачать и СДЛ 2 исходный код.
  2. открыть SDL_VS2013.sln в VS2015. Вам будет предложено преобразовать проекты. Сделать его.
  3. скомпилировать проект SDL2.
  4. скомпилировать проект SDL2main.
  5. используйте новые сгенерированные выходные файлы SDL2main.lib, SDL2.lib и SDL2.dll в ваш проект SDL 2 в VS2015.

как было сказано выше, правильным ответом является компиляция всего с VS2015, но для интереса ниже приведен мой анализ проблемы.

этот символ, по-видимому, не определен ни в одной статической библиотеке, предоставленной Microsoft как часть 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])

таким образом, мы видим, что задача функции-вернуть начало массива файловых объектов (не дескрипторы, "файл *" - это дескриптор, файл-это базовая непрозрачная структура данных, хранящая важные лакомства состояния). Пользователями этой функции являются три макроса stdin, stdout и stderr, которые используются для различных вызовов стилей fscanf, fprintf.

теперь давайте посмотрим, как 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? Есть два ключевых правила, которые Microsoft не может нарушить с точки зрения их первоначальной реализации через __iob_func:

  1. должен быть массив три структуры файлов, которые могут быть индексированы таким же образом, как и раньше.
  2. структурный макет файла не может измениться.

любое изменение в любом из вышеперечисленных будет означать, что существующий скомпилированный код, связанный с этим, будет сильно ошибочным, если этот API будет вызван.

давайте посмотрим, как файл был / определен.

сначала определение файла 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;
}

массив файлов _iob будет скомпилирован с VS2015 и поэтому он будет выложен как блок структур, содержащих пустоту*. Предполагая 32-разрядное выравнивание, эти элементы будут разделены 4 байтами. Так _iob[0] на смещение 0, _iob[1] является при смещении 4 и _iob[2] находится по смещению 8. Вместо этого вызывающий код будет ожидать, что файл будет намного длиннее, выровнен по 32 байтам в моей системе, и поэтому он возьмет адрес возвращаемого массива и добавит 0 байтов, чтобы добраться до нулевого элемента (это нормально), но для _iob[1] он выведет, что ему нужно добавить 32 байта и для _iob[2] он выведет, что ему нужно добавить 64-байт (потому что так это выглядело в заголовках VS2008). И действительно, разобранный код для VS2008 демонстрирует это.

вторичной проблемой с вышеуказанным решением является то, что это копии содержимое файловой структуры (*stdin), а не дескриптор FILE*. Таким образом, любой код VS2008 будет смотреть на другую базовую структуру VS2015. Это может сработать, если структура содержит только указатели, но это большой риск. В любом случае первый вопрос делает это неактуально.

единственный хак, который я смог придумать, - это тот, в котором __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,%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;
}

Я не знаю, почему, но:

#ifdef main
#undef main
#endif

после включения, но прежде чем ваш основной должен исправить это из моего опыта.


более недавнее решение этой проблемы: используйте более поздние SDL-библиотеки на

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

они, похоже, исправили проблему, хотя это только 32-битная библиотека (я думаю).


связать означает не работать должным образом. Покопавшись в студию.h VS2012 и VS2015 для меня работали следующие. Увы, вы должны решить, должен ли он работать для одного из { 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 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-array не совпадает с массивом внутренних структур IOB Windows. @Volker прав, что он никогда не будет работать более чем для одного из stdin, stdout или stderr.

если библиотека действительно использует один из этих потоков, он будет crash. Пока ваша программа не заставляет lib использовать их,вы никогда не узнаете. Например, png_default_error пишет stderr когда CRC не соответствует метаданным PNG. (Обычно не проблема, достойная аварии)

вывод: невозможно смешать VS2012 (набор инструментов платформы v110/v110_xp) и VS2015+ библиотеки, если они используют stdin, stdout и/или stderr.

решение: Перекомпилируйте свои библиотеки, которые имеют __iob_func неразрешенные символы с вашим текущая версия vs и соответствующий набор инструментов.


используйте предварительно скомпилированный SDL2main.lib и SDL.lib для библиотеки вашего проекта VS2015 : https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225.zip


мне удалось решить эту проблему.

источником ошибки была эта строка кода, которую можно найти в исходном коде SDLmain.

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

Так что я сделал, чтобы изменить исходный код в SDLmain этой строки:

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

а затем я построил SDLmain и скопировал и заменил старый SDLmain.lib в моем каталоге библиотеки SDL2 с недавно построенным и отредактированным.

затем, когда я запустил свою программу с SDL2, никаких сообщений об ошибках не появилось и код прошел гладко.

Я не знаю, укусит ли это меня позже, но так как все идет отлично.


Это может произойти, когда вы ссылаетесь на 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)
#endif

этот фрагмент не нужен для Visual Studio 6 и его компилятора. Поэтому #ifdef.