13

はじめに

長い質問を前もってお詫び申し上げます。できる限り短くしていますが、残念ながらそれほど短くはありません。

設定

A と B の 2 つのインターフェイスを定義しました。

class A // An interface
{
public:
  virtual ~A() {}

  virtual void whatever_A()=0;
};

class B // Another interface
{
public:
  virtual ~B() {}

  virtual void whatever_B()=0;
};

次に、クラス C のオブジェクトを構築し、A と B の両方を実装してから、それらの A インターフェイスへのポインタを渡す共有ライブラリ「testc」があります。

class C: public A, public B
{
public:
  C();
  ~C();

  virtual void whatever_A();
  virtual void whatever_B();
};

A* create()
{
  return new C();
}

最後に、2 番目の共有ライブラリ「testd」があります。これは、A*入力として を受け取り、それを にキャストしようとしますB*dynamic_cast

void process(A* a)
{
  B* b = dynamic_cast<B*>(a);
  if(b)
    b->whatever_B();
  else
    printf("Failed!\n");
}

最後に、メイン アプリケーションA*があり、ライブラリ間で 'sを渡します。

A* a = create();
process(a);

質問

「testc」および「testd」ライブラリにリンクしてメイン アプリケーションをビルドすると、すべてが期待どおりに機能します。ただし、「testc」および「testd」に対してリンクしないようにメイン アプリケーションを変更し、代わりにdlopen/を使用して実行時にロードするとdlsymdynamic_cast失敗します。

私はなぜなのか理解していない。手がかりはありますか?

追加情報

4

4 に答える 4

15

ここで私の質問に対する答えを見つけました。私が理解しているように、「testc」で使用できる typeinfo をライブラリ「testd」で使用できるようにする必要があります。を使用するときにこれを行うにはdlopen()、さらに 2 つのことを行う必要があります。

  • ライブラリをリンクするときは、リンカーに-Eオプションを渡して、未解決のシンボルだけでなく、すべてのシンボルを実行可能ファイルにエクスポートするようにします (何もないため)。
  • でライブラリをロードするときdlopen()に、オプションを追加して、RTLD_GLOBALによってエクスポートされたシンボルtestcも使用できるようにします。testd
于 2010-02-28T18:58:01.810 に答える
5

一般に、gcc は dlopen 境界を越えた RTTI をサポートしていません。私はこのめちゃくちゃなtry/catchの個人的な経験がありますが、あなたの問題はもっと同じように見えます. 残念ながら、dlopen 全体で単純なものに固執する必要があると思います。

于 2010-02-28T17:20:26.747 に答える
3

この問題にも遭遇したので、この質問に追加する必要があります。

を提供-Wl,-Eして使用してRTLD_GLOBALも、dynamic_casts は失敗しました。-Wl,-Eただし、ライブラリだけでなく、実際のアプリケーションのリンケージも渡すことで修正されたようです。

于 2010-03-10T15:56:10.013 に答える
2

メインアプリケーションのソースを制御できない場合、-Wl,-E は適用されません。独自のバイナリ (ホスト so とプラグイン) をビルドする際に -Wl,-E をリンカーに渡しても役に立ちません。私の場合、唯一の有効な解決策は、RTLD_GLOBAL フラグを使用して、ホストの _init 関数からホストをロードおよびアンロードすることでした (以下のコードを参照)。この解決策は、次の両方の場合に機能します。

  1. メインアプリケーションはホストに対してリンクします。
  2. メイン アプリケーションはホストをロードするので、dlopen を使用します (RTLD_GLOBAL なし)。

どちらの場合も、 gcc 可視性 wikiに記載されている指示に従う必要があります。

プラグインとホストのシンボルを相互に見えるようにし (#pragma GCC visibility push/pop または対応する属性を使用)、RTLD_GLOBAL 1 を使用してプラグインを (ホスト so から) ロードすると、ロードしなくても機能します。独自のものをアンロードします(上記のリンクで述べたように)。このソリューションにより、2. も機能しますが、これは以前にはありませんでした。


// get the path to the module itself
static std::string get_module_path() {
    Dl_info info;
    int res = dladdr( (void*)&get_module_path, &info);
    assert(res != 0); //failure...

    std::string module_path(info.dli_fname);
    assert(!module_path.empty()); // no name? should not happen!
    return module_path;
}

void __attribute__ ((constructor)) init_module() {
    std::string module = get_module_path();

    // here the magic happens :)
    // without this 2. fails
    dlclose(dlopen(module.c_str(), RTLD_LAZY | RTLD_GLOBAL));
}
于 2011-04-20T09:47:26.607 に答える