ライブラリがリンクされている順序によって GCC でエラーが発生することがあるのはなぜですか?
9 に答える
(より精巧なテキストを取得するには、この回答の履歴を参照してください。ただし、読者が実際のコマンドラインを見る方が簡単だと思います)。
以下のすべてのコマンドで共有される共通ファイル
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
静的ライブラリへのリンク
$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o
$ g++ -L. -ld -lb a.cpp # wrong order
$ g++ -L. -lb -ld a.cpp # wrong order
$ g++ a.cpp -L. -ld -lb # wrong order
$ g++ a.cpp -L. -lb -ld # right order
リンカは左から右に検索し、未解決のシンボルを記録します。ライブラリがシンボルを解決する場合、そのライブラリのオブジェクト ファイルを使用してシンボルを解決します (この場合は libb.a から bo を取り出します)。
スタティック ライブラリ同士の依存関係は同じように機能します。シンボルを必要とするライブラリを最初に配置し、次にシンボルを解決するライブラリを配置する必要があります。
静的ライブラリが別のライブラリに依存しているが、別のライブラリが以前のライブラリに再び依存している場合、サイクルが発生します。-(
これは、循環的に依存するライブラリをand-)
などで囲むことで解決できます( and-( -la -lb -)
などの括弧をエスケープする必要がある場合があります)。次に、リンカーはそれらの同封されたライブラリを複数回検索して、循環依存関係が解決されるようにします。または、ライブラリを複数回指定して、それぞれが互いの前にあるようにすることもできます: .-\(
-\)
-la -lb -la
動的ライブラリへのリンク
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency!
$ g++ -L. -lb a.cpp # wrong order (works on some distributions)
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order
$ g++ -Wl,--as-needed a.cpp -L. -lb # right order
ここでも同じです。ライブラリは、プログラムのオブジェクト ファイルに従う必要があります。ここでの静的ライブラリとの違いは、動的ライブラリは依存関係自体を整理するため、ライブラリ同士の依存関係を気にする必要がないことです。
最近のディストリビューションの中に--as-needed
は、プログラムのオブジェクト ファイルが動的ライブラリの前にあることを強制するリンカー フラグを使用するようにデフォルトで設定されているものがあります。そのフラグが渡された場合、リンカーは、実行可能ファイルが実際には必要としないライブラリーにリンクしません (これを左から右に検出します)。私の最近の archlinux ディストリビューションはデフォルトでこのフラグを使用していないため、正しい順序に従わなくてもエラーは発生しませんでした。
前者を作成するときに、b.so
に対する依存関係を省略するのは正しくありません。d.so
リンク時にライブラリを指定する必要がありますa
がa
、整数自体は実際には必要ないため、自身の依存関係b
を気にする必要はありません。b
の依存関係を指定しなかった場合の影響の例を次に示します。libb.so
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -o libb.so # wrong (but links)
$ g++ -L. -lb a.cpp # wrong, as above
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above
$ g++ a.cpp -L. -lb # wrong, missing libd.so
$ g++ a.cpp -L. -ld -lb # wrong order (works on some distributions)
$ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs)
$ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right"
バイナリが持つ依存関係を調べてみると、バイナリ自体が必要なlibd
だけでなく、にも依存libb
していることがわかります。libb
この方法で行う場合、後で別のライブラリに依存する場合は、バイナリを再リンクする必要があります。また、他の誰かが実行時にlibb
使用dlopen
してロードした場合 (プラグインを動的にロードすることを考えてください)、呼び出しも失敗します。したがって、はも同様"right"
である必要があります。wrong
GNU ld リンカーは、いわゆるスマート リンカーです。以前の静的ライブラリで使用されている関数を追跡し、使用されていない関数をルックアップ テーブルから永久に破棄します。その結果、静的ライブラリをリンクするのが早すぎると、そのライブラリ内の関数は、リンク行の後半で静的ライブラリで使用できなくなります。
典型的な UNIX リンカは左から右に動作するため、すべての依存ライブラリを左側に配置し、それらの依存関係を満たすライブラリをリンク行の右側に配置します。一部のライブラリが他のライブラリに依存していると同時に、他のライブラリがそれらに依存している場合があります。ここが複雑なところです。循環参照に関しては、コードを修正してください。
静的ライブラリが関係している場合に GCC がどのように機能するかを明確にするための例を次に示します。したがって、次のシナリオがあるとします。
myprog.o
-main()
依存する機能を含むlibmysqlclient
libmysqlclient
- 例のための静的 (もちろん、共有ライブラリlibmysqlclient
は巨大なので、共有ライブラリを好むでしょう); で/usr/local/lib
; からのものに依存していますlibz
libz
(動的)
これをどのようにリンクしますか?(注: gcc 4.3.4 を使用して Cygwin でコンパイルした例)
gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'
# myprog depends on libmysqlclient
# so myprog has to come earlier on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# we have to link with libz, too
gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# libz is needed by libmysqlclient
# so it has to appear *after* it on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
私をつまずかせた簡単なヒント: リンカを "gcc" または "g++" として呼び出している場合、"--start-group" と "--end-group" を使用すると、これらのオプションがリンカ -- また、エラーのフラグも立てません。ライブラリの順序が間違っていると、未定義のシンボルでリンクが失敗します。
GCCに引数をリンカーに渡すように指示するには、「-Wl、--start-group」などと記述する必要があります。
-Xlinker オプションを使用できます。
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
ほぼ等しい
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
気をつけろ !
- グループ内の順序は重要です。例を次に示します。デバッグ ライブラリにはデバッグ ルーチンがありますが、非デバッグ ライブラリには同じものの弱いバージョンがあります。デバッグ ライブラリを最初にグループに配置する必要があります。そうしないと、非デバッグ バージョンに解決されます。
- グループ リスト内の各ライブラリの前に -Xlinker を付ける必要があります
私はこれをたくさん見ました、私たちのモジュールのいくつかは私たちのコードの100以上のライブラリとシステムとサードパーティのライブラリをリンクしています。
さまざまなリンカーHP/Intel / GCC / SUN / SGI / IBM / etcによっては、未解決の関数/変数などを取得する可能性があります。一部のプラットフォームでは、ライブラリを2回リストする必要があります。
ほとんどの場合、ライブラリ、コア、プラットフォーム、抽象化のさまざまなレイヤーの構造化された階層を使用しますが、一部のシステムでは、linkコマンドの順序を試す必要があります。
ソリューションドキュメントを見つけたら、次の開発者が再度解決する必要がないようにします。
私の昔の講師は、「高凝集度と低結合度」と言っていましたが、それは今日でも当てはまります。
少なくとも一部のプラットフォームでは、リンクの順序は確かに重要です。ライブラリに間違った順序でリンクされたアプリケーションのクラッシュを見てきました (間違った場合、A は B の前にリンクされているが、B は A に依存していることを意味します)。