2つのライブラリが同じ名前の関数を提供して競合が発生した場合はどうすればよいですか?


93

同等の名前の関数を提供する2つのライブラリがある場合はどうすればよいですか?


2
これらの静的ライブラリまたは動的にリンクされていますか?
アルニタク

詳細が必要です...それらの名前はエクスポートされますか?またはそれらは内部でのみ使用されますか?名前を変えてもらえますか?
ヨハネスシャウブ-litb 2009年

どちらも動的にリンクされています。ライブラリを所有していないため、名前を変更できません。
qeek 2009年

すばらしい質問です。もちろん、それはすべてのシンボルが固有のIDが付いた場合は、これらの2つのライブラリでの問題ではないであろう(例えばvorbis_...sf_...sdl_...)。これは基本的に、C ++が名前空間付き関数のシンボル名に対して行うことです。
Vortico 2013

これは非常に興味深い質問ですが、残念ながら不正確すぎて、あまりに幅広い回答が多すぎる理由です。
yugr

回答:


52
  • 1つまたは両方を制御する場合:1つを編集して名前を変更して再コンパイルするか、同等に、Benunknownの回答を参照してください。ソースコードにアクセスしなくても機能します。
  • それらのいずれかを制御しない場合は、そのうちの1つをラップできます。これは別の静的にリンクされた)ライブラリをコンパイルして、別の名前のラッパーを介して到達した問題のシンボルを除いて、元のすべてのシンボルを再エクスポートする以外は何もしません。なんて面倒か。
  • 後で追加しました: qeekは、彼は動的ライブラリの話だと言うので、によって提案されたソリューションフェルッチョmouvicielはおそらく最高です。(私は静的リンケージがデフォルトだった昔の時代に生きているようです。それは私の考えを色づけます。)

コメントを適切に:「エクスポート」とは、ライブラリにリンクしているモジュールから見えるようにすることを意味します--- externファイルスコープのキーワードと同等です。これがどのように制御されるかは、OSとリンカーに依存します。そしてそれは私がいつも見上げなければならないものです。


それも私の最初の考えでしたが、同じ衝突の問題になってしまいませんか?最後に、プロジェクト全体をリンクする必要があります-コンパイル/リンク時または実行時-この時点で、問題のある両方のライブラリをそのままロードする必要があります。
Sniggerfardimungus 2009年

@unknown:ラッパー静的リンケージでコンパイルする必要あり、問題のシンボルをエクスポートしないでください。その後も、ラッパーを動的にリンクできます。より明確にするために編集していただき、ありがとうございます。
dmckee ---元モデレーターの子猫

qeekの問題が静的ライブラリではなくddlにある場合、ラッパーを使用して新しいライブラリを作成するにはどうすればよいですか?なぜなら、ラッパーライブラリは、最初にリンクしたくないライブラリ内の関数を動的にラップする必要があるからです。
jeffD

@dmckee-「エクスポート」とはどういう意味ですか?

4
おそらく誰かがこのテクニックの簡単な例を提供できますか?1つのexe、それぞれに同じ名前の1つの関数を含む2つのライブラリ。

52

を使用してオブジェクトファイル内のシンボルの名前を変更できますobjcopy --redefine-sym old=new file(man objcopyを参照)。

次に、新しい名前を使用して関数を呼び出し、新しいオブジェクトファイルにリンクします。


1
いいね。これはMakefileに追加するのは簡単です。ライブラリが更新される場合、objcopyの呪文は、他のいくつかのソリューションよりもはるかに簡単に更新できます。
sigjuice 2009年

8
ヘッダーファイル内のシンボルの名前も変更することを忘れないでください。
mouviciel 2009

^ sed / awk / perlは、ヘッダー内のシンボルの名前を自動的に変更するのにも役立ちます
Alex Reinking

16

Windowsでは、LoadLibrary()を使用してこれらのライブラリの1つをメモリにロードし、次にGetProcAddress()を使用して、呼び出す必要がある各関数のアドレスを取得し、関数ポインターを介して関数を呼び出すことができます。

例えば

HMODULE lib = LoadLibrary("foo.dll");
void *p = GetProcAddress(lib, "bar");
// cast p to the approriate function pointer type (fp) and call it
(*fp)(arg1, arg2...);
FreeLibrary(lib);

foo.dll内のbarという名前の関数のアドレスを取得して呼び出します。

Unixシステムが同様の機能をサポートしていることは知っていますが、その名前は思いつきません。


dlopen dlsym、およびdlclose。ただし、Unixでのカプセル化は、Windowsほど効果的ではない場合があります。
user877329


8

ここに考えがあります。16進エディタで問題のあるライブラリの1つを開き、問題のある文字列のすべての出現箇所を別のものに変更します。これで、今後のすべての呼び出しで新しい名前を使用できるようになります。

更新: 私はこの目的のためにそれをやっただけで、うまくいくようです。 もちろん、私はこれを徹底的にテストしていません-hexeditショットガンで脚を吹き飛ばす本当に良い方法に過ぎないかもしれません。


実際にはひどい解決策ではありません。少しハックですが、シンボルテーブルの文字列を変更するだけです。その中に実際の機能上の害はありません。
エヴァンテラン

おそらく、ライブラリの名前も変更する必要があります。他の人がやって来て、もう一度ロードを試みないようにするためです。あなたは1つの紛争から数十または数百に行くでしょう。=] stackoverflowについて私はこれが大好きです:質問に対するテスト済みの回答があり、3票があります。最初の(不完全な)答え:17. =]
Sniggerfardimungus 2009年

名前を短くすることしかできないため、名前を変更する機会は限られています。また、LinuxではELFハッシュテーブルの更新に苦労します。
yugr

7

Linuxを使用すると仮定すると、最初に追加する必要があります

#include <dlfcn.h>

たとえば、適切なコンテキストで関数ポインタ変数を宣言します。

int (*alternative_server_init)(int, char **, char **);

https://stackoverflow.com/a/678453/1635364で述べられているFerruccioのように、実行して、使用するライブラリを明示的にロードします(お気に入りのフラグを選択します)。

void* dlhandle;
void* sym;

dlhandle = dlopen("/home/jdoe/src/libwhatnot.so.10", RTLD_NOW|RTLD_LOCAL);

後で呼び出す関数のアドレスを読み取る

sym = dlsym(dlhandle, "conflicting_server_init");

次のように割り当ててキャストする

alternative_server_init = (int (*)(int, char**, char**))sym;

オリジナルと同じように呼び出します。最後に、実行してアンロードします

dlclose(dlhandle);


6

一緒に使用しないでください。私が正しく覚えていると、そのような場合にリンカがエラーを発行します。

私がしようとしませんでしたが、解決策がある可能性がありdlopen()dlsym()そしてdlclose()そのあなたがプログラムで動的ライブラリを処理することができます。2つの関数が同時に必要ない場合は、最初のライブラリを開き、最初の関数を使用して、2番目のライブラリ/関数を使用する前に最初のライブラリを閉じます。


ありがとう。これについては考えていませんでした。でも、両方同時に食べたいです。
qeek

両方を同時に使用したい場合はどうなりますか?
QZHua 2016年

@QZHua:他のアンサー(たとえば、シンボル名の変更を含む)が問題を解決するはずです。
mouviciel 2016年

6

そこに.oファイルがある場合、良い答えはここにあります:https : //stackoverflow.com/a/6940389/4705766

概要:

  1. objcopy --prefix-symbols=pre_string test.o .oファイル内のシンボルの名前を変更するには

または

  1. objcopy --redefine-sym old_str=new_str test.o .oファイル内の特定のシンボルの名前を変更します。

4

この問題が、c ++に名前空間がある理由です。同じ名前を持つ2つのサードパーティライブラリのcには、本当に素晴らしいソリューションはありません。

動的オブジェクトの場合、共有オブジェクト(LoadLibrary / dlopen / etc)を明示的にロードして、その方法で呼び出すことができる場合があります。あるいは、同じコードで同時に両方のライブラリが必要ない場合は、静的リンクを使用して何かを実行できます(.lib / .aファイルがある場合)。

もちろん、これらのソリューションはすべてのプロジェクトに適用されません。


1
そうそう。この一般的な質問では、これは良い答えのようです。ただし、同じコンパイラですべてをまとめてコンパイルする場合、名前空間は便利です。名前:衝突はありません。しかし、バイナリ形式のライブラリを取得し、それを別のコンパイラと統合したい場合は、幸運を祈ります。オブジェクトファイル内の名前変換ルールは、最初のオブスタックルにすぎません(名前空間の効果を元に戻す外部「C」が役立つ場合があります)。
Tomasz Gandor 2014

3

誓う?私が知る限り、同じ名前のリンクポイントを公開する2つのライブラリがあり、両方に対してリンクする必要がある場合、できることは多くありません。


12
誓いは間違いなく最初のステップです。間違いなし。
dmckee ---元モデレーターの子猫

1
「できることはあまりない」-これはまだ関連性があるのでしょうか?他の答えは多くの異なる解決策を提供ます。
yugr

2

あなたはそれらの一つの周りのラッパーライブラリを書くべきです。ラッパーライブラリは、一意の名前のシンボルを公開する必要があります。一意でない名前のシンボルは公開しないでください。

もう1つのオプションは、ヘッダーファイルで関数名を変更し、ライブラリオブジェクトアーカイブでシンボルの名前を変更することです。

どちらにしても、両方を使用するには、ハックジョブになります。



1

質問は10年前に近づいていますが、常に新しい検索があります...

すでに答えたように、-redefine-symフラグを指定したobjcopyは、Linuxでは適切な選択です。完全なドキュメントについては、たとえば、https://linux.die.net/man/1/objcopyを参照してください。変更を加えながら基本的にライブラリ全体をコピーしており、更新のたびにこの作業を繰り返す必要があるため、少し不格好です。しかし、少なくともそれはうまくいくはずです。

Windowsの場合、ライブラリを動的にロードすることは解決策であり、Linuxのdlopen代替のような永続的なものは解決策です。ただし、dlopen()とLoadLibrary()の両方でコードが追加され、名前の重複のみが問題の場合は回避できます。ここで、Windowsソリューションはobjcopyアプローチよりもエレガントです。ライブラリ内のシンボルが他の名前で知られていることをリンカーに伝え、その名前を使用するだけです。それを行うにはいくつかのステップがあります。defファイルを作成し、EXPORTSセクションで名前の変換を提供する必要があります。https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx(VS2015、最終的には新しいバージョンに置き換えられる)を参照してくださいまたは http://www.digitalmars.com/ctg/ctgDefFiles.html(おそらくより永続的)defファイルの完全な構文の詳細。このプロセスでは、ライブラリの1つにdefファイルを作成し、このdefファイルを使用してlibファイルを作成し、そのlibファイルにリンクします。(Windows DLLの場合、libファイルはリンクにのみ使用され、コード実行には使用されません。)libファイルをビルドするプロセスの.dllファイルとヘッダーファイルがある場合に.libファイルを作成する方法を参照してください。ここでの唯一の違いは、エイリアスの追加です。

LinuxとWindowsの両方で、名前がエイリアスされているライブラリのヘッダーの関数の名前を変更します。機能するもう1つのオプションは、新しい名前を参照するファイルで、#define old_name new_nameを実行し、#エクスポートのエイリアスが設定されているライブラリのヘッダーを含め、呼び出し元で#undef old_nameを実行することです。ライブラリを使用しているファイルが多数ある場合、より簡単な代替方法は、定義、インクルード、およびundefsをラップするヘッダーを作成し、そのヘッダーを使用することです。

この情報がお役に立てば幸いです。


0

私はdlsym、dlopen、dlerror、dlclose、dlvsymなどを使用したことがありませんが、manページを調べています。これは、libm.soを開いてcos関数を抽出する例を示しています。dlopenは衝突を探すプロセスを実行しますか?そうでない場合、OPは両方のライブラリを手動でロードし、ライブラリが提供するすべての関数に新しい名前を割り当てるだけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.