Considere el siguiente escenario:
- Biblioteca compartida libA.so, sin dependencias.
- Biblioteca compartida libB.so, con libA.so como su dependencia.
Quiero compilar un archivo binario que se vincule con libB. ¿Debo vincular el binario solo con libB o con libA tampoco?
¿Hay alguna forma de vincular solo con las dependencias directas, permitiendo la resolución de símbolos no resueltos de las dependencias para el tiempo de ejecución?
Me preocupa el hecho de que la implementación de la biblioteca libB pueda cambiar en el futuro, introduciendo otras dependencias (libC, libD, libE, por ejemplo). ¿Voy a tener problemas con eso?
En otras palabras:
- Archivos libA: a.cpp ah
- archivos libB: b.cpp bh
- archivos del programa principal: main.cpp
Por supuesto, b.cpp incluye ah y main.cpp incluye bh
Comandos de compilación:
g++ -fPIC a.cpp -c
g++ -shared -o libA.so a.o
g++ -fPIC b.cpp -c -I.
g++ -shared -o libB.so b.o -L. -lA
¿Cuál de las siguientes opciones debo utilizar?
g++ main.cpp -o main -I. -L. -lB
o
g++ main.cpp -o main -I. -L. -lB -lA
No pude usar la primera opción. El enlazador se queja de los símbolos no resueltos de la biblioteca libA. Pero me suena un poco extraño.
Muchas gracias.
- Comentarios actualizados:
Cuando vinculo el binario, el vinculador intentará resolver todos los símbolos del principal y del libB. Sin embargo, libB tiene símbolos indefinidos de libA. Es por eso que el enlazador se queja de eso.
Por eso también necesito vincularme con la libA. Sin embargo, encontré una manera de ignorar los símbolos no resueltos de las bibliotecas compartidas. Parece que debería usar la siguiente línea de comando para hacer eso:
g++ main.cpp -o main -I. -L. -lB -Wl,-unresolved-symbols=ignore-in-shared-libs
Parece que todavía es posible utilizar la -rpath
opción. Sin embargo, necesito entenderlo un poco mejor.
¿Alguien conoce los posibles obstáculos al usar la -Wl,-unresolved-symbols=ignore-in-shared-libs
opción?
- Comentarios actualizados 2:
-rpath
no debe utilizarse para este propósito. Es útil forzar la búsqueda de una biblioteca en un directorio determinado. El -unresolved-symbol
enfoque se ve mucho mejor.
Gracias de nuevo.
fuente
Respuestas:
Parece que ya has recorrido la mayor parte del camino. Bien hecho con tu investigación. Veamos si puedo ayudar a aclarar el "por qué" detrás de esto.
Esto es lo que está haciendo el enlazador. Cuando vincula su ejecutable ('principal' arriba), tiene algunos símbolos (funciones y otras cosas) que no están resueltos. Buscará en la lista de bibliotecas que sigue, tratando de resolver los símbolos no resueltos. En el camino, encuentra que algunos de los símbolos son proporcionados por libB.so, por lo que observa que ahora están resueltos por esta biblioteca.
Sin embargo, también descubre que algunos de esos símbolos usan otros símbolos que aún no están resueltos en su ejecutable, por lo que ahora también debe resolverlos. Sin la vinculación contra libA.so, su aplicación estaría incompleta. Una vez que se vincula con libA.so, todos los símbolos se resuelven y se completa la vinculación.
Como vio, el uso de
-unresolved-symbols-in-shared-libs
no soluciona el problema. Simplemente lo pospone para que esos símbolos se resuelvan en tiempo de ejecución. Para eso-rpath
es: especificar las bibliotecas que se buscarán en tiempo de ejecución. Si esos símbolos no se pueden resolver, su aplicación no se iniciará.No es fácil averiguar las dependencias de la biblioteca porque un símbolo podría ser proporcionado por más de una biblioteca y quedar satisfecho al vincularlo con cualquiera de ellas.
Aquí hay otra descripción de este proceso: ¿Por qué el orden en el que se vinculan las bibliotecas a veces causa errores en GCC?
fuente
Para enlaces dinámicos solo con dependencias directas, puede usar
-Wl,--as-needed
agregando las bibliotecas después de-Wl,--as-needed
:Para verificar las dependencias directas, debe usar readelf en lugar de ldd porque ldd también muestra las dependencias indirectas.
$ readelf -d main | grep library 0x0000000000000001 (NEEDED) Shared library: [libB.so] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
ldd también muestra las dependencias indirectas:
$ LD_LIBRARY_PATH=. ldd ./main linux-vdso.so.1 (0x00007fff13717000) libB.so => ./libB.so (0x00007fb6738ed000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6734ea000) libA.so => ./libA.so (0x00007fb6732e8000) /lib64/ld-linux-x86-64.so.2 (0x00007fb673af0000)
Si usa cmake , puede agregar las siguientes líneas para incluir solo dependencias directas:
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")
fuente
Otra opción es usar
libtool
Si cambia la
g++
llamada alibtool --mode=compile g++
para compilar el código fuente y luegolibtool --mode=link g++
para crear la aplicaciónlibB
,libA
se vinculará automáticamente.fuente
Esta es una publicación interesante: también me estaba golpeando la cabeza con esto, pero creo que se te escapa un punto ...
La idea es la siguiente, ¿verdad?
Consideremos además que ...
Ahora, libB.so y libA.so se han compilado como describió anteriormente. Después de eso, su primera opción debería funcionar, es decir:
Supongo que tu problema se origina en el hecho de que
en main.cpp también se refiere a symA
¿Estoy en lo correcto?
Si usa un símbolo en su código, entonces ese símbolo debe encontrarse en un archivo .so
La idea de interreferenciar bibliotecas compartidas (es decir, crear API) es que los símbolos en las capas más profundas están ocultos (piense en pelar cebollas) y no se utilizan. .. es decir, no se refiera a symA en su main.cpp, sino solo a symB en su lugar (y deje que symB se refiera únicamente a symA).
fuente