enlace estático solo algunas bibliotecas

¿Cómo puedo vincular estáticamente solo algunas bibliotecas específicas a mi binario cuando me enlace con GCC?

gcc ... -static ... intenta vincular estáticamente todas las bibliotecas vinculadas, pero no tengo la versión estática de algunas de ellas (p. ej .: libX11).

gcc -lsome_dynamic_lib code.c some_static_lib.a

También puedes usar la opción ld -Bdynamic

 gcc  -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2 

Todas las bibliotecas posteriores (incluidas las del sistema vinculadas por gcc automáticamente) se vincularán dinámicamente.

 gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2 

también puede usar: -static-libgcc -static-libstdc++ flags para las bibliotecas de gcc

tenga en cuenta que si libs1.so y libs1.a , el enlazador seleccionará libs1.so si está antes -Wl,-Bstatic o después de -Wl,-Bdynamic . No olvide pasar -L/libs1-library-location/ antes de llamar a -ls1 .

Desde la página de manual de ld (esto no funciona con gcc), refiriéndose a la opción --static :

Puede usar esta opción varias veces en la línea de comando: afecta a la biblioteca que busca las opciones -l que lo siguen.

Una solución es colocar sus dependencias dinámicas antes de la opción --static en la línea de comando.

Otra posibilidad es no usar --static , sino proporcionar el nombre de archivo / ruta de acceso completo del archivo de objeto estático (es decir, no usar la opción -l) para vincular estáticamente una biblioteca específica. Ejemplo:

 # echo "int main() {}" > test.cpp # c++ test.cpp /usr/lib/libX11.a # ldd a.out linux-vdso.so.1 => (0x00007fff385cc000) libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f9a5b233000) libm.so.6 => /lib/libm.so.6 (0x00007f9a5afb0000) libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f9a5ad99000) libc.so.6 => /lib/libc.so.6 (0x00007f9a5aa46000) /lib64/ld-linux-x86-64.so.2 (0x00007f9a5b53f000) 

Como puede ver en el ejemplo, libX11 no está en la lista de bibliotecas vinculadas dinámicamente, ya que estaba vinculado estáticamente.

Cuidado: un archivo .so siempre está vinculado dinámicamente, incluso cuando se especifica con un nombre de archivo / ruta de acceso completo.

El problema tal como lo entiendo es el siguiente. Tiene varias bibliotecas, algunas estáticas, algunas dinámicas y otras tanto estáticas como dinámicas. El comportamiento predeterminado de gcc es vincular “principalmente dynamic”. Es decir, gcc se vincula a bibliotecas dinámicas cuando es posible, pero de lo contrario vuelve a las bibliotecas estáticas. Cuando usa la opción -static para gcc, el comportamiento es solo vincular bibliotecas estáticas y salir con un error si no se puede encontrar una biblioteca estática, incluso si hay una biblioteca dinámica apropiada.

Otra opción, que en varias ocasiones he deseado que tenga GCC , es lo que llamo -más-estático y es esencialmente lo opuesto a -dynamic (el predeterminado). -más que nada estático , si existiera, preferiría vincularse con bibliotecas estáticas pero recurriría a bibliotecas dinámicas.

Esta opción no existe pero puede ser emulada con el siguiente algoritmo:

  1. Construir la línea de comando de enlace sin incluir estático .

  2. Itera sobre las opciones de enlace dynamic.

  3. Acumula rutas de biblioteca, es decir, aquellas opciones de la forma -L en una variable

  4. Para cada opción de enlace dynamic, es decir, las de la forma -l , ejecute el comando gcc -print-file-name = lib .a y capture la salida.

  5. Si el comando imprime algo diferente a lo que pasó, será la ruta completa a la biblioteca estática. Reemplace la opción de biblioteca dinámica con la ruta completa a la biblioteca estática.

Enjuague y repita hasta que haya procesado toda la línea de comando del enlace. Opcionalmente, el script también puede tomar una lista de nombres de bibliotecas para excluir del enlace estático.

El siguiente script bash parece ser el truco:

 #!/bin/bash if [ $# -eq 0 ]; then echo "Usage: $0 [--exclude ]. . . " fi exclude=() lib_path=() while [ $# -ne 0 ]; do case "$1" in -L*) if [ "$1" == -L ]; then shift LPATH="-L$1" else LPATH="$1" fi lib_path+=("$LPATH") echo -n "\"$LPATH\" " ;; -l*) NAME="$(echo $1 | sed 's/-l\(.*\)/\1/')" if echo "${exclude[@]}" | grep " $NAME " >/dev/null; then echo -n "$1 " else LIB="$(gcc $lib_path -print-file-name=lib"$NAME".a)" if [ "$LIB" == lib"$NAME".a ]; then echo -n "$1 " else echo -n "\"$LIB\" " fi fi ;; --exclude) shift exclude+=(" $1 ") ;; *) echo -n "$1 " esac shift done echo 

Por ejemplo:

 mostlyStatic gcc -o test test.c -ldl -lpthread 

en mi sistema regresa:

 gcc -o test test.c "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libdl.a" "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a" 

o con una exclusión:

 mostlyStatic --exclude dl gcc -o test test.c -ldl -lpthread 

Entonces obtengo:

 gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a" 

Algunos cargadores (vinculadores) proporcionan interruptores para activar y desactivar la carga dinámica. Si GCC se ejecuta en un sistema de este tipo (Solaris, y posiblemente otros), puede utilizar la opción correspondiente.

Si sabe qué bibliotecas desea vincular estáticamente, puede simplemente especificar el archivo de biblioteca estática en la línea de enlace, por ruta completa.

También existe la variante -l:libstatic1.a (menos 1 coma) de la opción -l en gcc que se puede usar para vincular la biblioteca estática (gracias a https://stackoverflow.com/a/20728782 ). Está documentado? No en la documentación oficial de gcc (que tampoco es exacta para bibliotecas compartidas): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

 -llibrary -l library 

Busque en la biblioteca llamada biblioteca al enlazar. (La segunda alternativa con la biblioteca como argumento separado es solo para cumplimiento con POSIX y no se recomienda) … La única diferencia entre usar una opción -l y especificar un nombre de archivo es que -l rodea la biblioteca con ‘lib’ y ‘.a’ y busca en varios directorios.

El binutils ld doc lo describe. La opción -lname buscará libname.so luego para libname.a añadiendo libname.a lib y .so (si está habilitado en este momento) o .a sufijo. Pero la opción -l:name solo buscará exactamente el nombre especificado: https://sourceware.org/binutils/docs/ld/Options.html

 -l namespec --library=namespec 

Agregue el archivo de archivo o objeto especificado por namespec a la lista de archivos para vincular. Esta opción se puede usar cualquier cantidad de veces. Si namespec tiene el formato :filename , ld buscará en la ruta de la biblioteca un archivo llamado filename , de lo contrario buscará en la ruta de la biblioteca un archivo llamado libnamespec.a .

En los sistemas que admiten bibliotecas compartidas, ld también puede buscar archivos que no sean libnamespec.a . Específicamente, en sistemas ELF y SunOS, ld buscará en un directorio una biblioteca llamada libnamespec.so antes de buscar una llamada libnamespec.a . (Por convención, una extensión .so indica una biblioteca compartida.) Tenga en cuenta que este comportamiento no se aplica a :filename , que siempre especifica un archivo llamado filename .

El vinculador buscará un archivo solo una vez, en la ubicación donde se especifica en la línea de comando. Si el archivo define un símbolo que no estaba definido en algún objeto que apareció antes del archivo en la línea de comando, el vinculador incluirá los archivos correspondientes del archivo. Sin embargo, un símbolo indefinido en un objeto que aparece más adelante en la línea de comando no hará que el vinculador busque nuevamente en el archivo.

Consulte la opción -( opción para forzar al enlazador a buscar archivos varias veces).

Puede enumerar el mismo archivo varias veces en la línea de comando.

Este tipo de búsqueda de archivos es estándar para los vinculadores de Unix. Sin embargo, si está utilizando ld en AIX, tenga en cuenta que es diferente del comportamiento del vinculador de AIX.

La variante -l:namespec está documentada desde la versión 2.18 de binutils (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html

para vincular la biblioteca dinámica y estática dentro de una línea, debe poner las bibliotecas estáticas después de las bibliotecas dinámicas y los archivos de objetos, como este:

gcc -lssl main.o -lFooLib -o main

De lo contrario, no funcionará. me toma algún tiempo resolverlo.