Program startup details
The Pintos C library for user programs designates _start()
, in lib/user/entry.c
, as the entry point for user programs. This function is a wrapper around main()
that calls exit()
if main()
returns:
void _start (int argc, char *argv[]) {
exit (main (argc, argv));
}
The kernel must put the arguments for the initial function on the stack before it allows the user program to begin executing. The arguments are passed in the same way as the normal calling convention (see 80x86 Calling Convention).
Consider how to handle arguments for the following example command: /bin/ls -l foo bar
. First, break the command into words: /bin/ls
, -l
, foo
, bar
. Place the words at the top of the stack. Order doesn’t matter, because they will be referenced through pointers.
Then, push the address of each string plus a null pointer sentinel, on the stack, in right-to-left order. These are the elements of argv. The null pointer sentinel ensures that argv[argc]
is a null pointer, as required by the C standard. The order ensures that argv[0]
is at the lowest virtual address. The x86 ABI requires that %esp
be aligned to a 16-byte boundary at the time the call
instruction is executed (e.g., at the point where all arguments are pushed to the stack), so make sure to leave enough empty space on the stack so that this is achieved.
Then, push argv
(the address of argv[0]
) and argc
, in that order. Finally, push a fake “return address”; although the entry function will never return, its stack frame must have the same structure as any other.
The table below shows the state of the stack and the relevant registers right before the beginning of the user program, assuming PHYS_BASE
is 0xc0000000:
Address Name Data Type
0xbffffffc argv[3][...] bar\0 char[4]
0xbffffff8 argv[2][...] foo\0 char[4]
0xbffffff5 argv[1][...] -l\0 char[3]
0xbfffffed argv[0][...] /bin/ls\0 char[8]
0xbfffffec stack-align 0 uint8_t
0xbfffffe8 argv[4] 0 char *
0xbfffffe4 argv[3] 0xbffffffc char *
0xbfffffe0 argv[2] 0xbffffff8 char *
0xbfffffdc argv[1] 0xbffffff5 char *
0xbfffffd8 argv[0] 0xbfffffed char *
0xbfffffd4 argv 0xbfffffd8 char **
0xbfffffd0 argc 4 int
0xbfffffcc return address 0 void (*) ()
In this example, the stack pointer would be initialized to 0xbfffffcc
.
As shown above, your code should start the stack at the very top of the user virtual address space, in the page just below virtual address PHYS_BASE
(defined in threads/vaddr.h
).
You may find the non-standard hex_dump() function, declared in <stdio.h>
, useful for debugging your argument passing code. Here’s what it would show in the above example:
bfffffc0 00 00 00 00 | ....|
bfffffd0 04 00 00 00 d8 ff ff bf-ed ff ff bf f5 ff ff bf |................|
bfffffe0 f8 ff ff bf fc ff ff bf-00 00 00 00 00 2f 62 69 |............./bi|
bffffff0 6e 2f 6c 73 00 2d 6c 00-66 6f 6f 00 62 61 72 00 |n/ls.-l.foo.bar.|