403


236

ライブラリがリンクされている順序がGCCでエラーを引き起こすことがあるのはなぜですか?

10 Answer


509


〜(より複雑なテキストを得るためにこの答えの歴史を見なさい、しかし私は今読者が本当のコマンドラインを見ることがより簡単であると思う)〜

'' '' '

以下のすべてのコマンドで共有される共通ファイル

$ 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からb.o)。

静的ライブラリ同士の依存関係も同様に機能します。シンボルを必要とするライブラリが最初になり、次にシンボルを解決するライブラリが最初になります。

静的ライブラリが他のライブラリに依存していても、他のライブラリが以前のライブラリに依存している場合、サイクルが発生します。 循環的に依存するライブラリを ` - (-la -lb - )のように ` - (`と ` - )`で囲むことでこれを解決できます( ` - \(`や - \)) その後、リンカはそれらの囲まれたlibを複数回検索して、循環の依存関係が解決されるようにします。 あるいは、ライブラリを複数回指定することができ、それぞれがお互いの前にあります: `-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"

バイナリーがどのような依存関係を持っているのかを調べてみると、バイナリー自体が本来あるべき libb`ではなく libd`にも依存していることに気付くでしょう。 このようにすると、後で libb`が他のライブラリに依存するようになった場合には、バイナリを再リンクする必要があります。 そして他の誰かが実行時に `dlopen`を使って libb`をロードすると(動的にプラグインをロードすることを考えて)、呼び出しも失敗します。 ですから、「正しい」ということも、実際には「間違っている」ことになります。


92


GNU ldリンカは、いわゆるスマートリンカです。 前の静的ライブラリで使用されていた関数を追跡し、ルックアップテーブルから使用されていない関数を永続的に破棄します。 その結果、静的ライブラリを早くリンクしすぎると、そのライブラリ内の関数は、後でリンク行で静的ライブラリで使用できなくなります。

典型的なUNIXリンカは左から右へ動作するので、すべてのあなたの依存ライブラリを左側に置き、それらの依存関係を満たすものをリンク行の右側に置く。 あるライブラリは他のライブラリに依存していますが、同時に他のライブラリはそれらに依存しています。 これはそれが複雑になるところです。 循環参照に関しては、コードを修正してください。


49


これは* static *ライブラリが関係しているときに物事が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


24


リンカのフラグに `-Wl、 - start-group`を付け加えても、それらがどの順番になっていても、循環依存関係があっても構いません。

Qtではこれは以下を追加することを意味します。

QMAKE_LFLAGS += -Wl,--start-group

面倒な時間を節約し、リンクを遅くすることはありません(とにかくコンパイルよりもはるかに時間がかかりません)。


8


もう1つの方法は、ライブラリのリストを2回指定することです。

gcc prog.o libA.a libB.a libA.a libB.a -o prog.x

こうすることで、参照は2番目のブロックで解決されるので、正しい順序を気にする必要はありません。


5


-Xlinkerオプションを使うことができます。

g -o foobar -Xリンカ-start-group -XリンカlibA.a -XリンカlibB.a -XリンカlibC.a -Xリンカ-end-group

ALMOSTは等しい

g -o foobar -Xリンカ-start-group -XリンカlibC.a -XリンカlibB.a -XリンカlibA.a -Xリンカ-end-group

注意してください。

  1. グループ内の順番は重要です。 例を示します。デバッグライブラリにはデバッグルーチンがありますが、非デバッグライブラリには同じものの弱いバージョンがあります。 デバッグライブラリを最初にグループに入れる必要があります。そうしないと、デバッグ以外のバージョンに解決されます。

  2. グループリストの各ライブラリの前に-Xlinkerを付ける必要があります。


4


私はこれをたくさん見ました、私たちのモジュールのいくつかは私たちのコードとシステムの100のライブラリを超えてリンクします

異なるリンカーのHP / Intel / GCC / SUN / SGI / IBM / etcに依存して、未解決の関数/変数などを得ることができます、いくつかのプラットフォームでは、あなたは二度ライブラリをリストしなければなりません。

ほとんどの場合、ライブラリの構造化された階層、コア、プラットフォーム、さまざまな抽象化層を使用しますが、一部のシステムではlinkコマンドの順序で再生する必要があります。

ソリューション文書を見つけたら、次の開発者はそれをもう一度解決する必要はありません。

私の昔の講師は、「_高い結束力」と言っていました。


4


「gcc」または「g」としてリンカを起動している場合は、「 - start-group」および「--end-group」を使用してもこれらのオプションは使用できません。リンカ - また、エラーを報告しません。 ライブラリの順序が間違っていると、未定義のシンボルでリンクが失敗するだけです。

あなたはそれらを "-Wl、 - start-group"などと書く必要があります。 GCCに引数をリンカーに渡すように指示します。


3


リンクの順序は、少なくとも一部のプラットフォームでは確かに重要です。 ライブラリとリンクされているアプリケーションが間違った順序でクラッシュしています(間違ったのはAがBの前にリンクされているがBはAに依存している場合)。


0


それらのライブラリのいくつかは他のライブラリと依存関係があり、それらがまだリンクされていなければリンカエラーになるからだと思います。