Krafton_Jungle/pintOS
Project 2 User program - argument_stack
전낙타
2024. 5. 23. 13:02
구현 목표
f_name
을 통째로file_name
으로 바꿔주고 있다.- [[strtok_r]] 를 사용해 토큰별로 쪼개준다 (공백 여러개 있을 때도 처리해야 함)
- 그렇게 쪼개진 토큰을 list에 담아
intr_frame
의register
에 세팅해준다.
int
process_exec (void *f_name) {
char *file_name = f_name;
bool success;
/* We cannot use the intr_frame in the thread structure.
* This is because when current thread rescheduled,
* it stores the execution information to the member. */
struct intr_frame _if;
_if.ds = _if.es = _if.ss = SEL_UDSEG;
_if.cs = SEL_UCSEG;
_if.eflags = FLAG_IF | FLAG_MBS;
/* We first kill the current context */
process_cleanup ();
/* And then load the binary */
// f_name을 그대로 load에 넣어줘 file_load에 실패하고 있다.
success = load (file_name, &_if);
/* If load failed, quit. */
palloc_free_page (file_name);
if (!success)
return -1;
/* Start switched process. */
do_iret (&_if);
NOT_REACHED ();
}
수정해야 할 함수
process_exec
string token으로 분할
char *token_arr[128];
char *save_ptr, *token;
int count = 0;
for (token = strtok_r(file_name, " ", &save_ptr);
token != NULL;
token = strtok_r(NULL, " ", &save_ptr))
token_arr[count++] = token;
/* And then load the binary
* 2. 디스크에서 해당 바이너리 파일을 메모리로 로드한다 -> load() */
success = load (file_name, &_if);
token
을 저장할token_arr
를 선언해준다. 배열의 크기는 깃북을 참고함buffer
역할을 해줄save_ptr
과token
을 선언, 인자의 개수를 저장할count
를 초기화해준다.- 분할 후
token_arr
에 담아주고,file_name
으로load
추가해야 할 함수
argument_stack
전체 코드
static void
argument_stack(char *parse[], int count, struct intr_frame *_if) {
for (int i = count - 1; i >= 0; i--) {
size_t len = strlen(parse[i]) + 1;
_if->rsp -= len;
memcpy(_if->rsp, parse[i], len);
parse[i] = _if->rsp;
}
uint8_t padding = _if->rsp % 8;
if (padding) {
memset(_if->rsp -= (sizeof(uint8_t) * padding), 0, sizeof(uint8_t) * padding);
}
_if->rsp -= sizeof(uintptr_t);
memset(_if->rsp, 0, sizeof(uintptr_t));
for (int i = count - 1; i >= 0; i--) {
_if->rsp -= sizeof(uintptr_t);
_if->rsp = memcpy(_if->rsp, &parse[i], sizeof(uintptr_t));
}
_if->R.rsi = _if->rsp;
_if->R.rdi = count;
_if->rsp -= sizeof(uintptr_t);
memset(_if->rsp, 0, sizeof(uintptr_t));
}
개요
args-single
라는 file을 load하고, onearg
인자를 전달하는 테스트를 진행할것이다.
우리가 원하는 동작은 위 그림과 같이 전달받은 인자, 파일의 이름을 [[stack]]에 적재하는 것인데, 이를 코드를 따라가며 한줄 한줄 정리해보자.
인수의 메모리 배치 & 8byte 정렬
for (int i = count - 1; i >= 0; i--) {
size_t len = strlen(parse[i]) + 1;
_if->rsp -= len;
memcpy(_if->rsp, parse[i], len);
parse[i] = _if->rsp;
}
uint8_t padding = _if->rsp % 8;
if (padding) {
memset(_if->rsp -= (sizeof(uint8_t) * padding), 0, sizeof(uint8_t) * padding);
}
- rsp 주소를
token의 사이즈 + 1(null 종단문자)
만큼 감소시키면서stack
에 데이터를 할당한다. - 해당
token
이 저장되어 있는 주소를buffer
로 사용될 parse에 저장시켜둔다. - 이 과정이 종료되면 추가로 할당해줘야 할
padding
의 크기를 구하고 할당해준다.
인자의 마지막을 알려주는 null pointer sentinel
_if->rsp -= sizeof(uintptr_t);
memset(_if->rsp, 0, sizeof(uintptr_t));
그 다음, 스택에 NULL 포인터를 추가합니다. 이는 인수 목록의 끝을 표시하기 위해 사용됩니다.
인수 포인터 배열
for (int i = count - 1; i >= 0; i--) {
_if->rsp -= sizeof(uintptr_t);
_if->rsp = memcpy(_if->rsp, &parse[i], sizeof(uintptr_t));
}
그 후, 각 인수의 주소를 스택에 저장합니다. 이를 통해 인수 포인터 배열을 구성합니다.
레지스터 설정
_if->R.rsi = _if->rsp;
_if->R.rdi = count;
마지막으로, 레지스터에 인수 포인터 배열의 시작 주소와 인수 개수를 설정합니다. 이는 프로그램 시작 시 argc
와 argv
를 설정하는 역할을 합니다.
_if->rsp -= sizeof(uintptr_t);
memset(_if->rsp, 0, sizeof(uintptr_t));
마지막으로, 스택에 NULL 포인터를 추가하여 함수가 종료됩니다.
요약
이 과정은 스택에 명령줄 인수들을 저장하고, 인수 배열을 구성하여, 프로그램이 실행될 때 인수들을 올바르게 접근하고 사용할 수 있게 합니다. 스택에 저장된 주소들을 통해 프로그램은 인수들을 올바르게 참조하고 처리할 수 있습니다. 이는 명령줄 인수 전달 규약에 따른 표준적인 처리 방식입니다.