JUNGLE 240424 (W05)

KRAFTON JUNGLE Week 05

7장: Linker

Linking: 코드와 데이터의 여러 조각을 합침으로써 메모리에 복사해 바로 실행할 수 있는 하나의 파일로 만드는 과정

linux> gcc -Og -o prog main.c sum.c
  1. 전처리기를 돌리고, 이는 main.cASCII 중간 파일main.i로 번역
  2. C 컴파일러를 돌려서 main.iASCII 어셈블리 언어 파일main.s로 번역
  3. 어셈블러를 돌려서 main.s재배치 가능한 바이너리 목적 파일main.o로 번역
  4. sum.o를 생성하기 위해 위의 과정을 반복하고, 마지막으로 링커 프로그램 ld 실행
  5. 링커는 필요한 시스템 목적 파일들과 함께 실행 가능 목적파일 prog 프로그램을 생성하기 위해 main.osum.o 연결

정적 연결

  1. 심볼 해석 (Symbol Resolution)
    • 목적 파일들은 심볼들을 정의하고 참조하며 여기서 각 심볼은 함수, 전역 변수 또는 정적 변수에 대응
    • 심볼 해석의 목적은 각각의 심볼 참조를 정확하게 하나의 심볼 정의에 연결하는 것
  2. 재배치 (Relocation)
    • 컴파일러와 어셈블러는 주소 0번지에서 시작하는 코드와 데이터 섹션들을 생성
    • 링커는 이 섹션들을 각 심볼 정의와 연결시켜서 재배치하며, 이 심볼들로 가는 모든 참조들을 수정해서 이들이 이 메모리 위치를 가리키도록 함

목적 파일

  • 재배치 가능 목적 파일 (Relocatable Object File)

    실행 가능 목적 파일을 만들기 위해 다른 재배치 가능 목적 파일과 결합될 수 있는 바이너리 코드와 데이터를 포함하는 목적 파일

  • 실행 가능 목적 파일 (Executable Object File)

    메모리에 직접 복사될 수 있고, 실행될 수 있는 형태로 바이너리 코드와 데이터를 포함하는 목적 파일

  • 공유 목적 파일 (Shared Object File)

    로드타임 혹은 런타임 시에 동적으로 링크되고 메모리에 로드될 수 있는 특수한 유형의 재배치 가능 목적 파일

재배치 가능 목적 파일

전형적인 ELF 재배치 가능 목적파일은 다음과 같은 섹션들을 가짐

  • .text : 컴파일된 프로그램의 머신 코드
  • .rodata : printf 문장의 포맷 스트링, switch 문의 점프 테이블과 같은 읽기-허용 데이터
  • .data : 초기화된 C 전역변수, 정적변수
  • .bss : 초기화되지 않은 C 전역변수, 정적변수, 0으로 초기화된 변수 (이는 실제 공간을 차지하지는 않음)
  • .symtab : 프로그램에서 정의되고 참조되는 전역변수들과 함수에 대한 정보들
  • .rel.text : 링커가 이 목적파일을 다른 파일들과 연결할 때 수정되어야 하는 .text 섹션 내 위치들
  • .rel.data : 이 모듈에 의해 정의되거나 참조되는 전역변수들에 대한 재배치 정보
  • .debug : 프로그램 내에서 정의된 지역변수들과 typedef 등을 갖는 디버깅 심볼 테이블 (-g 옵션으로 호출된 경우에만 생김)
  • .line : 최초 C 소스 프로그램과 .text 섹션 내 머신 코드 인스트럭션 내 라인 번호 간의 매핑 (-g 옵션으로 호출된 경우에만 생김)
  • .strtab : .strtab.debug 섹션들 내에 있는 심볼 테이블과 섹션 헤더들에 있는 섹션 이름들을 위한 스트링 테이블

실행 가능 목적 파일의 로딩

  • 실행 가능 목적 파일 prog를 실행하기 위해 ./prog를 입력한다고 가정하자.
  • 쉘은 로더(loader)라고 알려진 메모리 상주 운영체제 코드를 호출해서 이 프로그램을 실행
  • 로더는 실행 가능 목적 파일에서 코드와 데이터를 메모리로 복사해와서 entry point부터 차례대로 프로그램을 실행하는 역할을 함
  • 이와 같이 프로그램을 메모리로 복사하고 실행하는 과정을 로딩이라고 부른다.

런타임 메모리

메모리 구성
  1. 코드 세그먼트 (Read-only code segment)
    • 프로그램의 실행 코드는 메모리의 하위 영역에 저장
    • 주로 프로그램의 실행을 위해 필요한 명령어를 저장하는 영역
    • 실행 중에는 주로 읽기 전용으로 사용
  2. 데이터 세그먼트 (Read/write segment)
    • 전역 변수정적 변수들이 데이터 세그먼트에 저장
    • 이 세그먼트는 초기화된 데이터초기화되지 않은 데이터로 나뉨
    • 데이터 세그먼트는 일반적으로 코드 세그먼트 바로 위에 위치
  3. BSS 세그먼트 (Read/write segment)
    • 초기화되지 않은 전역 변수정적 변수들이 저장되는 영역
    • 데이터 세그먼트와 함께 프로그램의 전역 변수와 정적 변수 관리
  4. (Run-time heap)
    • 동적 메모리 할당을 위한 메모리 영역
    • 프로그램이 실행되는 동안 malloc()이나 new와 같은 함수로 동적으로 메모리 할당 & free()delete를 통해 해제
    • 힙 영역은 BSS 세그먼트 위에 위치하며, 로 확장
  5. 스택 (User stack)
    • 함수 호출 및 반환, 지역 변수 저장, 함수 호출 스택 프레임 관리 등을 담당하는 메모리 영역
    • 스택은 힙 영역 위에 위치하며, 아래로 확장
⭐️ brk()
int brk(void *end_data_segment);
  • brk()시스템 콜 중 하나로, 프로세스의 데이터 세그먼트의 끝지정된 주소이동시키는 함수
  • 이를 통해 프로세스가 사용할 수 있는 힙 영역의 크기를 변경할 수 있음
  • brk() 시스템 호출은 포인터 방식으로 작동
  • brk()를 사용하여 프로그램의 데이터 세그먼트의 끝 주소를 설정할 때는 주소를 나타내는 포인터를 사용
  • brk()의 인수로 전달된 주소 포인터는 데이터 세그먼트의 끝 주소를 나타내며, 이 주소를 기준으로 메모리 할당 및 해제가 이루어짐