2010-07-12 35 views
26

Ho un'applicazione che si collega staticamente con la versione X di una libreria, libfoo, dal fornitore di terze parti, VENDOR1. Si collega anche con una libreria dinamica (condivisa), libbar, da un fornitore di terze parti diverso, VENDOR2, che collega staticamente la versione Y di libfoo da VENDOR1.Collegamento con più versioni di una libreria

contiene così libbar.so versione Y di libfoo.a e il mio eseguibile contiene la versione X di libfoo.a libbar utilizza solo libfoo internamente che non vi siano oggetti libfoo passati dalla mia app per libbar.

Non ci sono errori in fase di costruzione ma in fase di esecuzione l'app segifica errori. La ragione sembra essere che la versione X utilizza strutture che hanno una dimensione diversa nella loro versione Y e il linker di runtime sembra essere in fase di rimescolamento, che viene usato da quale.

Entrambi VENDOR1 & VENDOR2 sono closed source, quindi non posso ricostruirli.

C'è un modo per creare/collegare la mia app in modo tale che risolva sempre alla versione X e libbar si risolva sempre nella versione Y e le due non si mescolano mai?

+0

Puoi rendere la tua app collegata dinamicamente a VENDOR1? –

+0

Non è in alcun modo neutrale rispetto alla lingua. Questo è molto specifico per il compilatore linker e il SO come funzionano tutti insieme. Il modo più semplice è inviare via e-mail entrambi i distributori e vedere come risolvono questo problema. –

+0

Il nostro modo di pensare corrente è, almeno su linux, usare dlopen() su libbar.so con il flag RTLD_DEEPBIND. Un'altra possibilità è quella di separare l'uso delle app di libfoo.a in una libreria condivisa, libbaz.so che avvolge l'uso di libfoo.a, quindi avere l'app dlopen libbaz.so e libbar.so con RTLD_LOCAL che pensiamo possa mantenere tutto i simboli duplicati interni. Questo potrebbe funzionare per Linux ma ne abbiamo bisogno, quindi lavora anche su Solaris, AIX e HPUX. – YerBlues

risposta

11

Grazie per tutte le risposte. Ho una soluzione che sembra funzionare. Ecco il problema in dettaglio con un esempio.

In main.c abbiamo:

#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 abbiamo:

extern int bar(); 

int foo() 
{ 
    int x = bar(); 
    return x; 
} 

In bar.c abbiamo:

#include <stdio.h> 

int bar() 
{ 
    printf("bar in bar.c called\n"); 
    return 2; 
} 

Compile bar.c and foo.c:

$ gcc -fPIC -c bar.c 
$ gcc -fPIC -c foo.c 

Aggiungi bar.o ad una libreria statica:

$ ar r libbar.a bar.o 

Ora creare una libreria condivisa utilizzando foo.o e il collegamento con statico libbar.a

$ gcc -shared -o libfoo.so foo.o -L. -lbar 

Compilare main.ce legame con la libreria condivisa libfoo.so

$ gcc -o main main.c -L. -lfoo 

Impostare LD_LIBRARY_PATH per trovare libfoo.così ed eseguire principale:

$ 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 

Si noti che la versione di bar main.c si chiama, non la versione legata alla libreria condivisa.

In main2.c abbiamo:

#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()); 
} 

Compilare ed main2.c run (Avviso dont abbiamo bisogno di collegare in modo esplicito con libfoo.so):

$ 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 

Ora pippo nella condiviso libreria bar chiamate nella libreria condivisa e nella barra delle chiamate principale in main.c

Non penso che questo comportamento sia intuitivo ed è più utile usare dlopen/dlsym, ma risolve il mio problema.

Grazie ancora per i commenti.

1

Spiacente, no. La mia comprensione del modo in cui Linux (e forse la maggior parte * nix) è ciò che non è possibile. L'unica 'soluzione' per il tuo problema che riesco a pensare è se crei un'applicazione proxy, che espone ciò che ti serve da libbar sotto forma di alcuni IPC. È quindi possibile fare in modo che quel proxy carichi la versione corretta usando LD_LIBRARY_PATH o qualcosa di simile.

+0

"e probabilmente la maggior parte * nix" - tranne AIX. Su AIX che è effettivamente possibile e il comportamento predefinito del linker fa esattamente quale domanda richiede. – Dummy00001

+0

OS X gestisce anche questo con garbo. Ogni .so/.dylib ha una propria tabella di collegamenti/riferimenti. Ho detto * la maggior parte * proprio perché so che non erano tutti. Ad ogni modo, Linux non lo fa, AFAIK. – Gianni

5

Provare un collegamento parziale in modo da avere un file oggetto "partial.o" con libbar e libfoo-Y. Utilizzare objcopy con "--localize-symbols" per rendere i simboli in partial.o da libfoo-Y local. Dovresti essere in grado di generare eseguendo nm su libfoo-Y e massaggiando l'output. Quindi prendi il partial.o modificato e collegalo alla tua app.

Ho fatto qualcosa di simile con gcc toolchain su vxWorks dove le librerie dinamiche non sono una complicazione, ma due versioni della stessa lib sono necessarie per collegarsi in modo pulito in un'app monolitica.

Problemi correlati