일부 라이브러리 만 정적 링크


108

GCC와 연결할 때 일부 특정 라이브러리 만 내 바이너리에 정적으로 연결할 수있는 방법은 무엇입니까?

gcc ... -static ...링크 된 모든 라이브러리 를 정적으로 링크하려고 하지만 일부 라이브러리의 정적 버전이 없습니다 (예 : libX11).


답변:


112

gcc -lsome_dynamic_lib code.c some_static_lib.a


5
개체 파일, 특히 정적 라이브러리 뒤에 라이브러리를 연결합니다. 고대 및 현대 버전의 링크 환경 (2010 년 11 월 현재 약간 오래된 버전에 대한 현상 유지 여부는 확실하지 않음)에서 code.c파일 앞에 정적 라이브러리를 나열하면 해당 기호가 무시되지 않는 한 무시됩니다. main()라이브러리 오브젝트 파일 중 하나에 기능.
Jonathan Leffler

44
이게 어떻게 작동하는지 자세히 설명 해주세요. 코드 전용 답변은 초보자에게 도움이되지 않습니다.
jb.

8
기본적으로 @jb, gcc는 동적으로 연결됩니다. -lsome_dynamic_lib를 사용하면 예상대로 동적으로 연결됩니다. 그러나 gcc에 정적 라이브러리가 명시 적으로 주어지면 항상 정적으로 링크하려고합니다. 그러나 기호가 해결되는 순서에 대한 몇 가지 까다로운 세부 사항이 있습니다. 어떻게 작동하는지 잘 모르겠습니다. 의심
스러운 경우

4
예를 들어 GPL 라이브러리
HiB

1
@HiB GPL은 정적 및 동적 링크에 동일한 방식을 적용합니다
osvein

50

ld옵션 을 사용할 수도 있습니다.-Bdynamic

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

그 이후의 모든 라이브러리 (gcc에 의해 자동으로 연결된 시스템 라이브러리 포함)는 동적으로 연결됩니다.


19
-Wl, -Bdynamic에는 GNU ld가 필요하므로이 솔루션은 gcc가 시스템 ld를 사용하는 시스템 (예 : Mac OS X)에서는 작동하지 않습니다.
pts

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

다음을 사용할 수도 있습니다. -static-libgcc -static-libstdc++gcc 라이브러리 용 플래그

경우에 것을 명심 libs1.so하고 libs1.a모두가 존재 링커가 선택할 것 libs1.so이 전인지 -Wl,-Bstatic또는 후 -Wl,-Bdynamic. -L/libs1-library-location/전화하기 전에 통과하는 것을 잊지 마십시오 -ls1.


1
적어도이 솔루션은 libgomp에 대한 정적 링크에서 작동합니다!
Jérôme

이것은 나를 위해 잘 작동하지만 -static명령의 어딘가 를 사용 하면 실패합니다 (원하는 라이브러리보다 더 많은 것을 정적으로 연결하려고한다고 가정합니다).
nh2

4
NB. 의 순서 -Wl,-Bstatic와는 -Wl,-Bdynamic중요하다.
Pavel Vlasov

27

ld(gcc에서는 작동하지 않음) 맨 페이지 에서 --static옵션을 참조하십시오 .

이 옵션은 명령 줄에서 여러 번 사용할 수 있습니다.이 옵션은 뒤에 오는 -l 옵션을 검색하는 라이브러리에 영향을줍니다.

한 가지 해결책은 --static명령 줄 의 옵션 앞에 동적 종속성을 배치하는 것 입니다.

또 다른 가능성은를 사용하지 --static않고 대신 특정 라이브러리에서 정적으로 링크하기 위해 정적 개체 파일의 전체 파일 이름 / 경로 (즉, -l 옵션을 사용하지 않음)를 제공하는 것입니다. 예:

# 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)

예에서 볼 수 libX11있듯이은 정적으로 링크 되었기 때문에 동적으로 링크 된 라이브러리 목록에 없습니다.

주의 : .so파일은 전체 파일 이름 / 경로로 지정된 경우에도 항상 동적으로 연결됩니다.


libX11.a와 출력 사이의 관계는 무엇입니까 ldd a.out?
Raffi Khatchadourian

1
아, 알겠습니다. ldd필요한 공유 라이브러리를 출력하고 libX11은 해당 목록에 나타나지 않습니다.
Raffi Khatchadourian

이것은 명확하지 않습니다. '이 옵션'과 '그 옵션'이라고 말합니다. 어떤 옵션?
Octopus

19

내가 이해하는 문제는 다음과 같습니다. 일부는 정적, 일부는 동적, 일부는 정적 및 동적의 여러 라이브러리가 있습니다. gcc 의 기본 동작은 "대부분 동적"을 연결하는 것입니다. 즉, gcc 는 가능한 경우 동적 라이브러리에 연결되지만 그렇지 않으면 정적 라이브러리로 대체됩니다. gcc에 -static 옵션을 사용하는 경우 적절한 동적 라이브러리가 있더라도 정적 라이브러리 만 연결하고 정적 라이브러리를 찾을 수없는 경우 오류와 함께 종료됩니다.

gcc 가 갖고 싶었던 또 다른 옵션 은 내가 -mostly-static 이라고 부르고 본질적으로 -dynamic (기본값) 의 반대입니다 . -대부분 정적 은 존재한다면 정적 라이브러리에 대한 링크를 선호하지만 동적 라이브러리로 됩니다.

이 옵션은 존재하지 않지만 다음 알고리즘으로 에뮬레이션 할 수 있습니다.

  1. -static 을 포함하여 out으로 링크 명령 줄을 구성합니다 .

  2. 동적 링크 옵션을 반복합니다.

  3. 라이브러리 경로, 즉 변수 <lib_path>-L <lib_dir> 형식의 옵션을 누적합니다.

  4. 각 동적 링크 옵션, 즉 -l <lib_name> 형식의 옵션에 대해 gcc <lib_path> -print-file-name = lib <lib_name> .a 명령을 실행 하고 출력을 캡처하십시오.

  5. 명령이 전달한 것 이외의 것을 인쇄하면 정적 라이브러리의 전체 경로가됩니다. 동적 라이브러리 옵션을 정적 라이브러리의 전체 경로로 바꿉니다.

전체 링크 명령 줄을 처리 할 때까지 헹구고 반복합니다. 선택적으로 스크립트는 정적 링크에서 제외 할 라이브러리 이름 목록을 가져올 수도 있습니다.

다음 bash 스크립트가 트릭을 수행하는 것 같습니다.

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

예를 들면 :

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

내 시스템에서 다음을 반환합니다.

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"

또는 제외 :

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

다음을 얻습니다.

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

7

-l:libstatic1.agcc에는 정적 라이브러리를 연결하는 데 사용할 수있는 -l 옵션의 (l 콜론 빼기) 변형 도 있습니다 ( https://stackoverflow.com/a/20728782 감사합니다 ). 문서화되어 있습니까? gcc의 공식 문서에는 없습니다 (공유 라이브러리에도 정확하지 않음) : https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library 

연결할 때 library라는 라이브러리를 검색합니다. (별도의 인수로 라이브러리를 사용하는 두 번째 대안은 POSIX 준수만을위한 것이며 권장되지 않습니다.) ... -l 옵션을 사용하는 것과 파일 이름을 지정하는 것의 유일한 차이점은 -l이 라이브러리를 'lib'로 둘러싸고 '.a'는 여러 디렉토리를 검색합니다.

binutils ld doc에서 설명합니다. 이 -lname옵션은 lib 접두사 및 (현재 활성화 된 경우) 또는 접미사 libname.solibname.a추가 하기 위해 검색합니다 . 그러나 옵션은 지정된 이름 만 정확하게 검색합니다 : https://sourceware.org/binutils/docs/ld/Options.html.so.a-l:name

-l namespec
--library=namespec

namespec링크 할 파일 목록에에 지정된 아카이브 또는 개체 파일을 추가합니다 . 이 옵션은 여러 번 사용할 수 있습니다. 이 namespec형식 :filename이면 ld는 라이브러리 경로에서라는 파일 filename을 검색하고, 그렇지 않으면 라이브러리 경로에서라는 파일을 검색합니다 libnamespec.a.

공유 라이브러리를 지원하는 시스템에서 ld는 libnamespec.a. 특히 ELF 및 SunOS 시스템에서 ld는라는 라이브러리 libnamespec.so를 검색하기 전에 라는 라이브러리에 대한 디렉토리를 검색합니다 libnamespec.a. (관습 적으로 .so확장자는 공유 라이브러리를 나타냅니다.)이 동작은에 적용되지 않으며 :filename항상라는 파일을 지정합니다.filename .

링커는 명령 줄에 지정된 위치에서 아카이브를 한 번만 검색합니다. 아카이브가 명령 줄에서 아카이브 이전에 나타난 일부 개체에서 정의되지 않은 기호를 정의하는 경우 링커는 아카이브의 적절한 파일을 포함합니다. 그러나 나중에 명령 줄에 나타나는 개체의 정의되지 않은 기호로 인해 링커가 아카이브를 다시 검색하지 않습니다.

-(링커가 아카이브를 여러 번 검색하도록하는 방법 은 옵션을 참조하십시오 .

명령 줄에 동일한 아카이브를 여러 번 나열 할 수 있습니다.

이러한 유형의 아카이브 검색은 Unix 링커의 표준입니다. 그러나 AIX에서 ld를 사용하는 경우 AIX 링커의 동작과 다릅니다.

변종 -l:namespec은 2.18 버전의 binutils (2007) 이후 문서화되었습니다. https://sourceware.org/binutils/docs-2.18/ld/Options.html


이 옵션은 다른 모든 것이 실패하는 경우 작동하는 것 같습니다. 빌드 머신은 libjsocpp.so.0에 대해 링크 된 바이너리를 생성하는 반면 대상 OS는 libjsoncpp.so.1 만 제공하기 때문에 libjsoncpp.a를 정적 링크해야하는 경우를 발견했습니다. 이 차이를 해결할 수있을 때까지 이것이 우리의 경우에 적절한 결과를내는 유일한 해결책이었습니다.
Tomasz W

4

일부 로더 (링커)는 동적로드를 켜고 끄는 스위치를 제공합니다. GCC가 이러한 시스템 (Solaris 및 기타)에서 실행중인 경우 관련 옵션을 사용할 수 있습니다.

정적으로 링크하려는 라이브러리를 알고있는 경우 링크 라인에서 전체 경로로 정적 라이브러리 파일을 간단히 지정할 수 있습니다.


6
이 답변이 수락되었지만 문제를 완전히 해결하지는 못합니다. @peoro가 해결하고자하는 문제는 그가 가능한 한 많은 라이브러리를 정적으로 링크하고 싶다는 것을 암시하는 모든 라이브러리의 정적 버전이 없다는 것입니다. 내 대답을 참조하십시오.
jcoffland

2

한 줄 내에서 동적 및 정적 라이브러리를 연결하려면 다음 과 같이 동적 라이브러리 및 개체 파일 뒤에 정적 라이브러리를 배치해야합니다 .

gcc -lssl main.o -lFooLib -o main

그렇지 않으면 작동하지 않습니다. 그것을 알아내는 데 언젠가는 걸립니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.