Ich habe eine Anwendung, die statisch mit Version X einer Bibliothek, libfoo, des Drittanbieters VENDOR1 verknüpft ist. Es wird auch mit einer dynamischen (gemeinsam genutzten) Bibliothek, libbar, von einem anderen Drittanbieter, VENDOR2, verknüpft, die Version Y von libfoo von VENDOR1 statisch verknüpft.
libbar.so enthält also Version Y von libfoo.a und meine ausführbare Datei enthält Version X von libfoo.a libbar verwendet libfoo nur intern und es werden keine libfoo-Objekte von meiner App an libbar übergeben.
Zur Buildzeit gibt es keine Fehler, aber zur Laufzeit treten die App-Seg-Fehler auf. Der Grund scheint zu sein, dass Version X Strukturen verwendet, die eine andere Größe haben als Version Y, und der Laufzeitlinker scheint zu verwechseln, welche von welchen verwendet werden.
Sowohl VENDOR1 als auch VENDOR2 sind Closed Source, daher kann ich sie nicht neu erstellen.
Gibt es eine Möglichkeit, meine App so zu erstellen/verknüpfen, dass sie immer in Version X und libbar immer in Version Y aufgelöst wird und die beiden sich nie vermischen?
Danke für alle Antworten. Ich habe eine Lösung, die zu funktionieren scheint. Hier ist das Problem im Detail mit einem Beispiel.
In main.c haben wir:
#include <stdio.h>
extern int foo();
int bar()
{
printf("bar in main.c called\n");
return 0;
}
int main()
{
printf("result from foo is %d\n", foo());
printf("result from bar is %d\n", bar());
}
In foo.c haben wir:
extern int bar();
int foo()
{
int x = bar();
return x;
}
In bar.c haben wir:
#include <stdio.h>
int bar()
{
printf("bar in bar.c called\n");
return 2;
}
bar.c und foo.c kompilieren:
$ gcc -fPIC -c bar.c
$ gcc -fPIC -c foo.c
bar.o zu einer statischen Bibliothek hinzufügen:
$ ar r libbar.a bar.o
Erstellen Sie nun eine gemeinsam genutzte Bibliothek mit foo.o und verknüpfen Sie sie mit der statischen libbar.a
$ gcc -shared -o libfoo.so foo.o -L. -lbar
Kompilieren Sie main.c und verknüpfen Sie es mit der gemeinsam genutzten Bibliothek libfoo.so
$ gcc -o main main.c -L. -lfoo
Stellen Sie LD_LIBRARY_PATH ein, um libfoo.so zu finden, und führen Sie main aus:
$ setenv LD_LIBRARY_PATH `pwd`
$ ./main
bar in main.c called
result from foo is 0
bar in main.c called
result from bar is 0
Beachten Sie, dass die Version von bar in main.c aufgerufen wird, nicht die Version, die mit der gemeinsam genutzten Bibliothek verknüpft ist.
In main2.c haben wir:
#include <stdio.h>
#include <dlfcn.h>
int bar()
{
printf("bar in main2.c called\n");
return 0;
}
int main()
{
int x;
int (*foo)();
void *handle = dlopen("libfoo.so", RTLD_GLOBAL|RTLD_LAZY);
foo = dlsym(handle, "foo");
printf("result from foo is %d\n", foo());
printf("result from bar is %d\n", bar());
}
Kompilieren Sie main2.c und führen Sie es aus (beachten Sie, dass wir nicht explizit mit libfoo.so verknüpfen müssen):
$ gcc -o main2 main2.c -ldl
$ ./main2
bar in bar.c called
result from foo is 2
bar in main2.c called
result from bar is 0
Jetzt foo in der Aufrufleiste der gemeinsam genutzten Bibliothek in der gemeinsam genutzten Bibliothek und der Hauptaufrufleiste in main.c
Ich glaube nicht, dass dieses Verhalten intuitiv ist und es mehr Arbeit macht, dlopen/dlsym zu verwenden, aber es löst mein Problem.
Nochmals vielen Dank für die Kommentare.
Versuchen Sie es mit einem partiellen Link, sodass Sie eine Objektdatei „partial.o“ mit libbar und libfoo-Y haben. Verwenden Sie objcopy mit “–localize-symbols”, um die Symbole in partial.o von libfoo-Y lokal zu machen. Sie sollten in der Lage sein, zu generieren, indem Sie nm auf libfoo-Y ausführen und die Ausgabe massieren. Nehmen Sie dann die modifizierte partial.o und verknüpfen Sie sie mit Ihrer App.
Ich habe etwas Ähnliches mit der gcc-Toolchain auf vxWorks gemacht, wo dynamische Bibliotheken keine Komplikation darstellen, sondern zwei Versionen derselben Bibliothek benötigt werden, um sauber in eine monolithische App zu verlinken.
Entschuldige Nein. Mein Verständnis von Linux (und möglicherweise den meisten * Nixen) ist, dass dies nicht möglich ist. Die einzige “Lösung” für Ihr Problem, die mir einfällt, besteht darin, eine Proxy-App zu erstellen, die das, was Sie von libbar benötigen, in Form von einigen bereitstellt IPC. Sie können dann dafür sorgen, dass dieser Proxy die richtige Version lädt, indem Sie verwenden LD_LIBRARY_PATH
oder etwas ähnliches.
Können Sie Ihre App dynamisch mit VENDOR1 verknüpfen?
– Billy ONeal
12. Juli 2010 um 23:31 Uhr
In keiner Weise sprachneutral. Dies ist sehr spezifisch für den Compiler-Linker und das Betriebssystem, wie diese alle zusammenarbeiten. Am einfachsten ist es, beiden Anbietern eine E-Mail zu senden und zu sehen, wie sie das lösen.
– Martin York
12. Juli 2010 um 23:31 Uhr
Unsere derzeitige Überlegung ist, zumindest unter Linux, dlopen() auf libbar.so mit dem RTLD_DEEPBIND-Flag zu verwenden. Eine andere Möglichkeit besteht darin, die Verwendung von libfoo.a durch Apps in eine gemeinsam genutzte Bibliothek, libbaz.so, aufzuteilen, die die Verwendung von libfoo.a umschließt, und dann die App libbaz.so und libbar.so mit RTLD_LOCAL öffnen zu lassen, was unserer Meinung nach alle behalten könnte die doppelten Symbole intern. Dies funktioniert möglicherweise für Linux, aber wir brauchen es, damit es auch unter Solaris, AIX und HPUX funktioniert.
– YerBlues
13. Juli 2010 um 0:05 Uhr
Ohhhh Yuck. Leider nein, unter Linux gibt es keinen “einfachen” Weg, dies zu umgehen.
– Tim Post
13. Juli 2010 um 1:24 Uhr