45

私はちょうどここに興味があります。共有オブジェクトを作成しました:

gcc -o liba.so -fPIC -shared liba.c

そして、もう 1 つの共有オブジェクトは、前のものにリンクします。

gcc -o libb.so -fPIC -shared libb.c liba.so

ここで、 に対してリンクする実行可能ファイルを作成するときに、libb.sold に -rpath-link を指定して、ld に依存していることを検出liba.soしたときに検出できるようにする必要があります。libb.so

gcc -o test -Wl,-rpath-link,./ test.c libb.so

そうしないと、ld が文句を言います。

liba.sold がリンク時に位置を特定できなければならないのはなぜtestですか? liba.so私には、 ldの存在を確認する以外に多くのことをしているようには見えないからです。たとえば、必要に応じreadelf --dynamic ./testてリストのみを実行するため、動的リンカーは独自に依存関係を検出し、独自に検索するlibb.so必要があると思います。libb.so -> liba.soliba.so

私は x86-64 GNU/Linux プラットフォームを使用しており、 の main() ルーチンtestが の関数をlibb.so呼び出し、それが の関数を呼び出しますliba.so

4

4 に答える 4

33

liba.sold がリンク時に位置を特定できなければならないのはなぜtestですか? liba.so私には、 ldの存在を確認する以外に多くのことをしているようには見えないからです。たとえば、必要に応じreadelf --dynamic ./testてリストのみを実行するため、動的リンカーは独自に依存関係を検出し、独自に検索するlibb.so必要があると思います。libb.so -> liba.soliba.so

リンクプロセスを正しく理解していれば、実際にはldを見つける必要さえありませんlibb.sotest実行時にロードするときに動的リンカーがそれらを解決することを期待して、未解決の参照をすべて無視することができlibb.soます。しかし、ldがこのように実行していた場合、多くの「未定義の参照」エラーはリンク時に検出されず、代わりに実行時にロードしようとしたときに検出さtestれます。そのため、 ldは、それ自体では見つからないすべてのシンボルが、依存するtest共有ライブラリで実際に見つかるかどうかを追加でチェックします。testしたがって、プログラムに「未定義の参照」エラーがある場合 (一部の変数または関数がそれ自体でtestは見つからず、testlibb.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.solibb.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

また、他の共有ライブラリに依存する共有ライブラリをリンクすることの多くの側面を明確にする良い記事を見つけまし

于 2016-03-02T13:10:55.180 に答える
7

システムは、 、およびシステム環境などを通じてld.so.confシステム全体のライブラリ検索パスを提供します。これらのパスは、標準ライブラリに対してビルドするときに情報などを通じてインストールされたライブラリによって補完されます。ライブラリが定義された検索パスに存在する場合、標準のライブラリ検索パスが自動的にたどられ、必要なすべてのライブラリを見つけることができます。ld.so.conf.dLD_LIBRARY_PATHpkg-config

自分で作成したカスタム共有ライブラリには、標準のランタイム ライブラリ検索パスはありません。-L/path/to/libコンパイルおよびリンク中に、指定によってライブラリへの検索パスを指定します。非標準の場所にあるライブラリの場合、実行可能ファイルが必要なライブラリを見つけることができるように、ライブラリ検索パスをコンパイル時にオプションで実行可能ファイルのヘッダー (ELF ヘッダー) に配置できます。

rpathカスタム ランタイム ライブラリの検索パスを ELF ヘッダーに埋め込む方法を提供します。これにより、使用するたびに検索パスを指定しなくても、カスタム ライブラリを見つけることができます。これは、ライブラリに依存するライブラリにも当てはまります。おわかりのように、コマンド ラインでライブラリを指定する順序が重要であるだけでなく、リンク先の依存ライブラリごとにランタイム ライブラリ検索パス (rpath) 情報も提供する必要があります。実行に必要なすべてのライブラリの場所が含まれています。

コメントからの補遺

私の質問は主に、ld が「共有ライブラリ (liba.so) を自動的に見つけようとし」、「リンクに含める」必要がある理由です。

それが単に機能する方法ldです。「man ld-rpath オプションは、リンクに明示的に含まれる共有オブジェクトが必要とする共有オブジェクトを見つけるときにも使用されます... ELF実行可能ファイルをリンクするときに -rpath を使用しない場合、環境変数「LD_RUN_PATH」の内容は定義されている場合に使用されます。」あなたの場合liba、 に配置されていないLD_RUN_PATHため、実行可能ファイルのコンパイル中に(上記で説明したように) または明示的な検索パスを指定して、実行可能ファイルのld場所を特定する方法が必要になります。libarpath

第二に、「リンクに含める」が実際に意味すること。libb.so の ELF ヘッダーは変更されておらず (liba.so に対して既に NEEDED タグが付けられていた)、exec のヘッダーは libb のみを宣言しているため、「存在を確認する」(liba.so の) という意味のように思えます。必要に応じて。ld が liba.so の検索を気にかけているのはなぜですか? タスクを実行時リンカーに任せることができないのでしょうか?

いいえ、 のセマンティクスに戻りldます。「適切なリンク」を生成するには、すべての依存ライブラリldを見つけることができなければなりません。そうしないと、良好なリンクを保証できません。実行時リンカーは、プログラムが必要とする共有ライブラリを見つけるだけでなく、見つけてロードする必要があります。プログラムがリンクされたときに必要なすべての共有ライブラリを見つけることができない限り、それが起こることを保証することはできません。ldldld

于 2014-07-07T01:39:38.037 に答える
1

あなたは実際に ld に(libbに対してリンクするときlibaどこに libaあるのかを伝えていません-それが依存関係であることだけです。クイックldd libb.soは、それが見つからないことを示しますliba

おそらくこれらのライブラリはリンカー検索パスにないため、実行可能ファイルをリンクするとリンカー エラーが発生します。liba 自体をリンクすると、libb の関数はまだ未解決ですが、ldのデフォルトの動作は、最終的な実行可能ファイルをリンクするまで、DSO の未解決のシンボルを気にしないことに注意してください。

于 2014-07-06T23:58:29.807 に答える