2.3. GCC에서 라이브러리 사용
코드에서 라이브러리 사용에 대해 알아봅니다.
2.3.1. 라이브러리 이름 지정 규칙 링크 복사링크가 클립보드에 복사되었습니다!
특수 파일 이름 규칙은 라이브러리에 사용됩니다. foo라는 라이브러리는 lib foo .so 또는 파일로 존재할 것으로 예상됩니다. 이 규칙은 GCC의 입력 옵션을 연결하지만 출력 옵션에 의해 자동으로 이해되지 않습니다.
libfoo . a
라이브러리에 대해 연결할 때 -lfoo:
-l옵션을 사용하여 foo 라는 이름으로만 라이브러리를 지정할 수 있습니다.$ gcc ... -lfoo ...-
라이브러리를 생성할 때 전체 파일 이름
libfoo.so또는libfoo.a를지정해야 합니다.
추가 리소스
2.3.2. 정적 및 동적 연결 링크 복사링크가 클립보드에 복사되었습니다!
개발자는 완전히 컴파일된 언어로 애플리케이션을 빌드할 때 정적 또는 동적 링크를 사용할 수 있습니다. 특히 Red Hat Enterprise Linux에서 C 및 C++ 언어 사용의 맥락에서 정적 연결과 동적 연결의 차이점을 이해하는 것이 중요합니다. 요약하면 Red Hat은 Red Hat Enterprise Linux용 애플리케이션에서 정적 링크를 사용하지 않는 것이 좋습니다.
- 정적 연결 및 동적 연결 비교
- 정적 링크를 사용하면 라이브러리가 결과 실행 파일의 일부가 됩니다. 동적 링크는 이러한 라이브러리를 별도의 파일로 유지합니다.
정적 연결에는 여러 가지 단점이 있으며 특히 전체 애플리케이션 및 glibc 및 libstdc++ 라이브러리의 경우 피해야 합니다.
resource use: 정적 연결로 인해 더 큰 실행 파일이 더 많은 코드가 포함됩니다. 라이브러리에서 제공되는 이 추가 코드는 시스템의 여러 프로그램에서 공유할 수 없으므로 런타임 시 파일 시스템 사용량과 메모리 사용량을 늘릴 수 있습니다. 동일한 정적으로 연결된 프로그램을 실행하는 여러 프로세스가 여전히 코드를 공유합니다.
반면 정적 애플리케이션에는 더 적은 런타임 재배치가 필요하므로 시작 시간을 단축하고 개인 상주 설정 크기(RSS) 메모리가 줄어듭니다. 정적 연결을 위한 생성된 코드는 위치 독립 코드(PIC)에 의해 도입된 오버헤드로 인해 동적 연결을 위한 것보다 더 효율적일 수 있습니다.
보안: 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() 함수에 대한 스레드 및 모든 형태의 호출을 지원하지 않습니다.
- 정적 연결 사례
정적 연결은 다음과 같은 경우에 합리적인 선택일 수 있습니다.
- 동적 연결에 사용할 수 없는 라이브러리를 사용하는 경우
-
빈 chroot 환경 또는 컨테이너에서 코드를 실행하는 데 완전히 정적 링크가 필요한 경우입니다. 그러나
glibc-static패키지를 사용한 정적 연결은 Red Hat에서 지원되지 않습니다.
2.3.3. 링크 시간 최적화 링크 복사링크가 클립보드에 복사되었습니다!
LTO(링크 시간 최적화)는 다음과 같은 이점이 있습니다.
- LTO를 사용하면 컴파일러가 링크 타임에 중간 표현을 사용하여 프로그램의 모든 변환 단위에서 다양한 최적화를 수행할 수 있습니다. 결과적으로 실행 파일과 라이브러리가 작아서 더 빨리 실행됩니다.
LTO를 사용하여 컴파일 시간에 패키지 소스 코드를 보다 철저하게 분석할 수 있으므로 잠재적인 코딩 오류에 대한 다양한 GCC 진단이 향상됩니다.
- 확인된 문제
- LTO에는 다음과 같은 알려진 문제가 있습니다.
하나의 정의 규칙(ODR)을 위반하면
-Wodr경고가 생성됩니다.ODR을 위반하면 정의되지 않은 동작이 발생하면
-Wodr경고가 생성됩니다. 이는 일반적으로 프로그램의 버그를 가리킵니다.-Wodr경고는 기본적으로 활성화되어 있습니다.LTO로 인해 메모리 사용량 증가
컴파일러는 프로그램이 구성된 변환 단위를 처리할 때 더 많은 메모리를 사용합니다. 프로그램이 제한된 시스템에서 LTO를 비활성화하거나 병렬 처리 수준을 낮춥니다.
GCC가 사용되지 않는 기능 제거
GCC는 컴파일러가 asm() 문을 참조하는 기호를 인식할 수 없기 때문에 사용되지 않는 함수를 제거할 수 있습니다. 이로 인해 컴파일 오류가 발생할 수 있습니다. 이를 방지하려면 프로그램에서 사용하는 기호에
__attribute__(used)를 추가합니다.-fPIC옵션으로 컴파일하면 오류가 발생합니다.GCC는 asm() 문의 내용을 구문 분석하지 않으므로 코드를
-fPIC명령줄 옵션으로 컴파일하면 오류가 발생할 수 있습니다. 이를 방지하려면 변환 단위를 컴파일할 때-fno-lto옵션을 사용합니다. 자세한 내용은 LTO FAQ {mdash}; 어셈블리 언어의 Symbol에서 확인할 수 있습니다.asm() 문에서
.symver지시문을 사용하여 기호 버전 관리를 구현하는 것은 LTO와 호환되지 않습니다. 그러나symver특성을 사용하여 기호 버전 관리를 구현할 수 있습니다. 예를 들면 다음과 같습니다.__attribute__ ((_symver_ ("<symbol>@VERS_1"))) void <symbol>_v1 (void) { }
2.3.4. GCC가 있는 라이브러리 사용 링크 복사링크가 클립보드에 복사되었습니다!
라이브러리는 프로그램에서 재사용할 수 있는 코드의 패키지입니다. C 또는 C++ 라이브러리는 다음 두 부분으로 구성됩니다.
- 라이브러리 코드
헤더 파일
- 라이브러리를 사용하는 코드 컴파일
- 헤더 파일은 라이브러리의 인터페이스, 즉 라이브러리에서 사용할 수 있는 함수 및 변수를 설명합니다. 헤더 파일의 정보는 코드를 컴파일하는 데 필요합니다.
일반적으로 라이브러리의 헤더 파일은 애플리케이션 코드와 다른 디렉터리에 배치됩니다. GCC 헤더 파일이 어디에 있는지 알려주려면 -I 옵션을 사용합니다.
$ gcc ... -Iinclude_path ...
include_path 를 헤더 파일 디렉터리의 실제 경로로 바꿉니다.
예를 들어 상대 경로를 지정하려면 some/interesting/directory:
$ gcc ... -Isome/interesting/directory ...
I 옵션을 여러 번 사용하여 헤더 파일이 있는 여러 디렉터리를 추가할 수 있습니다. 헤더 파일을 찾을 때 이러한 디렉터리는 -I 옵션에 표시되는 순서대로 검색됩니다.
- 라이브러리를 사용하는 코드 연결
실행 파일을 연결할 때 애플리케이션의 오브젝트 코드와 라이브러리의 바이너리 코드를 모두 사용할 수 있어야 합니다. 정적 및 동적 라이브러리의 코드는 다음과 같은 다양한 형태로 제공됩니다.
-
정적 라이브러리는 아카이브 파일로 사용할 수 있습니다. 오브젝트 파일 그룹이 포함되어 있습니다. 아카이브 파일의 확장자는
.a입니다. -
동적 라이브러리는 공유 오브젝트로 사용할 수 있습니다. 이는 일종의 실행 파일입니다. 공유 오브젝트에는 파일 이름 확장자
.so가 있습니다.
-
정적 라이브러리는 아카이브 파일로 사용할 수 있습니다. 오브젝트 파일 그룹이 포함되어 있습니다. 아카이브 파일의 확장자는
GCC에 라이브러리의 아카이브 또는 공유 오브젝트 파일이 어디에 있는지 알려주려면 -L 옵션을 사용합니다.
$ gcc ... -Llibrary_path -lfoo ...
library_path 를 라이브러리 디렉터리의 실제 경로로 바꿉니다.
L 옵션을 여러 번 사용하여 여러 디렉터리를 추가할 수 있습니다. 라이브러리를 찾으면 이러한 디렉터리가 -L 옵션 순서대로 검색됩니다.
옵션 순서는 중요합니다. GCC는 이 라이브러리가 있는 디렉토리를 모르는 한 라이브러리 foo 에 대해 연결할 수 없습니다. 따라서 라이브러리에 연결하는 -l 옵션을 사용하기 전에 -L 옵션을 사용하여 라이브러리 디렉터리를 지정합니다.
- 한 단계에서 라이브러리를 사용하는 코드 컴파일 및 연결
-
단일
gcc명령으로 컴파일 및 링크를 연결하면 컴파일 시간 및 링크 시간 옵션을 결합합니다.
2.3.5. GCC가 있는 정적 라이브러리 사용 링크 복사링크가 클립보드에 복사되었습니다!
정적 라이브러리는 오브젝트 파일이 포함된 아카이브로 사용할 수 있습니다. 연결 후 결과 실행 파일의 일부가 됩니다.
Red Hat은 보안상의 이유로 정적 연결을 사용하지 않는 것이 좋습니다. 정적 및 동적 연결을 참조하십시오. 특히 Red Hat이 제공하는 라이브러리와 관련하여 필요할 때만 정적 링크를 사용하십시오.
사전 요구 사항
- GCC가 시스템에 설치되어 있어야 합니다.
- 정적 및 동적 연결을 이해해야 합니다.
- 유효한 프로그램을 구성하는 소스 또는 오브젝트 파일 세트가 있으므로 일부 정적 라이브러리 foo 와 다른 라이브러리가 필요하지 않습니다.
foo 라이브러리는
libfoo.a파일로 사용할 수 있으며 동적 연결을 위해libfoo.so파일이 제공되지 않습니다.참고Red Hat Enterprise Linux의 일부인 대부분의 라이브러리는 동적 연결에서만 지원됩니다. 아래 단계는 동적 링크에 사용할 수 없는 라이브러리에서만 작동합니다.
프로세스
소스 및 오브젝트 파일에서 프로그램을 연결하려면
lib foo.a파일로 찾을 정적으로 연결된 라이브러리 foo를 추가합니다.- 코드가 포함된 디렉터리로 변경합니다.
foo 라이브러리의 헤더를 사용하여 프로그램 소스 파일을 컴파일합니다.
$ gcc ... -Iheader_path -c ...header_path 를 foo 라이브러리의 헤더 파일이 포함된 디렉터리의 경로로 바꿉니다.
프로그램을 foo 라이브러리와 연결합니다.
$ gcc ... -Llibrary_path -lfoo ...library_path 를
libfoo.a파일이 포함된 디렉터리의 경로로 바꿉니다.나중에 프로그램을 실행하려면 다음을 수행하십시오.
$ ./program주의정적 링크와 관련된
-staticGCC 옵션은 모든 동적 연결을 금지합니다. 대신-Wl,-Bstatic및-Wl,-Bdynamic옵션을 사용하여 링커 동작을 보다 정확하게 제어합니다. GCC에서 정적 및 동적 라이브러리 사용을 참조하십시오.
2.3.6. GCC를 사용하는 동적 라이브러리 사용 링크 복사링크가 클립보드에 복사되었습니다!
동적 라이브러리는 독립 실행형 실행 파일로 사용할 수 있으며, 연결 시간과 런타임에 필요합니다. 애플리케이션의 실행 파일과 무관하게 유지됩니다.
사전 요구 사항
- GCC는 시스템에 설치되어 있어야 합니다.
- 유효한 프로그램을 구성하는 소스 또는 오브젝트 파일 세트에는 일부 동적 라이브러리 foo 및 기타 라이브러리가 필요하지 않습니다.
foo 라이브러리는 libfoo.so 파일로 사용 가능해야 합니다.
- 동적 라이브러리에 대한 프로그램 연결
- 동적 라이브러리 foo 에 대해 프로그램을 연결하려면 다음을 수행합니다.
$ *gcc ... -L__library_path__ -l__foo__ ...*
프로그램이 동적 라이브러리에 연결되어 있는 경우 결과 프로그램은 항상 런타임 시 라이브러리를 로드해야 합니다. 라이브러리를 찾는 방법은 다음 두 가지가 있습니다.
-
실행 파일 자체에 저장된 실행 경로값 사용 런타임에
LD_LIBRARY_PATH변수 사용실행 파일에 저장된 실행 경로값 사용-
실행 경로는연결될 때 실행 파일의 일부로 저장된 특수 값입니다. 나중에 프로그램이 실행 파일에서 로드되면 런타임 링커는실행 경로값을 사용하여 라이브러리 파일을 찾습니다.
GCC 와 연결하는 동안 library_path 를 실행 경로로 저장하기 위해 다음을 수행합니다.
$ gcc ... -Llibrary_path -lfoo -Wl,-run path=library_path ...
path library_path 는 libfoo.so 파일이 포함된 디렉토리를 가리켜야 합니다.
-Wl,-run path= 옵션에 쉼표 뒤에 공백을 추가하지 마십시오.
나중에 프로그램을 실행하려면 다음을 수행합니다.
$ ./program
LD_LIBRARY_PATH환경 변수 사용라이브러리를 찾기 위해 검색 경로를 설정하는 또 다른 방법은
LD_LIBRARY_PATH환경 변수를 사용하는 것입니다. 이 변수의 값은 각 프로그램에 대해 변경해야 합니다. 이 값은 공유 라이브러리 오브젝트가 있는 경로를 나타내며 모든 프로그램 호출에 대해 설정해야 합니다.LD_LIBRARY_PATH환경 변수를 설정합니다.$ export LD_LIBRARY_PATH=library_path:$LD_LIBRARY_PATH프로그램을 실행합니다.
$ ./program
- 실행 경로 및
LD_LIBRARY_PATH의 상호 작용 -
Red Hat Enterprise Linux 10에서 연결 중에 프로그램에 인코딩된
실행 경로는연결된 라이브러리를LD_LIBRARY_PATH에서 찾을 수 없는 경우에만 사용됩니다.LD_LIBRARY_PATH전에 실행 경로가 검색되는 Red Hat Enterprise Linux 10의 이전 동작을 복원하기 위해-Wl,--disable-new-dtags옵션을 사용할 수 있습니다. - 라이브러리가 기본 디렉터리에 배치
- 런타임 링커 구성은 여러 디렉터리를 동적 라이브러리 파일의 기본 위치로 지정합니다. 이 기본 동작을 사용하려면 라이브러리를 적절한 디렉터리에 복사합니다.
이 문서에서는 동적 연결 동작을 자세히 다루지 않습니다. 자세한 내용은 다음 리소스를 참조하십시오.
- 동적 링커에 대한 Linux 도움말 페이지:
$ man ld.so
-
/etc/ld.so.conf구성 파일의 콘텐츠:
$ cat /etc/ld.so.conf
- 추가 구성 없이 동적 링커에서 인식하는 라이브러리의 보고서이며, 여기에는 디렉터리가 포함됩니다.
$ ldconfig -v
2.3.7. GCC에서 정적 및 동적 라이브러리 사용 링크 복사링크가 클립보드에 복사되었습니다!
경우에 따라 일부 라이브러리를 정적 및 기타 라이브러리를 동적으로 연결해야 하는 경우가 있습니다. 이 혼합 연결 접근 방식을 사용하려면 GCC가 다양한 라이브러리 유형을 처리하는 방법을 이해해야 합니다.
GCC 는 동적 라이브러리와 정적 라이브러리를 모두 인식합니다. lfoo 옵션이 발생하면 gcc 는 먼저 foo 라이브러리의 동적으로 연결된 버전이 포함된 공유 오브젝트( .so 파일)를 찾은 다음 라이브러리의 정적 버전이 포함된 아카이브 파일(.a)을 찾습니다. 따라서 이 검색으로 인해 다음과 같은 상황이 발생할 수 있습니다.
- 공유 오브젝트만 발견되고 gcc 만 동적으로 연결됩니다.
- 아카이브만 발견되고 gcc 만 정적으로 연결됩니다.
- 공유 오브젝트와 아카이브가 모두 있으며 기본적으로 gcc 는 공유 오브젝트에 대한 동적 연결을 선택합니다.
- 공유 오브젝트 또는 아카이브를 찾을 수 없으며 연결에 실패합니다.
이러한 규칙으로 인해 연결을 위한 정적 또는 동적 라이브러리 버전을 선택하는 가장 좋은 방법은 gcc 에서 해당 버전만 찾는 것입니다. 이는 -L경로 옵션을 지정할 때 라이브러리 버전이 포함된 디렉터리를 사용하거나 남겨 두어 어느 정도 제어할 수 있습니다.
또한 동적 링크가 기본값이므로 두 버전이 모두 있는 라이브러리를 정적으로 연결해야 하는 유일한 상황은 링크에서 명시적으로 지정해야 합니다. 두 가지 가능한 해결 방법이 있습니다.
-
-l옵션 대신 파일 경로별 정적 라이브러리 지정 링커에 옵션을 전달하려면
-Wl옵션 사용- 파일별 정적 라이브러리 지정
-
일반적으로 gcc 는 foo 라이브러리에 대해
-lfoo옵션을 사용하여 연결하라는 지시를 받습니다. 그러나 대신 라이브러리를 포함하는libfoo.a파일의 전체 경로를 지정할 수 있습니다.
$ *gcc ... path/to/libfoo.a ...*
파일 확장자 .a 에서gcc 는 프로그램과 연결할 라이브러리임을 이해할 것입니다. 그러나 라이브러리 파일의 전체 경로를 지정하는 것은 덜 유연합니다.
Wl옵션 사용-
gcc 옵션
-Wl은 옵션을 기본 링커에 전달하는 특수 옵션입니다. 이 옵션의 구문은 다른 gcc 옵션과 다릅니다. wl옵션 뒤에는 쉼표로 구분된 링커 옵션 목록이 있고 다른 gcc 옵션에는 공백으로 구분된 옵션 목록이 필요합니다.
gcc 에서 사용하는 ld 링커는 이 옵션의 라이브러리를 정적으로 연결하는 -Bstatic 옵션과 -Bdynamic 을 제공하여 동적으로 연결합니다. Bstatic 및 라이브러리를 링커에 전달한 후에는 다음 라이브러리가 옵션과 동적으로 연결되도록 기본 동적 연결 동작을 수동으로 복원해야 합니다.
-B dynamic
프로그램을 연결하려면 먼저 정적(lib first.a) 및 두 번째 동적으로(lib second.so ) 링크 라이브러리를 연결합니다.
$ gcc ... -Wl,-Bstatic -lfirst -Wl,-Bdynamic -lsecond ...
GCC 는 기본 ld 이외의 링커를 사용하도록 구성할 수 있습니다.