2010-07-12 10 views
26

Tengo una aplicación que enlaza estáticamente con la versión X de una biblioteca, libfoo, del proveedor de terceros, VENDOR1. También se vincula con una biblioteca dinámica (compartida), libbar, de un proveedor de terceros diferente, VENDOR2, que vincula estáticamente la versión Y de libfoo desde VENDOR1.Vinculación con múltiples versiones de una biblioteca

contiene Así libbar.so versión Y del libfoo.a y mi ejecutable contiene la versión X de libfoo.a libbar sólo utiliza libtal internamente y no hay objetos libfoo pasaron de mi aplicación a libbar.

No hay errores en el tiempo de compilación, pero en el tiempo de ejecución la aplicación seg falla. La razón parece ser que la versión X usa estructuras que tienen un tamaño diferente que la versión Y y el enlazador en tiempo de ejecución parece estar mezclando, lo cual se usa para cada una.

Ambos VENDOR1 & VENDOR2 son de fuente cerrada, por lo que no puedo reconstruirlos.

¿Hay alguna manera de compilar/vincular mi aplicación de manera que siempre se resuelva en la versión X y libbar siempre se resuelve en la versión Y y las dos nunca se mezclan?

+0

¿Puedes hacer que tu aplicación se vincule dinámicamente con VENDOR1? –

+0

No es de ninguna manera que es un idioma neutral. Esto es muy específico para el enlazador del compilador y el sistema operativo, ya que todos estos funcionan juntos. La forma más fácil es enviar un correo electrónico a ambos vendedores y ver cómo lo resuelven. –

+0

Nuestro pensamiento actual es, al menos en Linux, usar dlopen() en libbar.so con el indicador RTLD_DEEPBIND. Otra posibilidad es separar el uso de aplicaciones de libfoo.a en una biblioteca compartida, libbaz.so que envuelve el uso de libfoo.a, luego tener la aplicación dlopen libbaz.so y libbar.so con RTLD_LOCAL que creemos que podría mantener todo los símbolos duplicados internos. Esto puede funcionar para Linux, pero lo necesitamos así que trabaje en Solaris, AIX y HPUX también. – YerBlues

Respuesta

11

Gracias por todas las respuestas. Tengo una solución que parece estar funcionando. Aquí está el problema en detalle con un ejemplo.

En main.c tenemos:

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

En foo.c tenemos:

extern int bar(); 

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

En bar.c tenemos:

#include <stdio.h> 

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

Compilar bar.c y foo.c:

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

Añadir bar.o a una biblioteca estática:

$ ar r libbar.a bar.o 

A continuación, cree una biblioteca compartida utilizando foo.o y el enlace con la estática libbar.a

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

Compilar main.c y el enlace con la biblioteca compartida libfoo.so

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

Establezca LD_LIBRARY_PATH para encontrar libfoo.y ejecutar de manera principal:

$ 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 

en cuenta que la versión de la barra en main.c se llama, no la versión vinculada a la biblioteca compartida.

En main2.c tenemos:

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

compilar y ejecutar main2.c (nótese que no necesitamos para vincular de manera explícita 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 

Ahora foo en el compartida barra de llamadas de la biblioteca en la biblioteca compartida y barra de llamadas principal en main.c

No creo que este comportamiento sea intuitivo y que sea más útil usar dlopen/dlsym, pero resuelve mi problema.

Gracias de nuevo por los comentarios.

1

Lo sentimos, no. Mi comprensión de la forma en que Linux (y posiblemente la mayoría de los * nixes) es que eso no es posible. La única 'solución' para su problema que puedo pensar es si crea una aplicación proxy, que expone lo que necesita de libbar en la forma de algunos IPC. A continuación, puede hacer que ese proxy cargue la versión correcta utilizando LD_LIBRARY_PATH o algo similar.

+0

"y posiblemente la mayoría * nixes" - excepto para AIX. En AIX eso es realmente posible y el comportamiento del enlazador predeterminado hace precisamente lo que pregunta. – Dummy00001

+0

OS X también maneja esto con gracia. Cada .so/.dylib tiene su propia tabla de enlaces/referencias. Dije * la mayoría * precisamente porque sé que no era todo. De todos modos, Linux no hace esto, AFAIK. – Gianni

5

Pruebe un enlace parcial para que tenga un archivo de objeto "partial.o" con libbar y libfoo-Y. Use objcopy con "--localize-symbols" para hacer los símbolos en partial.o desde libfoo-Y local. Debería poder generar ejecutando nm en libfoo-Y y masajeando la salida. A continuación, tome partial.o modificado y vincúlelo a su aplicación.

He hecho algo similar con gcc toolchain en vxWorks donde las librerías dinámicas no son una complicación, pero dos versiones de la misma lib necesitaban enlazarse limpiamente en una aplicación monolítica.

Cuestiones relacionadas