Xv6 Makefile解析
XV6构建过程,概览
写在前面的话
本文主要是对xv6-riscv的Makefile进行分析
“只有读懂了Makefile才能更好理解项目的结构”
Makefile可以理解为是make的配置文件,类似的有docker的Dockerfile
分析
严格按照文件从上到下分析
变量声明
模块声明
声明了两个路径,kernel & user路径
推测这两个路径分别对应的是——“系统内核和用户程序”
输出文件定义,乍一看全是kernel模块的文件
(可能会疑惑为啥叫OBJS呢,因为.c文件的编译文件就是.o即object)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 OBJS = \ $K/entry.o \ $K/start.o \ $K/console.o \ $K/printf.o \ $K/uart.o \ $K/kalloc.o \ $K/spinlock.o \ $K/string.o \ $K/main.o \ $K/vm.o \ $K/proc.o \ $K/swtch.o \ $K/trampoline.o \ $K/trap.o \ $K/syscall.o \ $K/sysproc.o \ $K/bio.o \ $K/fs.o \ $K/log.o \ $K/sleeplock.o \ $K/file.o \ $K/pipe.o \ $K/exec.o \ $K/sysfile.o \ $K/kernelvec.o \ $K/plic.o \ $K/virtio_disk.o
工具链寻找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 TOOLPREFIX = /opt/riscv/bin/rename/ ifndef TOOLPREFIXTOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ then echo 'riscv64-unknown-elf-'; \ elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ then echo 'riscv64-linux-gnu-'; \ elif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ then echo 'riscv64-unknown-linux-gnu-'; \ else echo "***" 1>&2; \ echo "*** Error: Couldn't find a riscv64 version of GCC/binutils." 1>&2; \ echo "*** To turn off this error , run 'gmake TOOLPREFIX= ...'." 1>&2; \ echo "***" 1>&2; exit 1; fi) endif
拼接工具路径
1 2 3 4 5 6 7 8 9 QEMU = qemu-system-riscv64 CC = $(TOOLPREFIX) gcc AS = $(TOOLPREFIX) gas LD = $(TOOLPREFIX) ld OBJCOPY = $(TOOLPREFIX) objcopy OBJDUMP = $(TOOLPREFIX) objdump
拼接gcc编译参数 & ld链接参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2 CFLAGS += -MD CFLAGS += -mcmodel=medany CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax CFLAGS += -I. CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie') ,)CFLAGS += -fno-pie -no-pie end ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie') ,)CFLAGS += -fno-pie -nopie endif LDFLAGS = -z max-page-size=4096
构建脚本 $K/kernel
kernel模块构建任务
这里需要说明下默认情况下会有这么一条task
n.o is made automatically from n.c with a recipe of the form
GNU Make
1 $(CC) $(CPPFLAGS) $(CFLAGS) -c
1 2 3 4 5 6 7 8 $K/kernel: $(OBJS) $K/kernel.ld $U/initcode $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym
$U/initcode
user/initcode任务
1 2 3 4 5 6 7 8 9 10 $U/initcode: $U/initcode.S $(CC) $(CFLAGS) -march=rv64g -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o $(OBJCOPY) -S -O binary $U/initcode.out $U/initcode $(OBJDUMP) -S $U/initcode.o > $U/initcode.asm
tags任务
➜ xv6-riscv make tags make: *** No rule to make target ‘_init’, needed by ‘tags’. Stop.
1 2 3 tags: $(OBJS) _init etags *.S *.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ULIB = $U/ulib.o $U/usys.o $U/printf.o $U/umalloc.o _%: %.o $(ULIB) $(LD) $(LDFLAGS) -T $U/user.ld -o $@ $^ $(OBJDUMP) -S $@ > $* .asm $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $* .sym $U/usys.S : $U/usys.pl perl $U/usys.pl > $U/usys.S $U/usys.o : $U/usys.S $(CC) $(CFLAGS) -c -o $U/usys.o $U/usys.S $U/_forktest: $U/forktest.o $(ULIB) $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o $(OBJDUMP) -S $U/_forktest > $U/forktest.asm
mkfs/mkfs
构建工具
1 2 mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h gcc -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c
防止删除中间产物
用户态程序声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 UPROGS=\ $U/_cat\ $U/_echo\ $U/_forktest\ $U/_grep\ $U/_init\ $U/_kill\ $U/_ln\ $U/_ls\ $U/_mkdir\ $U/_rm\ $U/_sh\ $U/_stressfs\ $U/_usertests\ $U/_grind\ $U/_wc\ $U/_zombie\
fs.img
创建镜像
依赖mkfs任务 & 用户态所有程序
1 2 3 4 fs.img: mkfs/mkfs README $(UPROGS) mkfs/mkfs fs.img README $(UPROGS) -include kernel/*.d user/*.d
其他
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 GDBPORT = $(shell expr `id -u` % 5000 + 25000) QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT) "; \ else echo "-s -p $(GDBPORT) "; fi) ifndef CPUSCPUS := 3 endif QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic QEMUOPTS += -global virtio-mmio.force-legacy=false QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0 QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
qemu
qemu任务
依赖$K/kernel fs.img任务
1 2 3 qemu: $K/kernel fs.img $(QEMU) $(QEMUOPTS)
.gdbinit
.gdbinit任务
依赖.gdbinit.tmpl-riscv文件
将1234端口替换为指定端口
1 2 .gdbinit: .gdbinit.tmpl-riscv sed "s/:1234/:$(GDBPORT) /" < $^ > $@
qemu-gdb
qemu-gdb任务
依赖$K/kernel .gdbinit fs.img人物
1 2 3 4 qemu-gdb: $K/kernel .gdbinit fs.img @echo "*** Now run 'gdb' in another window." 1>&2 $(QEMU) $(QEMUOPTS) -S $(QEMUGDB)
其他 有几个比较重要的task需要说明
1.$K/kernel
构建内核
2.fs.img
构建镜像
3.qemu
构建内核 + 构建镜像 + 启动Qemu
4..gdbinit/qemu-gdb
调试