JUNGLE 240424 (W05)
KRAFTON JUNGLE Week 05
7장: Linker
Linking: 코드와 데이터의 여러 조각을 합침으로써 메모리에 복사해 바로 실행할 수 있는 하나의 파일로 만드는 과정
linux> gcc -Og -o prog main.c sum.c
- 전처리기를 돌리고, 이는
main.c
를 ASCII 중간 파일인main.i
로 번역 - C 컴파일러를 돌려서
main.i
를 ASCII 어셈블리 언어 파일인main.s
로 번역 - 어셈블러를 돌려서
main.s
를 재배치 가능한 바이너리 목적 파일인main.o
로 번역 sum.o
를 생성하기 위해 위의 과정을 반복하고, 마지막으로 링커 프로그램ld
실행- 링커는 필요한 시스템 목적 파일들과 함께 실행 가능 목적파일
prog
프로그램을 생성하기 위해main.o
와sum.o
연결
정적 연결
- 심볼 해석 (Symbol Resolution)
- 목적 파일들은 심볼들을 정의하고 참조하며 여기서 각 심볼은 함수, 전역 변수 또는 정적 변수에 대응
- 심볼 해석의 목적은 각각의 심볼 참조를 정확하게 하나의 심볼 정의에 연결하는 것
- 재배치 (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부터 차례대로 프로그램을 실행하는 역할을 함
- 이와 같이 프로그램을 메모리로 복사하고 실행하는 과정을 로딩이라고 부른다.
런타임 메모리
메모리 구성
- 코드 세그먼트 (Read-only code segment)
- 프로그램의 실행 코드는 메모리의 하위 영역에 저장
- 주로 프로그램의 실행을 위해 필요한 명령어를 저장하는 영역
- 실행 중에는 주로 읽기 전용으로 사용
- 데이터 세그먼트 (Read/write segment)
- 전역 변수와 정적 변수들이 데이터 세그먼트에 저장
- 이 세그먼트는 초기화된 데이터와 초기화되지 않은 데이터로 나뉨
- 데이터 세그먼트는 일반적으로 코드 세그먼트 바로 위에 위치
- BSS 세그먼트 (Read/write segment)
- 초기화되지 않은 전역 변수와 정적 변수들이 저장되는 영역
- 데이터 세그먼트와 함께 프로그램의 전역 변수와 정적 변수 관리
- 힙 (Run-time heap)
- 동적 메모리 할당을 위한 메모리 영역
- 프로그램이 실행되는 동안
malloc()
이나new
와 같은 함수로 동적으로 메모리 할당 &free()
나delete
를 통해 해제 - 힙 영역은 BSS 세그먼트 위에 위치하며, 위로 확장
- 스택 (User stack)
- 함수 호출 및 반환, 지역 변수 저장, 함수 호출 스택 프레임 관리 등을 담당하는 메모리 영역
- 스택은 힙 영역 위에 위치하며, 아래로 확장
⭐️ brk()
int brk(void *end_data_segment);
brk()
는 시스템 콜 중 하나로, 프로세스의 데이터 세그먼트의 끝을 지정된 주소로 이동시키는 함수- 이를 통해 프로세스가 사용할 수 있는 힙 영역의 크기를 변경할 수 있음
brk()
시스템 호출은 포인터 방식으로 작동brk()
를 사용하여 프로그램의 데이터 세그먼트의 끝 주소를 설정할 때는 주소를 나타내는 포인터를 사용brk()
의 인수로 전달된 주소 포인터는 데이터 세그먼트의 끝 주소를 나타내며, 이 주소를 기준으로 메모리 할당 및 해제가 이루어짐