web-dev-qa-db-ja.com

ldリンカの質問:--whole-archiveオプション

私が見た--whole-archiveリンカーオプションの唯一の実際の使用は、静的ライブラリから共有ライブラリを作成することです。最近、社内静的ライブラリとリンクするときに常にこのオプションを使用するMakefileに出会いました。もちろん、これにより、実行可能ファイルは、参照されていないオブジェクトコードを不必要にプルします。これに対する私の反応は、これが明らかに間違っているということでした、ここに何かが欠けていますか?

2番目の質問は、アーカイブ全体のオプションに関して読んだものに関係していますが、完全には解析できませんでした。実行可能ファイルが静的ライブラリと同じオブジェクトコードを(部分的に)共有ライブラリとリンクしている場合、静的ライブラリとリンクしているときに--whole-archiveオプションを使用する必要があるという効果があります。これは、共有ライブラリと静的ライブラリがオブジェクトコードに関して重複していることです。このオプションを使用すると、すべてのシンボル(使用に関係なく)が実行可能ファイルで強制的に解決されます。これにより、オブジェクトコードの重複を避けることができます。これは混乱を招きます。プログラムでシンボルが参照される場合、リンク時に一意に解決する必要があります。複製に関するこのビジネスは何ですか? (この段落が明確さの縮図ではない場合はご容赦ください)

ありがとう

38
jasmeet

実行可能ファイルを静的ライブラリにリンクする場合、_--whole-archive_の正当な使用法があります。 1つの例は、グローバルインスタンスがコンストラクターで自身を「登録」するC++コードの構築です(警告:テストされていないコード)。

main.cc

_typedef void (*handler)(const char *protocol);
typedef map<const char *, handler> M;
M m;

void register_handler(const char *protocol, handler) {
   m[protocol] = handler;
}
int main(int argc, char *argv[])
{
   for (int i = 1; i < argc-1; i+= 2) {
      M::iterator it = m.find(argv[i]);
      if (it != m.end()) it.second(argv[i+1]);
   }
}
_

http.cc(libhttp.aの一部)

_class HttpHandler {
  HttpHandler() { register_handler("http", &handle_http); }
  static void handle_http(const char *) { /* whatever */ }
};
HttpHandler h; // registers itself with main!
_

_http.cc_には_main.cc_が必要とするシンボルがないことに注意してください。これを次のようにリンクすると

_g++ main.cc -lhttp
_

notメインの実行可能ファイルにリンクされたhttpハンドラーを取得し、handle_http()を呼び出すことはできません。これを次のようにリンクするとどうなるかを比較してください。

_g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive
_

同じ「自己登録」スタイルは、プレーンCでも可能です。 __attribute__((constructor)) GNU拡張機能を使用します。

62

--whole-archiveのもう1つの合法的な使用方法は、ツールキット開発者が複数の機能を含むライブラリを単一の静的ライブラリに配布することです。この場合、プロバイダーは、ライブラリーのどの部分がコンシューマーによって使用されるかわからないため、すべてを含める必要があります。

10
Steve Brooks

— whole-archiveを使用して実行可能ファイルをビルドすることは、おそらく必要ではないことに同意します(不要なコードをリンクして肥大化したソフトウェアを作成するため)。正当な理由がある場合は、ビルドシステムにドキュメント化する必要がありますが、今は推測するしかありません。

質問の2番目の部分について。実行可能ファイルが、静的ライブラリと(一部)静的ライブラリと同じオブジェクトコードを持つ動的ライブラリの両方をリンクする場合、-whole-archiveは、リンク時は、静的ライブラリのコードが優先されます。これは通常、静的リンクを行うときに必要なものです。

4
lothar

古いクエリですが、最初の質問(「理由」)で、主にそれらのライブラリ間の循環参照を回避するために、社内ライブラリにも--whole-archiveが使用されているのを見ました。ライブラリの貧弱なアーキテクチャを隠す傾向があるため、お勧めしません。ただし、クイックトライアルを迅速に機能させる方法です。

2番目のクエリで、共有オブジェクトと静的ライブラリに同じシンボルが存在する場合、リンカーは最初に一致するライブラリの参照を満たします。
共有ライブラリと静的ライブラリがコードを正確に共有している場合、これはすべて正常に機能する可能性があります。ただし、共有ライブラリと静的ライブラリに同じシンボルの異なる実装がある場合、プログラムはコンパイルされますが、ライブラリの順序に基づいて異なる動作をします。

静的ライブラリからすべてのシンボルを強制的にロードすることは、どこから何がロードされるかという混乱を取り除く1つの方法です。しかし、一般的にこれは間違った問題を解決するように聞こえます。ほとんどの場合、異なるライブラリに同じシンボルは必要ありません。

3
Rob Swarbrick

_--whole-archive_がよく使用される追加の適切なシナリオは、静的ライブラリand増分リンクを扱う場合です。

それを仮定しましょう:

  1. libAは、a()およびb()関数を実装します。
  2. プログラムの一部はlibAのみに対してリンクする必要があります。 _--wrap_を使用したいくつかの関数ラッピングが原因です(古典的な例はmallocです)
  3. libCc()関数を実装し、a()を使用します
  4. 最後のプログラムはa()c()を使用します

増分リンクの手順は次のとおりです。

_ld -r -o step1.o module1.o --wrap malloc --whole-archive -lA
ld -r -o step2.o step1.o module2.o --whole-archive -lC
cc step3.o module3.o -o program
_

--whole-archiveの挿入に失敗すると、programで使用される関数c()が削除され、正しいコンパイルプロセスが妨げられます。

もちろん、これはすべてのモジュールでmallocへのすべての呼び出しをラップすることを避けるためにインクリメンタルリンクを行う必要がある特定のコーナーケースですが、_--whole-archive_で正常にサポートされるケースです。

0
ilpelle