ここに興味があります。共有オブジェクトを作成しました:
gcc -o liba.so -fPIC -shared liba.c
前のオブジェクトにリンクするもう1つの共有オブジェクト:
gcc -o libb.so -fPIC -shared libb.c liba.so
libb.so
にリンクする実行可能ファイルを作成するとき、liba.so
が依存していることを発見したときにlibb.so
を見つけることができるように、ldに-rpath-linkを指定する必要があります。
gcc -o test -Wl,-rpath-link,./ test.c libb.so
そうでなければ、ldは文句を言います。
なぜtest
をリンクするときにldはliba.so
を見つけなければならないのですか?私には、ldがliba.so
の存在を確認する以外に多くのことをしているようには思えないからです。たとえば、readelf --dynamic ./test
を実行すると、必要に応じてlibb.so
のみがリストされるため、動的リンカーはlibb.so -> liba.so
依存関係を独自に検出し、liba.so
を独自に検索する必要があります。
私はx86-64 GNU/Linuxプラットフォームを使用しており、test
のmain()ルーチンがlibb.so
の関数を呼び出し、それがliba.so
の関数を呼び出します。
なぜ、
test
をリンクするときにldがliba.so
を見つけなければならないのですか?私には、ldがliba.so
の存在を確認する以外に多くのことをしているようには思えないからです。たとえば、readelf --dynamic ./test
を実行すると、必要に応じてlibb.so
のみがリストされるため、動的リンカーはlibb.so -> liba.so
依存関係を独自に検出し、liba.so
を独自に検索する必要があります。
リンクプロセスを正しく理解していれば、ldは実際にはlibb.so
でさえ見つける必要はありません。実行時にlibb.so
をロードするときに動的リンカーがそれらを解決することを期待して、test
内のすべての未解決の参照を無視できます。ただし、ldがこの方法で実行されている場合、リンク時に多くの「未定義の参照」エラーが検出されず、代わりにランタイムでtest
をロードしようとしたときに検出されます。したがって、ldは、test
自体にないすべてのシンボルがtest
に依存する共有ライブラリに実際に見つかるかどうかを追加チェックするだけです。したがって、test
プログラムに「未定義の参照」エラー(一部の変数または関数がtest
自体ではなく、libb.so
でも見つからない場合)は、実行時だけでなくリンク時にも明らかになります。したがって、このような動作は単なる追加の健全性チェックです。
しかし、ldはさらに先へ進みます。 test
をリンクすると、ldは、libb.so
のすべての未解決の参照が、libb.so
が依存する共有ライブラリで見つかることもチェックします(この場合、libb.so
はliba.so
に依存するため、リンクにあるliba.so
が必要です)時間)。実際、ldは、libb.so
をリンクしていたときに既にこのチェックを行っています。なぜ2回目にこのチェックを行うのか... ldの開発者は、リンクされたときにロードされる可能性のある古いライブラリに対してプログラムをリンクしようとすると、壊れた依存関係を検出するのに役立つこのダブルチェックを見つけたかもしれませんが、ただし、依存しているライブラリが更新されているため、ロードできません(たとえば、liba.so
が後で修正され、一部の関数が削除されました)。
[〜#〜] upd [〜#〜]
わずかな実験を行っただけです。私の仮定「libb.so
をリンクしていたときに、実際にldはすでにこのチェックを行っていた」は間違っているようです。
liba.c
に次のコンテンツがあると仮定します。
int liba_func(int i)
{
return i + 1;
}
そしてlibb.c
には次のものがあります:
int liba_func(int i);
int liba_nonexistent_func(int i);
int libb_func(int i)
{
return liba_func(i + 1) + liba_nonexistent_func(i + 2);
}
およびtest.c
#include <stdio.h>
int libb_func(int i);
int main(int argc, char *argv[])
{
fprintf(stdout, "%d\n", libb_func(argc));
return 0;
}
libb.so
をリンクする場合:
gcc -o libb.so -fPIC -shared libb.c liba.so
リンカはliba_nonexistent_func
を解決できないエラーメッセージを生成せず、代わりに壊れた共有ライブラリlibb.so
をサイレントに生成します。動作は、arで静的ライブラリ(libb.a
)を作成する場合と同じですが、生成されたライブラリのシンボルも解決しません。
ただし、test
をリンクしようとすると:
gcc -o test -Wl,-rpath-link=./ test.c libb.so
エラーが表示されます:
libb.so: undefined reference to `liba_nonexistent_func'
collect2: ld returned 1 exit status
ldがすべての共有ライブラリを再帰的にスキャンしなかった場合、このようなエラーを検出することはできません。したがって、質問への答えは上で言ったのと同じように見えます:ld needs -rpath-linkリンクされた実行可能ファイルが動的ロードによって後でロードできることを確認するため。ただ健全性チェック。
UPD2
未解決の参照をできるだけ早くチェックするのは理にかなっています(libb.so
をリンクする場合)が、何らかの理由でldはこれを行いません。おそらく共有ライブラリの循環依存関係を作成できるようにするためです。
liba.c
には次の実装を含めることができます。
int libb_func(int i);
int liba_func(int i)
{
int (*func_ptr)(int) = libb_func;
return i + (int)func_ptr;
}
したがって、liba.so
はlibb.so
を使用し、libb.so
はliba.so
を使用します(このようなことは絶対にしないでください)。これは正常にコンパイルされ動作します:
$ gcc -o liba.so -fPIC -shared liba.c
$ gcc -o libb.so -fPIC -shared libb.c liba.so
$ gcc -o test test.c -Wl,-rpath=./ libb.so
$ ./test
-1217026998
readelfはliba.so
はlibb.so
を必要としないと言っていますが:
$ readelf -d liba.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [libc.so.6]
$ readelf -d libb.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [liba.so]
0x00000001 (NEEDED) Shared library: [libc.so.6]
ldが共有ライブラリのリンク中に未解決のシンボルをチェックした場合、liba.so
のリンクはできません。
-rpath-linkの代わりに-rpathキーを使用したことに注意してください。違いは、-rpath-linkはリンク時に最終実行可能ファイルのすべてのシンボルが解決できることを確認するためだけに使用されるのに対し、-rpathは実際にELFにパラメーターとして指定したパスを埋め込むことです:
$ readelf -d test | grep RPATH
0x0000000f (RPATH) Library rpath: [./]
そのため、共有ライブラリ(liba.so
およびlibb.so
)が現在の作業ディレクトリ(./
)にある場合、test
を実行できるようになりました。 -rpath-linkを使用した場合は、test
ELFにそのようなエントリはなく、共有ライブラリへのパスを/etc/ld.so.conf
ファイルまたはLD_LIBRARY_PATH
環境変数に追加する必要があります。
UPD3
実際には、共有ライブラリのリンク中に未解決のシンボルをチェックすることが可能です。そのためには--no-undefined
オプションを使用する必要があります。
$ gcc -Wl,--no-undefined -o libb.so -fPIC -shared libb.c liba.so
/tmp/cc1D6uiS.o: In function `libb_func':
libb.c:(.text+0x2d): undefined reference to `liba_nonexistent_func'
collect2: ld returned 1 exit status
また、他の共有ライブラリに依存する共有ライブラリのリンクの多くの側面を明確にする良い記事を見つけました: Linuxの二次依存関係の例をよりよく理解する 。
システムは、ld.so.conf
、ld.so.conf.d
、およびシステム環境LD_LIBRARY_PATH
などを通じて、システム全体ライブラリ検索パスを提供します。標準ライブラリに対してビルドするときに、pkg-config
情報などを介してインストールされたライブラリ。ライブラリが定義済みの検索パスにある場合、標準ライブラリ検索パスが自動的にたどられ、必要なすべてのライブラリを見つけることができます。
自分で作成したカスタム共有ライブラリには、標準の実行時ライブラリ検索パスはありません。コンパイルおよびリンク時に-L/path/to/lib
を指定して、ライブラリへの検索パスを指定します。標準以外の場所にあるライブラリの場合、必要に応じてライブラリ検索パスを実行可能ファイルのヘッダー(ELFヘッダー)に配置して、実行可能ファイルが必要なライブラリを見つけられるようにすることができます。
rpath
は、カスタムランタイムライブラリ検索パスをELFヘッダーに埋め込む方法を提供するため、使用するたびに検索パスを指定しなくてもカスタムライブラリを見つけることができます。これは、ライブラリに依存するライブラリにも適用されます。見つけたように、コマンドラインでライブラリを指定する順序が重要であるだけでなく、リンクする各依存ライブラリのランタイムライブラリ検索パスまたはrpath情報も提供する必要があります実行に必要なallライブラリの場所が含まれています。
コメントからの補遺
私の質問は、主にldが「共有ライブラリを自動的に見つけよう」(liba.so)と「リンクに含める」必要がある理由です。
これは、単にld
が機能する方法です。 From man ld
"共有オブジェクトが必要とする共有オブジェクトリンクに明示的に含まれる場合、-rpathオプションも使用されます... ELF実行可能ファイルのリンク時に-rpathが使用されない場合、環境変数「LD_RUN_PATH」の内容は、定義されている場合に使用されます。あなたの場合、liba
はLD_RUN_PATH
にないため、ld
は、実行可能ファイルのコンパイル中にliba
(上記)を使用するか、明示的な検索パスを提供することにより、rpath
を見つける方法を必要とします。
第二に、「リンクに含める」とは本当に意味します。私にとっては、それは単に「存在を確認する」(liba.soのもの)を意味しているようです、なぜならlibb.soのELFヘッダーは変更されていないため(すでにliba.soに対するNEEDEDタグがありました)、execのヘッダーはlibbを宣言するだけです必要に応じて。 ldがliba.soを見つけることに関心があるのはなぜですか、タスクを実行時リンカーに任せるだけではいけませんか?
いいえ、ld
のセマンティクスに戻ります。 "good link"を生成するには、ld
がall依存ライブラリを見つけられる必要があります。それ以外の場合、ld
は適切なリンクを保証できません。実行時リンカーは、find共有ライブラリプログラムが必要だけでなく、find and loadでなければなりません。 ld
は、プログラムがリンクされたときにld
自体が必要なすべての共有ライブラリを見つけるでない限り、それが起こることを保証できません。
-rpath
オプションと-rpath-link
オプションをいつ使用するかを知る必要があると思います。最初にman ld
が指定したものを引用します:
- -rpathと-rpath-linkの違いは、-rpathオプションで指定されたディレクトリが実行可能ファイルに含まれ、実行時に使用されるのに対し、-rpath-linkオプションはリンク時にのみ有効であることです。この方法での-rpathの検索は、--with-sysrootオプションで設定されたネイティブリンカーおよびクロスリンカーでのみサポートされます。
リンク時間とランタイムを区別する必要があります。受け入れられたanton_rhの回答によると、未定義シンボルのチェックは、共有ライブラリまたは静的ライブラリをコンパイルおよびリンクする場合は有効になりませんが、実行可能ファイルをコンパイルおよびリンクする場合は有効になります。 (ただし、共有ライブラリと実行可能ファイルであるいくつかのファイルが存在することに注意してください。たとえば、ld.so
。これを調べるにはman ld.so
と入力します。未定義をチェックするかどうかはわかりませんこれらの「デュアル」種類のファイルをコンパイルすると、シンボルが有効になります)。
したがって、-rpath-link
はリンク時のチェックに使用され、-rpath
はリンク時とランタイムに使用されます。これは、rpath
がELFヘッダーに埋め込まれているためです。ただし、両方が指定されている場合、リンク時に-rpath-link
オプションが-rpath
オプションをオーバーライドすることに注意する必要があります。
それでも、なぜ-rpath-option
および-rpath
オプションなのですか?それらは「オーバーリンク」を排除するために使用されると思います。こちらをご覧ください 例で解決するLinuxの二次依存関係をよく理解してください。 、単にctrl + F
を使用して、「オーバーリンク」に関連するコンテンツに移動します。 「オーバーリンク」が悪い理由に焦点を当てる必要があり、「オーバーリンク」を避けるために採用する方法のため、ld
オプション-rpath-link
および-rpath
の存在は合理的です。 「オーバーリンク」を回避するために、コンパイルおよびリンク用のコマンドで一部のライブラリを省略します。省略したため、ld
はこれらの省略されたライブラリを見つけるために-rpath-link
または-rpath
を必要とします.
実際にldに通知しているわけではありません(libb
をliba
にリンクする場合)whereliba
は-依存関係であることだけです。クイック ldd libb.so
は、liba
が見つからないことを示します。
おそらくこれらのライブラリはリンカ検索パスにないため、実行可能ファイルをリンクするときにリンカエラーが発生します。 liba自体をリンクする場合、libbの関数はstill unresolvedですが、ld
のデフォルトの動作は、最終実行可能ファイルをリンクするまでDSOの未解決シンボルを気にしないことに注意してください。