web-dev-qa-db-ja.com

ld:共有ライブラリ内で-rpath、$ Originを使用(再帰的)

Ldの-rpathオプションを$Originhere とともに使用する基本的な例を作成しました(動作するバージョンの2番目の応答を参照)。 main.runfoo.soにリンクし、次にbar.soにリンクし、すべてrpathおよび$Originを使用する例を作成しようとしています。

実行時のファイル構造は次のとおりです。

  • 事業/
    • lib /
      • dir /
        • サブ/
          • bar.so
        • foo.so
    • run /
      • main.run(ビルドに失敗)

私はfoo.soを構築しています:

g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,'$Origin/sub' -Llib/dir/sub -l:bar.so

それはうまくいきます。 ldd lib/dir/foo.sobar.soを見つけることもできます。

しかし、main.runfoo.soにリンクしようとすると、foo.soがbar.soを見つけることができません。

私はmain.soをビルドしています:

g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$Origin/../lib/dir' -Llib/dir -l:foo.so

これは、再帰的にリンクしない別のバージョンのfoo.soが使用されている場合に正常に機能します。 (以下のプロジェクトのmake.shの行のコメントを外してテストします)。

ただし、通常のfoo.soを使用すると、main.runのビルド時にこのエラーが発生します。

/ usr/bin/ld:警告:bar.so、lib/dir/foo.soで必要、見つかりません(-rpathまたは-rpath-linkを使用してみてください)

だから私の質問は:

  1. Foo.so内の$Originproject/lib/dirfoo.soが存在する場合)またはproject/runmain.run(それをリンクしている実行可能ファイル)が存在する場合)に解決されますか?
    lddはproject/lib/dirであることを示しているようですが、これは最良の方法のようです(ただし、両方を想定してみました)。
  2. これらを(再配置可能性を維持しながら)リンクするにはどうすればよいですか。できれば-rpath-linkを使用しないでください。

プロジェクトをダウンロードできます here 。それは私が作ることができるのと同じくらい簡単です。 4つの短いソースとスクリプト。
抽出後、./make.sh内からproject/を実行するだけです。

注:私は-l:を使用しています。これは、ライブラリの名前がfoo.soではなくlibfoo.soのようになり、-l:foo.soではなく-lfooでランキングされることを除いて、何も変更しないでください。

29
Simon

まあ、私は何かが働いています。しかし、なぜそれが機能するのか本当にわかりません。これは、ldのバグのように感じます。

Main.runコンパイルのためにstrace -f -o /var/tmp/strace.out -- g++ ...を実行しました。静的リンカーは、実際には、リテラル名が "$ Origin/lib/dir/sub/bar.so"のように見えるファイルを20〜30個開くようにしています。 (つまり、$Originという名前の実際のディレクトリを探しています。真剣に。)

「bar.so」だけでなく、「lib/dir/sub/bar.so」という名前の-rpath-linkパスも検索しているようです。理由がわかりません。

とにかく、これは私のために働いているmain.runへのリンクです:

g++ -o run/main.run obj/main.o -Wl,-rpath,'$Origin/../lib/dir' -Wl,-rpath-link,. -Llib/dir -l:foo.so

これはあなたのものと同じですが、-Wl,-rpath-link,.が挿入されています。

[補遺]

何が起こっているのかわかりました。まず、静的リンカー(GNU ld)は、リンク先のライブラリーで$ Originを受け入れません。

次に、-lbar-l:bar.soを使用したときの動作は大きく異なります。

readelf -afoo.soを実行します。ビルドでは、「lib/dir/sub/bar.so」への依存関係が表示されます。これが、rpath-linkを "。"に設定する理由です。 main.runのビルドを修正します。静的リンカで「。」を検索します。見つかった「lib/dir/sub/bar.so」の場合。

Bar.soの名前をlibbar.soに変更し、foo.soをリンクして-lbarの代わりに-l:bar.soを使用する場合、同じreadelfは、foo.soが「libbar.so」に依存していることを示します(パスコンポーネント)。そのfoo.soを使用すると、静的リンカーが$ Originを尊重しないことを知っていれば、-Wl,-rpath-link,lib/dir/subを使用してmain.runリンクを機能させることができます。

ちなみに、GNU ldマニュアルに記載されている-l:bar.so構文はありません。好奇心から、どうやってそれを思いついたのですか?

サポートされている機能だとすると、これはバグのように見えます(-l:bar.soは、bar.soだけでなく、lib/dir/sub/bar.soへの依存関係を作成します)。 rpath-linkを「。」に設定することで、このバグに対処できます。 main.runの場合、または通常の方法で名前を変更できます(libxxx.so)。

7
Nemo

ld-linux(8) マンページから:

$ Originとrpath

ld.soは、rpath仕様(DT_RPATHまたはDT_RUNPATH)の文字列$ Origin(または同等の$ {Origin})を理解して、アプリケーション実行可能ファイルを含むディレクトリを意味します。したがって、somedir/appにあるアプリケーションをgcc -Wl、-rpath、 '$ Origin /../ lib'でコンパイルすると、somedirがディレクトリのどこにあるかに関係なく、関連する共有ライブラリをsomedir/libで見つけることができます。階層。これにより、特別なディレクトリにインストールする必要がない「ターンキー」アプリケーションの作成が容易になりますが、代わりに任意のディレクトリに解凍しても、独自の共有ライブラリを見つけることができます。

したがって、最初の質問への回答では、$Originの値は1つだけです:project/run

したがって、2番目の質問に対する答えは、次のコマンドを使用してfoo.soをリンクすることです。

g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,'$Origin/../lib/dir/sub' -Llib/dir/sub -l:bar.so
6
Rob Stewart

まず、問題を引き起こす可能性のある$記号の展開に関する問題があります。私はPython=ソースからビルドしています、そしてこれを行います:

export LDFLAGS='-Wl,-rpath,\$${Origin}/../lib -Wl,-rpath,\$${Origin}/../usr/lib -Wl,--enable-new-dtags'

makeを実行する前。これは正常に機能し、第1レベルの依存関係を見つけます。このタイプのマクロ展開の問題を処理するときは、一重引用符と二重引用符に注意してください。

次に、バイナリまたはライブラリでobjdump -xを実行すると、実際に含まれているRPATHヘッダーを確認できます。 objdump -x path/to/python |grep RPATHを実行すると、this.RPATH $ {Origin} /../ lib:$ {Origin} /../ usr/lib`が表示されます

バイナリをチェックして、実際にRPATHヘッダーに何があるかを確認することをお勧めします。残念ながら、これで問題が解決するとは思いません。 ldd path/to/pythonを実行すると、次のようになります。

libpython2.7.so.1.0 => /data1/python27/bin/../lib/libpython2.7.so.1.0 (0x00002ad369d4f000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00002ad36a12f000)
libdl.so.2 => /lib/libdl.so.2 (0x00002ad36a34d000)
libutil.so.1 => /lib/libutil.so.1 (0x00002ad36a551000)
libm.so.6 => /lib/libm.so.6 (0x00002ad36a754000)
libc.so.6 => /lib/libc.so.6 (0x00002ad36a9d8000)
/lib64/ld-linux-x86-64.so.2 (0x00002ad369b2d000)

ご覧のとおり、第1レベルの依存関係はrpathによって正しく処理されますが、第2レベルの依存関係、つまりlibpythonの依存関係はシステムライブラリに戻ります。そして、はい、libpythonのバイナリにはまったく同じRPATHヘッダーがあります。ディストリビューションに依存しないパッケージを作成するという私の問題を解決するためにrpath recursiveをグーグル検索しているときに、あなたの質問を見つけました。

後で追加rpathヘッダーは、ライブラリを検索する最初のパスのみを変更します。そこで見つからない場合、ローダーは通常の場所で検索を続けます。 lddは、検索の結果として見つかったライブラリの実際のパスのみをリストします。これらのライブラリをrpathディレクトリにコピーすると、すべてが機能しました。基本的に、すべての依存関係を見つけてコピーするための整頓された方法はなく、ldd -v path/to/pythonとその出力のいくつかの解析だけです。

5
Michael Dillon

私もこれについて調査してきましたが、Origin展開を使用するすべてのパスには-rpath-linkを使用する必要があることをお伝えします。例えば:

CC -shared (other flags) -R'$Origin/../lib/' -o /buildpath/lib/libmylib1.so
CC -shared (other flags) -R'$Origin/../lib/' -lmylib1 -o /buildpath/lib/libmylib2.so
# This fails to link 'somebinary'
CC (various flags) -R'$Origin/../lib/' -lmylib2 -o /buildpath/bin/somebinary
# This works correctly
CC (various flags) -R'$Origin/../lib/' -Wl,-rpath-link,/buildpath/lib/mylib1 -lmylib2 -o /buildpath/bin/somebinary
# The text above the carets to the right is a typo: ------------------^^^^^^
# I'm fairly sure it should read like this (though it has been awhile since I wrote this):
# (...) -Wl,-rpath-link,/buildpath/lib -lmylib1 (...)

ldは、$Originを使用して指定されたパスまたはサブ依存関係のRPATHから取得したパス内で-rpath-linkを展開しません。上記の例では、mylib2mylib1に依存しています。 somebinaryのリンク中に、ldはlibmylib2.soに埋め込まれたリテラル/非拡張文字列mylib1を使用して$Origin/../lib/を見つけようとします。 ld.soは実行時に実行されますが、ldは実行されません。

また、-Lで指定されたパスを使用して、サブ依存関係のlibrar(y | ies)を検索しません。

1

チェック 私の変更したmakeスクリプトのバージョン 。基本的に、ldは_-rpath-link_をまったく理解しないため、_$Origin_なしの追加の_$Origin_を使用する必要があります。

あなたの質問も。

  1. _$Origin_は実行時にのみ機能し、w.r.tです。各ライブラリ。したがって、共有ライブラリごとに_$Origin_が異なります。
  2. _rpath-link_を追加するのが最善の方法だと思います。これは相対的であり、最終バージョンの実行可能ファイルには存在しないため、移植性には影響しません。これは、私のバージョンの_make.sh_

また、 これはリンクするもの全体についての私自身の理解です です。お役に立てば幸いです。

1
zym1010

私の理解から、これはld(つまりbinutils)の問題であり、「Secondary Dependencies」をどのように解決するか

AFAIKはbinutils >= 2.30から始まりました。依存関係のrpathが検索に追加されます。つまり、ld ... main.exe find foo.so、次にfoo.soのRPATHを読み取り、bar.soを見つけます。

ここで私のスタックオーバーフローの質問: Binutilsセカンダリ依存関係の変更

そして、さまざまなbinutilsバージョンをテストするためのさまざまなディストリビューション(dockerコンテナー内)の私の調査 https://github.com/Mizux/SecondaryDependency

注:travis-CIログを見てください...

0
Mizux