cs

[크래프톤 정글] 재배치 가능 목적 파일(CSAP 7.4장)

하루이2222 2024. 10. 11. 19:56

7.4 재배치 가능 오브젝트 파일 (Relocatable Object Files)

재배치 가능 오브젝트 파일은 컴파일된 프로그램의 기본 단위로, 여러 소스 파일에서 생성된 오브젝트 파일들을 링크하여 하나의 실행 파일을 만들기 위해 사용된다. 각 오브젝트 파일은 기계어 코드데이터, 메타데이터(심볼 테이블)를 포함하고 있으며, 이 파일들은 링킹 과정에서 결합되어 최종 실행 파일을 생성한다. 이 재배치 가능 오브젝트 파일의 주요 기능은 프로그램을 재배치할 수 있도록 설계되어 있다는 것이다.

재배치 가능 오브젝트 파일의 각 섹션은 실행 시점에 특정 메모리 주소에 고정되지 않으며, 링커에 의해 실제 실행 시 필요한 주소로 "재배치"될 수 있다. 이를 통해 여러 모듈(소스 파일)을 독립적으로 컴파일하고, 링커가 나중에 이들을 결합할 때 올바르게 작동할 수 있도록 한다.

재배치 가능 오브젝트 파일의 주요 구성

  1. 코드와 데이터 섹션:
    소스 파일에서 컴파일된 기계어 코드와 전역 또는 정적 데이터가 포함된 섹션이다.
    • .text 섹션: 실행할 코드가 들어있는 섹션이다.
    • .data 섹션: 초기화된 전역 변수와 정적 변수가 저장된 섹션이다.
    • .bss 섹션: 초기화되지 않은 전역 변수와 정적 변수가 저장되는 섹션이다.
  2. 심볼 테이블 (Symbol Table):
    심볼 테이블은 각 오브젝트 파일 내에서 정의된 함수와 변수의 이름 및 위치 정보를 담고 있는 테이블이다. 이 테이블을 통해 링커는 다른 오브젝트 파일이나 라이브러리에서 필요한 함수 및 변수를 참조할 수 있다.
  3. 재배치 정보 (Relocation Information):
    링커가 재배치할 수 있도록 각 기계어 명령어와 데이터에 대한 재배치 정보가 포함된다. 이 정보는 링커가 오브젝트 파일을 결합할 때, 특정 함수나 변수가 어디에 위치할지 결정하도록 돕는다.

재배치 가능 오브젝트 파일의 동작 예시

예를 들어, 다음과 같은 두 개의 소스 파일 main.csum.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.osum.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.osum.o를 결합하며, 이때 재배치 정보가 사용된다. main.omain() 함수가 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

결론

재배치 가능 오브젝트 파일은 프로그램을 독립적인 모듈로 컴파일하고, 나중에 링킹을 통해 결합할 수 있는 유연성을 제공한다. 이 파일들은 실행 시점에 정확한 메모리 주소로 재배치되기 때문에, 각 오브젝트 파일은 링커에 의해 결합되기 전까지 특정 메모리 주소에 고정되지 않는다. 이를 통해 대규모 프로젝트에서 여러 모듈을 병렬로 컴파일하고, 나중에 결합할 수 있는 효율성을 제공한다.