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


449

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


現在、stackoverflow.com / questions / 7826448 /… も参照してください-TLDR gccは(比較的)最近、より厳密な動作に変更されました。
Tripleee、2015年

回答:


558

(この回答の履歴を参照して、より複雑なテキストを入手してください。しかし、読者が実際のコマンドラインを見る方が簡単だと思います)。


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

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

静的ライブラリの相互依存関係は同じように機能します-シンボルを必要とするライブラリが最初になければならず、次にシンボルを解決するライブラリが必要です。

静的ライブラリが別のライブラリに依存しているが、他のライブラリも以前のライブラリに依存している場合、サイクルがあります。次の方法で周期的に依存ライブラリを囲むことにより、この問題を解決することができます-(し、-)のような、-( -la -lb -)(次のような括弧、エスケープする必要があるかもしれない-\(とし-\))。次に、リンカーはそれらの囲まれたlibを複数回検索して、循環依存関係が解決されるようにします。または、ライブラリを複数回指定して、それぞれを1つずつ優先させることもできます-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です。


10
すべてのシンボルが解決されるまで繰り返します。ええと、トポロジーソートを管理できると思います。LLVMには、78個の静的ライブラリーがあり、依存関係を知っています。確かに、コンパイル/リンクオプションを理解するためのスクリプトもありますが、すべての状況で使用できるわけではありません。
Steve314、2010

6
プログラムは何@Steve lorder+ tsortください。しかし、循環参照がある場合は、順序がないことがあります。次に、すべてが解決されるまで、ライブラリリストを循環させるだけです。
ヨハネスシャウブ-litb '18

10
@Johannes-最大に強く接続されたコンポーネント(たとえば、Tarjansアルゴリズム)を決定し、コンポーネントの(本質的に非循環的な)ダイグラフをトポロジ的に並べ替えます。各コンポーネントは1つのライブラリとして扱うことができます。コンポーネントのいずれかのライブラリが必要な場合、依存サイクルにより、そのコンポーネント内のすべてのライブラリが必要になります。したがって、すべてを解決するためにすべてのライブラリを循環する必要はなく、厄介なコマンドラインオプションも必要ありません。2つの既知のアルゴリズムを使用する1つの方法ですべてのケースを正しく処理できます。
Steve314、2011

4
「--start-グループのアーカイブは--end-グループ」「 - - (アーカイブ)」または使用:私はこの優秀な答えは1つの重要なディテールを追加したい、円形の依存関係を解決する唯一の確実な方法があるたびにあるため、リンカーはアーカイブにアクセスし、現在未解決のシンボルを解決するオブジェクトファイルのみをプル(および未解決のシンボルを登録)します。このため、依存関係グラフで接続コンポーネントを繰り返すCMakeのアルゴリズムが失敗することがあります。(詳細については、Ian Lance Taylorのリンカーに関する優れたブログ投稿も参照してください。)
jorgen 2013年

3
あなたの答えは私のリンクエラーを解決するのに役立ち、トラブルに巻き込まれないようにする方法を非常に明確に説明しましたが、なぜこのように動作するように設計されたのか、何か考えがありますか?
アントンダネイコ2014年

102

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

典型的なUNIXリンカーは左から右に機能するため、依存関係にあるすべてのライブラリを左側に配置し、依存関係を満たすライブラリをリンク行の右側に配置します。一部のライブラリは他のライブラリに依存していると同時に、他のライブラリはそれらに依存している場合があります。ここが複雑になります。循環参照に関しては、コードを修正してください!


2
これはgnu ld / gccだけのものですか?または、これはリンカで一般的なものですか?
マイク

2
どうやら、より多くのUnixコンパイラに同様の問題があります。MSVCには、これらの問題が完全に解消されているわけではありませんが、それほど問題ではないようです。
MSalters 2009

4
MS開発ツールはこれらの問題をあまり表示しない傾向があります。これは、MS全体のツールチェーンを使用すると、リンカーの順序が適切に設定され、問題に気付かないためです。
マイケルコーン

16
MSVCリンカーは、すべてのライブラリで参照されていないシンボルを検索するため、この問題の影響を受けにくくなっています。ライブラリの順序は、複数のライブラリにシンボルがある場合、どのシンボルが解決されるかに影響与える可能性があります。MSDNから:「ライブラリもコマンドライン順に検索されますが、次の警告があります。ライブラリからオブジェクトファイルを取り込むときに未解決のシンボルが最初にそのライブラリで検索され、次にコマンドラインから次のライブラリが検索されます。 / DEFAULTLIB(Specify Default Library)ディレクティブ、次にコマンドラインの先頭にある任意のライブラリへ」
Michael Burr

4
「...スマートリンカー...」 -「スマートリンカー」ではなく、「シングルパス」リンカーに分類されると思います。
jww 2018

54

静的ライブラリが関係する場合に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

31

-Wl,--start-groupリンカーフラグに追加する場合、それらがどの順序にある​​か、または循環依存関係があるかどうかは関係ありません。

Qtでは、これは追加することを意味します:

QMAKE_LFLAGS += -Wl,--start-group

いちゃつく時間の負荷を節約し、リンクの速度を大幅に低下させないようです(とにかくコンパイルよりも時間がかかりません)。


8

別の代替方法は、ライブラリのリストを2回指定することです。

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

これを行うと、参照が2番目のブロックで解決されるため、正しいシーケンスを気にする必要はありません。


5

-Xlinkerオプションを使用できます。

g++ -o foobar  -Xlinker -start-group  -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a  -Xlinker -end-group 

ALMOSTは等しい

g++ -o foobar  -Xlinker -start-group  -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a  -Xlinker -end-group 

気をつけて!

  1. グループ内の順序は重要です!次に例を示します。デバッグライブラリにはデバッグルーチンがありますが、非デバッグライブラリには同じバージョンの弱いバージョンがあります。デバッグライブラリを最初にグループに入れる必要があります。そうしないと、非デバッグバージョンに解決されます。
  2. グループリストの各ライブラリの前に-Xlinkerを付ける必要があります。

5

私をつまずかせた簡単なヒント:「gcc」または「g ++」としてリンカーを呼び出している場合、「-start-group」と「--end-group」を使用しても、これらのオプションはリンカ-エラーにフラグを立てることもしません。ライブラリの順序が間違っていると、未定義のシンボルでリンクが失敗するだけです。

引数をリンカーに渡すようにGCCに指示するには、それらを「-Wl、-start-group」などのように記述する必要があります。


2

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


2

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

さまざまなリンカーHP / Intel / GCC / SUN / SGI / IBM / etcに応じて、未解決の関数/変数などを取得できます。一部のプラットフォームでは、ライブラリを2回リストする必要があります。

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

ソリューションのドキュメントを見つけたら、次の開発者が再度解決する必要はありません。

私の昔の講師は、「凝集度が高くカップリングが低い」と言っていましたが、今日でもそうです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.