¿Resolviendo dependencias circulares enlazando la misma biblioteca dos veces?


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, así:

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group

Pero en nuestros Makefiles existentes, el problema se maneja típicamente así:

g++ -o myApp -lfoo -lbar -lfoo

(Imagine esto extendido a ~20 bibliotecas con interdependencias complejas.)

He estado pasando por nuestro Makefiles cambiando la segunda forma a la primera, pero ahora mis compañeros de trabajo me preguntan por qué... Y aparte de" porque es más limpio " y una vaga sensación de que la otra forma es arriesgada, no tengo una buena respuesta.

Por lo tanto, puede vincular la misma biblioteca varias veces alguna vez crear un problema? Por ejemplo, podría fallar el enlace con símbolos definidos por multiplicación si es el mismo .¿o es arrastrado dos veces? ¿O hay algún riesgo de que podamos terminar con dos copias del mismo objeto estático, creando ¿bichos sutiles?

Básicamente, quiero saber si hay alguna posibilidad de errores en tiempo de enlace o en tiempo de ejecución al vincular la misma biblioteca varias veces; y si es así, cómo activarlos. Gracias.

Author: JMB, 2012-02-21

3 answers

Todo lo que puedo ofrecer es una falta de contraejemplo. En realidad, nunca he visto la primera forma antes (a pesar de que es claramente mejor) y siempre he visto esto resuelto con la segunda forma, y no he observado problemas como resultado.

Aún así, sugeriría cambiar a la primera forma porque muestra claramente la relación entre las bibliotecas en lugar de confiar en que el enlazador se comporta de una manera particular.

Dicho esto, sugeriría al menos considerar si hay un posibilidad de refactorizar el código para extraer las piezas comunes en bibliotecas adicionales.

 6
Author: Mark B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-22 15:43:13

Dado que es una aplicación heredada, apuesto a que la estructura de las bibliotecas se hereda de algún arreglo que probablemente ya no importa, como ser utilizado para construir otro producto que ya no se hace.

Incluso si aún persisten 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 del arreglo heredado. Simplemente ponga todos los módulos de las 20 bibliotecas en una nueva biblioteca, liballofthem.a. Luego cada la aplicación única es simplemente g++ -o myApp -lallofthem ...

 1
Author: wallyk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-22 16:34:22

El problema con

g++ -o myApp -lfoo -lbar -lfoo

Es que no hay garantía, que dos pases sobre libfoo y uno 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 archivos objeto diferentes):

  • myApp necesita el símbolo fooA definido en libfoo.
  • Symbol fooA necesita symbol barB definido en libbar.
  • Symbol barB necesita symbol fooC definido en libfoo. Esto es la dependencia circular, que puede ser manejada por -lfoo -lbar -lfoo.
  • Symbol fooC necesita symbol barD definido en libbar.

Para poder construir en el caso anterior, necesitaríamos pasar -lfoo -lbar -lfoo -lbar al enlazador. ¿Por qué?

  1. El enlazador ve libfoo por primera vez y usa definiciones de símbolo fooA pero no fooC, porque hasta ahora no ve la necesidad de incluir también fooC en el binario. El enlazador sin embargo comienza a buscar la definición de barB, porque su la definición es necesaria para que fooA funcione.
  2. El vinculador ve -libbar, incluye la definición de barB (pero no barD) y comienza a buscar la definición de fooC.
  3. La definición de fooC se encuentra en libfoo, cuando se procesa por segunda vez. Ahora se hace evidente, que también se necesita la definición de barD - pero demasiado tarde ya no hay libbar en la línea de comandos!

El ejemplo anterior se puede extender a una profundidad de dependencia arbitraria (pero esto sucede rara vez en la vida real).

Usando así

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group

Es un enfoque más robusto, porque el enlazador pasa tan a menudo sobre el grupo de bibliotecas como sea necesario, solo cuando un pase no cambia la tabla de símbolos, el enlazador pasará a la siguiente biblioteca en la línea de comandos.

Sin embargo, hay una pequeña penalización de rendimiento a pagar: en el primer ejemplo -lbar se escanearon una vez más en comparación con la línea de comandos manual -lfoo -lbar -lfoo. No estoy seguro de si vale la pena mencionar / pensar a través.

 0
Author: ead,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-12 11:51:28