[CSAPP] Linker 7.9 Loading Executable Object Files

JunHyeok Kim·2024년 4월 22일

Loading Executable Object Files

Since prog does not correspond to a built-in shell command, the shell assumes that prog is an executable object file, which it runs for us by invoking some memory- resident operating system code known as the loader.

-> ./prog 라는 명령어가 없잖아? 그러니까 쉘인 이것이 '실행 가능 목적 파일' 임을 인지하고, loader라는 메모리에 상주하는 운영체제 코드를 호출해서 이 프로그램을 실행해줍니다.

Any Linux program can invoke the loader by calling the execve function, which we will describe in detail in Section 8.4.6. The loader copies the code and data in the executable object file from disk into memory and then runs the program by jumping to its first instruction, or entry point. This process of copying the program into memory and then running it is known as loading.

어떠한 리눅스 프로그램이라도 execve 를 호출함으로써 loader를 실행할 수 있으며 이것은 섹션 8.4.6에서 다룰 것이다.
로더는 디스크에 있는 실행가능한 목적 파일의 코드와 데이터를 '카피'해서 메모리로 옮긴 뒤, 첫 인스트럭션 혹은 엔트리 포인트로 점프하여 프로그램을 실행합니다.

Every running Linux program has a run-time memory image similar to the one in Figure 7.15. On Linux x86-64 systems, the code segment starts at address 0x400000, followed by the data segment. The run-time heap follows the data segment and grows upward via calls to the malloc library.

모든 리눅스 프로그램은 그림과 같은 런타임 메모리 구조를 갖있으며, x86-64 리눅스 시스템에서는 코드 세그먼트는 0x40000 주소에서 시작하며 뒤이어 데이터 세그먼트가 따라온다. 런타임 힙은 데이터 세그먼트 뒤에 있으며, malloc 라이브러를 통해 위로 힙 영역이 증가한다.

(We will describe malloc and the heap in detail in Section 9.9.)

This is followed by a region that is reserved for shared modules. The user stack starts below the largest legal user address (2^48 − 1) and grows down, toward smaller memory addresses. The region above the stack, starting at address 248, is reserved for the code and data in the kernel, which is the memory-resident part of the operating system.

힙영역 뒤로는 공유 영역이 따라온다. 유저 스택은 가장 큰 legal 사용자 주소 아래에서 시작해서 더 작은 메모리 주소인 아래로 영역이 확장된다. 스택 위의 영역은 운영체제의 메모리 상주 부분인 커널의 코드와 데이터를 위해 예약되어있다.

For simplicity, we’ve drawn the heap, data, and code segments as abutting each other, and we’ve placed the top of the stack at the largest legal user address. In practice, there is a gap between the code and data segments due to the alignment requirement on the .data segment (Section 7.8). Also, the linker uses address-space layout randomization (ASLR, Section 3.10.4) when it assigns run- time addresses to the stack, shared library, and heap segments. Even though the locations of these regions change each time the program is run, their relative positions are the same.

단순화를 위해서, 힙,데이터 및 코드 영역을 붙여있게 그렸으며 유저 스택 영역을 최대 합법 사용자 주소에 배치하였습니다. 실제로는 .data 세그먼트의 정렬조건으로 인해 코드와 데이터 세그먼트 사이에 공간이 존재합니다. 또한, 링커는 런타임 주소를 스택, 공유라이브러리, 힙세그먼트에 할당할 때 ASLR(Address Space Layout Randomization)를 사용한다. 비록, 프로그램이 실행될 때 마다 조금씩 위치는 변하더라도 상대적인 위치는 동일하다.

When the loader runs, it creates a memory image similar to the one shown in Figure 7.15. Guided by the program header table, it copies chunks of the executable object file into the code and data segments. Next, the loader jumps to the program’s entry point, which is always the address of the start function. This function is defined in the system object file crt1.o and is the same for all C programs. The _start function calls the system startup function, __libc_start main, which is defined in libc.so. It initializes the execution environment, calls the user-level main function, handles its return value, and if necessary returns control to the kernel.

로더가 실행될 때, 로더는 위의 그림과 비슷한 메모리 구조를 생성한다. Program header Table의 지시에 따라서 로더는 실행가능한 목적 파일을 code 와 data 영역에 복사합니다. 그 다음, 로더는 프로그램의 start function의 시작 위치인 프로그램의 진입점으로 점프합니다. 이 start 함수는 시스템 목적 파일인 crt1.o에 정의되어 있으며 모든 C 프로그램들에 대해 동일합니다. 그리고 이 start 함수는 system startup 함수들을 실행합니다.

"_libc_start main"은 libc.so 에 정의되어 있으며, 이는 유저레벨의 main 함수, 리턴 값 그리고 제어권을 커널에 리턴하는 실행 환경등을 초기화 합니다.

0개의 댓글