xv6, line by line
Makefile

Makefile

Makefile · 204 lines · annotated 100% · build system · upstream

About this file

This file tells make / Makefile how to turn xv6’s source code into three things, and how to run them:

  1. kernel/kernel, the kernel: every kernel/*.c and kernel/*.S compiled into an object file (.o), then linked by kernel/kernel.ld.
  2. The user programs (user/_sh, user/_cat, …): each user/X.c compiled and linked with a small library, by user/user.ld.
  3. fs.img, a disk image (fs.img) holding the user programs, built by the host program mkfs/mkfs.c.

Then make qemu starts QEMU with the kernel and the disk image.

Everything here runs on your computer, not inside xv6. The compiler is a cross-compiler / toolchain: it runs on your machine but produces RISC-V code.

If Makefiles are new to you, read the make / Makefile glossary entry first; then the block notes below go through this one from top to bottom. The most useful lines to understand are the compiler flags (lines 66–89), the kernel link rule (93–96) and the QEMU command line (177–183).

1K=kernel
2U=user
4OBJS = \
33# riscv64-unknown-elf- or riscv64-linux-gnu-
34# perhaps in /opt/riscv/bin
35#TOOLPREFIX =
37# Try to infer the correct TOOLPREFIX if not set
38ifndef TOOLPREFIX
39TOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
40 then echo 'riscv64-unknown-elf-'; \
41 elif riscv64-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
42 then echo 'riscv64-elf-'; \
43 elif riscv64-none-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
44 then echo 'riscv64-none-elf-'; \
45 elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
46 then echo 'riscv64-linux-gnu-'; \
47 elif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
48 then echo 'riscv64-unknown-linux-gnu-'; \
49 else echo "***" 1>&2; \
50 echo "*** Error: Couldn't find a riscv64 version of GCC/binutils." 1>&2; \
51 echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
52 echo "***" 1>&2; exit 1; fi)
53endif
55QEMU = qemu-system-riscv64
60OBJCOPY = $(TOOLPREFIX)objcopy
61OBJDUMP = $(TOOLPREFIX)objdump
63# Deterministic builds.
64DETFLAGS = -ffile-prefix-map=$(CURDIR)=.
66CFLAGS = -Wall -Werror -Wno-unknown-attributes -O -fno-omit-frame-pointer -ggdb -gdwarf-2
68CFLAGS += -march=rv64gc
69CFLAGS += -std=gnu99
70CFLAGS += -MD
71CFLAGS += -mcmodel=medany
72CFLAGS += -ffreestanding
73CFLAGS += -fno-common -nostdlib
74CFLAGS += -fno-builtin-strncpy -fno-builtin-strncmp -fno-builtin-strlen -fno-builtin-memset
75CFLAGS += -fno-builtin-memmove -fno-builtin-memcmp -fno-builtin-log -fno-builtin-bzero
76CFLAGS += -fno-builtin-strchr -fno-builtin-exit -fno-builtin-malloc -fno-builtin-putc
77CFLAGS += -fno-builtin-free
78CFLAGS += -fno-builtin-memcpy -Wno-main
79CFLAGS += -fno-builtin-printf -fno-builtin-fprintf -fno-builtin-vprintf
80CFLAGS += -I.
81CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
83# Disable PIE when possible (for Ubuntu 16.10 toolchain)
84ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
85CFLAGS += -fno-pie -no-pie
86endif
87ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),)
88CFLAGS += -fno-pie -nopie
89endif
91LDFLAGS = -z max-page-size=4096
95 $(OBJDUMP) -S $K/kernel > $K/kernel.asm
96 $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym
99 $(CC) -march=rv64gc -g $(DETFLAGS) -c -o $@ $<
102 etags kernel/*.S kernel/*.c
107 $(LD) $(LDFLAGS) -T $U/user.ld -o $@ $< $(ULIB)
108 $(OBJDUMP) -S $@ > $*.asm
109 $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
115 $(CC) $(CFLAGS) -c -o $U/usys.o $U/usys.S
118 # forktest has less library code linked in - needs to be small
119 # in order to be able to max out the proc table.
120 $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o
121 $(OBJDUMP) -S $U/_forktest > $U/forktest.asm
124 gcc -Wno-unknown-attributes -I. -o mkfs/mkfs mkfs/mkfs.c
126# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
127# that disk image changes after first build are persistent until clean. More
128# details:
129# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html
130.PRECIOUS: %.o
157-include kernel/*.d user/*.d
160 rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
161 */*.o */*.d */*.asm */*.sym \
167# try to generate a unique GDB port
168GDBPORT = $(shell expr `id -u` % 5000 + 25000)
169# QEMU's gdb stub command line changed in 0.11
170QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
171 then echo "-gdb tcp::$(GDBPORT)"; \
172 else echo "-s -p $(GDBPORT)"; fi)
173ifndef CPUS
174CPUS := 3
175endif
177QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic
178QEMUOPTS += -global virtio-mmio.force-legacy=false
179QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0
180QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
186 sed "s/:1234/:$(GDBPORT)/" < $^ > $@
189 @echo "*** Now run 'gdb' in another window." 1>&2
190 $(QEMU) $(QEMUOPTS) -S $(QEMUGDB)
193 @echo $(GDBPORT)
195QEMU_VERSION := $(shell $(QEMU) --version | head -n 1 | sed -E 's/^QEMU emulator version ([0-9]+\.[0-9]+)\..*/\1/')
197 @if [ "$(shell echo "$(QEMU_VERSION) >= $(MIN_QEMU_VERSION)" | bc)" -eq 0 ]; then \
198 echo "ERROR: Need qemu version >= $(MIN_QEMU_VERSION)"; \
199 exit 1; \
200 fi
202.PHONY: fmt
204 clang-format -i $(wildcard kernel/*.[ch] user/*.[ch] mkfs/*.c)