vinculação estática apenas algumas bibliotecas

108

Como posso vincular estaticamente apenas algumas bibliotecas específicas ao meu binário ao vincular ao GCC?

gcc ... -static ...tenta vincular estaticamente todas as bibliotecas vinculadas, mas não tenho a versão estática de algumas delas (por exemplo: libX11).

Peoro
fonte

Respostas:

112

gcc -lsome_dynamic_lib code.c some_static_lib.a

Šimon Tóth
fonte
5
Vincule bibliotecas a arquivos de objeto - especialmente bibliotecas estáticas. Em versões antigas e modernas do ambiente de link (não tenho certeza do status quo para versões modestamente antigas em novembro de 2010), listar a biblioteca estática antes do code.carquivo garante que os símbolos nela serão ignorados, a menos que haja uma main()função em um dos arquivos de objeto da biblioteca.
Jonathan Leffler de
44
Podes explicar como isto funciona? Respostas apenas em código não são úteis para iniciantes.
jb.
8
@jb por padrão, gcc se vincula dinamicamente. Quando você usa -lsome_dynamic_lib, ele é vinculado dinamicamente conforme o esperado. Mas, quando o gcc recebe uma biblioteca estática explicitamente, ele sempre tentará vinculá-la estaticamente. Existem, no entanto, alguns detalhes complicados sobre a ordem em que os símbolos são resolvidos; Não tenho certeza de como isso funciona. Aprendi que, em caso de dúvida, tente reorganizar a ordem dos sinalizadores de biblioteca :-)
bchurchill
4
há um problema de lincense se você vincular estaticamente, por exemplo, uma biblioteca GPL
HiB
1
@HiB GPL se aplica da mesma forma para links estáticos e dinâmicos
osvein
50

Você também pode usar a ldopção-Bdynamic

gcc <objectfiles> -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2

Todas as bibliotecas posteriores (incluindo as do sistema vinculadas pelo gcc automaticamente) serão vinculadas dinamicamente.

Dmitry Yudakov
fonte
19
-Wl, -Bdynamic requer GNU ld, portanto esta solução não funciona em sistemas onde o gcc usa o sistema ld (por exemplo, Mac OS X).
pontos
33
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

você também pode usar: -static-libgcc -static-libstdc++flags para bibliotecas gcc

tenha em mente que se libs1.soe libs1.aambos existirem, o vinculador escolherá libs1.sose é antes -Wl,-Bstaticou depois -Wl,-Bdynamic. Não se esqueça de passar -L/libs1-library-location/antes de ligar -ls1.

wgodoy
fonte
1
Pelo menos, esta solução funciona para link estático contra libgomp!
Jérôme
Isso funciona bem para mim, embora o uso de -staticalgum lugar no comando falhe (presumo que ele tente vincular mais coisas estaticamente do que apenas as bibliotecas que desejo).
nh2
4
NB. A ordem de -Wl,-Bstatice -Wl,-Bdynamicé importante.
Pavel Vlasov
27

Na página de manual de ld(isso não funciona com gcc), referindo-se à --staticopção:

Você pode usar esta opção várias vezes na linha de comando: ela afeta a pesquisa da biblioteca pelas opções -l que a seguem.

Uma solução é colocar suas dependências dinâmicas antes da --staticopção na linha de comando.

Outra possibilidade é não usar --static, mas em vez disso fornecer o nome do arquivo / caminho completo do arquivo de objeto estático (ou seja, não usar a opção -l) para vincular estaticamente a uma biblioteca específica. Exemplo:

# 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 você pode ver no exemplo, libX11não está na lista de bibliotecas vinculadas dinamicamente, pois estava vinculado estaticamente.

Cuidado: um .soarquivo está sempre vinculado dinamicamente, mesmo quando especificado com um nome de arquivo / caminho completo.

ypnos
fonte
Qual é o relacionamento entre libX11.a e a saída de ldd a.out?
Raffi Khatchadourian
1
Ah, entendo. lddgera as bibliotecas compartilhadas necessárias e libX11 não aparece nessa lista.
Raffi Khatchadourian
isso não está claro. você diz 'esta opção' e 'aquela opção'. qual opção?
Octopus
19

O problema, pelo que entendi, é o seguinte. Você tem várias bibliotecas, algumas estáticas, algumas dinâmicas e algumas estáticas e dinâmicas. O comportamento padrão do gcc é vincular "principalmente dinâmico". Ou seja, o gcc se vincula a bibliotecas dinâmicas quando possível, mas de outra forma recorre a bibliotecas estáticas. Quando você usa a opção -static para gcc, o comportamento é apenas vincular bibliotecas estáticas e sair com um erro se nenhuma biblioteca estática puder ser encontrada, mesmo se houver uma biblioteca dinâmica apropriada.

Outra opção, que em várias ocasiões desejei que o gcc tivesse, é o que chamo de -mostly-static e é essencialmente o oposto de -dynamic (o padrão). -mostly-static , se existisse, preferiria vincular-se a bibliotecas estáticas, mas voltaria para bibliotecas dinâmicas.

Esta opção não existe, mas pode ser emulada com o seguinte algoritmo:

  1. Construindo a linha de comando do link sem incluir -static .

  2. Repita as opções de link dinâmico.

  3. Acumule caminhos de biblioteca, ou seja, aquelas opções da forma -L <lib_dir> em uma variável <lib_path>

  4. Para cada opção de link dinâmico, ou seja, aqueles do formulário -l <lib_name> , execute o comando gcc <lib_path> -print-file-name = lib <lib_name> .a e capture a saída.

  5. Se o comando imprimir algo diferente do que você passou, será o caminho completo para a biblioteca estática. Substitua a opção de biblioteca dinâmica pelo caminho completo para a biblioteca estática.

Enxágue e repita até processar toda a linha de comando do link. Opcionalmente, o script também pode obter uma lista de nomes de bibliotecas para excluir da vinculação estática.

O seguinte script bash parece fazer o truque:

#!/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 exemplo:

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

no meu sistema retorna:

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"

ou com uma exclusão:

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

Eu então obtenho:

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

Também há -l:libstatic1.a(menos l dois pontos) variante da opção -l no gcc que pode ser usada para vincular a biblioteca estática (graças a https://stackoverflow.com/a/20728782 ). Está documentado? Não na documentação oficial do gcc (que não é exata para libs compartilhadas também): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library 

Pesquise a biblioteca chamada biblioteca ao vincular. (A segunda alternativa com a biblioteca como um argumento separado é apenas para conformidade com POSIX e não é recomendada.) ... A única diferença entre usar uma opção -l e especificar um nome de arquivo é que -l envolve a biblioteca com 'lib' e '.a' e pesquisa vários diretórios.

O doc binutils ld o descreve. A -lnameopção fará a busca por libname.soentão para libname.aadicionar prefixo lib e .so(se habilitado no momento) ou .asufixo. Mas a -l:nameopção pesquisará apenas exatamente o nome especificado: https://sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

Adicione o arquivo ou arquivo de objeto especificado por namespecà lista de arquivos a serem vinculados. Esta opção pode ser usada quantas vezes quiser. Se namespecestiver no formato :filename, ld pesquisará o caminho da biblioteca para um arquivo chamado filename, caso contrário, ele pesquisará o caminho da biblioteca para um arquivo chamado libnamespec.a.

Em sistemas que suportam bibliotecas compartilhadas, o ld também pode procurar por arquivos diferentes de libnamespec.a. Especificamente, em sistemas ELF e SunOS, ld pesquisará um diretório para uma biblioteca chamada libnamespec.soantes de pesquisar por uma chamada libnamespec.a. (Por convenção, uma .soextensão indica uma biblioteca compartilhada.) Observe que esse comportamento não se aplica a :filename, que sempre especifica um arquivo chamado filename.

O vinculador pesquisará um arquivo apenas uma vez, no local onde ele está especificado na linha de comando. Se o arquivo definir um símbolo que era indefinido em algum objeto que apareceu antes do arquivo na linha de comando, o vinculador incluirá o (s) arquivo (s) apropriado (s) do arquivo. No entanto, um símbolo indefinido em um objeto que aparece posteriormente na linha de comando não fará com que o vinculador pesquise o arquivo novamente.

Veja a -(opção para forçar o vinculador a pesquisar arquivos várias vezes.

Você pode listar o mesmo arquivo várias vezes na linha de comando.

Este tipo de pesquisa de arquivo é padrão para vinculadores Unix. No entanto, se você estiver usando ld no AIX, observe que ele é diferente do comportamento do vinculador AIX.

A variante -l:namespecestá documentada desde a versão 2.18 de binutils (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html

osgx
fonte
Esta opção parece funcionar onde tudo mais falha. Acabamos de nos deparar com um caso em que precisávamos fazer o link estático de libjsoncpp.a, porque nossas máquinas de construção produziriam binários vinculados a libjsocpp.so.0, enquanto o sistema operacional de destino fornece apenas libjsoncpp.so.1. Até que possamos esclarecer essa diferença, essa foi a única solução que produziu resultados adequados em nosso caso.
Tomasz W
4

Alguns carregadores (linkers) fornecem interruptores para ligar e desligar o carregamento dinâmico. Se o GCC estiver rodando em tal sistema (Solaris - e possivelmente outros), então você pode usar a opção relevante.

Se você souber quais bibliotecas deseja vincular estaticamente, pode simplesmente especificar o arquivo de biblioteca estática na linha de link - pelo caminho completo.

Jonathan Leffler
fonte
6
Mesmo que essa resposta tenha sido aceita, ela não aborda o problema completamente. Como @peoro explicou, o problema que ele está tentando resolver é que ele não possui versões estáticas de todas as bibliotecas, o que implica que ele gostaria de vincular o maior número de bibliotecas estaticamente possível. Veja minha resposta.
jcoffland
2

para vincular a biblioteca dinâmica e estática em uma linha, você deve colocar libs estáticas após libs dinâmicas e arquivos de objeto, como este:

gcc -lssl main.o -lFooLib -o main

caso contrário, não funcionará. leva algum tempo para eu descobrir.

Vincent
fonte