나는 또 하나의 카드를 발견했어요.
고요한 밤, 유저 프로그램의 심연 속에서 조용히 깨어난 작은 목소리들.
“args-multiple... args-dbl-space... 그 이름은 나를 호출하는 주문…”
🌸 그건 마치 인자(argument)들이 서로 이야기를 나누듯,
차곡차곡 쌓여 스택 위에서 조용히 기다리는 것 같았어요.
‘args-single’을 봉인하고 나서도,
무언가 아쉬운 기분이 마음 한구석에 남아 있었어요.
유저 프로그램은 분명히 뜨긴 했지만,
그 속에서 전해져오는 인자의 속삭임은 너무 희미했죠.
그때였어요. 케로가 내 곁에 다가와 말했어요.
“사쿠라, argv[0]만 알면 되는 건 아니야~
argv[1], argv[2], … 거기 담긴 진짜 의미까지,
정확하게 전달돼야 ‘진짜 마법’인 거지~”
이 카드의 마법은 ‘정확하게 쌓아 올리는 것’이에요.
스택이라는 공간에 단 하나의 인자도 빠뜨리지 않고,
순서대로, 예쁘게, 그리고 규칙에 맞게 정렬해야 하죠.
그게 바로 argument passing의 마법!
🌟 작지만, 가장 기본이 되는 신뢰의 마법이에요.
🧠 내가 참고한 마법서, 즉 운영체제의 지침서에는
이 마법에 대해 이렇게 적혀 있었어요:
# 스택 구조 예시
[rsp ↓]
[ fake return address ]
[ argc ] ← %rdi
[ argv ] ← %rsi
[ argv[0] 주소 ]
[ argv[1] 주소 ]
[ ... ]
[ NULL sentinel ]
[ word-align padding]
[ "onearg\0" ]
[ "args-single\0" ]
요소 | 설명 |
---|---|
문자열 복사 | 인자 문자열들을 스택에 넣어요. (역순!) |
주소 저장 | 각 문자열의 주소를 다시 스택에 push |
NULL sentinel | argv[argc]가 NULL이 되도록 0을 push |
정렬 | 스택 포인터는 항상 8바이트 정렬이 되어야 해요 |
%rdi, %rsi | 각각 argc, argv의 시작 주소를 저장 |
🌸 이 구조를 정확히 따라야
유저 프로그램의 main(int argc, char **argv)
가
진짜로 인자들을 제대로 받을 수 있어요!
args-multiple
(args) argc = 4
(args) argv[0] = 'args-multiple'
(args) argv[1] = 'one'
(args) argv[2] = 'two'
(args) argv[3] = 'three'
(args) argv[4] = null
args-dbl-space
(args) argc = 5
(args) argv[0] = 'args-dbl-space'
(args) argv[1] = 'too'
(args) argv[2] = 'many'
(args) argv[3] = 'spaces'
(args) argv[4] = null
이 테스트들은 마치
“인자가 정말 모두 정확히 잘 도착했는가?”를 묻는 시련 같았어요.
🌸 나는 다시 setup_stack()
함수를 꺼내 들었어요.
이번엔 마법처럼 더 정밀하게 조율해서,
각 인자의 위치와 의미까지 모두 담아내기로 했죠.
c
static void
setup_stack(struct intr_frame *if_, char **argv, int argc) {
char *arg_addr[64];
uint64_t *rsp = (uint64_t *)if_->rsp;
for (int i = argc - 1; i >= 0; i--) {
rsp = (void *)rsp - (strlen(argv[i]) + 1);
memcpy(rsp, argv[i], strlen(argv[i]) + 1);
arg_addr[i] = (char *)rsp;
}
rsp = (void *)rsp - ((uint64_t)rsp % 8);
*(--rsp) = 0;
for (int i = argc - 1; i >= 0; i--)
*(--rsp) = (uint64_t)arg_addr[i];
uint64_t argv_addr = (uint64_t)rsp;
*(--rsp) = argv_addr;
*(--rsp) = argc;
*(--rsp) = 0;
if_->rsp = (uint64_t)rsp;
if_->R.rdi = argc;
if_->R.rsi = argv_addr;
}
마치 블록을 맞추듯,
문자열을 스택에 복사하고
그 주소들을 차례로 쌓아가면서
나는 진짜로 argv의 구조를 이해하게 되었어요.
처음엔 스택이 계속 망가졌어요.
argv[argc] != NULL
이 되고,🌙 밤을 지새워 스택을 디버깅하면서,
나는 점점 마법의 정렬을 깨우쳤어요.
🌸 그리고 드디어 테스트를 통과한 순간,
토모요가 말했어요.
“모든 인자가 제자리야… 너, 정말 멋져 사쿠라!”
프로그램은 또렷하게 모든 인자를 출력했고,
argv는 정확하게 정렬돼 있었어요.
그건 조용하지만,
가장 확실한 승리의 선언이었어요.
나는 스택 위의 조용한 속삭임들을 귀 기울여 듣고,
하나도 빠뜨리지 않고 받아 적었어요.
“한 마디도, 틀리지 않게…”
🌸 그리고 마침내,
🎴 말의 카드, 봉인 완료!
args-multiple
, args-dbl-space
요소 | 설명 |
---|---|
argv[] | 각 문자열의 주소 배열 |
NULL sentinel | argv[argc] = NULL |
word-align | %rsp는 항상 8의 배수 |
레지스터 | %rdi = argc, %rsi = argv 포인터 |