Skip to main content Link Menu Expand (external link) Document Search Copy Copied

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.|