Verknüpfen mit mehreren Versionen einer Bibliothek

Lesezeit: 5 Minuten

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?

  • 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

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.

  • Vielen Dank für den Tipp, aber könnten Sie bitte erklären, warum Sie verwenden RTLD_GLOBAL wenn es darum geht, Bibliotheken voneinander zu isolieren?

    – ScumCoder

    3. August 2021 um 15:49 Uhr

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.

  • “und möglicherweise die meisten *nixes” – außer AIX. Unter AIX ist das tatsächlich möglich und das standardmäßige Linker-Verhalten macht genau das, was die Frage stellt.

    – Dummy00001

    13. Juli 2010 um 0:07 Uhr

  • OS X handhabt dies auch elegant. Jede .so/.dylib hat ihre eigene Linktabelle/Referenzen. Ich sagte die meisten gerade weil ich weiß, dass es nicht alles war. Wie auch immer, Linux tut dies nicht, AFAIK.

    – Gianni

    13. Juli 2010 um 0:29 Uhr

1382200cookie-checkVerknüpfen mit mehreren Versionen einer Bibliothek

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy