Was kann ich tun, wenn zwei Bibliotheken eine Funktion mit demselben Namen bereitstellen, die einen Konflikt erzeugt?

93

Was soll ich tun, wenn ich zwei Bibliotheken habe, die Funktionen mit gleichwertigen Namen bereitstellen?

qeek
quelle
2
Sind diese statischen Bibliotheken oder dynamisch verknüpft?
Alnitak
Wir brauchen mehr Details ... werden diese Namen exportiert? oder werden sie nur intern verwendet? Können Sie die Namen ändern?
Johannes Schaub - litb
Sie sind beide dynamisch miteinander verbunden. Ich kann die Namen nicht ändern, da ich die Bibliotheken nicht besitze.
Qeek
Gute Frage. Natürlich wäre es kein Problem mit diesen beiden Bibliotheken, wenn alle Symbole mit einer eindeutigen ID vorangestellt wurden (zB vorbis_..., sf_..., sdl_...). Dies ist im Wesentlichen das, was C ++ mit den Symbolnamen für Funktionen mit Namespace macht.
Vortico
Dies ist eine sehr interessante Frage, aber leider zu ungenau, weshalb zu viele zu breite Antworten vorliegen.
yugr

Antworten:

52
  • Wenn Sie eine oder beide steuern: Bearbeiten Sie eine, um den Namen zu ändern und neu zu kompilieren. Oder sehen Sie gleichwertig die Antworten von Ben und Unbekannt , die ohne Zugriff auf den Quellcode funktionieren .
  • Wenn Sie keinen von ihnen kontrollieren, können Sie einen von ihnen einpacken. Das heißt, kompilieren Sie eine andere ( statisch verknüpfte !) Bibliothek, die nichts anderes tut, als alle Symbole des Originals erneut zu exportieren, außer das fehlerhafte, das über einen Wrapper mit einem alternativen Namen erreicht wird. Was für ein Ärger.
  • Später hinzugefügt: Da qeek sagt, er spreche von dynamischen Bibliotheken, sind die von Ferruccio und mouviciel vorgeschlagenen Lösungen wahrscheinlich die besten. (Ich scheine in längst vergangenen Tagen zu leben, als statische Verknüpfung die Standardeinstellung war. Sie beeinflusst mein Denken.)

Apropos die Kommentare: Mit "exportieren" meine ich für Module sichtbar zu machen, die mit der Bibliothek verknüpft sind - entspricht dem externSchlüsselwort im Dateibereich. Wie dies gesteuert wird, hängt vom Betriebssystem und vom Linker ab. Und das muss ich immer nachschlagen.

dmckee --- Ex-Moderator Kätzchen
quelle
Das war auch mein erster Gedanke, aber werden Sie nicht das gleiche Kollisionsproblem haben? Am Ende muss das gesamte Projekt - zur Kompilierungs- / Verknüpfungszeit oder zur Laufzeit - verknüpft werden. Zu diesem Zeitpunkt müssen beide fehlerhaften Bibliotheken unverändert geladen werden.
Sniggerfardimungus
@unknown: Der Wrapper muss mit statischer Verknüpfung kompiliert werden und darf das fehlerhafte Symbol nicht exportieren. Dann können Sie den Wrapper weiterhin dynamisch verknüpfen. Für mehr Klarheit bearbeitet, danke.
dmckee --- Ex-Moderator Kätzchen
Wenn das Problem von qeek bei ddl und nicht bei statischen Bibliotheken liegt, wie ist es dann möglich, eine neue Bibliothek mit einem Wrapper zu erstellen? Da die Wrapper-Bibliothek eine Funktion in der Bibliothek, mit der Sie überhaupt keine Verknüpfung herstellen möchten, dynamisch umschließen müsste.
JeffD
@dmckee - was meinst du mit "exportieren"?
4
Vielleicht könnte jemand ein einfaches Beispiel für diese Technik liefern? Eine exe, zwei Bibliotheken, die jeweils eine Funktion mit demselben Namen enthalten.
52

Es ist möglich, Symbole in einer Objektdatei mit umzubenennen objcopy --redefine-sym old=new file(siehe man objcopy).

Rufen Sie dann einfach die Funktionen mit ihren neuen Namen auf und verknüpfen Sie sie mit der neuen Objektdatei.

Ben
quelle
1
Nett. Das Hinzufügen zu einem Makefile wäre trivial. Wenn die Bibliotheken jemals aktualisiert werden, ist eine Objektbeschwörung viel einfacher zu aktualisieren als einige der anderen Lösungen.
Sigjuice
8
Vergessen Sie nicht, die Symbole in den Header-Dateien ebenfalls umzubenennen.
Mouviciel
^ sed / awk / perl wäre nützlich, um das Umbenennen der Symbole in der Kopfzeile zu automatisieren
Alex Reinking
16

Unter Windows können Sie mit LoadLibrary () eine dieser Bibliotheken in den Speicher laden und dann mit GetProcAddress () die Adresse jeder Funktion abrufen, die Sie aufrufen müssen, und die Funktionen über einen Funktionszeiger aufrufen.

z.B

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);

würde die Adresse einer Funktion namens bar in foo.dll erhalten und sie aufrufen.

Ich weiß, dass Unix-Systeme ähnliche Funktionen unterstützen, aber ich kann mir ihre Namen nicht vorstellen.

Ferruccio
quelle
dlopen dlsymund dlclose. Die Kapselung unter Unix ist jedoch möglicherweise nicht so effektiv wie unter Windows.
user877329
8

Hier ist ein Gedanke. Öffnen Sie eine der fehlerhaften Bibliotheken in einem Hex-Editor und ändern Sie alle Vorkommen der fehlerhaften Zeichenfolgen in etwas anderes. Sie sollten dann in der Lage sein, die neuen Namen in allen zukünftigen Anrufen zu verwenden.

UPDATE: Ich habe es gerade zu diesem Zweck gemacht und es scheint zu funktionieren. Natürlich habe ich das nicht gründlich getestet - es ist möglicherweise nur eine wirklich gute Möglichkeit, Ihr Bein mit einer Hexedit-Schrotflinte abzublasen.

Sniggerfardimungus
quelle
eigentlich keine schreckliche lösung. Ein bisschen hackisch, aber alles, was Sie tun würden, ist die Zeichenfolgen in der Symboltabelle zu ändern. Kein wirklicher Funktionsschaden.
Evan Teran
Sie möchten wahrscheinlich auch die Bibliothek umbenennen, damit nicht jemand anderes mitkommt und versucht, das Ding erneut zu laden. Sie würden von einem Konflikt zu Dutzenden oder Hunderten wechseln. =] Ich liebe das über Stackoverflow: Wir haben eine getestete Antwort auf eine Frage und sie hat 3 Stimmen. Die erste (unvollständige) Antwort: 17. =]
Sniggerfardimungus
Die Umbenennungsmöglichkeiten sind begrenzt, da Sie nur Namen kürzer machen können . Auch unter Linux fällt es Ihnen schwer, ELF-Hash-Tabellen zu aktualisieren.
Yugr
7

Angenommen, Sie verwenden Linux, müssen Sie zuerst hinzufügen

#include <dlfcn.h>

Deklarieren Sie die Funktionszeigervariable im richtigen Kontext, z.

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

Laden Sie, wie Ferruccio unter https://stackoverflow.com/a/678453/1635364 angegeben hat , explizit die Bibliothek, die Sie verwenden möchten, indem Sie sie ausführen (wählen Sie Ihre Lieblingsflags aus).

void* dlhandle;
void* sym;

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

Lesen Sie die Adresse der Funktion, die Sie später aufrufen möchten

sym = dlsym(dlhandle, "conflicting_server_init");

zuweisen und wie folgt besetzen

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

Rufen Sie auf ähnliche Weise wie das Original an. Zum Schluss durch Ausführen entladen

dlclose(dlhandle);
vraa
quelle
6

Sie sollten sie nicht zusammen verwenden. Wenn ich mich richtig erinnere, gibt der Linker in einem solchen Fall einen Fehler aus.

Ich habe nicht versucht, aber eine Lösung kann sein mit dlopen(), dlsym()und dlclose()die es erlauben Sie programmatisch dynamische Bibliotheken handhaben . Wenn Sie die beiden Funktionen nicht gleichzeitig benötigen, können Sie die erste Bibliothek öffnen, die erste Funktion verwenden und die erste Bibliothek schließen, bevor Sie die zweite Bibliothek / Funktion verwenden.

Mouviciel
quelle
Vielen Dank. Ich habe nicht darüber nachgedacht. Obwohl ich gerne beide gleichzeitig haben würde.
Qeek
Was ist, wenn ich beide gleichzeitig verwenden möchte?
QZHua
@QZHua: Andere Antworten (z. B. das Umbenennen von Symbolen) sollten Ihr Problem lösen.
Mouviciel
6

Wenn Sie dort .o-Dateien haben, finden Sie hier eine gute Antwort: https://stackoverflow.com/a/6940389/4705766

Zusammenfassung:

  1. objcopy --prefix-symbols=pre_string test.o um die Symbole in der .o-Datei umzubenennen

oder

  1. objcopy --redefine-sym old_str=new_str test.o um das spezifische Symbol in der .o-Datei umzubenennen.
Jee Lee
quelle
4

Dieses Problem ist der Grund, warum c ++ Namespaces hat. Es gibt in c keine wirklich gute Lösung für 2 Bibliotheken von Drittanbietern mit demselben Namen.

Wenn es sich um ein dynamisches Objekt handelt, können Sie die freigegebenen Objekte (LoadLibrary / dlopen / etc) möglicherweise explizit laden und auf diese Weise aufrufen. Wenn Sie nicht beide Bibliotheken gleichzeitig im selben Code benötigen, können Sie möglicherweise etwas mit statischer Verknüpfung tun (wenn Sie die .lib / .a-Dateien haben).

Keine dieser Lösungen gilt natürlich für alle Projekte.

Brian Mitchell
quelle
1
Oh ja. Für diese allgemeine Frage scheint dies eine gute Antwort zu sein. Namespaces sind jedoch cool, wenn Sie alles zusammen im selben Compiler kompilieren. Hurra, kein Name kollidiert. Aber wenn Sie eine Bibliothek in binärer Form erhalten und sie in einen anderen Compiler integrieren möchten, dann - viel Glück. Regeln für die Namensverfälschung in Objektdateien sind nur das erste Hindernis (externes "C" kann helfen, wodurch der Effekt der Namespaces rückgängig gemacht wird).
Tomasz Gandor
3

Schwören? Soweit mir bekannt ist, können Sie nicht viel tun, wenn Sie zwei Bibliotheken haben, die Verknüpfungspunkte mit demselben Namen verfügbar machen, und Sie müssen gegen beide verknüpfen.

Vatine
quelle
12
Schwören ist definitiv der erste Schritt. Daran besteht kein Zweifel.
dmckee --- Ex-Moderator Kätzchen
1
"Sie können nicht viel tun" - ist das noch relevant? Andere Antworten bieten zahlreiche unterschiedliche Lösungen.
yugr
2

Sie sollten eine Wrapper-Bibliothek um eine von ihnen schreiben. Ihre Wrapper-Bibliothek sollte Symbole mit eindeutigen Namen und nicht die Symbole der nicht eindeutigen Namen verfügbar machen.

Sie können auch den Funktionsnamen in der Header-Datei umbenennen und das Symbol im Archiv des Bibliotheksobjekts umbenennen.

So oder so, um beides zu nutzen, wird es ein Hack-Job.

James Caccese
quelle
1

Die Frage nähert sich einem Jahrzehnt, aber es gibt ständig neue Suchanfragen ...

Wie bereits beantwortet, ist objcopy mit dem Flag --redefine-sym unter Linux eine gute Wahl. Eine vollständige Dokumentation finden Sie beispielsweise unter https://linux.die.net/man/1/objcopy . Es ist etwas umständlich, da Sie im Wesentlichen die gesamte Bibliothek kopieren, während Sie Änderungen vornehmen, und jedes Update erfordert, dass diese Arbeit wiederholt wird. Aber zumindest sollte es funktionieren.

Für Windows ist das dynamische Laden der Bibliothek eine Lösung und eine dauerhafte Lösung wie die dlopen-Alternative unter Linux. Sowohl dlopen () als auch LoadLibrary () fügen jedoch zusätzlichen Code hinzu, der vermieden werden kann, wenn das einzige Problem doppelte Namen sind. Hier ist die Windows-Lösung eleganter als der objcopy-Ansatz: Sagen Sie dem Linker einfach, dass die Symbole in einer Bibliothek unter einem anderen Namen bekannt sind, und verwenden Sie diesen Namen. Es gibt ein paar Schritte dazu. Sie müssen eine def-Datei erstellen und die Namensübersetzung im Abschnitt EXPORTS bereitstellen. Siehe https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015 wird möglicherweise durch neuere Versionen ersetzt) ​​oder http://www.digitalmars.com/ctg/ctgDefFiles.html(wahrscheinlich dauerhafter) für vollständige Syntaxdetails einer def-Datei. Der Prozess wäre, eine Def-Datei für eine der Bibliotheken zu erstellen und diese Def-Datei dann zu verwenden, um eine Lib-Datei zu erstellen und dann mit dieser Lib-Datei zu verknüpfen. (Bei Windows-DLLs werden lib-Dateien nur zum Verknüpfen und nicht zur Codeausführung verwendet.) Weitere Informationen zum Erstellen der lib-Datei finden Sie unter Erstellen einer .lib-Datei, wenn eine DLL-Datei und eine Header- Datei vorhanden sind. Hier besteht der einzige Unterschied darin, die Aliase hinzuzufügen.

Benennen Sie unter Linux und Windows die Funktionen in den Headern der Bibliothek um, deren Namen als Alias ​​verwendet werden. Eine andere Option, die funktionieren sollte, wäre, in Dateien, die sich auf die neuen Namen beziehen, # alten_Namen neuen_Namen zu definieren, # die Header der Bibliothek einzuschließen, deren Exporte aliasiert werden, und dann #undef alten_namen im Aufrufer. Wenn die Bibliothek viele Dateien verwendet, besteht eine einfachere Alternative darin, einen oder mehrere Header zu erstellen, die die Definitionen, Includes und Undefs umschließen, und diesen Header dann zu verwenden.

Hoffe diese Info war hilfreich!

Jim Monte
quelle
0

Ich habe noch nie dlsym, dlopen, dlerror, dlclose, dlvsym usw. verwendet, aber ich schaue auf die Manpage und sie enthält ein Beispiel für das Öffnen von libm.so und das Extrahieren der cos-Funktion. Geht dlopen nach Kollisionen? Wenn dies nicht der Fall ist, kann das OP beide Bibliotheken einfach manuell laden und allen Funktionen, die seine Bibliotheken bereitstellen, neue Namen zuweisen.

Sniggerfardimungus
quelle