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


90

Ubuntu12.10で構築されたC ++アプリケーションとGCC4.7のlibstdc ++を、かなり古いバージョンのlibstdc ++が付属しているUbuntu10.04を実行しているシステムにデプロイする必要があります。

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

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


いいえ、libstdc ++に静的にリンクすることはそれを意味するものではありません。その場合、-static-libstdc++オプションに意味がないことを意味する場合は、使用するだけです-static
Jonathan Wakely 2012

@JonathanWakely -staticはkernel too old、一部のubuntu1404システムでエラーが発生します。glibc.soはkernel32.dllウィンドウのようなもので、オペレーティングシステムインターフェイスの一部であるため、バイナリに埋め込むべきではありません。を使用objdump -T [binary path]して、動的にロードされているlibstdc++.soかどうかを確認できます。golangプログラマーの場合、#cgo linux LDFLAGS: -static-libstdc++ -static-libgcc「C」をインポートする前に追加できます
ブロンズマン

@bronzemanが、我々は話をしている-static-libstdc++ではない-staticので、libc.so静的にリンクされることはありません。
Jonathan Wakely 2018年

1
@NickHutchinsonリンク先のブログ投稿はなくなりました。このSOの質問は、ここで関連する用語の人気のある検索ヒットです。質問のそのブログ投稿から重要な情報を再現できますか、または移動先がわかっている場合は新しいリンクを提供できますか?
ブライアンカイン

1
インターネットアーカイブはそれを持っている@BrianCain:web.archive.org/web/20160313071116/http://www.trilithium.com/...
ロブKeniger

回答:


134

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

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

違います。GCC3.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リリースとほぼ確実に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)、実行可能ファイルには、実行可能ファイル自体と同じディレクトリで共有ライブラリを探すようにダイナミックリンカに指示するRPATH$ORIGINあります。あなたが新しいものを置くならlibstdc++.so実行可能ファイルと同じディレクトリにあり、実行時に検出され、問題が解決されます。(別のオプションは、実行可能ファイル/some/path/bin/と新しいlibstdc ++。soを配置し、実行可能ファイルまたはその他の固定された場所に/some/path/lib/リンクし'-Wl,-rpath,$ORIGIN/../lib'、RPATHをに設定することです$ORIGIN


8
この説明、特にRPATHについては、すばらしい説明です。
nilweed 2014

3
Linux上のアプリと一緒にlibstdc ++を出荷することは悪いアドバイスです。これがもたらすすべてのドラマを見るために「スチームlibstdc ++」のためのグーグル。要するに、exeがlibstdc ++を再びdlopenしたい外部ライブラリ(openglなど)をロードする場合(radeonドライバーなど)、それらのライブラリは、独自のものではなく、すでにロードされているため libstdc ++を使用します。期待します。だから、あなたは正方形に戻っています。

7
@ cap、OPは、システムlibstdc ++が古いディストリビューションへのデプロイについて具体的に質問しています。Steamの問題は、システムよりも古いlibstdc ++。soをバンドルしたことです(おそらく、バンドルした時点では新しいものでしたが、ディストリビューションはさらに新しいものに移行しました)。これはlibstdc++.so.6、インストール時にバンドルされたライブラリを指すように設定されたシンボリックリンクを含むディレクトリをRPATHが指すようにするか、新しい場合はシステムライブラリを指すようにすることで解決できます。Red Hat DTSで使用されているように、より複雑な混合リンケージモデルがありますが、自分で行うのは困難です。
Jonathan Wakely 2015年

5
ちょっと男、下位互換性のあるバイナリに「libstdc ++ ABI互換性を維持するために他の人を信頼する」または「実行時にlibstdc ++を条件付きでリンクする」を含めたくない場合は申し訳ありません...それがここでいくつかの羽を乱す場合そしてそこで、私に何ができるか、私は無礼を意味しません。そして、memcpy @ GLIBC_2.14のドラマを覚えているなら、これで信頼の問題を抱えていることで私を責めることはできません:)

6
'-Wl、-rpath、$ ORIGIN'を使用する必要がありました(rpathの前の '-'に注意してください)。編集は6文字以上である必要があるため、回答を編集できません....
user368507 2016年

11

Jonathan Wakelyの優れた答えへの1つの追加、なぜdlopen()が問題になるのか:

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


1
この関数__gnu_cxx::__freeres()は、プールオブジェクトの内部バッファーを解放するため、この問題に少なくともある程度の助けを提供しているようです。しかし、私にとっては、この関数の呼び出しが、後で誤ってスローされた例外に関してどのような意味を持つのかはかなり不明確です。
phlipsy 2018

3

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

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

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

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

ここで、GCC5.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は非常に便利なツールですが、その実装にはまだいくつかの落とし穴があります。


したがって、共有ライブラリのRPATHは無意味です。そして、私は、彼らは、Linuxに最後の2数十年で、この点で少し...改善することを、期待していた
フランク・パック

2

また、動的glibcに依存しないようにする必要がある場合もあります。実行してldd、あなたの結果として実行可能にし、任意の動的な依存関係に注意してください(libcの/ libmを/ libpthreadのUSAL容疑者です)。

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


1
動的glibcに依存することの問題は何ですか?
Nick Hutchinson 2012

少なくともしばらく前は、libstdc ++はglibcへの依存を暗示していたと思います。今日の状況がわからない。
アレクサンダーL.ベリコフ2012

9
libstdc ++はglibcに依存します(たとえば、iostreamはの観点から実装されますprintf)が、Ubuntu 10.04のglibcが新しいlibstdc ++に必要なすべての機能を提供する限り、動的glibcに依存しても問題はありません。実際、リンクしないことを強くお勧めします。静的にglibcに
Jonathan Wakely 2012

1

JonathanWakelyの回答に次のように追加したいと思います。

-static-libstdc++Linuxで遊んでいると、で問題が発生しましたdlclose()。アプリケーション「A」が静的にリンクされてlibstdc++おりlibstdc++、実行時にプラグイン「P」に動的にリンクされてロードされるとします。それはいいです。ただし、「A」が「P」をアンロードすると、セグメンテーション違反が発生します。私の仮定では、アンロード後libstdc++.so、「A」はに関連するシンボルを使用できなくなりますlibstdc++。'A'と 'P'の両方が静的にリンクされているlibstdc++場合、または 'A'が動的にリンクされて 'P'が静的にリンクされている場合、問題は発生しないことに注意してください。

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


1
これはおそらくlibc実装の混合に似ています(たとえば、プラグインに動的にリンクすると、プラグインがglibcに動的にリンクしますが、アプリケーション自体は静的にmusl-libcにリンクされます)。musl-libcの作者であるRichFelkerは、このようなシナリオでの問題は、glibcのメモリ管理(を使用sbrk)が特定の仮定を行い、1つのプロセス内で単独であるとほぼ予想していることであると主張しています...これがに限定されているかどうかはわかりませんただし、特定のglibcバージョンなど。
0xC0000022L

そして人々は、単一のプロセス内でlibc ++ / libcの複数の独立したコピーを処理できるWindowsヒープインターフェイスの利点をまだ理解していません。そのような人々はソフトウェアを設計すべきではありません。
フランクパック

@FrankPuckはWindowsとLinuxの両方の経験が豊富で、MSVCがどのアロケーターをどのように使用するかを決定する当事者である場合、「Windows」の方法は役に立ちません。Windowsのヒープで私が目にする主な利点は、小片を配って、一挙にそれらを解放できることです。ただし、MSVCを使用すると、たとえば、別のVCランタイムによって割り当てられたポインターを渡す場合(リリースとデバッグ、または静的と動的にリンク)など、上記の問題のほとんどに遭遇します。したがって、「Windows」は免除されません。両方のシステムに注意を払う必要があります。
0xC0000022L
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.