Respuestas:
(Vea el historial de esta respuesta para obtener el 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 a continuación
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
$ 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 enlazador busca de izquierda a derecha y observa símbolos sin resolver a medida que avanza. Si una biblioteca resuelve el símbolo, toma los archivos de objetos de esa biblioteca para resolver el símbolo (en este caso, fuera de libb.a).
Las dependencias de las bibliotecas estáticas entre sí funcionan igual: la biblioteca que necesita símbolos debe ser primero, 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 que dependen cíclicamente por -(
y -)
, como -( -la -lb -)
(puede que necesite escapar de los parens, como -\(
y -\)
). Luego, el vinculador busca esas bibliotecas adjuntas varias veces para garantizar que se resuelvan las dependencias del ciclo. Alternativamente, puede especificar las bibliotecas varias veces, de modo que cada una esté una frente a la otra:-la -lb -la
.
$ 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í es lo mismo: las bibliotecas deben seguir los archivos de objetos del programa. La diferencia aquí en comparación con las bibliotecas estáticas es que no necesita preocuparse por las dependencias de las bibliotecas entre sí, porque las bibliotecas dinámicas clasifican sus dependencias ellos mismos .
Aparentemente, algunas distribuciones recientes usan de manera predeterminada el --as-needed
indicador de enlazador, lo que exige que los archivos de objetos del programa vengan antes que las bibliotecas dinámicas. Si se pasa ese indicador, el vinculador no se vinculará a bibliotecas que el ejecutable realmente no necesita (y lo detecta de izquierda a derecha). Mi distribución reciente de archlinux no usa este indicador por defecto, por lo que no dio un error por no seguir el orden correcto.
No es correcto omitir la dependencia de b.so
contra d.so
al crear el primero. Se le pedirá que especifique la biblioteca al vincular en a
ese momento, pero en a
realidad no necesita el entero en b
sí mismo, por lo que no debe preocuparse por b
las propias dependencias.
Aquí hay un ejemplo de las implicaciones si no especifica las dependencias para libb.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, observa que el binario en sí mismo también depende libd
, no solo libb
como debería. Será necesario volver a vincular el binario si libb
más tarde depende de otra biblioteca, si lo hace de esta manera. Y si alguien más carga libb
usando dlopen
en tiempo de ejecución (piense en cargar complementos dinámicamente), la llamada también fallará. Entonces, el "right"
realmente debería ser wrong
también.
lorder
+ tsort
. Pero a veces no hay orden, si tiene referencias cíclicas. Luego solo tiene que recorrer la lista de bibliotecas hasta que todo esté resuelto.
El enlazador GNU ld es un llamado enlazador inteligente. Realizará un seguimiento de las funciones utilizadas por las bibliotecas estáticas anteriores, eliminando permanentemente las funciones que no se utilizan en 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 enlace.
El enlazador típico de UNIX funciona de izquierda a derecha, por lo tanto, coloque todas sus bibliotecas dependientes a la izquierda y las que satisfagan esas dependencias a la derecha de la línea de enlace. Es posible que algunas bibliotecas dependan de otras mientras que al mismo tiempo otras bibliotecas dependen de ellas. Aquí es donde se complica. ¡Cuando se trata de referencias circulares, arregle su código!
Aquí hay un ejemplo para dejar en claro cómo funcionan las cosas con GCC cuando están involucradas las bibliotecas estáticas . Así que supongamos que tenemos el siguiente escenario:
myprog.o
- que contiene la main()
función, dependiente delibmysqlclient
libmysqlclient
- static, por el bien del ejemplo (preferiría la biblioteca compartida, por supuesto, ya que libmysqlclient
es enorme); en /usr/local/lib
; y depende 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
Si agregas -Wl,--start-group
a las banderas del vinculador, no le importa en qué orden están o si hay dependencias circulares.
En Qt esto significa agregar:
QMAKE_LFLAGS += -Wl,--start-group
Ahorra mucho tiempo jugando y no parece ralentizar mucho la vinculación (que de todos modos lleva mucho menos tiempo que la compilación).
Puede usar la opción -Xlinker.
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
es casi igual a
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
Cuidado !
Un consejo rápido que me hizo tropezar: si está invocando el enlazador como "gcc" o "g ++", el uso de "--start-group" y "--end-group" no pasará esas opciones al enlazador - ni marcará un error. Simplemente fallará el enlace con símbolos indefinidos si el orden de la biblioteca fue incorrecto.
Debe escribirlos como "-Wl, - start-group", etc. para indicarle a GCC que pase el argumento al enlazador.
El orden de los enlaces ciertamente importa, al menos en algunas plataformas. He visto bloqueos para aplicaciones vinculadas con bibliotecas en orden incorrecto (donde incorrecto significa A vinculado antes de B pero B depende de A).
Lo he visto mucho, algunos de nuestros módulos enlazan más de 100 bibliotecas de nuestro código más sistema y librerías de terceros.
Dependiendo de los diferentes enlazadores HP / Intel / GCC / SUN / SGI / IBM / etc., puede obtener funciones / variables no resueltas, etc., en algunas plataformas debe enumerar las bibliotecas dos veces.
En su mayor parte, usamos una jerarquía estructurada de bibliotecas, núcleo, plataforma, diferentes capas de abstracción, pero para algunos sistemas todavía tiene que jugar con el orden en el comando de enlace.
Una vez que encuentre un documento de solución, el próximo desarrollador no tendrá que resolverlo nuevamente.
Mi antiguo profesor solía decir, " alta cohesión y bajo acoplamiento ", todavía es cierto hoy.
gcc
cambió a un comportamiento más estricto (relativamente) recientemente.