7.11 애플리케이션에서 공유 라이브러리 로딩 및 링킹
지금까지는 프로그램이 시작되기 전에 동적 링커가 .so 파일을 로드하고 심볼을 해결하는 과정만 다뤘다. 그러나 실제로는 프로그램이 실행된 이후, 즉 런타임 중에 공유 라이브러리를 로드하고 연결할 수도 있다.
런타임 동적 링킹의 유용성
- 소프트웨어 배포
- 윈도우 프로그램은 자주 사용되는 기능을 DLL로 나눠서 제공한다.
- 새로운 DLL 버전을 배포하면, 사용자는 프로그램을 다시 컴파일하지 않고도 자동으로 새로운 기능을 사용할 수 있다.
- 고성능 웹 서버
- 초기 웹 서버는 CGI를 위해 fork와 execve로 외부 프로그램을 호출했다.
- 현대 서버는 요청마다 동적 함수를 라이브러리로부터 로드하고 함수 포인터로 직접 호출한다.
- 함수는 서버 메모리에 캐시 되며, 이후 요청에서는 단순한 함수 호출로 처리된다.
주요 시스템 함수들 (<dlfcn.h>)
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);
const char *dlerror(void);
- dlopen: .so 파일을 열고 로드
- RTLD_LAZY: 사용 시에만 심볼 해석
- RTLD_NOW: 즉시 모든 심볼 해석
- dlsym: 특정 심볼(함수/변수)의 주소 획득
- dlclose: 열었던 .so를 닫음
- dlerror: 오류 메시지 반환
예제 코드
handle = dlopen("./libvector.so", RTLD_LAZY);
addvec = dlsym(handle, "addvec");
addvec(x, y, z, 2);
dlclose(handle);
이 코드는 libvector.so 안의 addvec() 함수를 런타임에 로드하고 호출한 후, 다시 언로드 하는 구조다.
컴파일 명령:
gcc -rdynamic -o prog2r dll.c -ldl
- -rdynamic: 실행 파일의 전역 심볼이 동적 링커에게 보이도록 설정
- -ldl: 동적 링커 라이브러리 사용
7.12 위치 독립 코드 (Position-Independent Code, PIC)
공유 라이브러리(shared library)는 여러 프로세스가 같은 코드 복사본을 메모리에 공유할 수 있도록 설계되어야 한다. 이를 위해, 코드가 메모리의 어느 위치에도 문제없이 로드 가능해야 하며, 이때 필요한 개념이 바로 위치 독립 코드(PIC)다.
고정 주소 방식의 문제점
과거 방식에서는 각 공유 라이브러리에 대해 고정된 주소 공간을 미리 할당하려 했다. 하지만 이는 여러 가지 문제가 있다:
- 주소 공간 낭비
- 라이브러리 추가/수정 시 주소 충돌
- 시스템마다 주소 배치가 달라짐 → 관리 복잡성 증가
PIC로 해결
현대 시스템에서는 공유 모듈의 코드 세그먼트를 어디든 로드 가능하게 컴파일한다. 이렇게 하면 하나의 코드 복사본을 여러 프로세스가 공유할 수 있고, 각 프로세스는 자신만의 데이터 세그먼트를 사용한다.
PIC는 gcc -fpic 옵션으로 생성되며, 공유 라이브러리는 반드시 PIC로 컴파일해야 한다.
내부 심볼 참조: PC-relative 방식
- 같은 모듈 내 심볼 참조는 특별한 처리 없이 PC-relative 주소 방식으로 해결할 수 있다.
- 정적 링커가 컴파일 시점에 처리
PIC에서의 전역 변수 참조: GOT(Global Offset Table)
기본 원리
- 코드 세그먼트와 데이터 세그먼트는 항상 일정한 거리를 유지
- 이를 활용해 코드에서 데이터까지의 거리(오프셋)를 상수로 사용 가능
구현 방식
- OT(Global Offset Table) 생성
- 각 전역 변수나 함수의 주소를 담는 테이블
- 데이터 세그먼트의 앞부분에 위치
- 각 항목은 8바이트 주소
- 컴파일 시점
- 각 전역 변수 참조는 GOT 엔트리 참조로 변환됨
- GOT 엔트리마다 재배치 정보도 함께 생성
- 실행 시점
- 동적 링커가 GOT의 각 항목을 실제 주소로 채움
예시
addvec:
mov 0x2008b9(%rip), %rax # %rax = *GOT[3] = &addcnt
addl $0x1, (%rax) # addcnt++
- addvec는 addcnt 전역 변수를 GOT을 통해 간접적으로 참조함
- GOT[3]에는 addcnt의 주소가 있고, 런타임에 addcnt++가 수행됨
- addcnt가 다른 공유 라이브러리에 있을 수도 있기 때문에 GOT 사용은 일반적인 해결책이 된다.
'크래프톤 정글 (컴퓨터 시스템: CSAPP) > 7장 링커' 카테고리의 다른 글
컴퓨터 시스템 : CSAPP 7장 정리 - 7.13 ~ 7.15 (0) | 2025.04.18 |
---|---|
컴퓨터 시스템 : CSAPP 7장 정리 - 7.8 ~ 7.10 (0) | 2025.04.18 |
컴퓨터 시스템 : CSAPP 7장 정리 - 7.7 재배치 (0) | 2025.04.18 |
컴퓨터 시스템 : CSAPP 7장 정리 - 7.6 심볼 결합 (0) | 2025.04.18 |
컴퓨터 시스템 : CSAPP 7장 정리 - 7.1 ~ 7.5 (0) | 2025.04.17 |