6

リンクする必要があるクローズドソースのサードパーティ共有ライブラリがあります。残念ながら、サードパーティのライブラリの作成者は、エクスポートするシンボルを制限したり、すべてのシンボルをエクスポートしたりすることを気にしませんでした。サードパーティのライブラリは、コードで使用している一般的なライブラリの互換性のないバージョンを内部的に使用していますが、競合するシンボル (Google の protobuf ライブラリ) をエクスポートしています。これにより、protobuffer ライブラリ バージョン チェックでライブラリのコンパイル時バージョンと実行時バージョンに互換性がないことが検出されると、ランタイム エラーが発生します。サードパーティのライブラリで使用されているバージョンと一致する古いバージョンの protobufs 2.3 に戻すことで、問題を解決できます。ただし、protbuf 2.3 にはパフォーマンスの問題があり、私のアプリケーションでは使用できなくなります。コードで protobuf 2.4 を使用し、サードパーティ ライブラリで使用できるようにする方法が必要です。

so ファイルのみを指定して、内部で使用される protobuf v 2.3 ライブラリからシンボルをエクスポートしないサードパーティ ライブラリの新しいバージョンを生成する方法はありますか? ソースがあれば、もっと簡単な問題になるでしょう。objcopy や strip などのツールは、実際には動的シンボル テーブルを変更できないようです。私がこれまでに持っている唯一のアイデアは、呼び出しをサードパーティのライブラリ (おそらく dlopen で開く?) にリダイレクトすることによって、必要なシンボルのみをエクスポートする独自の shim ライブラリを作成することです。

より良い解決策はありますか?

4

2 に答える 2

5

機能する解決策を見つけました...呼び出しをサードパーティ ライブラリにリダイレクトするシム ライブラリを作成し、ライブラリ外のコードが protbuf v2.4 シンボルを参照できるようにし、サードパーティ ライブラリ内のコードが protobuf v2.3 シンボルを参照できるようにしました。この回避策は、 http ://www.linuxjournal.com/article/7795 に投稿されたアイデアに基づいています。

RTLD_LAZY | を含めるように dlopen フラグを変更する必要がありました。RTLD_LOCAL | RTLD_DEEPBIND. RTLD_LOCAL フラグは、サードパーティ ライブラリ内のシンボルが shim ライブラリの外部に表示されないようにします (シンボルの漏洩を防ぎます)。RTLD_DEEPBIND は、サードパーティ ライブラリ内からの呼び出しに、シンボルの内部バージョンのみを表示するように強制します (シンボルが漏れないようにします)。

具体的には、これが私の shim ライブラリからの抜粋の例です。

#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
#include "libhdfs/hdfs.h"

//#define PRINT_DEBUG_STUFF

// Helper function to retrieve a function pointer to a function from libMapRClient
// while isolating the symbols used internally from those already linked externaly
// to workaround symbol collision problem with the current version of libMapRClient.
void* GetFunc(const char* name){
  #ifdef PRINT_DEBUG_STUFF
    printf("redirecting %s\n", name);
  #endif
  void *handle;
  char *error;

  handle = dlopen("/opt/mapr/lib/libMapRClient.so", RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);

  if (!handle) {
    fputs(dlerror(), stderr);
    exit(1);
  }
  void* fp = dlsym(handle, name);
  if ((error = dlerror()) != 0) {
    fprintf(stderr, "%s\n", error);
    exit(1);
  }
  return fp;
}


hdfsFS hdfsConnect(const char* host, tPort port) {
  typedef hdfsFS (*FP) (const char* host, tPort port);
  static FP ext = 0;
  if (!ext) {
    ext = (FP)GetFunc("hdfsConnect");
  }
  return ext(host, port);
}


int hdfsCloseFile(hdfsFS fs, hdfsFile file) {
  typedef int (*FP) (hdfsFS fs, hdfsFile file);
  static FP ext = 0;
  if (!ext) {
    ext = (FP)GetFunc("hdfsCloseFile");
  }
  return ext(fs, file);
}

... その他のパブリック API 関数についても同様

于 2012-04-19T04:42:12.107 に答える
0

リンカに渡すライブラリの順序を変更すると役立つ場合があります。複数のライブラリが同じシンボルをエクスポートする場合、リンカは最初のライブラリのシンボルを使用する必要があります。ただし、すべてのリンカーがそれに準拠しているわけではないので、そのドキュメントを調べてください。

于 2012-04-18T18:37:39.180 に答える