vinculação estática apenas algumas bibliotecas


Respostas:


112

gcc -lsome_dynamic_lib code.c some_static_lib.a


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.


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.


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.


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"

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


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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.