símbolo externo não resolvido __imp__fprintf e __imp____iob_func, SDL2

Alguém poderia explicar o que o

__imp__fprintf

e

__imp____iob_func

meios externos não resolvidos?

Porque eu recebo esses erros quando estou tentando compilar:

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 

Eu já posso dizer que o problema não é ligar errado. Eu vinculei tudo corretamente, mas por algum motivo não será compilado.

Estou tentando usar o SDL2.

Estou usando o Visual Studio 2015 como compilador.

Eu vinculei a SDL2.lib e SDL2main.lib em Linker -> Input -> Dependências Adicionais e verifiquei se os Diretórios do VC ++ estão corretos.

Eu finalmente descobri porque isso está acontecendo!

No visual studio 2015, stdin, stderr, stdout são definidos como segue:

 #define stdin (__acrt_iob_func(0)) #define stdout (__acrt_iob_func(1)) #define stderr (__acrt_iob_func(2)) 

Mas anteriormente, eles foram definidos como:

 #define stdin (&__iob_func()[0]) #define stdout (&__iob_func()[1]) #define stderr (&__iob_func()[2]) 

Então agora __iob_func não está mais definido, o que leva a um erro de link ao usar um arquivo .lib compilado com versões anteriores do visual studio.

Para resolver o problema, você pode tentar definir __iob_func() você mesmo que deve retornar um array contendo {*stdin,*stdout,*stderr} .

Quanto aos outros erros de link sobre as funções do stdio (no meu caso foi sprintf() ), você pode adicionar legacy_stdio_definitions.lib às suas opções de linker.

Para Milan Babuškov, IMO, isso é exatamente o que a function de substituição deve ser 🙂

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

A Microsoft tem uma nota especial sobre isso ( https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT ):

A família de funções printf e scanf agora é definida em linha.

As definições de todas as funções printf e scanf foram movidas em linha para stdio.h , conio.h e outros headers CRT. Essa é uma alteração importante que leva a um erro de vinculador (LNK2019, símbolo externo não resolvido) para todos os programas que declararam essas funções localmente sem include os headers de CRT apropriados. Se possível, você deve atualizar o código para include os headers CRT (ou seja, adicionar #include) e as funções embutidas, mas se você não quiser modificar seu código para include esses arquivos de header, uma solução alternativa é adicionar um adicional biblioteca para sua input do vinculador, legacy_stdio_definitions.lib .

Para adicionar essa biblioteca à sua input do vinculador no IDE, abra o menu de contexto do nó do projeto, escolha Propriedades e, na checkbox de diálogo Propriedades do projeto, escolha Vinculador e edite a Entrada vinculador para adicionar legacy_stdio_definitions.lib ao ponto-e-vírgula lista separada.

Se seu projeto vincula com bibliotecas estáticas que foram compiladas com uma versão do Visual C ++ anteriores a 2015, o vinculador pode relatar um símbolo externo não resolvido. Esses erros podem referenciar definições internas do stdio para _iob , _iob_func ou importações relacionadas para determinadas funções do stdio na forma de __imp_ * . A Microsoft recomenda que você recompilar todas as bibliotecas estáticas com a versão mais recente do compilador do Visual C ++ e bibliotecas quando você atualiza um projeto. Se a biblioteca é uma biblioteca de terceiros para qual fonte não está disponível, você deve solicitar um binário atualizado do terceiro ou encapsular seu uso dessa biblioteca em uma DLL separada que você compilar com a versão mais antiga do compilador do Visual C ++ e bibliotecas.

Eu tive o mesmo problema no VS2015. Eu o resolvi compilando as fonts SDL2 no VS2015.

  1. Vá para http://libsdl.org/download-2.0.php e faça o download do código fonte do SDL 2.
  2. Abra o SDL_VS2013.sln no VS2015 . Você será solicitado a converter os projetos. Faça.
  3. Compile o projeto SDL2.
  4. Compile o projeto SDL2main.
  5. Use os novos arquivos de saída gerados SDL2main.lib, SDL2.lib e SDL2.dll em seu projeto SDL 2 no VS2015.

Como respondido acima, a resposta correta é compilar tudo com o VS2015, mas, para fins de interesse, a seguinte é a minha análise do problema.

Esse símbolo não parece estar definido em nenhuma biblioteca estática fornecida pela Microsoft como parte do VS2015, o que é bastante peculiar, já que todos os outros são. Para descobrir por que, precisamos observar a declaração dessa function e, mais importante, como ela é usada.

Aqui está um trecho dos headers do Visual Studio 2008:

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

Assim, podemos ver que o trabalho da function é retornar o início de uma matriz de objects FILE (não manipuladores, o “FILE *” é o identificador, FILE é a estrutura de dados subjacente opaca que armazena os benefícios importantes do estado). Os usuários desta function são as três macros stdin, stdout e stderr que são usadas para várias chamadas de estilo fscanf, fprintf.

Agora vamos dar uma olhada em como o Visual Studio 2015 define as mesmas coisas:

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

Portanto, a abordagem foi alterada para a function de substituição retornar agora o identificador de arquivo em vez do endereço do array de objects de arquivo, e as macros foram alteradas para simplesmente chamar a function que passa em um número de identificação.

Então, por que eles não podem / nós fornecemos uma API compatível? Existem duas regras-chave que a Microsoft não pode violar em termos de sua implementação original via __iob_func:

  1. Deve haver uma matriz de três estruturas FILE que podem ser indexadas da mesma maneira que antes.
  2. O layout estrutural do FILE não pode mudar.

Qualquer alteração em qualquer um dos itens acima significaria que o código compilado existente vinculado seria muito mal se essa API fosse chamada.

Vamos dar uma olhada em como o FILE foi / está definido.

Primeiro a definição do arquivo VS2008:

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

E agora a definição do arquivo VS2015:

 typedef struct _iobuf { void* _Placeholder; } FILE; 

Portanto, há o ponto crucial: a estrutura mudou de forma. O código compilado existente que se refere a __iob_func depende do fato de que os dados retornados são ambos uma matriz que pode ser indexada e que, nessa matriz, os elementos estão à mesma distância.

As soluções possíveis mencionadas nas respostas acima ao longo destas linhas não funcionariam (se chamadas) por algumas razões:

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

O array _iob FILE seria compilado com o VS2015 e assim seria disposto como um bloco de estruturas contendo um void *. Assumindo o alinhamento de 32 bits, esses elementos teriam 4 bytes de diferença. Então _iob [0] está no offset 0, _iob [1] está no offset 4 e _iob [2] está no offset 8. O código de chamada irá esperar FILE ser muito mais longo, alinhado em 32 bytes no meu sistema, e assim ele pegará o endereço da matriz retornada e adicionará 0 bytes para chegar ao elemento zero (que está tudo bem), mas para _iob [1] ele deduzirá que ele precisa adicionar 32 bytes e para _iob [2] deduzirá que precisa adicionar 64 bytes (porque era assim nos headers do VS2008). E, de fato, o código desmontado do VS2008 demonstra isso.

Um problema secundário com a solução acima é que ele copia o conteúdo da estrutura FILE (* stdin), não o identificador FILE *. Portanto, qualquer código do VS2008 estaria olhando para uma estrutura subjacente diferente do VS2015. Isso pode funcionar se a estrutura contiver apenas pointers, mas isso é um grande risco. Em qualquer caso, o primeiro problema torna isso irrelevante.

O único hack que eu fui capaz de inventar é aquele em que __iob_func percorre a pilha de chamadas para descobrir qual identificador de arquivo real está procurando (com base no deslocamento adicionado ao endereço retornado) e retorna um valor computado tal que dá a resposta certa. Isso é tão insano quanto parece, mas o protótipo para x86 (não x64) está listado abaixo para sua diversão. Funcionou bem nos meus experimentos, mas sua milhagem pode variar – não é recomendada para uso em produção!

 #include  #include  #include  /* #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; } 

Eu não sei porque, mas:

 #ifdef main #undef main #endif 

Após os includes, mas antes de seu principal deve corrigi-lo da minha experiência.

Uma solução mais recente para este problema: Use o sdl libs mais recente em

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

Eles parecem ter corrigido o problema, embora seja apenas a biblioteca de 32 bits (eu acho).

Ligar significa não funcionar corretamente. Cavando em stdio.h de VS2012 e VS2015 o seguinte funcionou para mim. Infelizmente, você tem que decidir se deve funcionar para um de {stdin, stdout, stderr}, nunca mais de um.

 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); } 

Meu conselho é não (tentar) implementar __iob_func.

Ao corrigir esses erros:

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

Eu tentei soluções de outras respostas, mas no final, retornando um FILE* C-array não corresponde a uma matriz de estruturas IOB internas do Windows. @Volker está certo de que nunca funcionará para mais de um dos stdin , stdout ou stderr .

Se uma biblioteca realmente usa um desses streams, ela irá falhar . Contanto que seu programa não faça com que o lib os use, você nunca saberá . Por exemplo, png_default_error grava em stderr quando o CRC não corresponde aos metadados do PNG. (Normalmente não é um problema digno de um crash)

Conclusão: Não é possível misturar as bibliotecas VS2012 (Platform Toolset v110 / v110_xp) e VS2015 +, se elas usarem stdin, stdout e / ou stderr.

Solução: recompile suas bibliotecas que possuem símbolos __iob_func não resolvidos com sua versão atual do VS e um conjunto de ferramentas da plataforma correspondente.

Use o SDL2main.lib e o SDL.lib pré-compilados para a biblioteca do seu projeto VS2015: https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225.zip

Consegui consertar o problema.

A fonte do erro foi essa linha de código, que pode ser encontrada no código-fonte SDLmain.

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

Então o que eu fiz foi editar o código fonte no SDLmain dessa linha também:

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

E então eu construí o SDLmain e copiei e substituí o antigo SDLmain.lib no meu diretório de biblioteca SDL2 pelo recém construído e editado.

Então, quando eu executei meu programa com o SDL2, nenhuma mensagem de erro apareceu e o código foi executado sem problemas.

Eu não sei se isso vai me morder mais tarde, mas tudo está indo muito bem.

Isso pode acontecer quando você vincula a msvcrt.dll em vez de msvcr10.dll (ou similar), que é um bom plano. Porque ele irá liberar você para redistribuir a biblioteca de tempo de execução do seu Visual Studio dentro do seu pacote de software final.

Essa solução alternativa me ajuda (no 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 

Este trecho não é necessário para o Visual Studio 6 e seu compilador. Portanto, o #ifdef.