PintOS P2 #3 : Argument Passing 본편

파싱 자체는 사실 어렵지 않다. 문제는 파싱 대상의 문자열을 담은 공간을 언제까지 유의미한 값으로 남기게 시킬 것인가를 생각해야한다. 너무 빨리 해제하면 참조 시도시 프로그램이 박살난다. 너무 늦게 해제하면.. 엥? 애초부터 프로그램 끝날 때까지 써야하는데 해제를 수동으로 하는게 맞나?


해당 글에서 이어진다.

2025.05.21 - [분류 전체보기] - PintOS P2 #2 : Argument Passing을 다루기 전

 

PintOS P2 #2 : Argument Passing을 다루기 전

Unix, Linux에서 프로그램을 실행 할때는 보통 프로그램 이름 인자1 인자2 인자3 인자4 와 같은 실행 형태를 갖춘다. 그럼 이런 형태는 어떻게 이름으로 구분하고 인자를 구분하는가? 이제부터 그렇

hyeonistic.tistory.com

point of implement : parse 부분에 코드를 구현하겠다.

int process_exec (void *f_name) {
	char *file_name = f_name; // 이후 palloc_free_page(file_name)을 한다. 그럼 f_name도 없어진다!!
	bool success;
	
	char *saveptr, *token;
	char *args_ptr[32] = {NULL};
	
	if(f_name == NULL)
		return TID_ERROR;

	int argc = 0;

	for(token = strtok_r(f_name, " \t\r\n", &saveptr); token && argc < 31; token = strtok_r(NULL, " \t\r\n", &saveptr))
	{
		args_ptr[argc] = token;
		argc++;
	}

	struct intr_frame _if;
    ...

 

strtok_r은 어떤 문자열에 대해서 지정한 문자열을 발견하면 해당 포인터의 시작점을 반환하게 하고 있다. 이것을 반복적으로 수행시켜서 args_ptr에 띄어쓰기 단위로 나뉜 값을 담게 유도 할 수 있게 되었다.

 

 

Guud

팟힝을 한 내용은 palloc_free_page가 일어나기 전에 스택에 부여하도록 한다.

 

 

point of implement : stack here 에 코드를 구현하겠다.\

 

우리의 목표

다음 예제는 /bin/ls -l foo bar 라는 코드를 스택에 배치하는 과정이다.

  1. 입력 받은 문자열을 모두 띄어쓰기 단위로 자른다. 그리고 머리 속에서 역으로 배치해보자. 그럼 bar, foo, -l, /bin/ls 순이다.
  2. 1번에서 했던 역순의 순서대로 스택에 부여가 되어야한다. 이것은 스택의 특징에 의해서 그렇다.
  3. 2번이 다 끝나면 64비트 특성상 정렬을 맞춰주어야한다. 하지 않아도 작동은 되겠지만 위험하다. 위의 표에선 uint8_t[] 타입으로 배치되었다.
  4. 그리고 2번에서 넣었던 주소들을 1-2와 동일한 순서인 역순으로 한번 더 부여한다. 여기서 생각해야 할 것은, 첫번째 값은 반드시 0 내지 NULL이어야 한다. 얘는 스택 마지막 아이템이에용 넘어가면 디짐 을 명시적으로 표기하는것
  5. 6번을 하기전 RSI 변수에 값을 배정해준다. 왜냐하면 이 주소를 저장하려면 지금 타이밍이 적합하다. RDI는 갯수를 저장해서 상관없지만, 맥락상 이 타이밍에 같이 해준다.
  6. 마지막에 return address 0을 남겨두어야 한다. 가라로 작성하는 주소이지만 채워주어야 작동한다.

 

 

success = load (file_name, &_if);

char *argv_u[32];   /* argc 최대 32라 가정한다. 테케는 이것보다 적은 값들이라 괜찮아.. */

/* 1) 문자열을 역순으로 스택에 복사하고, 복사한 위치(=유저 스택 어드레스)를 argv_u에 저장 */

// 페이지 경계에 맞닿는 상황을 제거해야한다. 그래서 rsp를 시작 전에 좀 더 내려서 시작하게끔 조정
_if.rsp -= 8;

// 1. 문자열을 역순으로 스택에 복사한다. 복사한 위치를 argv_u에 저장해둔다.
for (int i = argc - 1; i >= 0; i--) {
    int len = strlen(args_ptr[i]) + 1;
    _if.rsp -= len;                         // 스택 포인터 내리고
    memcpy((void*)_if.rsp, args_ptr[i], len); 
    argv_u[i] = (char*)_if.rsp;            // 복사된 문자열의 주소 저장
}

// 2. 스택 워드 정렬, 32비트 기준 0x7을 사용했으니 64비트에서는 0xF를 사용해야한다.
_if.rsp = (uintptr_t)_if.rsp & ~0xF;

// 3. NULL sentinel
_if.rsp -= sizeof(char*);
 *(char**)_if.rsp = NULL;

// 4) argv_u[]에 모아둔 주소를 스택에 푸시
for (int i = argc - 1; i >= 0; i--) {
    _if.rsp -= sizeof(char*);
    *(char**)_if.rsp = argv_u[i];
}

_if.R.rsi = _if.rsp;

// Fake return address..
_if.rsp -= sizeof(void*);
*(void**)_if.rsp = 0;

_if.R.rdi = argc;

	// 저는 공짜입니다.
	palloc_free_page (file_name);

load로 시작한 stack 생성은 이러쿵저러쿵 이루어져서 palloc_free_page 전에 끝냈다.