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



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

_ _ imp__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 в Linker - > Input - > дополнительные зависимости, и я убедился, что каталоги VC++ верны.

12   90  

12 ответа:

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

в Visual Studio 2015, в стандартный ввод, стандартный вывод, стандартный вывод определяется следующим образом :

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

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

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

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

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.Либ.

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

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

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

  1. перейти к http://libsdl.org/download-2.0.php и скачать SDL 2 исходный код.
  2. открыть SDL_VS2013.sln in 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 байта и добавить 0 байтов. для _iob[2] он выведет, что ему нужно добавить 64 байта (потому что так это выглядело в заголовках VS2008). И действительно, разобранный код для VS2008 демонстрирует это.

вторичная проблема с вышеуказанным решением заключается в том, что он копии содержимое файловой структуры (*stdin), а не дескриптор файла*. Таким образом, любой код 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.

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

вывод: невозможно смешать библиотеки VS2012 (Platform Toolset 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.