# How exec() works

### Overview

Split the flow into 4 phases:&#x20;

1\. Read args.&#x20;

2\. Load program from disk to memory.&#x20;

3\. Allocate stacks.&#x20;

4\. Prepare the new images.

### Flow

1. Find `inode` by path
2. Check ELF header
3. Load program by memory. `uvmalloc` to allocate new size.
4. `loadseg` load file to memory address at `ph.vaddr`. Note: Exec loads bytes from the ELF file into memory at addresses specified by the ELF file.
5. Allocate 2 pages. 2nd one is user stack.
6. Push args to stack. Prepare `ustack` to save each argument with its address.
7. Push the array of argv\[] pointers (`ustack`) to stack.
8. Set `sp` to `a1`.
9. Commit to user image. Free old process’ page table.
10. Set `epc`, `sp`, `sz`.
11. `return argc` which set `argc` in reg `a0`. Now we have `main(args, args)` from `entry, a0, a1`

### Read User Args

```c
uint64
sys_exec(void)
{
  char path[MAXPATH], *argv[MAXARG];
  int I;
  uint64 uargv, uarg;

  if(argstr(0, path, MAXPATH) < 0 || argaddr(1, &uargv) < 0){
    return -1;
  }
  memset(argv, 0, sizeof(argv));
  for(I=0;; I++){
    if(i >= NELEM(argv)){
      goto bad;
    }
    if(fetchaddr(uargv+sizeof(uint64)*I, (uint64*)&uarg) < 0){
      goto bad;
    }
    if(uarg == 0){
      argv[I] = 0;
      break;
    }
    argv[I] = kalloc();
    if(argv[i] == 0)
      panic("sys_exec kalloc");
    if(fetchstr(uarg, argv[i], PGSIZE) < 0){
      goto bad;
    }
  }

  int ret = exec(path, argv);
```

The `argv` is a pointer to list of arguments from user space.&#x20;

![](/files/-M4fGaO9bDUvnzCtqCvt)

Each pointer in RISC-V is 64 bits.

`argaddr(1, &uargv)` is to get the base pointer. Then, do `fetchaddr(uargv+sizeof(uint64)*I, (uint64*)&uarg)` in a loop to find each argument’s address.

`fetchstr(uarg, argv[i], PGSIZE)` is to load the string in `argv[I]`.

#### Fetch address and string

```c
// Fetch the uint64 at addr from the current process.
int
fetchaddr(uint64 addr, uint64 *ip)
{
  struct proc *p = myproc();
  if(addr >= p->sz || addr+sizeof(uint64) > p->sz)
    return -1;
  if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0)
    return -1;
  return 0;
}
```

```c
// Fetch the nul-terminated string at addr from the current process.
// Returns length of string, not including nul, or -1 for error.
int
fetchstr(uint64 addr, char *buf, int max)
{
  struct proc *p = myproc();
  int err = copyinstr(p->pagetable, buf, addr, max);
  if(err < 0)
    return err;
  return strlen(buf);
}
```

`copyinstr` Copy a null-terminated string from user to kernel.

### Load program and Allocate pages

1. Check ELF header
2. Load program into memory
3. Allocate 2 pages, one for user stack. Another for guard page.

![](/files/-M4fGffc-0fkuAm_Uzld)

Note: this graph has a bug. The following 3 items should not exist: `argv, argc, 0xFFFFFF`

### Prepare the new image

Push argument strings to stack. Prepare rest of stack.

Push the array of `argv[]` pointers to stack.

Set `argv` in `a1` register.&#x20;

Set process info, including `pagetable`, `sz`&#x20;

Set `epc = elf.entry`, initial program counter to main initial stack pointer.&#x20;

Return `argc`, in RSIC-V, this sets `argc` in register `a0`. So arguments `main(argc, argv)` is set.

The jump preparation is done. Program will go to `main`, and get args set properly.

### How stack look after pushing args?

![](/files/-M4by0QmCzfNz-va-U6G)

### Core `exec` code for reference

```c
int
exec(char *path, char **argv)
{
  char *s, *last;
  int i, off;
  uint64 argc, sz, sp, ustack[MAXARG+1], stackbase;
  struct elfhdr elf;
  struct inode *ip;
  struct proghdr ph;
  pagetable_t pagetable = 0, oldpagetable;
  struct proc *p = myproc();

  begin_op(ROOTDEV);

  if((ip = namei(path)) == 0){
    end_op(ROOTDEV);
    return -1;
  }
  ilock(ip);

  // Check ELF header
  if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf))
    goto bad;
  if(elf.magic != ELF_MAGIC)
    goto bad;

  if((pagetable = proc_pagetable(p)) == 0)
    goto bad;

  // Load program into memory.
  sz = 0;
  for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
    if(readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))
      goto bad;
    if(ph.type != ELF_PROG_LOAD)
      continue;
    if(ph.memsz < ph.filesz)
      goto bad;
    if(ph.vaddr + ph.memsz < ph.vaddr)
      goto bad;
    if((sz = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0)
      goto bad;
    if(ph.vaddr % PGSIZE != 0)
      goto bad;
    if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0)
      goto bad;
  }
  iunlockput(ip);
  end_op(ROOTDEV);
  ip = 0;

  p = myproc();
  uint64 oldsz = p->sz;

  // Allocate two pages at the next page boundary.
  // Use the second as the user stack.
  sz = PGROUNDUP(sz);
  if((sz = uvmalloc(pagetable, sz, sz + 2*PGSIZE)) == 0)
    goto bad;
  uvmclear(pagetable, sz-2*PGSIZE);
  sp = sz;
  stackbase = sp - PGSIZE;

  // Push argument strings, prepare rest of stack in ustack.
  for(argc = 0; argv[argc]; argc++) {
    if(argc >= MAXARG)
      goto bad;
    sp -= strlen(argv[argc]) + 1;
    sp -= sp % 16; // riscv sp must be 16-byte aligned
    if(sp < stackbase)
      goto bad;
    if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
      goto bad;
    ustack[argc] = sp;
  }
  ustack[argc] = 0;

  // push the array of argv[] pointers.
  sp -= (argc+1) * sizeof(uint64);
  sp -= sp % 16;
  if(sp < stackbase)
    goto bad;
  if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0)
    goto bad;

  // arguments to user main(argc, argv)
  // argc is returned via the system call return
  // value, which goes in a0.
  p->tf->a1 = sp;

  // Save program name for debugging.
  for(last=s=path; *s; s++)
    if(*s == '/')
      last = s+1;
  safestrcpy(p->name, last, sizeof(p->name));

  // Commit to the user image.
  oldpagetable = p->pagetable;
  p->pagetable = pagetable;
  p->sz = sz;
```

### TODO

understand ELF.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiayingp.gitbook.io/build_a_os/traps-and-interrupts/how-exec-works.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
