web-dev-qa-db-ja.com

libstdc ++を静的にリンクする:落とし穴はありますか?

GCC 4.7のlibstdc ++を使用してUbuntu 12.10でビルドされたC++アプリケーションを、かなり古いバージョンのlibstdc ++が付属するUbuntu 10.04を実行しているシステムにデプロイする必要があります。

現在、このブログ投稿で示唆されているように、-static-libstdc++ -static-libgccでコンパイルしています: libstdc ++を静的にリンク 。著者は、libstdc ++を静的にコンパイルするときに、動的にロードされたC++コードを使用することに対して警告しています。これはまだ確認していません。それでも、これまでのところすべてが順調に進んでいるようです。Ubuntu10.04でC++ 11機能を使用できます。

この記事は2005年からのものであり、おそらくそれ以降、大幅に変更されていることに注意してください。そのアドバイスはまだ最新ですか?知っておくべき問題はありますか?

80
Nick Hutchinson

そのブログ投稿はかなり不正確です。

私の知る限り、C++ ABIの変更は、GCCのすべてのメジャーリリース(つまり、異なる第1または第2バージョン番号コンポーネントを持つリリース)で導入されています。

違います。 GCC 3.4以降に導入された唯一のC++ ABIの変更は、下位互換性があります。つまり、C++ ABIはほぼ9年間安定しています。

さらに悪いことに、ほとんどの主要なLinuxディストリビューションはGCCスナップショットを使用したり、GCCバージョンにパッチを適用したりするため、バイナリを配布するときにどのGCCバージョンを扱っているかを正確に知ることは事実上不可能です。

ディストリビューションのパッチを適用したGCCのバージョン間の違いはわずかであり、ABIの変更ではありませんFedora 4.6.3 20120306(Red Hat 4.6.3-2)は、アップストリームFSF 4.6.xリリースとABI互換性があり、他のディストリビューションの4.6.xとほぼ確実に互換性があります。

GNU/Linuxでは、GCCのランタイムライブラリはELFシンボルバージョン管理を使用しているため、オブジェクトとライブラリに必要なシンボルバージョンを簡単に確認できます。また、それらのシンボルを提供するlibstdc++.soがあれば、それが機能するかどうかは関係ありません。ディストリビューションの別のバージョンとは若干異なるパッチが適用されたバージョン。

ただし、これが機能する場合、C++コード(またはC++ランタイムサポートを使用するコード)を動的にリンクすることはできません。

これも事実ではありません。

ただし、libstdc++.aへの静的リンクは1つのオプションです。

ライブラリを(dlopenを使用して)動的にロードすると機能しない場合がある理由は、依存するlibstdc ++シンボルが(静的に)リンクされたときにアプリケーションで必要ないため、これらのシンボルが実行可能ファイルに存在します。これは、共有ライブラリをlibstdc++.soに動的にリンクすることで解決できます(依存している場合、これは正しいことです)。ELFシンボルの挿入は、実行可能ファイルに存在するシンボルが共有によって使用されることを意味しますライブラリですが、実行可能ファイルに存在しない他のライブラリは、リンク先のlibstdc++.soにあります。アプリケーションがdlopenを使用しない場合、それを気にする必要はありません。

別のオプション(および私が好むオプション)は、アプリケーションと一緒に新しいlibstdc++.soをデプロイし、それがデフォルトのシステムlibstdc++.soの前に見つかることを確認することです。これは、動的リンカーに実行時に$LD_LIBRARY_PATH環境変数を使用するか、リンク時に実行可能ファイルにRPATHを設定して、適切な場所に配置します。 RPATHは、アプリケーションが機能するために正しく設定されている環境に依存しないため、使用することを好みます。アプリケーションを'-Wl,-rpath,$Origin'とリンクする場合(シェルが$Originを展開しようとするのを防ぐために単一引用符に注意してください)、実行可能ファイルには$OriginRPATHがあります。実行可能ファイル自体と同じディレクトリで共有ライブラリを探す動的リンカー。新しいlibstdc++.soを実行可能ファイルと同じディレクトリに配置すると、実行時に検出され、問題が解決します。 (別のオプションは、実行可能ファイルを/some/path/bin/に、より新しいlibstdc ++。soを/some/path/lib/に配置し、'-Wl,-rpath,$Origin/../lib'または実行可能ファイルに関連するその他の固定場所にリンクし、RPATHを$Origin

120
Jonathan Wakely

Jonathan Wakelyの優れた答えに加えて、dlopen()に問題がある理由:

GCC 5の新しい例外処理プール( PR 64535 および PR 65434 を参照)により、libstdc ++に静的にリンクされているライブラリをdlopenおよびdlcloseすると、毎回(プールオブジェクトの)メモリリーク。したがって、dlopenを使用する可能性がある場合、libstdc ++を静的にリンクするのは本当に悪い考えのようです。これは PR 65434 で言及されている良性のものとは対照的に、実際のリークであることに注意してください。

9
Emil Styrke

また、動的glibcに依存しないことを確認する必要があります。結果の実行可能ファイルでlddを実行し、動的依存関係(libc/libm/libpthreadは通常の疑いがある)に注意します。

追加の演習では、この方法を使用して関連するC++ 11の例を多数構築し、実際の10.04システムで結果のバイナリを実際に試します。ほとんどの場合、動的な読み込みで何か変なことをしない限り、プログラムが機能するかクラッシュするかはすぐにわかります。

ジョナサン・ウェイクリーの答えに次のことを加えたいと思います。

Linuxで_-static-libstdc++_をいじって、dlclose()の問題に直面しました。 _libstdc++_に静的にリンクされたアプリケーション 'A'があり、実行時に_libstdc++_プラグイン 'P'に動的にリンクされてロードしたとします。それはいいです。しかし、「A」が「P」をアンロードすると、セグメンテーション障害が発生します。私の想定では、_libstdc++.so_をアンロードした後、「A」は_libstdc++_に関連するシンボルを使用できなくなります。 「A」と「P」の両方が_libstdc++_に静的にリンクされている場合、または「A」が動的にリンクされ、「P」が静的にリンクされている場合、問題は発生しません。

概要:アプリケーションが_libstdc++_に動的にリンクする可能性のあるプラグインをロード/アンロードする場合、アプリも動的にリンクする必要があります。これは私の観察であり、コメントを取得したいと思います。

0
Fedorov7890

RPATHに関するJonathan Wakelyの回答へのアドオン:

RPATHは、問題のRPATHが実行中のアプリケーションのRPATHである場合にのみ機能します。独自のRPATHを介して任意のライブラリに動的にリンクするライブラリがある場合、ライブラリのRPATHは、それをロードするアプリケーションのRPATHによって上書きされます。これは、アプリケーションのRPATHがライブラリのRPATHと同じであることを保証できない場合の問題です。依存関係が特定のディレクトリにあることが予想されるが、そのディレクトリがアプリケーションのRPATHの一部ではない場合。

たとえば、GCC 4.9のlibstdc ++。so.xに動的にリンクされた依存関係を持つアプリケーションApp.exeがあるとします。 App.exeには、RPATHを介してこの依存関係が解決されています。

App.exe (RPATH=.:./gcc4_9/libstdc++.so.x)

ここで、GCC 5.5のlibstdc ++。so.yに動的にリンクされた依存関係を持つ別のライブラリDependency.soがあるとします。ここでの依存関係は、ライブラリのRPATHを介して解決されます。

Dependency.so (RPATH=.:./gcc5_5/libstdc++.so.y)

App.exeがDependency.soをロードするとき、ライブラリのRPATHを追加も追加もしません。それはまったく相談しません。考慮される唯一のRPATHは、実行中のアプリケーションのRPATH、またはこの例ではApp.exeです。つまり、ライブラリがgcc5_5/libstdc ++。so.yにあるがgcc4_9/libstdc ++。so.xにはないシンボルに依存している場合、ライブラリはロードに失敗します。

私は過去に自分でこれらの問題にぶつかったので、これは警告の言葉と同じです。 RPATHは非常に便利なツールですが、その実装にはまだいくつかの落とし穴があります。

0