본문 바로가기
Review/SW Jungle

[WEEK09] Pintos _ Project2 User Program (1) Argument Passing

by jamiehun 2022. 11. 29.

1. 프로젝트 관련 공부

Pintos Project2

 

Pintos에서 User Program을 실행시켜라!

(Argument Passing, System Calls 등)

 

우리 과제는 프로그램을 실행시키기 위해서 아래와 같은 항목들의 일부를 수행하는 것으로 보인다.


2. 코드 구현 

첫번째 미션 : Argument Passing

관련함수 : process_exec / load / argument_stack 

 

첫번째로 풀어야할 문제는 Argument Passing이다.

유저프로그램을 실행하기 전 커널은 레지스터에 argument를 저장해야한다.

f_name 문자열로 인자값을 받아오나 해당 인자에 대한 passing을 제공하지는 않는다.

인자값을 parsing 하여 스택에 올림으로써 파일 실행에 대한 준비를 하는 것이다.

 

 

1) process_exec에서 'args-single onearg'를 f_name으로 받아오면 해당 값을 load의 인자로 넣음

 

int
process_exec (void *f_name) {	// f_name = 'args-single onearg'
	char *file_name = f_name;
	bool success;

	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 */
	// file_name : 프로그램(실행파일) 이름
	success = load (file_name, &_if);

	/* If load failed, quit. */
	palloc_free_page (file_name);
	if (!success)	//메모리 적재 실패시 -1 반환
		return -1;

	hex_dump(_if.rsp,_if.rsp,USER_STACK-_if.rsp,true);
	
	/* Start switched process. */
	// do interrupt return
	do_iret (&_if);
	NOT_REACHED ();
}

 

 

2) load 함수 내에서 파싱하여 arg_list를 만들고 해당 값을 argument_stack으로 전달함

 

static bool
load (const char *file_name, struct intr_frame *if_) { // file_name = 'args-single onearg'
	/* parsing */
	char *token, *save_ptr;
    char *arg_list[100];
    int idx=0;
	for (token = strtok_r (file_name, " ", &save_ptr); token != NULL; token = strtok_r (NULL, " ", &save_ptr))
    {
        arg_list[idx]=token;
		idx++;
    }

	memcpy(file_name,arg_list[0],strlen(arg_list[0])+1);

	struct thread *t = thread_current ();
	struct ELF ehdr;
	struct file *file = NULL;
	off_t file_ofs;
	bool success = false;
	int i;

	/* Allocate and activate page directory. */
	/* 페이지 디렉토리 생성 */
	t->pml4 = pml4_create ();
	if (t->pml4 == NULL)
		goto done;
	/* 페이지 테이블 활성화 */
	process_activate (thread_current ());

	/* Open executable file. */
	/* 프로그램파일 Open */
	file = filesys_open (file_name);
	if (file == NULL) {
		printf ("load: %s: open failed\n", file_name);
		goto done;
	}

	/* Read and verify executable header. */
	/* ELF파일의 헤더정보를 읽어와 저장*/
	if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
			|| memcmp (ehdr.e_ident, "\177ELF\2\1\1", 7)
			|| ehdr.e_type != 2
			|| ehdr.e_machine != 0x3E // amd64
			|| ehdr.e_version != 1
			|| ehdr.e_phentsize != sizeof (struct Phdr)
			|| ehdr.e_phnum > 1024) {
		printf ("load: %s: error loading executable\n", file_name);
		goto done;
	}

	/* Read program headers. */
	file_ofs = ehdr.e_phoff;
	for (i = 0; i < ehdr.e_phnum; i++) {
		/* 배치정보를 읽어와 저장. */
		struct Phdr phdr;

		if (file_ofs < 0 || file_ofs > file_length (file))
			goto done;
		file_seek (file, file_ofs);

		if (file_read (file, &phdr, sizeof phdr) != sizeof phdr)
			goto done;
		file_ofs += sizeof phdr;
		switch (phdr.p_type) {
			case PT_NULL:
			case PT_NOTE:
			case PT_PHDR:
			case PT_STACK:
			default:
				/* Ignore this segment. */
				break;
			case PT_DYNAMIC:
			case PT_INTERP:
			case PT_SHLIB:
				goto done;
			case PT_LOAD:
				if (validate_segment (&phdr, file)) {
					bool writable = (phdr.p_flags & PF_W) != 0;
					uint64_t file_page = phdr.p_offset & ~PGMASK;
					uint64_t mem_page = phdr.p_vaddr & ~PGMASK;
					uint64_t page_offset = phdr.p_vaddr & PGMASK;
					uint32_t read_bytes, zero_bytes;
					if (phdr.p_filesz > 0) {
					/* Normal segment.
					 * Read initial part from disk and zero the rest. */
					read_bytes = page_offset + phdr.p_filesz;
					zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE)
								- read_bytes);
					} else {
					/* Entirely zero.
					 * Don't read anything from disk. */
					read_bytes = 0;
					zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE);
					}
					/* 배치정보를통해 파일을 메모리에 적재. */
					if (!load_segment (file, file_page, (void *) mem_page,
								read_bytes, zero_bytes, writable))
						goto done;
				}
				else
					goto done;
				break;
		}
	}

	/* Set up stack. */
	// 스택 초기화
	if (!setup_stack (if_))
		goto done;

	/* Start address. */
	// text세그먼트 시작 주소
	if_->rip = ehdr.e_entry;

	argument_stack(arg_list,idx,if_);

	/* TODO: Your code goes here.
	 * TODO: Implement argument passing (see project2/argument_passing.html). */

	success = true;

done:
	/* We arrive here whether the load is successful or not. */
	file_close (file);
	return success;
}

 

 

3) argument stack 함수 내에서 passing 하는 작업을 수행함

 

void argument_stack(char **arg_list,int idx,struct intr_frame *if_){
	int i,j;
	int cnt=0;
	int start_addr=if_->rsp;

	for (int i=idx-1; i>-1; i--)
	{
		cnt+=strlen(arg_list[i])+1;
		for (j=strlen(arg_list[i]); j>-1 ; j--)
		{
			if_->rsp=if_->rsp-1;
			memset(if_->rsp, arg_list[i][j], sizeof(char));
		
		}
	
		if (i==0){
	
		/* word-align*/
		int align = 8 - (cnt % 8);
		for (int k=0; k < align ; k++)
		{
			if_->rsp=if_->rsp-1;
			memset(if_->rsp, 0, sizeof(char));
		}

		for (i=idx; i>-1; i--)
		{
			if_->rsp = if_->rsp-8;

			if (i==idx)
				memset(if_->rsp, 0, sizeof(char *));
			else {
				start_addr=start_addr-strlen(arg_list[i])-1;
				memcpy(if_->rsp, &start_addr, sizeof(start_addr));
			}
		}
		if_->rsp = if_->rsp-8;
		memset(if_->rsp, 0, sizeof(void *));
		if_->R.rdi=idx;
		if_->R.rsi=if_->rsp + 8; 
		}
	}
}

 

 

*) passing에 대한 조건은 아래와 같음

 

 

process_exec 함수 내에 hex_dump(_if.rsp,_if.rsp,USER_STACK-_if.rsp,true); 를 넣어 위의 값을 확인하면 성공!