작성일:

GNU C++ 프로그램에서 정적 라이브러리를 링크하는 방법

정적 라이브러리 사용

사용 시 고려 사항

  • 프로그램 크기 증가: 정적 라이브러리를 사용하면 실행 파일 크기가 크게 증가할 수 있슴
  • 라이브러리 업데이트: 정적 라이브러리를 사용하면 라이브러리 업데이트 시 프로그램을 다시 컴파일해야 필요

사용 장점

  • 운영 체제 의존성 감소: 정적 라이브러리를 사용하면 프로그램 실행에 필요한 모든 라이브러리가 실행 파일에 포함되어 있어 운영 체제에 동일한 라이브러리가 설치되어 있지 않아도 프로그램을 실행
  • 보안 강화: 정적 라이브러리를 사용하면 공유 라이브러리 공격과 같은 보안 취약점에 대한 위험을 줄일 수 있음
  • 성능 향상: 정적 라이브러리를 사용하면 프로그램 실행 속도를 향상시킬 수 있음

정적 라이브러리 링크

링크 옵션

  • -static
    • C 런타임 라이브러리뿐만 아니라 모든 외부 라이브러리를 정적으로 링크합니다.
    • 실행 파일에 모든 필요한 라이브러리 코드를 포함시킵니다.
    • 결과적으로 실행 파일의 크기가 크게 증가할 수 있습니다.
    • 시스템 라이브러리에 대한 의존성을 제거하여 이식성을 높입니다.
  • -static-libstdc++
    • C++ 표준 라이브러리만 정적으로 링크합니다.
    • 다른 시스템 라이브러리나 사용자 정의 라이브러리는 여전히 동적으로 링크될 수 있습니다.
    • -static 옵션보다 실행 파일 크기 증가가 덜합니다.
    • C++ ABI 호환성 문제를 해결하는 데 유용할 수 있습니다.
g++ -static main.cpp -o myprogram

라이브러리 링크 지정 방법

  • -l 옵션 (일반적인 방법)
    • 컴파일러에게 특정 라이브러리를 링크하도록 지시.
      • 먼저 동적 라이브러리(.so 파일)를 찾습니다.
      • 동적 라이브러리가 없으면 정적 라이브러리(.a 파일)를 찾습니다.
    • 표준 라이브러리 경로(예: /usr/lib, /lib)와 -L 옵션으로 지정된 추가 경로에서 라이브러리를 찾습니다.
      • 정적 라이브러리를 명시적으로 지정하려면, 전체 파일 경로를 사용할 수 있습니다.
      • 예: g++ main.cpp /path/to/libmylib.a -o myprogram
    • 뒤에 오는 라이브러리 이름은 관례에 따라 lib 접두사와 .a 확장자를 가진 정적 라이브러리 파일명
    • -static 옵션과 함께 사용 시
      • -static 옵션과 함께 -l을 사용하면, 링커는 오직 정적 라이브러리만을 찾습니다.
      • 이 경우 정적 라이브러리가 없다면 링크 에러가 발생합니다.
  • -Wl,–whole-archive 옵션
    • 컴파일러에게 라이브러리에 있는 모든 객체 파일을 링크하도록 지시
    • 이 옵션을 사용하면 라이브러리에 있는 모든 함수가 사용될 수 있음
    • 주로 정적 라이브러리(.a)에 사용됩니다.
    • 전역 객체의 생성자나 정적 초기화 함수가 반드시 링크되도록 보장합니다.

주요 차이점 : -l -Wl,–whole-archive

  • -l은 선택적으로 필요한 부분만 링크하지만, –whole-archive는 라이브러리 전체를 링크합니다.
  • -l은 동적/정적 라이브러리 모두에 사용 가능하지만, –whole-archive는 주로 정적 라이브러리에 사용됩니다.
  • –whole-archive를 사용하면 실행 파일 크기가 더 커질 수 있습니다.
  • -Wl,–whole-archive는 플러그인 시스템이나 동적으로 로드되는 모듈을 구현할 때 특히 유용할 수 있습니다
g++ main.cpp -lstdc++ -Wl,--whole-archive my_lib.a -o myprogram

의존성 확인

실행 파일의 의존성을 확인하는 것은 프로그램이 어떤 라이브러리에 의존하고 있는지 파악하는 데 중요합니다. 이를 통해 정적 링킹이 제대로 되었는지, 혹은 동적 라이브러리에 대한 의존성이 있는지 확인할 수 있습니다.

ldd

ldd 명령은 실행 파일의 의존성을 확인하는 데 사용. 다음 명령은 myprogram 실행 파일의 의존성을 확인합니다.

$ ldd myprogram-static
	not a dynamic executable

$ ldd myprogram
	linux-vdso.so.1 (0x0000ffff8323c000)
	libstdc++.so.6 => /lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000ffff82f90000)
	libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ffff82f60000)
	libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff82db0000)
	/lib/ld-linux-aarch64.so.1 (0x0000ffff83203000)
	libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffff82d10000)

readelf 명령 사용

readelf 명령은 ELF (Executable and Linkable Format) 파일의 더 자세한 정보를 제공합니다.

$ readelf -d myprogram | grep NEEDED
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

이 명령은 프로그램이 필요로 하는 공유 라이브러리를 보여줍니다.

objdump 명령 사용

objdump 명령을 사용하여 실행 파일의 섹션 헤더를 검사할 수 있습니다.

$ objdump -h myprogram

main:     file format elf64-littleaarch64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001b  0000000000000238  0000000000000238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.gnu.build-id 00000024  0000000000000254  0000000000000254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.package 00000070  0000000000000278  0000000000000278  00000278  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .note.ABI-tag 00000020  00000000000002e8  00000000000002e8  000002e8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
...

이 명령은 실행 파일의 각 섹션에 대한 정보를 제공하며, 정적으로 링크된 파일의 경우 더 많은 섹션이 포함되어 있을 것입니다.

라이브러리 ABI(Application Binary Interface)의 버전 확인

strings 명령을 사용하여 실행 파일 내에 포함된 라이브러리 버전 정보를 추출할 수 있습니다.

$ strings myprogram | grep ^GLIB
GLIBC_2.17
GLIBC_2.32
GLIBC_2.34
GLIBCXX_3.4.20
GLIBCXX_3.4.26
GLIBCXX_3.4.9
GLIBCXX_3.4.29
GLIBCXX_3.4.15
GLIBCXX_3.4.11
GLIBCXX_3.4.30
GLIBCXX_3.4.21
GLIBCXX_3.4.14
GLIBCXX_3.4.22
GLIBCXX_3.4

이 출력은 프로그램이 사용하는 GLIBC와 GLIBCXX(C++ 표준 라이브러리)의 버전을 보여줍니다.

기타

  • pthread 정의 : 일부 Linux 배포판에서는 pthread 라이브러리를 명시적으로 정의 안해도 됨 (e.g. Ubuntu)
  • 표준 C/C++ 라이브러리의 static 버전을 별도로 설치가 필요할 수 있음
    • glibc : 표준 C 언어 헤더 파일 및 함수의 구현
    • libstdc++ : GNU Standard C++ Library의 약자이며, C++ 언어 표준에 따라 구현된 C++ 라이브러리.

참고자료

댓글남기기