web-dev-qa-db-ja.com

依存関係を持つ動的ライブラリとのリンク

次のシナリオを検討してください。

  • 共有ライブラリlibA.so、依存関係なし。
  • 依存関係としてlibA.soを持つ共有ライブラリlibB.so。

LibBとリンクするバイナリファイルをコンパイルします。バイナリをlibBのみにリンクするか、libAにリンクする必要がありますか?

直接の依存関係とのみリンクして、ランタイムの依存関係から未解決のシンボルを解決する方法はありますか?

ライブラリlibBの実装が将来変更され、他の依存関係(libC、libD、libEなど)が導入される可能性があることを心配しています。私はそれで問題があるでしょうか?

言い換えると:

  • libAファイル:a.cpp a.h
  • libBファイル:b.cpp b.h
  • メインプログラムファイル:main.cpp

もちろん、b.cppにはa.hが含まれ、main.cppにはb.hが含まれます。

コンパイルコマンド:

g++ -fPIC a.cpp -c
g++ -shared -o libA.so a.o

g++ -fPIC b.cpp -c -I.
g++ -shared -o libB.so b.o -L. -lA

どのベローズオプションを使用する必要がありますか?

g++ main.cpp -o main -I. -L. -lB

または

g++ main.cpp -o main -I. -L. -lB -lA

最初のオプションを使用できませんでした。リンカは、ライブラリlibAからの未解決のシンボルについて文句を言います。しかし、私には少し奇妙に聞こえます。

どうもありがとう。

-更新されたコメント:

バイナリをリンクすると、リンカーはメインとlibBからのすべてのシンボルを解決しようとします。ただし、libBにはlibAからの未定義のシンボルがあります。それがリンカがそれについて文句を言う理由です。

そのため、私もlibAとリンクする必要があります。しかし、共有ライブラリの未解決のシンボルを無視する方法を見つけました。これを行うには、次のコマンドラインを使用する必要があるようです。

g++ main.cpp -o main -I. -L. -lB -Wl,-unresolved-symbols=ignore-in-shared-libs

-rpathオプションを引き続き使用できるようです。しかし、もう少しよく理解する必要があります。

-Wl,-unresolved-symbols=ignore-in-shared-libsオプションを使用するときに、潜在的な落とし穴を知っている人はいますか?

-更新されたコメント2:

-rpathはこの目的には使用しないでください。特定のディレクトリでライブラリを強制的に検索すると便利です。 -unresolved-symbolアプローチはより良く見えます。

再度、感謝します。

67
Marcus

あなたはすでにほとんどの方法であるように見えます。よく調べました。その背後にある「なぜ」を解決するのに役立つかどうかを見てみましょう。

これがリンカの動作です。実行可能ファイル(上記の「メイン」)をリンクすると、未解決のいくつかのシンボル(関数など)が含まれます。未解決のシンボルを解決しようとして、後続のライブラリのリストを調べます。途中で、いくつかのシンボルがlibB.soによって提供されることを発見したため、このライブラリによって解決されたことに注意します。

ただし、これらのシンボルの一部は、実行可能ファイルでまだ解決されていない他のシンボルを使用することも検出するため、これらのシンボルも同様に解決する必要があります。 libA.soにリンクしないと、アプリケーションは不完全になります。 libA.soにリンクすると、すべてのシンボルが解決され、リンクが完了します。

ご覧のとおり、-unresolved-symbols-in-shared-libsを使用しても問題は解決しません。これらのシンボルが実行時に解決されるように、それを延期します。それが-rpathの目的です:実行時に検索されるライブラリを指定します。これらのシンボルを解決できない場合、アプリは起動に失敗します。

ライブラリの依存関係を把握するのは簡単ではありません。シンボルは複数のライブラリによって提供され、それらのいずれかとリンクすることで満たされる可能性があるためです。

このプロセスの別の説明がここにあります: なぜライブラリがリンクされている順序がGCCでエラーを引き起こすのですか?

38
kithril

直接の依存関係のみを使用した動的リンクの場合は、-Wl,--as-neededライブラリを追加-Wl,--as-needed

gcc main.c -o main -I. -L. -Wl,--as-needed -lB -lA

直接の依存関係を確認するには、lddも間接的な依存関係を表示するため、lddの代わりにreadelfを使用する必要があります。

$ readelf -d main | grep library
0x0000000000000001 (NEEDED)             Shared library: [libB.so]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

lddは間接的な依存関係も示します。

$ LD_LIBRARY_PATH=. ldd ./main
linux-vdso.so.1 (0x00007fff13717000)
libB.so => ./libB.so (0x00007fb6738ed000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6734ea000)
libA.so => ./libA.so (0x00007fb6732e8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb673af0000)

cmakeを使用する場合、次の行を追加して直接依存関係のみを含めることができます。

set(CMAKE_EXE_LINKER_FLAGS    "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")
16
user1047271

別のオプションは libtool を使用することです

g++呼び出しをlibtool --mode=compile g++に変更してソースコードをコンパイルし、libtool --mode=link g++を変更してlibBからアプリケーションを作成すると、libAは自動的にリンクされます。

1
mattmilten

これは興味深い投稿です-私もこれで頭を打ちましたが、ここでポイントを逃したと思います..

アイデアは次のとおりですよね?

main.cpp =(depends)=> libB.so =(depends)=> libA.so

さらに検討してみましょう..

  • A.cpp(でのみ)でクラス/変数を定義し、「symA」と呼びましょう
  • B.cpp(およびそこのみ)でクラス/変数を定義し、「symB」と呼びましょう。
  • symBはsymAを使用します
  • main.cppはsymBを使用します

これで、libB.soとlibA.soが上記のようにコンパイルされました。その後、最初のオプションshouldが機能する、つまり:

g++ main.cpp -o main -I. -L. -lB

あなたの問題は、

main.cppではsymAも参照

私は正しいですか?

コードでシンボルを使用する場合、そのシンボルは.soファイルで見つける必要があります

共有ライブラリの相互参照(つまり、APIの作成)の全体的な考え方は、より深いレイヤーのシンボルが隠され(タマネギの皮をむく)使用されないということです。 ..つまり、main.cppでsymAを参照せず、symBのみを参照します(symBにsymAのみを参照させます)。

0
El Sampsa