web-dev-qa-db-fra.com

Symbole externe non résolu __imp__fprintf et __imp____iob_func, SDL2

Quelqu'un pourrait-il expliquer ce que le

__imp__fprintf

et

__imp____iob_func

moyens externes non résolus?

Parce que je reçois ces erreurs lorsque j'essaie de compiler:

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

Je peux déjà dire que le problème ne vient pas d'un mauvais lien. J'ai tout lié correctement, mais pour une raison quelconque, il ne sera pas compilé.

J'essaie d'utiliser SDL2.

J'utilise Visual Studio 2015 en tant que compilateur.

J'ai lié à SDL2.lib et SDL2main.lib dans l'éditeur de liens -> Entrée -> Dépendances supplémentaires et je me suis assuré que les répertoires VC++ étaient corrects.

101
RockFrenzy

J'ai enfin compris pourquoi cela se produit!

Dans Visual Studio 2015, stdin, stderr, stdout sont définis comme suit:

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

Mais auparavant, ils étaient définis comme suit:

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

Donc, maintenant, __iob_func n'est plus défini, ce qui entraîne une erreur de lien lorsque vous utilisez un fichier .lib compilé avec les versions précédentes de visual studio.

Pour résoudre le problème, vous pouvez essayer de définir vous-même __iob_func(), ce qui devrait renvoyer un tableau contenant {*stdin,*stdout,*stderr}.

En ce qui concerne les autres erreurs de lien concernant les fonctions stdio (dans mon cas, c’était sprintf()), vous pouvez ajouter legacy_stdio_definitions.lib à vos options d’éditeur de liens.

111
god

Pour Milan Babuškov, de l’OMI, c’est exactement à quoi devrait ressembler la fonction de remplacement :-)

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

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

Microsoft a une note spéciale à ce sujet ( https://msdn.Microsoft.com/en-us/library/bb531344.aspx#BK_CRT ):

Les familles de fonctions printf et scanf sont maintenant définies en ligne.

Les définitions de toutes les fonctions printf et scanf ont été déplacées en ligne dans stdio.h , conio.h , et d'autres en-têtes CRT. Il s’agit d’un changement décisif qui entraîne une erreur de l'éditeur de liens (LNK2019, symbole externe non résolu) pour tous les programmes ayant déclaré ces fonctions localement sans inclure les en-têtes CRT appropriés. Si possible, vous devez mettre à jour le code pour inclure les en-têtes CRT (c'est-à-dire, ajouter #include) et les fonctions en ligne, mais si vous ne souhaitez pas modifier votre code pour inclure ces fichiers d'en-tête, une autre solution consiste à ajouter un élément supplémentaire. bibliothèque à votre entrée de l'éditeur de liens, legacy_stdio_definitions.lib .

Pour ajouter cette bibliothèque à votre entrée de l'éditeur de liens dans l'EDI, ouvrez le menu contextuel du nœud du projet, choisissez Propriétés, puis dans la boîte de dialogue Propriétés du projet, choisissez l'éditeur de liens et modifiez l'entrée de l'éditeur de liens pour ajouter legacy_stdio_definitions.lib au point-virgule. liste séparée.

Si votre projet est lié à des bibliothèques statiques compilées avec une version de Visual C++ antérieure à 2015, l'éditeur de liens peut signaler un symbole externe non résolu. Ces erreurs peuvent faire référence à des définitions stdio internes pour _ iob , _ iob_func ou connexes. importations pour certaines fonctions stdio sous la forme de __ imp _ * . Microsoft vous recommande de recompiler toutes les bibliothèques statiques avec la dernière version du compilateur Visual C++ et les bibliothèques lorsque vous mettez à niveau un projet. Si la bibliothèque est une bibliothèque tierce pour laquelle la source n'est pas disponible, vous devez demander un binaire mis à jour au tiers ou encapsuler votre utilisation de cette bibliothèque dans un DLL séparé que vous compilez avec l'ancien. version du compilateur Visual C++ et des bibliothèques.

37
kingsb

J'ai eu le même problème dans VS2015. Je l'ai résolu en compilant les sources SDL2 dans VS2015.

  1. Allez à http://libsdl.org/download-2.0.php et téléchargez le code source SDL 2.
  2. Ouvrez SDL_VS2013.sln dans VS2015 . Vous serez invité à convertir les projets. Fais le.
  3. Compiler le projet SDL2.
  4. Compiler le projet SDL2main.
  5. Utilisez les nouveaux fichiers de sortie générés SDL2main.lib, SDL2.lib et SDL2.dll dans votre projet SDL 2 dans VS2015.
27
user5157912

Comme indiqué ci-dessus, la bonne solution consiste à tout compiler avec VS2015, mais voici mon analyse du problème qui intéresse.

Ce symbole ne semble pas être défini dans les bibliothèques statiques fournies par Microsoft dans le cadre de VS2015, ce qui est assez particulier puisque tous les autres le sont. Pour découvrir pourquoi, nous devons examiner la déclaration de cette fonction et, plus important encore, son utilisation.

Voici un extrait des en-têtes de Visual Studio 2008:

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

Ainsi, nous pouvons voir que le travail de la fonction est de renvoyer le début d'un tableau d'objets FILE (pas de descripteurs, le "FILE *" est le descripteur, FILE est la structure de données opaque sous-jacente stockant les goodies d'état importants). Les utilisateurs de cette fonction sont les trois macros stdin, stdout et stderr, utilisées pour divers appels de style fscanf, fprintf.

Voyons maintenant comment Visual Studio 2015 définit les mêmes choses:

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

L'approche a donc changé pour que la fonction de remplacement renvoie maintenant le descripteur de fichier plutôt que l'adresse du tableau d'objets de fichier, et les macros ont été modifiées pour appeler simplement la fonction transmettant un numéro d'identification.

Alors, pourquoi ne peuvent-ils pas/nous fournissons une API compatible? Il y a deux règles clés que Microsoft ne peut pas enfreindre en termes de leur implémentation d'origine via __iob_func:

  1. Il doit exister un tableau de trois structures FILE pouvant être indexé de la même manière qu'auparavant.
  2. La structure de FILE ne peut pas changer.

Toute modification apportée à l’un des éléments ci-dessus signifierait que le code compilé existant lié à ce dernier irait très mal si cette API est appelée.

Voyons comment FILE était/est défini.

Tout d’abord la définition du fichier VS2008:

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

Et maintenant, la définition du fichier VS2015:

typedef struct _iobuf
{
    void* _Placeholder;
} FILE;

Donc, il y a l'essentiel: la structure a changé de forme. Le code compilé existant faisant référence à __iob_func repose sur le fait que les données renvoyées sont à la fois un tableau pouvant être indexé et que, dans ce tableau, les éléments sont à la même distance.

Les solutions possibles mentionnées dans les réponses ci-dessus dans ce sens ne fonctionneraient pas (si elles étaient appelées) pour plusieurs raisons:

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

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

Le tableau FILE _iob serait compilé avec VS2015 et serait donc présenté sous forme de bloc de structures contenant un vide *. En supposant un alignement sur 32 bits, ces éléments seraient séparés de 4 octets. Donc, _iob [0] est à l'offset 0, _iob [1] à l'offset 4 et _iob [2] à l'offset 8. Le code appelant s'attendra à ce que FILE soit beaucoup plus long, aligné à 32 octets sur mon système, etc. il faudra l'adresse du tableau retourné et ajouter 0 octet pour atteindre l'élément zéro (celui-ci est correct), mais pour _iob [1], il en déduira qu'il doit ajouter 32 octets et pour _iob [2], il en déduira qu’il doit ajouter 64 octets (car c’est ce qui se présentait dans les en-têtes de VS2008). Et en effet, le code désassemblé de VS2008 le montre bien.

Un problème secondaire avec la solution ci-dessus est qu'il copie le contenu de la structure FILE (* stdin), pas le descripteur FILE *. Ainsi, tout code VS2008 consisterait en une structure sous-jacente différente de VS2015. Cela pourrait fonctionner si la structure ne contenait que des pointeurs, mais c'est un gros risque. En tout état de cause, le premier problème rend cette question non pertinente.

Le seul piratage que j'ai pu imaginer est celui dans lequel __iob_func guide la pile d'appels pour déterminer le descripteur de fichier recherché (sur la base du décalage ajouté à l'adresse renvoyée) et renvoie une valeur calculée telle qu'il donne la bonne réponse. C'est tout aussi fou que cela puisse paraître, mais le prototype pour x86 seulement (pas x64) est listé ci-dessous pour votre divertissement. Cela a bien fonctionné dans mes expériences, mais votre kilométrage peut varier - déconseillé pour une utilisation en production!

#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;
}
24
MarkH

Je ne sais pas pourquoi mais:

#ifdef main
#undef main
#endif

Après l'inclusion mais avant votre principale devrait le réparer de mon expérience.

8
The XGood

Une solution plus récente à ce problème: utilisez les bibliothèques sdl plus récentes sur

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

Ils semblent avoir résolu le problème, même s'il ne s'agit que de la bibliothèque 32 bits (je pense).

7

Lier signifie ne pas fonctionner correctement. Creuser dans stdio.h de VS2012 et VS2015 a fonctionné comme suit: Hélas, vous devez décider si cela devrait fonctionner pour l'un des {stdin, stdout, stderr}, jamais plus d'un.

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

Mon conseil est de ne pas (essayer de) implémenter __iob_func.

En corrigeant ces erreurs:

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

J'ai essayé les solutions des autres réponses, mais finalement, renvoyer un C-array FILE* ne correspond pas à un tableau de structures IOB internes de Windows. @Volker a raison de dire que cela ne fonctionnera jamais avec plus d'un de stdin, stdout ou stderr.

Si une bibliothèque utilise réellement l'un de ces flux, il plantera. Tant que votre programme ne fait pas en sorte que la bibliothèque les utilise, vous ne saurez jamais. Par exemple, png_default_error écrit dans stderr lorsque le CRC ne correspond pas dans les métadonnées du PNG. (Normalement, pas un problème digne d'un crash)

Conclusion: Il n'est pas possible de mélanger les bibliothèques VS2012 (Platform Toolset v110/v110_xp) et VS2015 + si elles utilisent stdin, stdout et/ou stderr.

Solution: recompilez vos bibliothèques comportant __iob_func symboles non résolus avec votre version actuelle de VS et un ensemble d'outils de plateforme correspondant.

6
Luc Bloom

Utilisez les fichiers SDL2main.lib et SDL.lib précompilés pour la bibliothèque de votre projet VS2015: https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225.Zip

1
SolutionIsComing

Je résous ce problème avec la fonction suivante. J'utilise Visual Studio 2019.

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

parce que l'appel de la fonction définie par la macro stdin, l'expression "* stdin" est impossible avec l'initialiseur de tableau global. Mais l'initialisation locale du tableau est possible. désolé, je suis pauvre en anglais.

1
soen.kr

J'ai réussi à résoudre le problème.

La source de l'erreur était cette ligne de code, que l'on peut trouver dans le code source SDLmain.

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

Donc, ce que j’ai fait, c’est aussi de modifier le code source dans SDLmain de cette ligne:

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

Et puis, j'ai construit SDLmain, puis copié et remplacé l'ancien SDLmain.lib dans le répertoire de ma bibliothèque SDL2 par le nouveau construit et modifié.

Ensuite, lorsque j'ai exécuté mon programme avec SDL2, aucun message d'erreur n'est apparu et le code s'est déroulé correctement.

Je ne sais pas si cela va me mordre plus tard, mais pour tout va bien.

0
RockFrenzy

Pour tous ceux qui cherchent encore une réponse où les astuces ci-dessus ne fonctionnent pas. La liaison statique est le moyen de résoudre ce problème. Modifiez les paramètres de votre bibliothèque d'exécution comme indiqué ci-dessous.

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

0
Sisir

Cela peut arriver lorsque vous créez un lien vers msvcrt.dll au lieu de msvcr10.dll (ou similaire), ce qui est un bon plan. Cela vous permettra de redistribuer la bibliothèque d'exécution de Visual Studio dans votre package logiciel final.

Cette solution de contournement m'aide (dans 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

Cet extrait n'est pas nécessaire pour Visual Studio 6 et son compilateur. Par conséquent le #ifdef.

0
user2699548

Dans mon cas, cette erreur provient de mon essai visant à supprimer les dépendances de la bibliothèque d'exécution dépendante de la version MSVC DLL (msvcr10.dll ou autre) et/ou à supprimer également la bibliothèque d'exécution statique, afin de supprimer l'excès de graisse de mes exécutables. .

J'utilise donc le commutateur/NODEFAULTLIB de l'éditeur de liens, mes propres entrées "msvcrt-light.lib" (google lorsque vous en avez besoin) et mainCRTStartup()/WinMainCRTStartup().

C'est à mon humble avis depuis Visual Studio 2015, donc je suis resté sur les anciens compilateurs.

Cependant, définir le symbole _ NO_CRT_STDIO_INLINE supprime tous les tracas et une simple application "Hello World" a à nouveau une taille de 3 Ko et ne dépend pas de DLL inhabituelles. Testé dans Visual Studio 2017.

0
Henrik Haftmann

Pour apporter plus de confusion dans ce fil déjà riche, il m'est arrivé de tomber sur le même externe non résolu sur fprintf

main.obj : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _GenerateInfoFile

Même si dans mon cas, le contexte était assez différent: sous Visual Studio 2005 (Visual Studio 8.0) et que l'erreur se produisait dans mon propre code (le même que je compilais), pas un tiers.

Il est arrivé que cette erreur soit déclenchée par l'option/MD dans les drapeaux du compilateur. Le passage à/MT a résolu le problème. C'est bizarre parce qu'habituellement, lier statiquement (MT) pose plus de problème que dynamiquement (MD) ... mais juste au cas où il en servirait un autre, je l'ai mis là.

0
Zzirconium