liba.so
ld がリンク時に位置を特定できなければならないのはなぜtest
ですか? liba.so
私には、 ldの存在を確認する以外に多くのことをしているようには見えないからです。たとえば、必要に応じreadelf --dynamic ./test
てリストのみを実行するため、動的リンカーは独自に依存関係を検出し、独自に検索するlibb.so
必要があると思います。libb.so -> liba.so
liba.so
リンクプロセスを正しく理解していれば、実際にはldを見つける必要さえありませんlibb.so
。test
実行時にロードするときに動的リンカーがそれらを解決することを期待して、未解決の参照をすべて無視することができlibb.so
ます。しかし、ldがこのように実行していた場合、多くの「未定義の参照」エラーはリンク時に検出されず、代わりに実行時にロードしようとしたときに検出さtest
れます。そのため、 ldは、それ自体では見つからないすべてのシンボルが、依存するtest
共有ライブラリで実際に見つかるかどうかを追加でチェックします。test
したがって、プログラムに「未定義の参照」エラーがある場合 (一部の変数または関数がそれ自体でtest
は見つからず、test
libb.so
)、これは実行時だけでなく、リンク時に明らかになります。したがって、このような動作は単なる追加の健全性チェックです。
しかし、ldはさらに先へ進みます。をリンクするtest
と、ldlibb.so
は に依存する共有ライブラリにすべての未解決の参照があることも確認しますlibb.so
(この場合libb.so
は に依存するため、リンク時に見つけるliba.so
必要があります)。liba.so
まあ、実際にはldはすでにリンクしていたときにこのチェックを行っていlibb.so
ます。なぜこのチェックを 2 回目に行うのか... ldの開発者は、リンクされたときにロードされる可能性があった古いライブラリに対してプログラムをリンクしようとしたときに、壊れた依存関係を検出するのにこの二重チェックが役立つことに気付いたのかもしれませんが、今ではできるようになりました。依存するライブラリが更新されているため、読み込まれません (たとえば、liba.so
後に作り直され、一部の機能が削除されました)。
UPD
ほんの少しの実験をしました。「実際には ld は、リンク時にこのチェックをすでに行っている」libb.so
という私の仮定は間違っているようです。
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
。動作は、生成されたライブラリのシンボルも解決しないarlibb.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には-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 keyを使用したことに注意してください。違いは、-rpath-linkはリンク時に、最終的な実行可能ファイルのすべてのシンボルを解決できることを確認するためだけに使用されるのに対し、-rpathは実際にはパラメーターとして指定したパスを ELF に埋め込むことです。
$ readelf -d test | grep RPATH
0x0000000f (RPATH) Library rpath: [./]
test
そのため、共有ライブラリ (liba.so
およびlibb.so
) が現在の作業ディレクトリ ( ) にある場合に実行できるようになりまし./
た。-rpath-linkを使用しただけの場合、ELF にはそのようなエントリはなくtest
、共有ライブラリへのパスを/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
また、他の共有ライブラリに依存する共有ライブラリをリンクすることの多くの側面を明確にする良い記事を見つけまし
た。