2.3. GCC와 함께 라이브러리 사용

코드에서 라이브러리 사용에 대해 알아봅니다.

2.3.1. 라이브러리 이름 지정 규칙

특수 파일 이름 규칙은 라이브러리에 사용됩니다. foo라는 라이브러리가 파일 lib foo.so 또는 libfoo . a 로 존재할 것으로 예상됩니다. 이 규칙은 GCC의 입력 옵션을 연결하면 자동으로 이해되지만 출력 옵션에는 해당되지 않습니다.

  • 라이브러리에 대해 연결할 때 라이브러리는 -l 옵션을 -l foo : 로 사용하여 foo 이름으로만 지정할 수 있습니다.

    $ gcc ... -lfoo ...
  • 라이브러리를 생성할 때 전체 파일 이름 libfoo.so 또는 libfoo.a 를 지정해야 합니다.

2.3.2. 정적 및 동적 연결

개발자는 완전히 컴파일된 언어로 애플리케이션을 빌드할 때 정적 또는 동적 연결을 사용할 수 있습니다. 특히 Red Hat Enterprise Linux에서 C 및 C++ 언어를 사용하는 컨텍스트에서 정적 연결과 동적 연결의 차이점을 이해하는 것이 중요합니다. 요약하자면, Red Hat은 Red Hat Enterprise Linux의 애플리케이션에서 정적 링크 사용을 권장하지 않습니다.

정적 및 동적 연결 비교

정적 연결은 라이브러리가 결과 실행 파일의 일부로 만듭니다. 동적 연결은 이러한 라이브러리를 별도의 파일로 유지합니다.

동적 및 정적 연결은 다음과 같은 여러 방법으로 비교할 수 있습니다.

리소스 사용

정적 연결로 더 많은 코드가 포함된 실행 파일이 더 커집니다. 라이브러리에서 제공하는 이 추가 코드는 시스템의 여러 프로그램에서 공유할 수 없으므로 런타임에 파일 시스템 사용과 메모리 사용량이 증가합니다. 정적으로 연결된 동일한 프로그램을 실행하는 여러 프로세스가 여전히 코드를 공유합니다.

반면, 정적 애플리케이션에는 더 적은 가동 시간 재배치가 필요하므로 시작 시간이 줄어들고RSS(개인 상주 설정 크기) 메모리가 줄어듭니다. 정적 연결을 위한 생성된 코드는 PoC(위치 종속 코드)에서 도입한 오버헤드로 인해 동적 연결보다 효율적일 수 있습니다.

보안
ABI 호환성을 제공하는 동적으로 연결된 라이브러리는 이러한 라이브러리에 따라 실행 파일을 변경하지 않고 업데이트할 수 있습니다. 이는 Red Hat이 보안 업데이트를 제공하는 Red Hat Enterprise Linux의 일부로 Red Hat에서 제공하는 라이브러리에 특히 중요합니다. 이러한 라이브러리에 대한 정적 연결은 권장되지 않습니다.
호환성

정적 연결은 운영 체제에서 제공하는 라이브러리 버전과 관계없이 실행 파일을 제공하는 것으로 나타납니다. 그러나 대부분의 라이브러리는 다른 라이브러리에 의존합니다. 정적 연결을 사용하면 이 종속성이 유연해지므로 정방향 및 이전 버전과의 호환성이 모두 손실됩니다. 정적 연결은 실행 파일이 빌드된 시스템에서만 작동하도록 보장됩니다.

주의

GNU C 라이브러리(glibc)에서 정적 라이브러리를 연결하는 애플리케이션은 여전히 glibc 가 동적 라이브러리로서 시스템에 존재해야 합니다. 또한 애플리케이션 실행 시 사용할 수 있는 glibc 의 동적 라이브러리 변형은 애플리케이션을 연결하는 동안 현재 버전과 약간 동일한 버전이어야 합니다. 따라서 정적 연결은 실행 파일이 빌드된 시스템에서만 작동할 수 있습니다.

지원 범위
Red Hat에서 제공하는 대부분의 정적 라이브러리는 CodeReady Linux Builder 채널에 있으며 Red Hat에서 지원하지 않습니다.
기능

일부 라이브러리, 특히 GNU C 라이브러리(glibc)는 정적으로 연결될 때 축소된 기능을 제공합니다.

예를 들어, 정적으로 연결된 경우 glibc 는 스레드를 지원하지 않으며 동일한 프로그램에서 dlopen() 함수에 대한 호출 형식을 지원합니다.

나열된 단점으로 인해 특히 전체 애플리케이션 및 glibclibstdc++ 라이브러리의 경우 정적 연결을 피할 수 있습니다.

정적 연결 케이스

다음과 같은 일부 경우 정적 연결이 합리적인 선택이 될 수 있습니다.

  • 동적 연결에 사용할 수 없는 라이브러리 사용.
  • chroot 환경 또는 컨테이너에서 코드를 실행하려면 완전히 정적 연결이 필요할 수 있습니다. 그러나 Red Hat에서는 glibc-static 패키지를 사용한 정적 연결은 지원되지 않습니다.

2.3.4. GCC를 사용하여 라이브러리 사용

라이브러리는 프로그램에서 재사용할 수 있는 코드 패키지입니다. C 또는 C++ 라이브러리는 다음 두 부분으로 구성됩니다.

  • 라이브러리 코드
  • 헤더 파일

라이브러리를 사용하는 코드 컴파일

헤더 파일은 라이브러리의 인터페이스, 라이브러리에서 사용할 수 있는 함수 및 변수를 설명합니다. 헤더 파일의 정보는 코드를 컴파일하는 데 필요합니다.

일반적으로 라이브러리의 헤더 파일은 애플리케이션 코드와 다른 디렉터리에 저장됩니다. GCC에 헤더 파일이 있는 위치를 지정하려면 -I 옵션을 사용합니다.

$ gcc ... -Iinclude_path ...

include_path 를 헤더 파일 디렉터리의 실제 경로로 바꿉니다.

I 옵션을 여러 번 사용하여 헤더 파일로 여러 디렉터리를 추가할 수 있습니다. 헤더 파일을 찾을 때 이러한 디렉터리는 -I 옵션에 표시되는 순서대로 검색됩니다.

라이브러리를 사용하는 코드 연결

실행 파일을 연결할 때 애플리케이션의 개체 코드와 라이브러리의 바이너리 코드를 모두 사용할 수 있어야 합니다. 정적 및 동적 라이브러리의 코드는 다양한 형태로 제공됩니다.

  • 정적 라이브러리는 아카이브 파일로 사용할 수 있습니다. 개체 파일의 그룹이 포함되어 있습니다. 아카이브 파일에는 파일 이름 확장명이 있습니다 .
  • 동적 라이브러리는 공유 개체로 사용할 수 있습니다. 실행 파일의 형식입니다. 공유 오브젝트에는 파일 이름 확장명이 있습니다 .so.

GCC에 라이브러리의 아카이브 또는 공유 오브젝트 파일을 지정하려면 -L 옵션을 사용합니다.

$ gcc ... -Llibrary_path -lfoo ...

library_path 를 라이브러리 디렉터리의 실제 경로로 바꿉니다.

L 옵션을 여러 번 사용하여 여러 디렉터리를 추가할 수 있습니다. 라이브러리를 찾을 때 이러한 디렉터리는 -L 옵션 순서로 검색됩니다.

옵션의 순서가 중요합니다. GCC는 이 라이브러리의 디렉토리를 모르는 한 foo 라이브러리와 연결할 수 없습니다. 따라서 라이브러리를 연결하는 데 - l 옵션을 사용하기 전에 - L 옵션을 사용하여 라이브러리 디렉터리를 지정합니다.

한 단계에서 라이브러리를 사용하는 코드 컴파일 및 연결

코드를 컴파일하고 하나의 gcc 명령에 연결할 수 있는 경우 한 번에 위에서 언급한 두 가지 상황에 대해 옵션을 사용합니다.

추가 리소스

2.3.5. GCC와 함께 정적 라이브러리 사용

정적 라이브러리는 오브젝트 파일을 포함하는 아카이브로 사용할 수 있습니다. 연결한 후 결과 실행 파일의 일부가 됩니다.

참고

Red Hat은 보안상의 이유로 정적 링크 사용을 권장하지 않습니다. 2.3.2절. “정적 및 동적 연결”을 참조하십시오. 특히 Red Hat에서 제공하는 라이브러리에 대해 필요할 때만 정적 링크를 사용합니다.

사전 요구 사항

  • GCC는 시스템에 설치되어 있어야 합니다.
  • 정적 및 동적 연결을 이해해야 합니다.
  • 유효한 프로그램을 구성하는 일련의 소스 또는 오브젝트 파일이 있으므로 일부 정적 라이브러리 foo 및 다른 라이브러리가 필요하지 않습니다.
  • foo 라이브러리는 libfoo.a 파일로 사용할 수 있으며 동적 연결에는 libfoo.so 파일이 제공되지 않습니다.
참고

Red Hat Enterprise Linux의 일부인 대부분의 라이브러리는 동적 연결에만 지원됩니다. 아래 단계는 동적 연결에 사용할 수 없는 라이브러리에서만 작동합니다.

절차

소스 및 객체 파일에서 프로그램을 연결하려면 libfoo.a 파일로 찾을 수 있는 정적으로 연결된 라이브러리 foo 를 추가합니다.

  1. 코드가 포함된 디렉터리로 변경합니다.
  2. foo 라이브러리의 헤더를 사용하여 프로그램 소스 파일을 컴파일합니다.

    $ gcc ... -Iheader_path -c ...

    header_pathfoo 라이브러리의 헤더 파일이 포함된 디렉터리의 경로로 바꿉니다.

  3. foo 라이브러리와 프로그램을 연결합니다.

    $ gcc ... -Llibrary_path -lfoo ...

    library_pathlibfoo.a 파일이 포함된 디렉터리 경로로 바꿉니다.

  4. 나중에 프로그램을 실행하려면 다음을 수행하십시오.

    $ ./program
주의

정적 링크와 관련된 -static GCC 옵션은 모든 동적 연결을 금지합니다. 대신 -Wl,-Bstatic-Wl,-Bdynamic 옵션을 사용하여 링커 동작을 보다 정확하게 제어합니다. 2.3.7절. “GCC를 사용하여 정적 및 동적 라이브러리 사용”을 참조하십시오.

2.3.6. GCC와 함께 동적 라이브러리 사용

동적 라이브러리는 연결 시간과 런타임 모두에 필요한 독립 실행형 실행 파일로 사용할 수 있습니다. 애플리케이션의 실행 파일과는 별개로 유지됩니다.

사전 요구 사항

  • GCC는 시스템에 설치되어 있어야 합니다.
  • 유효한 프로그램을 구성하는 소스 또는 오브젝트 파일 세트로, 일부 동적 라이브러리 foo 및 다른 라이브러리가 필요하지 않습니다.
  • foo 라이브러리는 libfoo.so 파일로 사용할 수 있어야 합니다.

동적 라이브러리에 대해 프로그램 연결

동적 라이브러리 foo 에 대해 프로그램을 연결하려면 다음을 수행합니다.

$ gcc ... -Llibrary_path -lfoo ...

프로그램이 동적 라이브러리와 연결되어 있는 경우 결과 프로그램은 항상 런타임에 라이브러리를 로드해야 합니다. 라이브러리를 찾는 데는 두 가지 옵션이 있습니다.

  • 실행 파일 자체에 저장된 rpath 값 사용
  • 런타임 시 LD_LIBRARY_PATH 변수 사용

실행 가능한 파일에 저장된 rpath 값 사용

rpath 는 연결될 때 실행 파일의 일부로 저장되는 특수 값입니다. 나중에 프로그램이 실행 파일에서 로드되면 런타임 링커는 rpath 값을 사용하여 라이브러리 파일을 찾습니다.

GCC 와 연결하는 동안 경로 library_path를 rpath 로 저장합니다.

$ gcc ... -Llibrary_path -lfoo -Wl,-rpath=library_path ...

경로 library_pathlibfoo.so 파일이 포함된 디렉터리를 가리켜야 합니다.

중요

-Wl,-rpath= 옵션에 쉼표 뒤에 공백을 추가하지 마십시오.

프로그램을 나중에 실행하려면 다음을 수행합니다.

$ ./program

LD_LIBRARY_PATH 환경 변수 사용

프로그램의 실행 파일에 rpath 를 찾을 수 없는 경우 런타임 링커는 LD_LIBRARY_PATH 환경 변수를 사용합니다. 이 변수의 값은 각 프로그램에 대해 변경해야 합니다. 이 값은 공유 라이브러리 오브젝트가 있는 경로를 나타내야 합니다.

rpath 설정 없이 프로그램을 실행하려면 path library_path 에 라이브러리가 있음 :

$ export LD_LIBRARY_PATH=library_path:$LD_LIBRARY_PATH
$ ./program

rpath 값을 제외하면 유연성이 제공되지만, 프로그램을 실행할 때마다 LD_LIBRARY_PATH 변수를 설정해야 합니다.

라이브러리를 기본 디렉터리에 배치

런타임 링커 구성은 여러 디렉터리를 동적 라이브러리 파일의 기본 위치로 지정합니다. 이러한 기본 동작을 사용하려면 라이브러리를 적절한 디렉터리에 복사합니다.

동적 링커 동작에 대한 자세한 설명은 이 문서의 범위를 벗어납니다. 자세한 내용은 다음 리소스를 참조하십시오.

  • 동적 링커의 Linux 도움말 페이지:

    $ man ld.so
  • /etc/ld.so.conf 설정 파일의 내용:

    $ cat /etc/ld.so.conf
  • 디렉터리가 포함된 추가 구성 없이 동적 링커에서 인식하는 라이브러리 보고서:

    $ ldconfig -v

2.3.7. GCC를 사용하여 정적 및 동적 라이브러리 사용

경우에 따라 일부 라이브러리를 정적 및 일부 동적으로 연결해야 할 수도 있습니다. 이러한 상황은 몇 가지 도전 과제입니다.

사전 요구 사항

  • 정적 및 동적 연결 이해

소개

GCC 는 동적 라이브러리와 정적 라이브러리를 모두 인식합니다. l foo 옵션이 나오면 gcc 는 먼저 foo 라이브러리의 동적으로 연결된 버전이 포함된 공유 오브젝트(a .so 파일)를 찾은 다음, 정적 버전의 라이브러리를 포함하는 아카이브 파일(.a)을 찾습니다. 따라서 다음과 같은 상황이 발생할 수 있습니다.

  • 공유 오브젝트만 발견되고 gcc 링크가 동적으로 제공됩니다.
  • 아카이브만 발견되고 gcc 링크만 정적으로 연결됩니다.
  • 공유 오브젝트와 아카이브가 모두 발견되며 gcc 는 공유 오브젝트에 대한 동적 연결을 선택합니다.
  • 공유 개체와 아카이브를 모두 찾을 수 없으며 연결에 실패합니다.

이러한 규칙으로 인해 연결할 라이브러리의 정적 또는 동적 버전을 선택하는 가장 좋은 방법은 gcc 에서 해당 버전만 찾는 것입니다. 이 작업은 -L경로 옵션을 지정할 때 라이브러리 버전이 포함된 디렉터리를 사용하거나 종료하여 어느 정도까지 제어할 수 있습니다.

또한 동적 연결이 기본값이기 때문에 연결을 명시적으로 지정해야 하는 유일한 상황은 두 버전이 있는 라이브러리를 정적으로 연결해야 합니다. 두 가지 해결 방법이 있습니다.

  • l 옵션 대신 파일 경로로 정적 라이브러리 지정
  • 옵션을 링크에 전달하려면 -Wl 옵션을 사용합니다.

파일을 기준으로 정적 라이브러리 지정

일반적으로 gccfoo 라이브러리에 -l foo 옵션을 사용하여 연결하라는 지시를 받습니다. 대신 라이브러리를 포함하는 libfoo.a 파일의 전체 경로를 지정할 수 있습니다.

$ gcc ... path/to/libfoo.a ...

파일 확장자 에서 gcc 는 이것이 프로그램과 연결할 라이브러리임을 이해할 것입니다. 그러나 라이브러리 파일의 전체 경로를 지정하는 것은 덜 유연한 방법입니다.

W l 옵션 사용

gcc 옵션 -Wl 은 옵션을 기본 링커에 전달하는 특수 옵션입니다. 이 옵션의 구문은 다른 gcc 옵션과 다릅니다. w l 옵션 뒤에는 콤마로 구분된 링커 옵션 목록이 오는 반면, 다른 gcc 옵션에는 공백으로 구분된 옵션 목록이 필요합니다.

gcc 에서 사용하는 ld 링커는 이 옵션을 각각 정적으로 또는 동적으로 연결해야 하는지 여부를 지정하는 -Bstatic 및 -Bdynamic 옵션을 제공합니다. B static 및 라이브러리를 링커에 통과한 후에는 기본 동적 연결 동작을 수동으로 복원해야 합니다. 이 경우 라이브러리가 -Bdynamic 옵션과 동적으로 연결되도록 수동으로 복원해야 합니다.

프로그램을 연결하려면 먼저 라이브러리를 정적으로 연결(libfirst.a) 및 두 번째 동적으로 연결(libsecond.so).

$ gcc ... -Wl,-Bstatic -lfirst -Wl,-Bdynamic -lsecond ...
참고

gcc 는 기본 ld 이외의 링커를 사용하도록 구성할 수 있습니다.

추가 리소스