¿Por qué el orden en el que se vinculan las bibliotecas a veces provoca errores en GCC?

Resuelto Landon asked hace 16 años • 0 respuestas

¿Por qué el orden en el que se vinculan las bibliotecas a veces provoca errores en GCC?

Landon avatar Sep 05 '08 09:09 Landon
Aceptado

(Consulte el historial de esta respuesta para obtener un texto más elaborado, pero ahora creo que es más fácil para el lector ver líneas de comando reales).


Archivos comunes compartidos por todos los comandos siguientes

// a depends on b, b depends on d
$ cat a.cpp
extern int a;
int main() {
  return a;
}

$ cat b.cpp
extern int b;
int a = b;

$ cat d.cpp
int b;

Vinculación a bibliotecas estáticas

$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o

$ g++ -L. -ld -lb a.cpp # wrong order
$ g++ -L. -lb -ld a.cpp # wrong order
$ g++ a.cpp -L. -ld -lb # wrong order
$ g++ a.cpp -L. -lb -ld # right order

El vinculador busca de izquierda a derecha y observa los símbolos no resueltos a medida que avanza. Si una biblioteca resuelve el símbolo, se necesitan los archivos objeto de esa biblioteca para resolver el símbolo (bo fuera de libb.a en este caso).

Las dependencias de las bibliotecas estáticas entre sí funcionan de la misma manera: primero debe estar la biblioteca que necesita símbolos y luego la biblioteca que resuelve el símbolo.

Si una biblioteca estática depende de otra biblioteca, pero la otra biblioteca nuevamente depende de la biblioteca anterior, hay un ciclo. Puede resolver esto encerrando las bibliotecas dependientes cíclicamente entre -(y -), como -( -la -lb -)(es posible que deba escapar de los pares, como -\(y -\)). Luego, el vinculador busca esas bibliotecas adjuntas varias veces para garantizar que se resuelvan las dependencias cíclicas. Alternativamente, puede especificar las bibliotecas varias veces, de modo que cada una esté una delante de la otra: -la -lb -la.

Vinculación a bibliotecas dinámicas

$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency!

$ g++ -L. -lb a.cpp # wrong order (works on some distributions)
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order
$ g++ -Wl,--as-needed a.cpp -L. -lb # right order

Aquí ocurre lo mismo: las bibliotecas deben seguir los archivos objeto del programa. La diferencia aquí en comparación con las bibliotecas estáticas es que no es necesario preocuparse por las dependencias de las bibliotecas entre sí, porque las bibliotecas dinámicas clasifican sus dependencias por sí mismas .

Algunas distribuciones recientes aparentemente utilizan de forma predeterminada el --as-neededindicador del vinculador, que obliga a que los archivos objeto del programa aparezcan antes que las bibliotecas dinámicas. Si se pasa ese indicador, el vinculador no vinculará a bibliotecas que en realidad no son necesarias para el ejecutable (y lo detecta de izquierda a derecha). Mi distribución reciente de archlinux no usa este indicador de forma predeterminada, por lo que no dio ningún error por no seguir el orden correcto.

No es correcto omitir la dependencia de b.socontra d.soal crear el primero. Se le pedirá que especifique la biblioteca al vincular a, pero aen realidad no necesita el número entero ben sí, por lo que no debe preocuparse por blas propias dependencias.

A continuación se muestra un ejemplo de las implicaciones si no especifica las dependencias paralibb.so

$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -o libb.so # wrong (but links)

$ g++ -L. -lb a.cpp # wrong, as above
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above
$ g++ a.cpp -L. -lb # wrong, missing libd.so
$ g++ a.cpp -L. -ld -lb # wrong order (works on some distributions)
$ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs)
$ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right"

Si ahora observa qué dependencias tiene el binario, observará que el binario en sí también depende de libd, no solo libbcomo debería. Será necesario volver a vincular el binario si libbluego depende de otra biblioteca, si lo hace de esta manera. Y si alguien más carga libbel uso dlopenen tiempo de ejecución (piense en cargar complementos dinámicamente), la llamada también fallará. Así que "right"realmente debería serlo wrongtambién.

Johannes Schaub - litb avatar Jan 03 '2009 17:01 Johannes Schaub - litb

El enlazador GNU ld es el llamado enlazador inteligente. Realizará un seguimiento de las funciones utilizadas por las bibliotecas estáticas anteriores, descartando permanentemente aquellas funciones que no se utilizan de sus tablas de búsqueda. El resultado es que si vincula una biblioteca estática demasiado pronto, las funciones de esa biblioteca ya no estarán disponibles para las bibliotecas estáticas más adelante en la línea de vínculo.

El enlazador típico de UNIX funciona de izquierda a derecha, así que coloque todas sus bibliotecas dependientes a la izquierda y las que satisfacen esas dependencias a la derecha de la línea de enlace. Es posible que algunas bibliotecas dependan de otras y, al mismo tiempo, otras bibliotecas dependan de ellas. Aquí es donde se complica. Cuando se trata de referencias circulares, ¡arregle su código!

casualcoder avatar Jan 03 '2009 17:01 casualcoder

Aquí hay un ejemplo para dejar claro cómo funcionan las cosas con GCC cuando se trata de bibliotecas estáticas . Entonces supongamos que tenemos el siguiente escenario:

  • myprog.o- main()función que contiene, dependiente delibmysqlclient
  • libmysqlclient- estática, por el ejemplo (preferirías la biblioteca compartida, por supuesto, ya que libmysqlclientes enorme); en /usr/local/lib; y dependiente de cosas delibz
  • libz(dinámica)

¿Cómo vinculamos esto? (Nota: ejemplos de compilación en Cygwin usando gcc 4.3.4)

gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'
# myprog depends on libmysqlclient
# so myprog has to come earlier on the command line

gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# we have to link with libz, too

gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# libz is needed by libmysqlclient
# so it has to appear *after* it on the command line

gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
Lumi avatar Jul 16 '2011 12:07 Lumi

Si agrega -Wl,--start-groupindicadores a los del vinculador, no importa en qué orden estén o si hay dependencias circulares.

En Qt esto significa agregar:

QMAKE_LFLAGS += -Wl,--start-group

Ahorra mucho tiempo y no parece ralentizar mucho la vinculación (lo que de todos modos lleva mucho menos tiempo que la compilación).

SvaLopLop avatar Apr 05 '2015 12:04 SvaLopLop

Otra alternativa sería especificar la lista de bibliotecas dos veces:

gcc prog.o libA.a libB.a libA.a libB.a -o prog.x

Haciendo esto, no tienes que preocuparte por la secuencia correcta ya que la referencia se resolverá en el segundo bloque.

eckes avatar Mar 21 '2014 10:03 eckes