未解決の外部シンボル__imp__fprintfおよび__imp ____ iob_func、SDL2


108

誰かが何を説明できますか

__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にリンクし、VC ++ディレクトリが正しいことを確認しました。


1
リンカの設定を見せてください。
πάνταῥεῖ

@πάνταῥεῖ、入力リンカー設定でSDL2.libとSDL2main.libにリンクし、ディレクトリが正しいディレクトリを指していることを確認しました。
RockFrenzy

回答:


123

これがなぜ起こっているのか私はついに理解しました!

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

そのため、以前のバージョンのVisual Studioでコンパイルされた.libファイルを使用すると、__ iob_funcが定義されなくなり、リンクエラーが発生します。

この問題を解決するには、を__iob_func()含む配列を返す必要がある自分を定義してみてください{*stdin,*stdout,*stderr}

stdio関数に関する他のリンクエラー(私の場合はsprintf())に関しては、legacy_stdio_definitions.libをリンカーオプションに追加できます。


1
これを追跡してくれてありがとう。IIRC {* stdin、* stdout、* stderr}の問題は、異なるコンパイルユニットがstdinの「独自の」コピーを持っている可能性があるため、これらの関数が直接呼び出されました。
スティーブンR.ルーミス2015

3
それは私にとっても解決しましextern "C"た、宣言/定義で使用するためのリマインダーです。
Vargas

4
誰かが置換関数がどのように見えるべきかを正確に書くことができますか?さまざまなバリアントを試しましたが、コンパイルエラーが発生し続けます。ありがとう。
MilanBabuškov15年

55
extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; }
PoL0 2015年

1
上記のiob_func定義は機能しません。正しい定義については、MarkHの回答を参照してください。(関数を配列として定義して、呼び出しが機能することを期待することはできません。)
Hans Olsson

59

IMOのMilanBabuškovにとって、これはまさに置換関数が次のようになるはずです:-)

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

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

5
ただ、MSVCのためとMSVCのバージョン<2015年の#ifdef行方不明
paulm

1
MarkHが別の答えで指摘しているように、正しいように見えますが、機能しません。
Hans Olsson

4
@ paulm意味だと思う#if defined(_MSC_VER) && (_MSC_VER >= 1900)
ジェシーチザム2017

@JesseChisholmはおそらく、これがMSVCのすべての既知の将来のバージョンに適用されるかどうかに依存します;)
paulm

42

Microsoftはこれについて特別な注意を持っていますhttps://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT):

printfおよびscanf関数ファミリーがインラインで定義されるようになりました。

すべてのprintf関数とscanf関数の定義は、インラインでstdio.hconio.h、およびその他のCRTヘッダーに移動されました。これは、適切なCRTヘッダーを含めずにこれらの関数をローカルで宣言したプログラムのリンカーエラー(LNK2019、未解決の外部シンボル)につながる重大な変更です。可能であれば、コードを更新してCRTヘッダーを含める(つまり、#includeを追加する)とインライン関数を含める必要がありますが、これらのヘッダーファイルを含めるようにコードを変更したくない場合は、別の解決策として、リンカー入力へのライブラリ、legacy_stdio_definitions.lib

このライブラリをIDEのリンカー入力に追加するには、プロジェクトノードのコンテキストメニューを開き、[プロパティ]を選択して、[プロジェクトのプロパティ]ダイアログボックスで[リンカー]を選択し、リンカー入力を編集して、legacy_stdio_definitions.libをセミコロンに追加します。 -区切られたリスト。

プロジェクトが2015年より前のVisual C ++のリリースでコンパイルされた静的ライブラリとリンクしている場合、リンカーは未解決の外部シンボルを報告する可能性があります。これらのエラーは、_iob_iob_funcの内部stdio定義、または__imp_ *の形式の特定のstdio関数の関連インポートを参照する場合があります。プロジェクトをアップグレードするときは、すべての静的ライブラリをVisual C ++コンパイラとライブラリの最新バージョンで再コンパイルすることをお勧めします。ライブラリがソースが利用できないサードパーティのライブラリである場合は、サードパーティに更新されたバイナリを要求するか、そのライブラリの使用を、古いバージョンのVisual C ++コンパイラでコンパイルする別のDLLにカプセル化する必要があります。とライブラリ。


7
または#pragma comment(lib, "legacy_stdio_definitions.lib")-しかし、これは修正されません-これの__imp___iob_funcためのレガシーlibもありますか?
bytecode77 2017年

29

上記で答えたように、正しい答えはVS2015ですべてをコンパイルすることですが、興味のある方は、問題の私の分析を以下に示します。

このシンボルは、VS2015の一部としてMicrosoftが提供する静的ライブラリで定義されているようには見えません。理由を見つけるには、その関数の宣言を確認する必要があります。さらに重要なのは、その使用方法です。

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は重要な状態の良いものを格納する基になる不透明なデータ構造です)。この関数のユーザーは、さまざまなfscanf、fprintfスタイルの呼び出しに使用される3つのマクロstdin、stdout、およびstderrです。

次に、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を介した元の実装の点で違反できない2つの主要なルールがあります。

  1. 以前と同じ方法でインデックスを付けることができる3つのFILE構造の配列が必要です。
  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;
}

FILE配列_iobはVS2015でコンパイルされるため、void *を含む構造のブロックとして配置されます。32ビットアライメントと仮定すると、これらの要素は4バイト離れています。したがって、_iob [0]はオフセット0にあり、_iob [1]はオフセット4にあり、_iob [2]はオフセット8にあります。呼び出しコードは、FILEがはるかに長く、システム上で32バイトにアラインされることを期待します。返された配列のアドレスを取得して0バイトを追加し、要素0に到達します(これは問題ありません)が、_iob [1]の場合は32バイトを追加する必要があると推定され、_iob [2]の場合は推定されます64バイトを追加する必要があること(VS2008ヘッダーでこのように表示されていたため)。実際、VS2008の逆アセンブルされたコードはこれを示しています。

上記のソリューションの2番目の問題は、FILE *ハンドルではなく、FILE構造体(* 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;
}

正解はこれです。最も簡単な修正は、プロジェクトをVS2015にアップグレードしてからコンパイルすることです。
アクマバーン2017年

2
私の場合、Visual Studio 2015 Update 3を使用するには、Visual Studio 2013から多くのプロジェクト(C ++およびC#プロジェクト)をアップグレードする必要があります。C++プロジェクトをビルドするときにVC100(Visual Studio 2010 C ++コンパイラー)を保持したいが、同じエラーが発生上記のように。私は、固定された IMP _fprintfを追加することにより、legacy_stdio_definitions.libをリンカに。_imp____iob_funcもどのように修正できますか?
Mohamed BOUZIDI 2017

前の質問に答える前に、msbuild 14とIntelCompiler 2016およびVC100を使用してC ++プロジェクトをコンパイルすると、これらのエラーが発生するのは正常ですか?
Mohamed BOUZIDI 2017

1
x64コンパイルで何ができますか?
アトス2018年

28

VS2015でも同じ問題が発生しました。VS2015でSDL2ソースをコンパイルすることで解決しました。

  1. http://libsdl.org/download-2.0.phpに移動し、SDL 2ソースコードをダウンロードします。
  2. VS2015でSDL_VS2013.slnを開きます。プロジェクトを変換するよう求められます。やれ。
  3. SDL2プロジェクトをコンパイルします。
  4. SDL2mainプロジェクトをコンパイルします。
  5. VS2015のSDL 2プロジェクトで、新しく生成された出力ファイルSDL2main.lib、SDL2.lib、SDL2.dllを使用します。

4
ところで、SDL 2.0.3をビルドするには、2010年6月のDirectX SDKがインストールされている必要があります。
Joe

1
私のために働いた、ありがとう!! しかし、コンパイルSDL2mainしてコピーするだけで済みましたSDL2main.lib
kgwong

10

理由はわかりませんが:

#ifdef main
#undef main
#endif

インクルード後、メインの前に私の経験から修正する必要があります。


1
.....それで、これを試す前に、どういうわけかこれがうまくいくのではないかと自分自身に聞こえたのですが、完全にうまくいったとは思います。
Trevor Hart、

1
@TrevorHart 「未定義の」バッファへの「障害のある」SDLメインインクルード参照の定義を解除すると私は信じています。バッファが使用されている場合、すべて正常に機能します。
XGood

2
これはひどい悪い習慣のハックですが、3行でうまく機能し、SDLを構築するためにうさぎの穴を掘る必要がなくなりました。
Cheezmeister 2017

1
@Cheezmeister多くの場合、悪い習慣とハックがしばしば必要になります。特にそれらを必要とすべきではないもの。
XGood


7

リンクするとは、正しく機能しないことを意味します。VS2012とVS2015のstdio.hを掘り下げると、次のことがうまくいきました。悲しいかな、{stdin、stdout、stderr}の1つで機能するかどうかを決定する必要があります。

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

7

私のアドバイスは、__ 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は右のそれ以上のいずれかの仕事だろうということであるstdinstdoutまたはstderr

ライブラリが実際にこれらのストリームの1つを使用している場合、クラッシュします。プログラムがlibにそれらを使用させない限り、あなたは決して知ることはありません。たとえば、CRCがPNGのメタデータで一致しない場合にpng_default_error書き込みますstderr。(通常、クラッシュに耐える問題ではありません)

結論:stdin、stdout、stderrを使用する場合、VS2012(Platform Toolset v110 / v110_xp)とVS2015 +ライブラリを混在させることはできません。

解決策:__iob_func未解決のシンボルがあるライブラリを、VSの現在のバージョンと対応するプラットフォームツールセットで再コンパイルします。



2

以下の機能でこの問題を解決しました。Visual Studio 2019を使用しています。

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

stdinマクロ定義の関数呼び出しであるため、「* stdin」式はグローバル配列初期化子を使用できません。しかし、ローカル配列の初期化が可能です。すみません、私は英語が苦手です。


1

上記のトリックがうまくいかなかった答えをまだ探している人のために。静的リンクは、この問題を解決する方法です。ランタイムライブラリの設定を次のように変更します

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


ここでは、この解決策に関する議論がある:social.msdn.microsoft.com/Forums/vstudio/en-US/...
Sisir

0

私はなんとか問題を修正しました。

エラーの原因はこのコード行で、SDLmainソースコードにあります。

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

だから私はその行のSDLmainのソースコードを編集することもしました:

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

次に、SDLmainをビルドし、SDL2ライブラリディレクトリ内の古いSDLmain.libをコピーして、新しくビルドおよび編集したものに置き換えました。

その後、SDL2でプログラムを実行したところ、エラーメッセージは表示されず、コードはスムーズに実行されました。

これが後で私を噛むかどうかはわかりませんが、すべてが順調に進んでいます。


あなたの変更はそれ自体が間違いであり、あなたの質問に記述されている問題を修正することはできませんでした。リンカーエラーが発生しなくなったのは偶然にすぎません。これはおそらく、ライブラリの再構築方法の結果にすぎません。
ロス・リッジ

@RossRidge、そうそうそうだったかもしれない。まぁ。
RockFrenzy

0

これは、msvcr10.dll(または同様のもの)ではなくmsvcrt.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。


0

このすでにリッチなスレッドをさらに混乱させるために、私はたまたま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)よりも問題が発生するため、これは奇妙です。しかし、他のサービスを提供する場合に備えて、ここに配置します。


0

私の場合、このエラーは、MSVCバージョンに依存するランタイムライブラリDLL(msvcr10.dllなど)への依存関係を削除したり、静的ランタイムライブラリも削除したりして、実行可能ファイルから余分な脂肪を削除するための試用から発生します。

そこで、/ NODEFAULTLIBリンカースイッチ、自作の "msvcrt-light.lib"(必要に応じてGoogleで作成)、mainCRTStartup()/ WinMainCRTStartup()エントリを使用します。

Visual Studio 2015以降の私見なので、古いコンパイラーにこだわりました。

ただし、シンボル_NO_CRT_STDIO_INLINEを定義すると、すべての手間がなくなり、単純な「Hello World」アプリケーションも3 KB小さくなり、異常なDLLに依存しません。Visual Studio 2017でテスト済み。


-2

このコードを任意のソースファイルに貼り付けて、再ビルドします。私のために働いた!

#include stdio.h

ファイル_iob [3];

FILE * __cdecl __iob_func(void){

_iob [0] = * stdin;

_iob [0] = * stdout;

_iob [0] = * stderr;

_iobを返します。

}


`` `を使用した書式設定といくつかの説明を追加する必要があります
Antonin GAVREL

このコードは問題を解決する可能性がありますが、これが問題を解決する方法と理由の説明含めると、投稿の品質が向上し、おそらく投票数が増えることになります。あなたが今尋ねている人だけでなく、あなたが将来の読者のための質問に答えていることを思い出してください。回答を編集して説明を追加し、適用される制限と前提を示してください。
ブライアン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.