Vinculación con biblioteca dinámica con dependencias

79

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:

¿Cuál de las siguientes opciones debo utilizar?

o

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:

Parece que todavía es posible utilizar la -rpathopción. Sin embargo, necesito entenderlo un poco mejor.

¿Alguien conoce los posibles obstáculos al usar la -Wl,-unresolved-symbols=ignore-in-shared-libsopción?

- Comentarios actualizados 2:

-rpathno debe utilizarse para este propósito. Es útil forzar la búsqueda de una biblioteca en un directorio determinado. El -unresolved-symbolenfoque se ve mucho mejor.

Gracias de nuevo.

Marcus
fuente
Aquí hay un ejemplo mínimo ejecutable para aquellos que buscan probar este tipo de cosas: github.com/cirosantilli/cpp-cheat/tree/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

42

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-libsno soluciona el problema. Simplemente lo pospone para que esos símbolos se resuelvan en tiempo de ejecución. Para eso -rpathes: 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?

kithril
fuente
2
Sé que fue hace mucho tiempo, olvidé marcarlo como resuelto. Muchas gracias por tu respuesta.]
Marcus
¡Gracias a todos por la pregunta increíble y una respuesta adecuada! Tengo casi exactamente el mismo escenario. Estoy tratando de envolver objetos compartidos openCV en un objeto compartido personalizado. digamos que C.so es mi costumbre, y CV.so es algo de openCV. He vinculado con éxito C.so contra CV.so, y quiero vincular un main.cpp (¡lo entiendes!) Contra C.so, pero además de usar objetos de C.so, main.cpp usa un objeto 'cv :: Mat' que está definido en CV.so .. En este caso, para que main.cpp se ejecute, ¿necesito vincular tanto C.so como CV.so? Me encantaría que alguien aclarara esto. ¡Gracias! :)
Lenny Linus
2
¿Por qué el enlazador no resuelve todos los símbolos libA.so necesarios al producir libB.so? La aplicación principal no debería tener que vincularse a libA ya que debería ser transparente, por ejemplo, ¿protegida indirectamente por libB?
Wilson
Esto es incorrecto. Linker no hace uso de libA., salvo para notificar a un usuario de la cadena de dependencia incompleta. Sin proporcionar libA.so, sino pasando el -unresolved-symbols = ignore-in-shared-libs, obtendrá exactamente el mismo ejecutable.
listerreg
21

Para enlaces dinámicos solo con dependencias directas, puede usar -Wl,--as-neededagregando 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.

ldd también muestra las dependencias indirectas:

Si usa cmake , puede agregar las siguientes líneas para incluir solo dependencias directas:

usuario1047271
fuente
Lo siento, hice su ejemplo como programa c en lugar de c ++. Por lo tanto, usé el compilador gcc. Pero también funciona con g ++.
user1047271
1
Esa es información realmente útil. No sabía sobre el uso de readelf en lugar de ldd. Muchas gracias.
Marcus
1

Otra opción es usar libtool

Si cambia la g++llamada a libtool --mode=compile g++para compilar el código fuente y luego libtool --mode=link g++para crear la aplicación libB, libAse vinculará automáticamente.

mate
fuente
0

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 ...

  • En a.cpp (y solo allí) se define una clase / variable, llamémosla "symA"
  • En b.cpp (y solo allí) se define una clase / variable, llamémosla "symB".
  • symB usa symA
  • main.cpp usa symB

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).

El Sampsa
fuente
1
Lo entendiste mal. Evidentemente, debe resolver los símbolos requeridos de inmediato. El problema está relacionado con los símbolos que pueden requerir sus dependencias. Es común cuando se desarrollan bibliotecas para ser utilizadas por terceros y el usuario de la biblioteca tiene que proporcionar implementación a las funcionalidades que utiliza su biblioteca. Desarrolla usando un stub, sin dependencias en absoluto y el integrador desarrollará una lib para reemplazar el stub, luego vinculará todo. El cargador tendrá que resolver todas las dependencias. Sin embargo, sus scripts de compilación (y los de sus usuarios) deben tenerlo en cuenta.
Marcus