본문 바로가기
프로그래밍/C & C++

[C/C++] 정적 라이브러리

by 별준 2022. 11. 11.

References

  • Advanced C and C++ Compiling

Contents

  • Creating Static Libraries
  • Using Static Libraries
  • Tips and Tricks

Creating Static Libraries

정적 라이브러리는 컴파일러에 의해 생성된 object file들이 하나로 통합된 archive file입니다. 생성된 object file들을 하나로 통합하는 것은 archiver라는 툴로 수행됩니다.

 

리눅스에서 archiver tool은 ar 입니다. 이 툴은 GCC toolchain의 일부입니다. 예를 들어, 다음의 간단한 두 줄의 커맨드로 두 개의 소스 파일의 정적 라이브러리를 생성할 수 있습니다.

gcc -c first.c second.c
ar rcs libstaticlib.a first.o second.o

리눅스 컨벤션에 의해서 정적 라이브러리 이름은 lib로 시작하고, 파일의 확장자는 .a 입니다.

 

object file을 archive(static library)로 묶는 작업 이외에 ar은 다음의 몇 가지 작업들을 수행할 수 있습니다.

  • 라이브러리에서 하나 이상의 object file을 제거
  • 라이브러리에서 하나 이상의 object file을 교체
  • 라이브러리에서 하나 이상의 object file을 추출

 

윈도우의 경우에도 커맨드 라인을 통해 수행할 수 있지만, 일반적으로 IDE (visual studio)를 사용하므로 IDE에 맞는 방법을 적용하면 됩니다.

 

Using the Static Library

정적 라이브러리는 실행 파일 또는 동적 라이브러리를 빌드하는 프로젝트에서 링크 단계에 사용됩니다. 정적 라이브러리의 이름은 일반적으로 링크해야 하는 object file 목록과 함께 링커에 전달됩니다.

 

참조 문헌에서는 정적 라이브러리를 사용해야 하는 몇 가지 시나리오를 언급하고 있는데, 크게 중요하지 않는 내용인 것 같아서 따로 언급하지는 않겠습니다.

 

정적 라이브러리를 사용한다는 것은 유연성이 떨어지더라도 코드에 대해 더 엄격한 제어를 하겠다는 것을 의미합니다. 일반적으로 모듈성은 감소하고 새로운 버전이 나오면 일반적으로 이를 사용하는 모든 어플리케이션은 다시 컴파일되어야 합니다.

 

멀티미디어 영역에서 신호 처리(analysis, encoding, decoding) 루틴은 일반적으로 정적 라이브러리의 형태로 전달됩니다. 반면, 멀티미디어 프레임워크(DirectX, GStreamer, OpenMAX)로의 통합은 관련 정적 라이브러리에서 링크되는 동적 라이브러리 형태로 구현됩니다. 이러한 방식에서 프레임워크와의 통신은 쉘에 위임되는 반면, 복잡한 신호 처리는 정적 라이브러리 부분에 속하게 됩니다.

 

Static Libraries Tips and Tricks

클라이언트 라이브러리(client library)는 정적 또는 동적 라이브러리를 링크하는 target 라이브러리를 칭합니다.

링커가 정적 라이브러리 섹션과 심볼을 클라이언트 바이너리에 통합하는 방법은 매우 간단합니다. 클라이언트 바이너리에 링크되면 정적 라이브러리의 심볼은 클라이언트 바이너리 심볼 리스트의 일부가 되며 본연의 가시성을 유지합니다. 정적 라이브러리의 전역 심볼은 클라이언트 바이너리의 전역 심볼이 되고, 정적 라이브러리의 로컬 심볼은 클라이언트 바이너리의 로컬 심볼이 됩니다.

 

 

하지만, 클라이언트 바이너리가 동적 라이브러리인 경우에는 이러한 규칙이 다른 동적 라이브러리의 디자인 룰에 따라 손상될 수 있습니다.

 

동적 라이브러리의 개념에서 내재된 가정은 모듈성(modularity)입니다. 모듈화 개념을 적절하게 구현하기 위해 동적 라이브러리 코드는 일반적으로 모듈의 기능을 외부에 노출하는 함수 집합인 인터페이스를 중심으로 구성되지만, 동적 라이브러리의 내부는 라이브러리 사용자와 멀어지게 됩니다.

정적 라이브러리의 기여도가 전체 기능에서 얼마나 중요한지와는 상관없이 동적 라이브러리의 디자인 규칙은 라이브러리가 외부와 통신하는데 필요한 최소한의 것만 export해야 한다는 것입니다.

 

이러한 규칙의 직접적인 결과로 인해, 정적 라이브러리 심볼의 가시성은 억제됩니다. 정적 라이브러리에서 전역 심볼은 링크된 이후, 로컬 심볼로 강등되거나 삭제될 수도 있습니다.

 

 

여러 동적 라이브러리를 링킹해야하는 정적 라이브러리를 링킹할 때는 정적 라이브러리보다 일치하는 동적 라이브러리 옵션을 사용하는 것이 선호됩니다. 여기서 일치하는 동적 라이브러리 옵션은 다음 중 하나를 의미할 수 있습니다.

  • 동일한 라이브러리의 존재하는 동적 라이브러리의 버전
  • 동적 라이브러리를 생성하기 위해 라이브러리 소스 코드를 다시 빌드
  • 사용 가능한 정적 라이브러리가 있는 경우, 빌드 프로젝트에서 사용할 수 있는 object file로 분리

 

구현하는 기능이 클래스의 단일 인스턴스(singleton pattern)을 요구하는 경우, 정적 라이브러리 대신 동적 라이브러리를 사용하는 것을 제안합니다. 이러한 경우의 좋은 예는 로깅 유틸리티가 있습니다.

 

Specific Rules of Linking Static Libraries

리눅스에서 정적 라이브러리 링킹은 다음의 규칙을 따릅니다.

  • 정적 라이브러리의 링킹은 하나씩 순차적으로 발생한다
  • 정적 라이브러리의 링킹은 전달된 정적 라이브러리 리스트의 마지막 정적 라이브러리부터 시작하며, 첫 번째 라이브러리 방향으로 순차적으로 진행된다
  • 링커는 정적 라이브러리를 자세히 살펴보고, 클라이언트 바이너리에 실제로 필요한 심볼이 포함된 object file에만 링크한다

위와 같은 룰에 의해 링커에 전달된 정적 라이브러리 목록에서 동일한 정적 라이브러리를 두 번 이상 지정해야 하는 경우가 있는데, 정적 라이브러리가 관련 없는 여러 기능들을 제공할 때 이러한 일이 발생할 가능성이 높습니다.

 

Converting Static to Dynamic Library

정적 라이브러리는 간단하게 동적 라이브러리로 변환될 수 있으며, 다음의 작업만 수행해주면 됩니다.

  • Archive tool을 사용하여 다음과 같이 라이브러리에서 모든 object file을 추출한다
ar -x <static library>.a

윈도우의 경우, visual studio console을 통해 lib.exe를 사용하여 위 작업을 수행할 수 있습니다.

  • 추출된 object file들을 동적 라이브러리로 빌드한다

 

Static Libraries Issues on 64-bit Linux

64비트 리눅스에서 정적 라이브러리를 사용할 때, 실행 파일에 링크하는 것은 32비트 리눅스에서 동작하는 것과 다르지 않습니다.

그러나, 정적 라이브러리를 공유 라이브러리에 연결하려면 정적 라이브러리를 '-fPIC' 플래그 또는 '-mcmodel=large' 플래그로 빌드해야 합니다.

 

'-fPIC' 컴파일러 플래그는 동적 라이브러리에 대해 살펴볼 때 조금 더 자세히 살펴보겠지만, 일반적으로 '-fPIC' 플래그는 동적 라이브러리 빌드와 연관된 것입니다. 따라서, 일반적으로 정적 라이브러리를 컴파일하는 데는 절대 필요하지 않다고 보편적으로 알려져 있습니다.

 

정확하지는 않지만 틀린 내용은 아닙니다. 사실 '-fPIC' 플래그가 정적 또는 동적 라이브러리를 생성할 지 여부를 결정하는 것은 아니고, '-shared' 링커 플래그에 의해서 정적 또는 동적 라이브러리가 결정됩니다.

 

정적 라이브러리를 컴파일할 때, '-fPIC' 플래그를 사용하는 진짜 이유는 64비트 플랫폼에서 address offset 범위가 32비트 레지스터가 사용되는 일반적인 컴파일러 어셈블러로 커버되지 않기 때문입니다. 따라서, 컴파일러는 64비트 레지스터로 동일한 코드를 구현하기 위해 이러한 플래그 사용이 필요합니다.

댓글