重複したシンボルを含むC ++プラグインライブラリでのSegfault
-
10-07-2019 - |
質問
複数の共有ライブラリに分割され、プラグイン共有ライブラリから追加機能をロードするクロスプラットフォームC ++アプリケーションがあります。プラグインライブラリは、呼び出し側アプリケーションの知識や依存関係なしで、それ自体で自己完結型で機能することになっています。
プラグインの1つにはメインアプリケーションからコピーされたコードが含まれているため、エンジンのシンボル名と重複するシンボル名が含まれています。 (はい、私はそれが一般的にノーノーであることを知っていますが、プラグインが書かれた時点では、エンジンはモノリシックバイナリであり、ライブラリを共有できませんでした。)Windowsでは、すべてが正常に動作します。 Linuxでは、セグメンテーション違反が発生していました。エラーのスタックトレースを見ると、重複するクラス名で関数を呼び出すときにプラグインで発生していました。これは、エンジンとプラグインがわずかに異なるバージョンの共有コードを持っている結果であるように見えました(一部のクラス機能はプラグインでコメント化されていました)。プラグインが、それ自体の代わりにエンジンのシンボルランタイムにリンクされたシンボルランタイムを取得しているかのようでした。私たちは<!> quot; fixed <!> quot;問題は、dlopen
のパラメーターをdlopen(pFilepath, RTLD_LAZY | RTLD_LOCAL)
に変更することです。
ただし、(最終的にプラグインで再利用するために)エンジンを書き換えて共有ライブラリに分割すると、再びsegfaultエラーが発生します。スタックトレースを見ると、エンジンから取得されます-<!> gt;プラグイン-<!> gt;エンジン。
プラグインのシンボルをエンジンにマップしないようにランタイムリンカーを指定する方法はありますか(特にプラグインで定義されている場合)?
ありがとう! マット
2009-12-3を編集
最初にプラグインのコードを独自の名前空間でラップしようとしました。エンジンにもリンクされているライブラリに静的にリンクされているため、機能しませんでした。静的ライブラリのバージョンは異なるため、segfault!
次に、エンジンのビルドを変更し、ライブラリを静的にリンクしました。そして、私がそれを実行するとき、私はもはや問題を抱えていません。そのため、共有ライブラリシンボルがエクスポートされ、それが開かれたときにプラグインに動的に再配置された結果であるようです。ただし、エンジンのすべてのコードが単一の実行可能ファイルにある場合、そのシンボルはエクスポートされません(したがって、プラグインのシンボルをエンジンに再配置しようとしません)。
ただし、プログラムの並列化バージョン(Open-MPIを使用)があり、それでもセグメンテーション違反が発生するため、まだ問題があります。エンジンのシンボルをエクスポートし、プラグインを再配置しているという点で表示されます。これは、Open-MPIがアプリケーションを実行する方法に関係している可能性があります。
プラグイン共有ライブラリで使用できる、実行時にシンボルを動的に再配置しないように指示するリンカーフラグはありますか?または、シンボルを非表示にして、移動しないようにしますか? -s
(<!> quot;すべてのシンボル情報を省略します<!> quot;)を試しましたが、ダイナミックシンボルは変更されなかったようです(nm -D <plugin>
を使用してチェック)。
解決
解決策、リンカーフラグ-Bsymbolic
を見つけたと思います。基本的に、このフラグは共有ライブラリにフラグを追加して、ランタイムリンカーに、最初に自身内のシンボル名を試行して解決するように指示します。プラグインがそのフラグとリンクされている場合、エンジンはすべての場合(モノリシックexe、共有ライブラリ付きのexe、名前空間をラップするw / oのプラグイン)でプラグインで正常に実行できました。
<=>:
に関する警告を伴う中傷者がいるようです。
http://www.technovelty.org/code/c/bsymbolic.html
http://software.intel.com/en-us/articles/performance-tools-for-software-developers-bsymbolic-can-cause-dangerous-side-effects/
しかし、彼らの警告とプラグインの意図を考えると、それは私にとって正しい選択肢だと思います。少なくとも今のところ。
他のヒント
Glenに同意します。クラス名を変更する場合を除き、おそらく名前空間を介してこれを実際に解決するつもりはありません。 36個のファイルであっても、シンボル名を変更せずに確実に修正しようとするよりも、修正にかかる時間はおそらく少ないでしょう。
名前を調整する必要があるすべてのクラスを識別することから始めます。あなたのリンカはおそらく既にあなたのためにそれらをリストしています。次に、少なくとも一時的に両方のクラスセットの名前を(たとえばFooからEngine :: FooおよびPlugin :: Fooに)変更します。そうすれば、コンパイラに問題のあるクラスへのすべての参照を見つけさせることができます。プラグインが正しい新しいプラグインクラス名への参照を使用してコンパイルされるまで、プラグインのソースを少しずつ移動します。それが完了したら、Engine ::クラスを元の名前に戻します(エンジンソースも恒久的に変更したい場合を除き、そうではないようです)。これで、プラグインはコンパイルされ、一意の名前が付けられた正しいクラスにリンクするはずです。
プラグインのすべてのコードをPluginX名前空間でラップするだけです。それは間違いなくこれらのエラーからあなたを救います。 とにかくとても良い、重要な練習です。