컴파일러는 무슨 일들을 할까?
#include <stdio.h>
int main(int argc, char** argv)
{
printf("Hello World\n");
return 0;
}
위와 같은 hello.c 소스가 있을 경우 gcc hello.c 명령어로 컴파일을 하면 컴파일러는 다음과 같은 절차를 거쳐서 실행 파일을 생성하게 됩니다.
gcc는 위 과정을 간편하게 해주는 역할을 수행합니다.
공유 라이브러리
printf 같이 프로그램마다 자주 사용하는 외부 함수를 실행 프로그램에 포함시킬 경우 프로그램의 덩치가 커지고 외부 라이브러리가 업그레이드 됐을 경우 이를 사용하는 프로그램을 다시 컴파일해야 하는 부담이 있습니다.
그래서 라이브러리를 공유 라이브러리(shared library)라는 형식으로 만들어 놓고 컴파일 시점에 사용할 라이브러리를 연결만 하는 방법을 사용합니다.
어떤 프로그램이 공유 라이브러리를 사용하는지 알아보려면 리눅스에는 file 명령어를 사용하면 되며 아래는 공유 라이브러리를 사용했을 때 결과입니다.
$ file a.out a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped |
정적 링크된 프로그램은 아래와 같이 표시합니다.
$ file a.out a.out: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.18, not stripped |
dynamic loader
공유 라이브러리와 연결된 프로그램을 실행하면 내부적으로 dynamic loader라는 프로그램이 먼저 동작하여 대략 다음과 같은 작업을 실행합니다.
어디에서 shared library 를 찾을까?
LD_LIBRARY_PATH
loader 는 program 구동에 필요한 shared library 를 찾을 때 LD_LIBRARY_PATH(리눅스의 경우)와 같은 고유의 환경 변수를 참고하며 OS 마다 경로를 찾는 순서가 다릅니다.
RPATH
리눅스는 ELF 형식의 바이너리는 rpath 라고 부르는 "실행시 라이브러리를 찾을 경로 정보"를 컴파일 시점에 넣어줄 수 있으며 이 정보는 LD_LIBRARY_PATH 환경 변수 전에 참고됩니다.
rpath 정보는 아래와 같이 gcc 를 실행할 때 -Wl,-rpath 옵션을 추가해 주면 됩니다.
gcc -Wl,-rpath,/usr/local/lib hello.c |
바이너리에 rpath가 있는지 여부는 readelf 명령어를 사용하여 파일의 헤더를 확인하면 됩니다.
$ readelf -d a.out | grep RPATH 0x000000000000000f (RPATH) Library rpath: [/usr/local/lib] |
LD_PRELOAD
LD_PRELOAD 환경 변수는 라이브러리의 후킹(hooking)이 필요할 때 사용하는 환경 변수로 이게 설정되어 있으면 여기에 지정된 라이브러리내 함수를 먼저 호출해 줍니다.
대표적인 용도로는 debugging 때문에 구동시에 특정 동적 library 를 변경해야할 경우등에 사용합니다.
예로 메모리 leak을 찾는데 사용하는 memory debugger는 runtime 에 기존 프로그램에서 사용한 malloc/free 를 debugger 가 구현한 함수로 대체해서 동작하기 위해 LD_PRELOAD 환경 변수를 사용하여 디버깅을 수행합니다.
메모리에서 공유라이브러리 위치
'tmp' 카테고리의 다른 글
어셈블리어로 반복문 구현 (0) | 2022.09.26 |
---|