xv6, line by line
kernel/exec.c

kernel/exec.c

C · 176 lines · annotated 100% · kernel · upstream

About this file

The kernel side of exec: replace the calling process’s memory with a new program read from an ELF file, and arrange for it to start running at the program’s entry point with argc and argv as arguments. The process keeps its identity: its PID, its open files, its current directory and its parent all stay the same. Only the user memory, the registers and the name change.

kexec is called from two places: sys_exec (kernel/sysfile.c:488), after it has copied the arguments into the kernel, and forkret (kernel/proc.c:532), which uses it to start /init, the first user program.

The function is built around one rule: nothing about the old program is touched until the new one is completely ready. It builds a fresh page table, loads every segment, sets up the stack and pushes the arguments, and only then swaps the new page table in and frees the old one. Any failure before that point leaves the old program intact, so exec can return -1 to it like any other failed system call.

The steps:

  1. read the ELF header and check it (kernel/elf.h);
  2. for each loadable program header (segment), allocate memory and copy the segment in with loadseg;
  3. allocate a guard page and the user stack above the program;
  4. copy the argument strings and the argc and argv (program arguments) array onto the stack;
  5. commit: install the new page table, size, entry point and stack pointer; free the old image.

Read before: kernel/elf.h, kernel/vm.c (uvmalloc, copyout). Read next: user/ulib.c, whose start is where every program begins (except _forktest, which the Makefile links with -e main, so it starts directly in main).

1#include "types.h"
2#include "param.h"
4#include "riscv.h"
5#include "spinlock.h"
6#include "proc.h"
7#include "defs.h"
8#include "elf.h"
10static int loadseg(pde_t *, uint64, struct inode *, uint, uint);
12// map ELF permissions to PTE permission bits.
13int
16 int perm = 0;
17 if (flags & 0x1)
19 if (flags & 0x2)
21 return perm;
24//
25// the implementation of the exec() system call
26//
27int
28kexec(char *path, char **argv)
30 char *s, *last;
31 int i, off;
33 struct elfhdr elf;
34 struct inode *ip;
35 struct proghdr ph;
37 struct proc *p = myproc();
41 // Open the executable file.
42 if ((ip = namei(path)) == 0) {
44 return -1;
45 }
48 // Read the ELF header.
49 if (readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf))
50 goto bad;
52 // Is this really an ELF file?
54 goto bad;
56 if ((pagetable = proc_pagetable(p)) == 0)
57 goto bad;
59 // Load program into memory.
60 for (i = 0, off = elf.phoff; i < elf.phnum; i++, off += sizeof(ph)) {
61 if (readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))
62 goto bad;
64 continue;
66 goto bad;
68 goto bad;
69 if (ph.vaddr % PGSIZE != 0)
70 goto bad;
74 goto bad;
75 sz = sz1;
77 goto bad;
78 }
81 ip = 0;
83 p = myproc();
86 // Allocate some pages at the next page boundary.
87 // Make the first inaccessible as a stack guard.
88 // Use the rest as the user stack.
91 if ((sz1 = uvmalloc(pagetable, sz, sz + (USERSTACK + 1) * PGSIZE, PTE_W)) ==
92 0)
93 goto bad;
94 sz = sz1;
96 sp = sz;
99 // Copy argument strings into new stack, remember their
100 // addresses in ustack[].
101 for (argc = 0; argv[argc]; argc++) {
102 sp -= strlen(argv[argc]) + 1;
103 sp -= sp % 16; // riscv sp must be 16-byte aligned
105 goto bad;
106 if (copyout(pagetable, sz, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
107 goto bad;
109 }
112 // push a copy of ustack[], the array of argv[] pointers.
113 sp -= (argc + 1) * sizeof(uint64);
114 sp -= sp % 16;
116 goto bad;
117 if (copyout(pagetable, sz, sp, (char *)ustack, (argc + 1) * sizeof(uint64)) <
118 0)
119 goto bad;
121 // a0 and a1 contain arguments to user main(argc, argv)
122 // argc is returned via the system call return
123 // value, which goes in a0.
126 // Save program name for debugging.
127 for (last = s = path; *s; s++)
128 if (*s == '/')
129 last = s + 1;
130 safestrcpy(p->name, last, sizeof(p->name));
132 // Commit to the user image.
135 p->sz = sz;
136 p->trapframe->epc = elf.entry; // initial program counter = ulib.c:start()
137 p->trapframe->sp = sp; // initial stack pointer
140 return argc; // this ends up in a0, the first argument to main(argc, argv)
145 if (ip) {
148 }
149 return -1;
152// Load an ELF program segment into pagetable at virtual address va.
153// va must be page-aligned
154// and the pages from va to va+sz must already be mapped.
155// Returns 0 on success, -1 on failure.
156static int
163 for (i = 0; i < sz; i += PGSIZE) {
165 if (pa == 0)
166 panic("loadseg: address should exist");
167 if (sz - i < PGSIZE)
168 n = sz - i;
169 else
171 if (readi(ip, 0, (uint64)pa, offset + i, n) != n)
172 return -1;
173 }
175 return 0;