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):
-
myAppnecesita el símbolofooAdefinido enlibfoo. - El símbolo
fooAnecesita el símbolobarBdefinido enlibbar. - Symbol
barBnecesita el símbolofooCdefinido enlibfoo. Esta es la dependencia circular, que puede ser manejada por-lfoo -lbar -lfoo. - El símbolo
fooCnecesita el símbolobarDdefinido enlibbar.
Para poder construir en el caso anterior, deberíamos pasar -lfoo -lbar -lfoo -lbar al vinculador. ¿Por qué?
- El enlazador ve
libfoopor primera vez y usa definiciones del símbolofooApero nofooC, porque hasta el momento no ve la necesidad de incluir tambiénfooCen el binario. Sin embargo, el enlazador comienza a buscar la definición debarB, porque su definición es necesaria para quefooAfuncione. - El enlazador ve
-libbar, incluye la definición debarB(pero nobarD) y comienza a buscar la definición defooC. - La definición de
fooCse 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 unlibbaren 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.