2015-05-23 4 views
76

Может кто-нибудь объяснить, чтонеразрешенный внешний символ __imp__fprintf и __imp____iob_func, SDL2

__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:\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, и я убедился, что каталоги VC++ верны.

+0

Не могли бы вы проигнорировать это, указав параметры компоновщика, пожалуйста. –

+0

@ πάνταῥεῖ, я связался с SDL2.lib и SDL2main.lib в настройках компоновщика ввода, и я убедился, что каталоги указывают на правильный каталог. – RockFrenzy

+1

Возможный дубликат [ошибка LNK2001 \ _ \ _ imp \ _fprintf Visual Studio 2015 RC] (http://stackoverflow.com/questions/30366552/error-lnk2001-imp-fprintf-visual-studio-2015-rc) – dewaffled

ответ

78

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

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

#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 в параметры линкера.

+1

Спасибо. Нажатие этого в отделении интенсивной терапии: http://bugs.icu-project.org/trac/ticket/11798 –

+0

Спасибо за отслеживание этого. IIRC проблема с {* stdin, * stdout, * stderr} может заключаться в том, что разные единицы компиляции могут иметь свою «собственную» копию stdin, поэтому эти функции вызывались напрямую. –

+0

Фактически '{* stdin, * stdout, * stderr}' может работать. –

0

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

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

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

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

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

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

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

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

+0

Ваши изменения является ошибкой сама по себе и не устранит проблему, описанную в вашем вопросе. Совсем случайно вы больше не получаете ошибки компоновщика, что, вероятно, является исключительно результатом того, как вы перестроили библиотеку. –

+0

@RossRidge, о да, возможно, это было так. Ах хорошо. – RockFrenzy

26

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

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

BTW, для построения SDL 2.0.3 требуется установить установленный в июне 2010 DirectX SDK. – Joe

+1

Работал для меня, спасибо! Но мне нужно было только скомпилировать 'SDL2main' и скопировать поверх' SDL2main.lib' – kgwong

37

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

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

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

Просто отсутствует #ifdef для MSVC и для версии MSVC <2015 – paulm

+0

Как отмечает MarkH в другом ответе, который выглядит корректно, но не работает. –

+1

@paulm Я думаю, вы имеете в виду '#if defined (_MSC_VER) && (_MSC_VER> = 1900)'. –

0

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

18

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

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

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

Первое определение VS2008 FILE:

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

А теперь определение VS2015 FILE:

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. Вместо этого вызывающий код ожидает, что 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; 
} 
+0

Правильный ответ - это самое простое исправление, как указано в обновлении проекта до VS2015, а затем компиляции. – Akumaburn

+1

В моем случае мне нужно обновить многие проекты (проекты на C++ и C#) из visual studio 2013, чтобы использовать обновление Visual Studio 2015 3. Я хочу сохранить VC100 (Visual Studio 2010 C++-компилятор) при создании проектов на C++, но у меня есть то же самое как указано выше. Я исправил ** _ imp__fprintf **, добавив ** legacy_stdio_definitions.lib ** к компоновщику. Как я могу исправить также ** _ imp ____ iob_func **? –

+0

Прежде чем ответить на мой предыдущий вопрос, нормально ли, что эти ошибки возникают при использовании msbuild 14 и IntelCompiler 2016 и VC100 для компиляции проектов на C++? –

5

Ссылка не подходит. Копаясь в 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); 
} 
26

Microsoft имеет особое внимание на этом (https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT):

Printf и зсапЕ семейство функций теперь определяются рядный.

Определения всех 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_ *. Корпорация Майкрософт рекомендует перекомпилировать все статические библиотеки с последней версией компилятора и библиотек Visual C++ при обновлении проекта. Если библиотека является сторонней библиотекой, источник которой недоступен, вы должны либо запросить обновленный бинарный файл от третьей стороны, либо инкапсулировать использование этой библиотеки в отдельную DLL, которую вы компилируете с более старой версией компилятора Visual C++ и библиотеки.

+5

Или '#pragma comment (lib," legacy_stdio_definitions.lib ")' - но это не исправляет '__imp ___ iob_func' - есть ли унаследованная lib для этого? – bytecode77

7

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

#ifdef main 
#undef main 
#endif 

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

+0

.... Хорошо ....поэтому перед тем, как попробовать это, я слышу, что я как-то сомневаюсь, что это будет работать, но это полностью ... вы можете объяснить, почему это работает ...? –

+1

@TrevorHart I _believe_, что он отменяет «ошибочные» основные SDL ссылки на «неопределенные» буферы, и если ваш использует ваши буферы, тогда он работает хорошо и хорошо. –

+1

Это ужасно плохой хак, но это 3 линии, и это работает, и это спасло меня от необходимости раббитола в создании SDL, так что ... красиво сделано. – Cheezmeister

3

Мой совет - не пытаться реализовать __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, stdout или stderr.

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^