[크래프톤 정글] 재배치 가능 목적 파일(CSAP 7.4장)
7.4 재배치 가능 오브젝트 파일 (Relocatable Object Files)
재배치 가능 오브젝트 파일은 컴파일된 프로그램의 기본 단위로, 여러 소스 파일에서 생성된 오브젝트 파일들을 링크하여 하나의 실행 파일을 만들기 위해 사용된다. 각 오브젝트 파일은 기계어 코드와 데이터, 메타데이터(심볼 테이블)를 포함하고 있으며, 이 파일들은 링킹 과정에서 결합되어 최종 실행 파일을 생성한다. 이 재배치 가능 오브젝트 파일의 주요 기능은 프로그램을 재배치할 수 있도록 설계되어 있다는 것이다.
재배치 가능 오브젝트 파일의 각 섹션은 실행 시점에 특정 메모리 주소에 고정되지 않으며, 링커에 의해 실제 실행 시 필요한 주소로 "재배치"될 수 있다. 이를 통해 여러 모듈(소스 파일)을 독립적으로 컴파일하고, 링커가 나중에 이들을 결합할 때 올바르게 작동할 수 있도록 한다.
재배치 가능 오브젝트 파일의 주요 구성
- 코드와 데이터 섹션:
소스 파일에서 컴파일된 기계어 코드와 전역 또는 정적 데이터가 포함된 섹션이다.- .text 섹션: 실행할 코드가 들어있는 섹션이다.
- .data 섹션: 초기화된 전역 변수와 정적 변수가 저장된 섹션이다.
- .bss 섹션: 초기화되지 않은 전역 변수와 정적 변수가 저장되는 섹션이다.
- 심볼 테이블 (Symbol Table):
심볼 테이블은 각 오브젝트 파일 내에서 정의된 함수와 변수의 이름 및 위치 정보를 담고 있는 테이블이다. 이 테이블을 통해 링커는 다른 오브젝트 파일이나 라이브러리에서 필요한 함수 및 변수를 참조할 수 있다. - 재배치 정보 (Relocation Information):
링커가 재배치할 수 있도록 각 기계어 명령어와 데이터에 대한 재배치 정보가 포함된다. 이 정보는 링커가 오브젝트 파일을 결합할 때, 특정 함수나 변수가 어디에 위치할지 결정하도록 돕는다.
재배치 가능 오브젝트 파일의 동작 예시
예를 들어, 다음과 같은 두 개의 소스 파일 main.c
와 sum.c
가 있다고 가정하자.
// main.c
#include <stdio.h>
int sum(int a, int b);
int main() {
int result = sum(3, 4);
printf("Sum: %d\n", result);
return 0;
}
// sum.c
int sum(int a, int b) {
return a + b;
}
1. 컴파일 단계:
gcc -c main.c sum.c
명령어로 각 파일을 컴파일하면, main.o
와 sum.o
라는 재배치 가능 오브젝트 파일이 생성된다.
main.o
:main.o
파일에는main()
함수의 기계어 코드가 들어 있고, 이 코드는sum()
함수를 호출하려고 하지만,sum()
함수의 정의는 아직 존재하지 않는다. 따라서sum()
함수는 외부 심볼로 처리되며, 심볼 테이블에sum
함수가 존재하는지 나중에 링킹 과정에서 확인된다.
sum.o
:sum.o
파일에는sum()
함수의 기계어 코드가 들어 있다. 링커는 이 파일을 이용해sum()
함수를 정의하고,main.o
에서 참조하는sum()
의 위치를 재배치할 수 있도록 한다.
2. 심볼 테이블 및 재배치 정보
각 오브젝트 파일에는 다음과 같은 정보가 포함된다.
main.o
심볼 테이블:main
: 정의됨 (정의된 함수)sum
: 참조됨 (정의되지 않았지만 나중에 결합될 외부 함수)
sum.o
심볼 테이블:sum
: 정의됨 (이 함수는sum.o
내에서 정의됨)
링커는 각 오브젝트 파일의 심볼 테이블을 확인하고, main.o
에서 참조된 sum()
함수가 sum.o
에서 정의된 것을 발견하게 된다.
3. 주소 재배치
링킹 과정에서 링커는 main.o
와 sum.o
를 결합하며, 이때 재배치 정보가 사용된다. main.o
의 main()
함수가 sum()
을 호출할 때 실제로 어느 주소로 이동해야 하는지를 결정하게 된다. 이 과정에서 sum()
함수의 주소는 sum.o
파일 내에서 계산된 후, main.o
의 호출 코드에서 해당 주소로 재배치된다.
# main.o에서 sum 함수를 호출하는 부분
call sum ; 링킹 전에 sum의 실제 주소는 알 수 없음
; 링커는 이 부분을 수정하여 sum의 실제 주소로 대체
# sum.o에서 sum 함수의 정의 부분
sum:
add %edi, %esi
ret
링킹 후 call sum
명령어는 sum
함수의 실제 메모리 주소로 대체되며, 최종 실행 파일에서 main()
함수가 sum()
함수를 올바르게 호출할 수 있게 된다.
4. 실행 파일 생성
링커가 재배치 가능 오브젝트 파일을 결합하여 최종 실행 파일을 생성하면, 프로그램은 각 함수와 변수가 메모리에서 정확한 주소에 배치된 상태로 실행될 준비가 완료된다. 결과적으로, main()
함수는 sum()
함수를 호출하고, 최종적으로 "Sum: 7"이라는 출력이 나타난다.
gcc main.o sum.o -o prog
./prog
# 출력: Sum: 7
결론
재배치 가능 오브젝트 파일은 프로그램을 독립적인 모듈로 컴파일하고, 나중에 링킹을 통해 결합할 수 있는 유연성을 제공한다. 이 파일들은 실행 시점에 정확한 메모리 주소로 재배치되기 때문에, 각 오브젝트 파일은 링커에 의해 결합되기 전까지 특정 메모리 주소에 고정되지 않는다. 이를 통해 대규모 프로젝트에서 여러 모듈을 병렬로 컴파일하고, 나중에 결합할 수 있는 효율성을 제공한다.