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() once 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 mandated by 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 (the stack pointer) 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.|