Неразрешенный внешний символ __imp__fprintf и __imp____iob_func, SDL2

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

__ imp__fprintf

и

__ имп ____ 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:\Documents\Visual Studio 2015\Projects\SDL2_Test\Debug\SDL2_Test.exe : fatal error LNK1120: 2 unresolved externals

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

Я пытаюсь использовать SDL2.

Я использую Visual Studio 2015 в качестве компилятора.

Я связался с SDL2.lib и SDL2main.lib в Linker → Input → Additional Dependencies, и я убедился, что Версии VС++ верны.

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

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

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

Ответ 2

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

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. Если возможно, вы должны обновить код, включив в него заголовки CRT (то есть добавить #include) и встроенные функции, но если вы не хотите изменять свой код для включения этих заголовочных файлов, альтернативным решением является добавление дополнительного библиотека для ввода вашего компоновщика, legacy_stdio_definitions.lib.

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

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

Ответ 4

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

  • Перейдите в http://libsdl.org/download-2.0.php и загрузите исходный код SDL 2.
  • Откройте SDL_VS2013.sln в VS2015. Вам будет предложено преобразовать проекты. Сделайте это.
  • Скомпилируйте проект SDL2.
  • Скомпилируйте проект SDL2main.
  • Используйте новые сгенерированные выходные файлы SDL2main.lib, SDL2.lib и SDL2.dll в проекте SDL 2 в VS2015.

Ответ 5

Как уже говорилось выше, правильным ответом является компиляция всего с 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])

Таким образом, мы можем видеть, что задание функции состоит в том, чтобы вернуть начало массива объектов FILE (не обрабатывает, "FILE *" - это дескриптор, FILE - это базовая непрозрачная структура данных, хранящая важные преимущества состояния), Пользователями этой функции являются три макроса 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:

  • Должен быть массив из трех структур FILE, которые могут быть проиндексированы таким же образом, как и раньше.
  • Структурная компоновка FILE не может измениться.

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

Посмотрим, как FILE был/определен.

Сначала определение FILE VS2008:

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

И теперь определение 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. Это может сработать, если структура содержит только указатели, но это большой риск. В любом случае первый вопрос делает это несущественным.

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

Ответ 6

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

#ifdef main
#undef main
#endif

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

Ответ 7

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

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

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

Ответ 8

Ссылка означает, что она не работает должным образом. Копаясь в stdio.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);
}

Ответ 9

Мой совет - не пытаться реализовать __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-массива не совпадает с массивом внутренних структур IOB для Windows. @Volker прав, что он никогда не будет работать более чем на одном из stdin, stdout или stderr.

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

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

__iob_func ваших библиотек с __iob_func неразрешенными символами с текущей версией VS и соответствующим набором инструментов платформы.

Ответ 11

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

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

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

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

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

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

Тогда, когда я запускал свою программу с 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)
#endif

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

Ответ 13

Чтобы внести еще большую путаницу в этот и без того богатый поток, я случайно натолкнулся на тот же неразрешенный внешний элемент на 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)... но на всякий случай, если оно служит другим, я помещаю это туда.

Ответ 14

В моем случае эта ошибка возникает из-за моей попытки удалить зависимости от DLL-библиотеки, зависящей от версии MSVC (msvcr10.dll или около того), и/или удалить статическую библиотеку времени выполнения, чтобы удалить лишний жир из моих исполняемых файлов.

Поэтому я использую переключатель компоновщика /NODEFAULTLIB, мои собственные записи "msvcrt-light.lib" (Google для них, когда вам нужно) и записи mainCRTStartup()/WinMainCRTStartup().

Это IMHO начиная с Visual Studio 2015, поэтому я остановился на старых компиляторах.

Однако определение символа _NO_CRT_STDIO_INLINE устраняет все хлопоты, и простое приложение "Hello World" снова имеет размер 3 КБ и не зависит от необычных библиотек DLL. Протестировано в Visual Studio 2017.

Ответ 15

Я решаю эту проблему с помощью следующей функции. Я использую Visual Studio 2019.

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

Поскольку вызов функции, определяемой макросом stdin, выражение "* stdin" не может использоваться инициализатором глобального массива. Но инициализатор локального массива возможен. извини, я плохо знаю английский.

Ответ 16

Для тех, кто все еще ищет ответ, где вышеперечисленные трюки не сработали. Статическое связывание является способом решения этой проблемы. Измените настройки библиотеки времени выполнения, как показано ниже

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