c++ - rotas - ¿Resolviendo dependencias circulares uniendo la misma biblioteca dos veces?
instalar dependencias kali linux (3)
Tenemos una base de código dividida en bibliotecas estáticas. Desafortunadamente, las bibliotecas tienen dependencias circulares; Por ejemplo, libfoo.a
depende de libbar.a
y viceversa.
Sé que la forma "correcta" de manejar esto es usar las opciones --start-group
y --end-group
del enlazador, de esta manera:
g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group
Pero en nuestros Makefiles existentes, el problema se maneja así:
g++ -o myApp -lfoo -lbar -lfoo
(Imagine esto extendido a ~ 20 bibliotecas con interdependencias complejas).
He estado revisando nuestros Makefiles cambiando la segunda forma a la primera, pero ahora mis compañeros de trabajo me preguntan por qué ... Y aparte de "porque está más limpio" y tiene la vaga sensación de que la otra forma es peligrosa, no lo hago. tener una buena respuesta
Entonces, ¿vincular la misma biblioteca varias veces puede crear un problema? Por ejemplo, ¿podría el enlace fallar con símbolos definidos múltiples si el mismo .o se inserta dos veces? ¿O existe algún riesgo de que podamos terminar con dos copias del mismo objeto estático, creando errores sutiles?
Básicamente, quiero saber si existe alguna posibilidad de fallas en el tiempo de enlace o en el tiempo de ejecución al vincular la misma biblioteca varias veces; Y si es así, cómo activarlos. Gracias.
Dado que es una aplicación heredada, apuesto a que la estructura de las bibliotecas se hereda de una disposición que probablemente ya no importa, como el uso de otro producto que ya no se usa.
Incluso si aún existen razones estructurales para la estructura de la biblioteca heredada, es casi seguro que aún sería aceptable construir una biblioteca más a partir de la disposición anterior. Simplemente coloque todos los módulos de las 20 bibliotecas en una nueva biblioteca, liballofthem.a
. Entonces, cada aplicación es simplemente g++ -o myApp -lallofthem ...
El problema con
g++ -o myApp -lfoo -lbar -lfoo
es que no hay garantía, que dos pasadas sobre libfoo
y una pasada sobre libbar
son suficientes.
El enfoque con Wl,--start-group ... -Wl,--end-group
es mejor, porque es más robusto.
Considere el siguiente escenario (todos los símbolos están en diferentes archivos de objetos):
-
myApp
necesita el símbolofooA
definido enlibfoo
. - El símbolo
fooA
necesita el símbolobarB
definido enlibbar
. - Symbol
barB
necesita el símbolofooC
definido enlibfoo
. Esta es la dependencia circular, que puede ser manejada por-lfoo -lbar -lfoo
. - El símbolo
fooC
necesita el símbolobarD
definido enlibbar
.
Para poder construir en el caso anterior, deberíamos pasar -lfoo -lbar -lfoo -lbar
al vinculador. ¿Por qué?
- El enlazador ve
libfoo
por primera vez y usa definiciones del símbolofooA
pero nofooC
, porque hasta el momento no ve la necesidad de incluir tambiénfooC
en el binario. Sin embargo, el enlazador comienza a buscar la definición debarB
, porque su definición es necesaria para quefooA
funcione. - El enlazador ve
-libbar
, incluye la definición debarB
(pero nobarD
) y comienza a buscar la definición defooC
. - La definición de
fooC
se encuentra enlibfoo
, cuando se procesó por segunda vez. Ahora se hace evidente que también se necesita la definición debarD
, ¡pero demasiado tarde ya no hay unlibbar
en la línea de comandos!
El ejemplo anterior se puede extender a una profundidad de dependencia arbitraria (pero esto ocurre raramente en la vida real).
Así usando
g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group
es un enfoque más robusto, ya que el vinculador pasa con tanta frecuencia sobre el grupo de bibliotecas como sea necesario, solo cuando una pasada no cambió la tabla de símbolos el vinculador pasará a la siguiente biblioteca en la línea de comandos.
Sin embargo, hay una pequeña penalización de rendimiento que pagar: en el primer ejemplo, se analizó -lbar
una vez más en comparación con la línea de comando manual -lfoo -lbar -lfoo
. No estoy seguro de si vale la pena mencionar / pensar a través de.
Todo lo que puedo ofrecer es una falta de contra-ejemplo. De hecho, nunca he visto la primera forma antes (aunque es claramente mejor) y siempre he visto esto resuelto con la segunda forma, y como resultado no he observado problemas.
Aun así, sugeriría cambiar a la primera forma porque muestra claramente la relación entre las bibliotecas en lugar de confiar en que el vinculador se comporte de una manera particular.
Dicho esto, sugeriría al menos considerar si existe la posibilidad de refactorizar el código para extraer las piezas comunes en bibliotecas adicionales.