Sapphire9 개발 일지
article thumbnail

Argument Passing

 

PintOS의 process_exec() 함수의 인자를 file name과 argument로 pasing하여 전달해주도록 구현한다. 단순히 방법만을 따라서 구현하다 보니 parsing하여 전달된 인자들이 어떠한 의미를 가지는지 생각해보지 않았다.

 

따라서 해당 포스팅에서는 실행 가능한 목적파일을 메모리에 적재하고 실행하는 process_exec()와 load()가 어떠한 방식으로 구현되어 있고, 전달된 인자들과 함께 유저 프로그램이 어떻게 실행되는지 알아본다.

 

 

목적파일

 

목적파일에는 다음과 같은 세 가지 형태가 있다.

 

재배치 가능 목적파일(Relocatable object file)

실행 가능 목적파일을 생성하기 위해, 컴파일 할 때 다른 재배치 가능 목적파일과 결합될 수 있는 바이너리 코드와 데이터를 포함한다.

 

실행 가능 목적파일(Executable object file)

메모리에 직접 복사될 수 있고 실행될 수 있는 형태로 바이너리 코드와 데이터를 포함한다.

 

공유 목적파일(Shared object file)

load time이나 run time에 동적으로 링크되고 메모리에 로드될 수 있는 특수한 유형의 재배치 가능 목적파일이다.

 

 

목적파일들은 특정 목적파일 형식에 따라 구성되며, 이들은 시스템에 따라 다르다. 현대의 x86-64 리눅스와 유닉스 시스템들은 Executable and Linkable Format(ELF)을 사용한다. 기본적인 개념은 특정 포멧과 상관없이 유사하다.

 

ELF는 리눅스와 유닉스 시스템에서 사용하는 실행 파일과 목적 파일의 표준 파일 형식이다.

 

 

실행 가능한 목적파일

 

링커에 의해 다수의 목적파일들은 하나의 실행 가능 목적파일로 합쳐진다.

 

위 그림은 전형적인 ELF 실행 가능 목적파일의 포맷을 보여준다. 가상 메모리에 적재되는 code segment와 data segment를 위주로 각 섹션들의 특징을 간단히 나타내면 아래와 같다.

 

  • ELF header : 이 파일의 전체적인 포맷을 설명한다. 또한 이것은 프로그램이 실행될 때 첫 번째 인스트럭션의 주소인 프로그램 엔트리 포인트를 포함한다.

 

  • .init : 섹션은 _init라는 함수를 정의하는데, 이것은 프로그램의 초기화 코드에서 호출한다.

 

  • .text : 컴파일된 프로그램의 머신 코드

 

  • .data : 초기화된 C 전역변수 및 정적변수

 

  • .bss : 초기화되지 않은 C 전역변수와 정적변수 그리고 0으로 초기화된 전역변수 및 정적변수

 

  • Program header table : 프로그램 헤더 테이블에 명시된 정렬 요구사항을 기준으로 세그먼트들을 배치하여 프로그램이 실행될 때 목적파일의 세그먼트들이 메모리로 효과적으로 전송될 수 있다.

 

 

실행 가능 목적파일의 로딩

 

리눅스 쉘은 로더(loader)라고 알려진 메모리 상주 운영체제 코드를 호출해서 프로그램을 실행한다. 로더는 디스크로부터 실행 가능한 목적파일 내의 코드와 데이터를 메모리로 복사하고 이 프로그램의 첫 번째 인스트럭션, 즉 엔트리 포인트로 점프해서 프로그램을 실행한다. 이와 같이 프로그램을 메모리로 복사하고 실행하는 과정을 로딩이라고 부른다.

 

로더가 실행될 때, 아래 그림과 유사한 메모리 이미지를 생성한다.

 

실행파일 내부의 프로그램 헤더 테이블의 정렬 요구사항에 따라 실행파일의 덩어리를 코드와 데이터 세그먼트로 복사한다. 다음으로, 로더는 프로그램의 엔트리 포인트로 점프하며, 이것은 항상 _start함수의 주소가 된다. _start함수는 시스템 초기화 함수인 __libc_start_main을 호출하며, 이 함수는 실행 환경을 초기화하고, 사용자 수준의 main함수를 호출하고, 리턴 값을 처리하며, 필요한 경우 제어권을 커널로 넘겨준다.

 

 

핀토스에서 _start 함수는 다음과 같다.

void
_start (int argc, char *argv[]) {
    exit (main (argc, argv));
}

 

 

PintOS의 load()는 process_exec() 안에서 실행되는데, process_exec()는 현재 스레드의 컨텍스트 내에서 새로운 프로그램을 로드하고 실행한다. 이때 실행가능 목적파일 filename과 인자 리스트 argv를 사용한다.

 

 

새 프로그램의 메인 루틴은 다음과 같은 형태의 프로토타입을 갖는다.

 

int main(int argc, char **argv);

또는

int main(int argc, char *argv[]);

 

함수 main으로 가는 두 개의 인자가 존재하며, 이들 각각은 x86-64 스택 규범을 준수하며 레지스터에 저장된다.

 

 

(1) argc, argv[] 배열에 null이 아닌 포인터들의 수

 

(2) argv, argv[] 배열에서 첫 항목으로의 포인터

 

 

 

리눅스 - 스택의 맨 위에는 시스템 초기화 함수 libc_start_main에 대한 스택 프레임이 위치한다.

 

 

process_exec()와 load()의 실행 흐름

 

실행할 유저 프로그램을 위해 기존의 컨텍스트를 삭제한다. load()에서 새로운 코드와 데이터 세그먼트들은 가상 주소공간의 페이지들을 실행파일의 페이지 크기 덩어리들로 매핑시켜서 실행파일의 내용으로 초기화된다.

 

 

이후 do_iret이 실행되면서 kernel mode에서 user mode로 점프하고 응용 프로그램을 실행한다. 프로그램이 실행되기 전 레지스터에 initial function에 대한 argument들이 저장된다.

 

 

아래 코드는 ELF_header와 프로그램 헤더 테이블을 나타낸 것이다.

 

/* Executable header.  See [ELF1] 1-4 to 1-8.
 * This appears at the very beginning of an ELF binary. */
struct ELF64_hdr {
	unsigned char e_ident[EI_NIDENT];
	uint16_t e_type;
	uint16_t e_machine;
	uint32_t e_version;
	uint64_t e_entry;
	uint64_t e_phoff;
	uint64_t e_shoff;
	uint32_t e_flags;
	uint16_t e_ehsize;
	uint16_t e_phentsize;
	uint16_t e_phnum;
	uint16_t e_shentsize;
	uint16_t e_shnum;
	uint16_t e_shstrndx;
};

struct ELF64_PHDR {
	uint32_t p_type;
	uint32_t p_flags;
	uint64_t p_offset;
	uint64_t p_vaddr;
	uint64_t p_paddr;
	uint64_t p_filesz;
	uint64_t p_memsz;
	uint64_t p_align;
};

/* Abbreviations */
#define ELF ELF64_hdr
#define Phdr ELF64_PHDR

 

load()

ELF파일의 헤더정보를 읽어와 저장

ELF 헤더에는 파일을 생성한 워드 크기, 목적파일 타입(재배치 가능, 실행 가능, 공유), 머신 타입(x86-64), 섹션 헤더 테이블에 대한 정보가 들어있다.

 

 

배치 정보를 읽어와 저장

프로그램 헤더 테이블을 통해 메모리 주소의 시작 위치, 메모리 크기, 목적파일 세그먼트의 첫 섹션의 오프셋, 정렬 등이 명시되어 있다.

 

 

프로그램 엔트리 포인트

ELF header에 저장된 프로그램 엔트리 포인트로 점프할 수 있도록 설정한다.

 

 

Argument_stack

parsing된 argument들을 가상 메모리의 user stack에 push한다.

 

 

 

do_iret이 실행되면서 해당 프로그램의 첫 번째 인스트럭션인 엔트리 포인트로 점프해서 프로그램을 실행한다. 이는 결국 응용의 main 루틴을 호출하는 것이다. argument passing을 통해 최종적으로 응용 프로그램의 main 함수 실행에 대한 준비가 완료된 것이다.

profile

Sapphire9 개발 일지

@Sapphire9

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그