enlace estático solo algunas bibliotecas

Respuestas:

112

gcc -lsome_dynamic_lib code.c some_static_lib.a

Šimon Tóth
fuente
5
Vincular bibliotecas después de archivos de objetos, especialmente bibliotecas estáticas. En las versiones antiguas y modernas del entorno de enlace (no estoy seguro del status quo para versiones modestamente antiguas a noviembre de 2010), enumerar la biblioteca estática antes del code.carchivo garantiza que los símbolos que contiene serán ignorados a menos que haya una main()función en uno de los archivos objeto de la biblioteca.
Jonathan Leffler
44
¿Podrías explicar cómo funciona esto? Las respuestas de solo código no son útiles para los principiantes.
jb.
8
@jb por defecto, gcc enlaza dinámicamente. Cuando usa -lsome_dynamic_lib, se vincula dinámicamente como se esperaba. Pero, cuando gcc recibe una biblioteca estática explícitamente, siempre intentará vincularla estáticamente. Sin embargo, hay algunos detalles complicados sobre el orden en que se resuelven los símbolos; No estoy muy seguro de cómo funciona. Aprendí que, en caso de duda, intente reorganizar el orden de las banderas de la biblioteca :-)
bchurchill
4
hay un problema de lincense si enlaza estáticamente, por ejemplo, una biblioteca GPL
HiB
1
@HiB GPL se aplica de la misma manera a la vinculación estática y dinámica
osvein
50

También puedes usar la ldopción-Bdynamic

gcc <objectfiles> -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.

Dmitry Yudakov
fuente
19
-Wl, -Bdynamic requiere GNU ld, por lo que esta solución no funciona en sistemas donde gcc usa el sistema ld (por ejemplo, Mac OS X).
pts
33
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

también puede usar: -static-libgcc -static-libstdc++banderas para bibliotecas gcc

tenga en cuenta que si libs1.soy libs1.aambos existen, el vinculador seleccionará libs1.sosi es antes -Wl,-Bstatico después -Wl,-Bdynamic. No olvide pasar -L/libs1-library-location/antes de llamar -ls1.

wgodoy
fuente
1
¡Al menos, esta solución funciona para un enlace estático contra libgomp!
Jérôme
Esto funciona bien para mí, mientras que el uso -staticen algún lugar del comando falla (supongo que intenta vincular más cosas de forma estática que solo las bibliotecas que quiero).
nh2
4
NÓTESE BIEN. El orden de -Wl,-Bstaticy -Wl,-Bdynamices importante.
Pavel Vlasov
27

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

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

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

Otra posibilidad es no usar --static, sino proporcionar el nombre de archivo completo / ruta 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, libX11no está en la lista de bibliotecas vinculadas dinámicamente, ya que estaba vinculado estáticamente.

Atención: un .soarchivo siempre está vinculado de forma dinámica, incluso cuando se especifica con un nombre de archivo / ruta completa.

ypnos
fuente
¿Cuál es la relación entre libX11.a y la salida de ldd a.out?
Raffi Khatchadourian
1
Ah, ya veo. lddgenera las bibliotecas compartidas necesarias y libX11 no aparece en esa lista.
Raffi Khatchadourian
esto no está claro. dices 'esta opción' y 'esa opción'. que opcion
Octopus
19

El problema, según tengo entendido, es el siguiente. Tiene varias bibliotecas, algunas estáticas, otras dinámicas y otras tanto estáticas como dinámicas. El comportamiento predeterminado de gcc es vincular "principalmente dinámico". Es decir, gcc se vincula a bibliotecas dinámicas cuando es posible, pero por lo demás recurre a bibliotecas estáticas. Cuando usa la opción -static para gcc, el comportamiento es vincular solo bibliotecas estáticas y salir con un error si no se puede encontrar una biblioteca estática, incluso si hay una biblioteca dinámica adecuada.

Otra opción, que en varias ocasiones deseé que tuviera gcc , es lo que llamo -mostly-static y es esencialmente lo opuesto a -dynamic (el predeterminado). -mostly-static preferiría, si existiera, vincularse con bibliotecas estáticas, pero recurriría a bibliotecas dinámicas.

Esta opción no existe pero se puede emular con el siguiente algoritmo:

  1. Construyendo la línea de comando del enlace sin incluir -static .

  2. Repita las opciones de enlace dinámico.

  3. Acumular rutas de biblioteca, es decir, aquellas opciones de la forma -L <lib_dir> en una variable <lib_path>

  4. Para cada opción de enlace dinámico, es decir, aquellos de la forma -l <lib_name> , ejecute el comando gcc <lib_path> -print-file-name = lib <lib_name> .ay 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 de los enlaces estáticos.

El siguiente script bash parece funcionar:

#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Usage: $0 [--exclude <lib_name>]. . . <link_command>"
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 devuelve:

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"
jcoffland
fuente
7

También hay una -l:libstatic1.avariante (menos l dos puntos) 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 las bibliotecas compartidas): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library 

Busque la biblioteca denominada biblioteca al vincular. (La segunda alternativa con la biblioteca como argumento separado es solo para el cumplimiento de 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 doc de binutils ld lo describe. La -lnameopción buscará libname.soluego para libname.aagregar el prefijo lib y .so(si está habilitado en este momento) o el .asufijo. Pero la -l:nameopción solo buscará exactamente el nombre especificado: https://sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

Agregue el archivo o el archivo objeto especificado por namespeca la lista de archivos para vincular. Esta opción se puede utilizar tantas veces como desee. Si namespectiene 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 sistemas que admiten bibliotecas compartidas, ld también puede buscar archivos que no sean libnamespec.a. Específicamente, en los sistemas ELF y SunOS, ld buscará en un directorio una biblioteca llamada libnamespec.soantes de buscar una llamada libnamespec.a. (Por convención, una .soextensión 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 enlazador incluirá el archivo o archivos apropiados 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 vuelva a buscar en el archivo.

Consulte la -(opción para ver una forma de obligar al vinculador 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 enlazadores Unix. Sin embargo, si está utilizando ld en AIX, tenga en cuenta que es diferente del comportamiento del enlazador AIX.

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

osgx
fuente
Esta opción parece funcionar donde todo lo demás falla. Nos topamos con un caso en el que necesitábamos vincular estáticamente libjsoncpp.a, porque nuestras máquinas de compilación producirían binarios vinculados contra libjsocpp.so.0, mientras que el sistema operativo de destino solo proporciona libjsoncpp.so.1. Hasta que podamos aclarar esta diferencia, esta fue la única solución que arrojó resultados adecuados en nuestro caso.
Tomasz W
4

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

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

Jonathan Leffler
fuente
6
Aunque se aceptó esta respuesta, no aborda el problema por completo. Como explicó @peoro, el problema que está tratando de resolver es que no tiene versiones estáticas de todas las bibliotecas, lo que implica que le gustaría vincular tantas bibliotecas de forma estática como sea posible. Mira mi respuesta.
jcoffland
2

de enlace dinámico y estático de la biblioteca dentro de una línea, usted debe poner librerías estáticas después de bibliotecas dinámicas y archivos de objetos, como este:

gcc -lssl main.o -lFooLib -o main

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

Vincent
fuente