web-dev-qa-db-ja.com

同じライブラリを2回リンクして循環依存関係を解決しますか?

静的ライブラリに分割されたコードベースがあります。残念ながら、ライブラリには循環依存関係があります。例:libfoo.alibbar.aに依存し、その逆も同様です。

これを処理する「正しい」方法は、次のように、リンカーの--start-groupおよび--end-groupオプションを使用することです。

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group

しかし、既存のMakefileでは、問題は通常次のように処理されます。

g++ -o myApp -lfoo -lbar -lfoo

(これが複雑な相互依存関係を持つ最大20のライブラリに拡張されたと想像してください。)

私は2番目のフォームを最初のフォームに変更するMakefileを調べてきましたが、今では同僚から理由が尋ねられています...そして、「よりクリーンであるため」と、他のフォームは危険であるという漠然とした感覚以外は、私はしません。良い答えがあります。

それで、同じライブラリを複数回リンクすることはできますかever問題を引き起こす可能性がありますか?たとえば、同じ.oが2回引き込まれた場合、複数定義されたシンボルでリンクが失敗する可能性がありますか?または、同じ静的オブジェクトの2つのコピーが作成され、微妙なバグが発生するリスクはありますか?

基本的に、同じライブラリを複数回リンクすることによってリンク時または実行時エラーが発生する可能性があるかどうかを知りたいです。もしそうなら、それらをトリガーする方法。ありがとう。

34
Nemo

私が提供できるのは反例の欠如だけです。私は実際にはこれまでに最初のフォームを見たことがなく(明らかに優れているにもかかわらず)、これが2番目のフォームで解決されるのを常に見ており、結果として問題を確認していません。

それでも、最初の形式に変更することをお勧めします。これは、特定の方法で動作するリンカーに依存するのではなく、ライブラリ間の関係を明確に示すためです。

そうは言っても、少なくとも、コードをリファクタリングして、共通の部分を追加のライブラリに引き出す可能性があるかどうかを検討することをお勧めします。

6
Mark B

の問題

g++ -o myApp -lfoo -lbar -lfoo

libfooの2回のパスとlibbarの1回のパスで十分であるという保証はありません。

Wl,--start-group ... -Wl,--end-groupを使用したアプローチは、より堅牢であるため、より優れています。

次のシナリオを検討してください(すべてのシンボルは異なるオブジェクトファイルにあります)。

  • myAppには、fooAで定義されたシンボルlibfooが必要です。
  • シンボルfooAには、barBで定義されたシンボルlibbarが必要です。
  • シンボルbarBには、fooCで定義されたシンボルlibfooが必要です。これは循環依存関係であり、-lfoo -lbar -lfooで処理できます。
  • シンボルfooCには、barDで定義されたシンボルlibbarが必要です。

上記のケースでビルドできるようにするには、-lfoo -lbar -lfoo -lbarをリンカーに渡す必要があります。どうして?

  1. リンカはlibfooを初めて認識し、シンボルfooAではなくfooCの定義を使用します。これまでのところ、fooCバイナリに。ただし、リンカはbarBが機能するために必要なので、fooAの定義を探し始めます。
  2. リンカは-libbarを認識し、barBの定義を含み(ただしnotbarD)、fooCの定義の検索を開始します。
  3. fooCの定義は、2回目の処理時にlibfooにあります。ここで、barDの定義も必要であることが明らかになりましたが、遅すぎると、コマンドラインにlibbarがなくなりました。

上記の例は、任意の依存関係の深さに拡張できます(ただし、これは実際にはめったに起こりません)。

したがって、

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group

リンカは必要な頻度でライブラリグループを通過するため、より堅牢なアプローチです。パスによってシンボルテーブルが変更されなかった場合にのみ、リンカはコマンドラインの次のライブラリに移動します。

ただし、パフォーマンスにわずかなペナルティがあります。最初の例では、手動コマンドライン-lbarと比較して-lfoo -lbar -lfooがもう一度スキャンされました。言及/検討する価値があるかどうかわからない。

4
ead

これはレガシーアプリケーションであるため、ライブラリの構造は、もう使用しない別の製品を構築するために使用されるなど、おそらくもう問題にならない配置から継承されているに違いありません。

継承されたライブラリー構造の構造上の理由が残っている場合でも、ほぼ確実に、レガシー構成からもう1つのライブラリーを作成することは許容されます。 20個のライブラリのすべてのモジュールを新しいライブラリliballofthem.aに入れるだけです。そうすれば、すべてのアプリケーションは単純にg++ -o myApp -lallofthem ...になります。

2
wallyk