makefile 和 qemu¶
makefile 内部¶
指定编译使用的工具¶
TOOLPREFIX = riscv64-unknown-elf-
CC = $(TOOLPREFIX)gcc
AS = $(TOOLPREFIX)gas
LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump
GDB = $(TOOLPREFIX)gdb
这里makefile调用了大家设定好的PATH之中的riscv64工具链。如果没有设置好,那么之后的编译就会因为找不到这些文件而出错。
添加编译flag¶
CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb
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)
比较需要注意的是我们设置了警告也会报错,因此大家写代码的时候最好避免 warning 的出现,这是良好的编程习惯。
设置编译目标¶
# 目录定义
K = os
BUILDDIR = build
# .o 目标的确定,也就是 os 目录下所有的 .c 和 .s 都编译成 .o
C_SRCS = $(wildcard $K/*.c)
AS_SRCS = $(wildcard $K/*.S)
C_OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(C_SRCS))))
AS_OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(AS_SRCS))))
OBJS = $(C_OBJS) $(AS_OBJS)
# kernel 镜像由所有的 .o 按照 kernel.ld 链接而成
$(BUILDDIR)/kernel: $(OBJS) $(K)/kernel.ld
$(LD) $(LDFLAGS) -T kernel.ld -o kernel $(OBJS)
请同学们自行查阅并了解``wildcard``、addprefix
、addsuffix
、``basename``的意义。
运行 qemu¶
QEMU = qemu-system-riscv64
QEMUOPTS = \
-nographic \
-smp $(CPUS) \
-machine virt \
-bios $(BOOTLOADER) \
-kernel kernel
run: $(BUILDDIR)/kernel
$(QEMU) $(QEMUOPTS)
这里和前面一致。大家不需要太关心qemu的更多细节,我们涉及它的操作已经在makefile和sbi之中处理了。
gdb 调试¶
# QEMU's gdb stub command line changed in 0.11
QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
then echo "-gdb tcp::1234"; \
else echo "-s -p 1234"; fi)
debug: kernel .gdbinit
$(QEMU) $(QEMUOPTS) -S $(QEMUGDB) &
sleep 1
$(GDB)
使用 make debug 来使用 gdb 调试 qemu。程序自身执行的机制和直接 make run 一样。在解析 bootloader 的行为时可以使用 gdb 在其中添加断点来查看对应寄存器和内存的内容。gdb的具体使用方法和汇编课程上一致。不熟悉的同学可以在训练章节查看到可能用到的gdb指令的简单用法,也十分推荐同学们自学一些基础的 gdb 使用方法,掌握 gdb 对本课程帮助很大。
LOG 支持¶
ifeq ($(LOG), error)
CFLAGS += -D LOG_LEVEL_ERROR
else ifeq ($(LOG), warn)
CFLAGS += -D LOG_LEVEL_WARN
else ifeq ($(LOG), info)
CFLAGS += -D LOG_LEVEL_INFO
else ifeq ($(LOG), debug)
CFLAGS += -D LOG_LEVEL_DEBUG
else ifeq ($(LOG), trace)
CFLAGS += -D LOG_LEVEL_TRACE
endif
我们的 log 等级选择是通过 -D 参数来实现的,这也是大家 make run LOG=xxx
的原理。从这里我们也可以看到 LOG
的可选值。