web-dev-qa-db-ja.com

2つの共有ライブラリを同じシンボルのいくつかとリンクする

2つの異なる共有ライブラリにリンクしています。両方のライブラリは、名前を共有するが実装が異なるいくつかのシンボルを定義します。ライブラリごとに独自の実装を使用することはできません。

たとえば、両方のライブラリは、それぞれが内部的に呼び出すグローバル関数bar()を定義します。ライブラリ1はfoo1()から呼び出し、ライブラリ2はfoo2()から呼び出します。

Lib1.so:

_T bar
T foo1()     // calls bar()
_

Lib2.so:

_T bar
T foo2()     // calls bar()
_

アプリケーションをLib1.soにリンクし、次にLib2.soにリンクすると、foo2()を呼び出している場合でも、Lib1.soからのバー実装が呼び出されます。一方、アプリケーションをLib2.soにリンクし、次にLib1.soにリンクした場合、barは常にLib2.soから呼び出されます。

ライブラリに他のライブラリよりも常に独自の実装を優先させる方法はありますか?

46
drewag

これを解決するにはいくつかの方法があります。

  • -Bsymbolicまたは-Bsymbolic-functionsをリンカ。これにはグローバルな効果があります。ライブラリ内のシンボルに解決できる(-Bsymbolic-functionsの関数型の)グローバルシンボルへのすべての参照は、そのシンボルに解決されます。これにより、LD_PRELOADを使用してこれらのシンボルへの内部ライブラリ呼び出しを挿入する機能が失われます。 シンボルは引き続きエクスポートされるため、ライブラリの外部から参照できます。

  • バージョンスクリプトを使用して、ライブラリに対してシンボルをlocalとしてマークします。 {local: bar;};のようなものを使用し、--version-script=versionfileをリンカーに渡します。 シンボルはエクスポートされません

  • シンボルを適切なvisibilityGCC info page for visibility )でマークします。これはのどちらかになりますhiddeninternal、またはprotectedprotected可視性シンボル.protectedとしてエクスポートされますhiddenシンボルはエクスポートされません、およびinternalシンボルはエクスポートされず、関数ポインタを介して間接的にさえ、ライブラリの外部からそれらを呼び出さないように妥協します。

エクスポートされるシンボルはobjdump -Tで確認できます。

46
ninjalj

既存の各ライブラリに1つずつ、2つの「ラッパー」共有ライブラリを作成する必要があります。それぞれは、APIを定義する競合しないいくつかのシンボルのみをリストする--dynamic-listで構築する必要があります。グローバルな組み合わせを回避するには、-Bsymbolicも必要です。

同様に、適切なオプションを使用してdlopen経由で結果のライブラリにアクセスする方がストレスが少ないかもしれません。

3
bmargulies

この問題を解決する別の方法は、マクロを使用して名前空間を変更することです。

前提条件

  • すべての要素(関数、クラス、グローバル変数など)は名前空間にあります。
  • ライブラリは、ヘッダーのマクロに大きく依存していません。

ソリューション

  • ライブラリをコンパイルするときは、名前空間名を付けてマクロを定義し、別のものに定義します。たとえば、名前空間がLibNSの場合、1つのケースでは-DLibNS=LibNSv1を使用し、もう1つのケースでは-DLibNS=LibNSv2を使用します。
  • コードでライブラリを使用する場合は、現在の状況に応じてマクロを定義します。

    #define LibNS LibNSv1
    #include "my_lib.h"
    #undef LibNS
    

他のソリューションの代わりにこれを使用する理由

  • 問題のあるライブラリがヘッダーファイル(テンプレート、インラインなど)で(少なくとも部分的に)使用されている場合。実行可能ファイルのコードにこれらを含めると、リゾルバーは、これらの関数をLib1.soまたはLib2.soから呼び出す必要があるかどうかわかりません。
  • お使いのコンパイラは他のソリューションをサポートしていません/サポートしていません(インテル/ AMD 32/64ビットCPUでは発生しませんが、他の一部のプラットフォームに問題があるようです)。

潜在的な問題

  • 実行可能ファイルの1つのcppファイルで両方のバージョンを使用すると問題が発生する可能性があります。 #include "my_lib.h"は、おそらくマクロを使用して複数の包含から保護し、それらを未定義にしてこれを回避します。これにより、さまざまな問題が発生する可能性があります(ライブラリの作成者が将来マクロ名を変更する、ヘッダーが他のマクロを定義するなど)。
  • 名前LibNSは、ライブラリ内の他の何か(変数、関数など)に使用される場合があります。その場合、この名前もLibNSv1またはLibNSv2に変更されます。ライブラリとその使用方法によっては、他の問題が発生する可能性があります。

注意事項

  • これは、現在受け入れられている答え(ninjaljから自由にコピーアンドペーストできます)を置き換えるものではありませんが、別の方法で拡張できます。
  • 私がこの回答を投稿した主な理由は、今日この問題が発生したことですが、ヘッダーファイルに問題のあるコードが含まれているため、回答が役に立ちませんでした。
  • 私のソース: https://spin.atomicobject.com/2014/06/03/static-linking-c-plus-plus/
1
Tom