♧ 통상 GNU/리눅스의 공유 라이브러리를 만들 때는
각각의 .C 파일을 PIC(Position Independent Code)가 되도록 컴파일 한다.
그러나 실은 PIC로 컴파일 하지 안아도 공유 라이브러리는 만들 수 있다.
그러면 굳이 PIC로 컴파일 하는 이유가 있는 것일까?
▶ fpic.c 작성
#include <stdio.h> void func() { printf(""); printf(""); printf(""); } |
☞ PIC로 컴파일하기 위해 gcc -fpic 또는 fPIC 옵션을 지정한다.
-fpic :: 좀더 고속으로 코드를 생성할 가능성이 있지만,
CPU에 따라 -fpic로 생성할 수 있는 GOT(Glocal Offset Table)의 크기에 제한이 있다.
-fPIC :: CPU에 관계없이 사용할 수 있다. 여기서는 -fPIC를 사용한다.
(※ x86에서는 -fpic와 -fPIC가 동일)
% gcc -o fpic-no-pic.s -S fpic.c % gcc -fPIC -o fpic-pic.s -S fpic.c |
위와 같이 생성된 어셈블리어의 소스코드를 보면 PIC 버전은 printf를
PLT(Procedure Linkage Table)를 경유해서 호출하는 것을 알 수 있다.
(※ 우분투 7.04에서 테스트)
다음에는 공유 라이브러리를 만든다.
% gcc -shared -o fpic-no-pic.so fpic.c % gcc -shared -fPIC -o fpic-pic.so fpic.c |
▷ 위 공유 라이브러리의 동적 섹션(dynamic section)을 readelf 명령으로 보면,
비 PIC공유 라이브러리에서는 TEXTREL 이라는 엔트리가 있고 (텍스트 내의 재배치 필요),
RELCOUNT(재배치 수)가 5로, PIC 공유 라이브러리보다 3만큼 크다.
그 이유는 printf()를 3회 호출하기 때문이다.
▷ PIC 공유 라이브러리에서의 RELCOUNT가 0이 아닌 이유는 gcc가 기본적으로 사용하는
시작 파일에 포함된 코드 때문이다. gcc에 -nostartfiles 옵션을 지정하면 이 값은 0이 된다.
[ PIC와 비 PIC 공유 라이브러리의 성능 비교 ] | ||||||||||||
♧ 위 예에서는 비 PIC 버전은 실행 시에(동적 링크 시에) 5개의 주소가 재배치 되어야 한다고 했다. 그러면 재배치 수가 매우 커지면 어떻게 될 것인가?
공유 라이브버리를 PIC버전과 비 PIC버전printf()를 천만 번 호출 ( 셸 스크립트로 실행 비교) ▶ 다음 셸 스크립트를 실행하면 printf()를 천만 번 호출하는 공유 라이브러리를 비 PIC버전과 PIC 버전으로 만들고, 각각을 링크한 실행 파일 fpic-no-pic와 fpic-pic를 생성.
비 PIC 버전이 첫 회 2.15초, 두 번째 이후에는 약 0.55초가 걸리고,
PIC 버전은 최초 0.02초, 두 번째 이후에는 0.00초가 되었다.
☞ main()의 내용은 없으므로 비 PIC 버전은 동적 링크할 때 재배치에 2.15 ~ 0.55초를 필요로 함을 알 수 있다. 실행 환경은 Xeon 2.8GHz + Debian GNU/리눅스 sarge + GCC 3.3.5이다.
▷ 비 PIC 버전의 단점은 실행할 때 재배치에 시간이 걸린다는 점만이 아니다. 재배치가 필요한 부분의 코드를 재작성 하기 위해 『텍스트 섹션 내의 재배치가 필요한 페이지를 로드 → 재작성 → copy on write 발생 → 다른 프로세스와 텍스트를 공유할 수 없음』 이라는 사태가 발생. 즉, 여기서는 텍스트(프로그램 코드)를 다른 프로세스와 공유할 수 있는 『공유』라이브러리의 주요한 장점이 사라지고 만다.
한편, 비 PIC 버전의 fpic-no-pic.so 와 PIC 버전의 fpic-pic.so 파일 크기를 비교하면, 전자는 268MB, 후자는 134MB로 크게 차이가 난다. readelf -S로 섹션 데이터를 보면 다음과 같은 차이가 있다.
비 PIC 버전은 코드(.text) 크기는 PIC 버전보다 작지만, 재배치에 필요한 정보(.rel.dyn)가 상당한 용량을 차지.
[ 정리 ] 여기서는 공유 라이브러리를 작성할 때 PIC로 컴파일 해야 하는 필요성에 대해 알아보았다. 비 PIC 공유 라이브러리를 작성할 수는 있지만 실행할 때 재배치에 시간이 소요되고 다른 프로세스와 코드(.text)를 공유할 수 없는 커다란 단점이 있다. 따라서 공유 라이브러리를 작성할 때는 .c 파일을 PIC로 컴파일 하도록 한다. |
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #22] GCC 확장기능 (빌트인, 애트리뷰트, 레이블) (0) | 2011.07.05 |
---|---|
[Hack #19] 링크할 때 심볼 충돌 방지하기 (0) | 2011.07.04 |
[Hack #18] C와 C++ 프로그램 링크 방법 (0) | 2011.07.04 |
[Hack #17] ar - 정적 라이브러리 다루기 (0) | 2011.07.04 |
[Hack #16] strip - 오브젝트 파일에서 심볼 삭제 (0) | 2011.07.04 |