内核未加载的问题



我正在创建一个操作系统,当我编译代码时什么都没有发生,只是什么都没有(没有错误,警告或任何东西),我认为make文件有一些问题。

Makefile:

build_kernel:
echo "Building kernel..."
${ASM} ./src/kernel/kernel_entry.asm -f elf64 -o ${BUILD_DIR}/kernel_entry.o
${C_COMPILER} -c ./src/kernel/kernel.c -o ${BUILD_DIR}/kernel_start.o
${C_COMPILER} -c ./src/kernel/drivers/printutils.c -o ${BUILD_DIR}/kernel_printutils.o
${C_COMPILER} -c ./src/kernel/drivers/port.c -o ${BUILD_DIR}/kernel_ports.o
echo "kernel build complete."
link:
echo "Linking..."
${LINKER} -o ${BUILD_DIR}/kernel.bin ${BUILD_DIR}/kernel_ports.o 
${BUILD_DIR}/kernel_printutils.o ${BUILD_DIR}/kernel_start.o 
${BUILD_DIR}/kernel_entry.o -Ttext 0x1000 --oformat binary
echo "Linking complete"
run:
echo "Running qemu..."
qemu-system-x86_64 -fda ${BUILD_DIR}/os.bin
merge_binary:
echo "Merging binary..."
cat ${BUILD_DIR}/boot.bin ${BUILD_DIR}/kernel.bin > ${BUILD_DIR}/os.bin
echo "Binary merged."
post_build:
rm -f ${BUILD_DIR}/boot.bin
rm -f ${BUILD_DIR}/kernel.bin
rm -f ${BUILD_DIR}/kernel.o
rm -f ${BUILD_DIR}/kernel_entry.o
rm -f ${BUILD_DIR}/kernel_ports.o
rm -f ${BUILD_DIR}/kernel_printutils.o
rm -f ${BUILD_DIR}/kernel_start.o

我想知道makefile发生了什么,这是将所有代码编译成目标文件并链接它们的正确方法吗?

任何帮助都将不胜感激。

我只是在评论你的make风格——这并不能回答为什么你什么都不输出(而且不清楚你的意思是什么——如果你运行make,它应该至少输出echo "Building kernel..."…)。就makefile风格而言,这似乎是使用脚本思维而不是制作思维来构建的。考虑第一部分:

build_kernel:
echo "Building kernel..."
${ASM} ./src/kernel/kernel_entry.asm -f elf64 -o ${BUILD_DIR}/kernel_entry.o
${C_COMPILER} -c ./src/kernel/kernel.c -o ${BUILD_DIR}/kernel_start.o
${C_COMPILER} -c ./src/kernel/drivers/printutils.c -o ${BUILD_DIR}/kernel_printutils.o
${C_COMPILER} -c ./src/kernel/drivers/port.c -o ${BUILD_DIR}/kernel_ports.o
echo "kernel build complete."

这有几个问题。首先是名称——这看起来像是构建一堆构件,而不是构建内核。此外,该配方从未生成名为build_kernel的文件,因此这应该是一个假目标。接下来,这实际上是一个脚本,它构建四个独立的东西。这些可以分成四个单独的规则,每个规则构建一个东西,然后主要目标将依赖于此。因此,它可能看起来像:

.PHONY: build_kernel_objs
build_kernel_objs: ${C_OBJS} ${ASM_OBJS}
@echo done building $@
${BUILD_DIR}/kernel_start.o : ./src/kernel/kernel.c
${C_COMPILER} -c $< -o $@
${BUILD_DIR}/kernel_printutils.o : ./src/kernel/kernel_printutils.c
${C_COMPILER} -c $< -o $@
${BUILD_DIR}/kernel_ports.o : ./src/kernel/kernel_ports.c
${C_COMPILER} -c $< -o $@

注意以上是重复的,如果您有数百个文件,将会非常快地关闭。这也可以使用静态模式规则来完成:

C_FILES := 
./src/kernel/kernel_start.c
./src/kernel/kernel_printutils.c
./src/kernel/kernel_ports.c
ASM_FILES := 
./src/kernel/kernel_entry.asm
C_OBJS := ${C_FILES :./src/kernel/%.c=${BUILD_DIR}/%.o}
ASM_OBJS := ${ASM_FILES :./src/kernel/%.asm=${BUILD_DIR}/%.o}
${C_OBJS} : ${BUILD_DIR}/%.o : ./src/kernel/%.c
${C_COMPILER} -c $< $@
.PHONY: build_kernel_objs
build_kernel_objs: ${C_OBJS} ${ASM_OBJS}
@echo "done building $@"

这比您所做的有几个优点——首先,make将只构建过时的对象,因此它不会做不必要的工作。如果在make命令行上指定-j选项,它还可以并行地构建文件。其次,它的可维护性更强——如果您必须添加额外的文件,您可以在一个地方添加,并且一切都解决了。另外,如果您的make目录中碰巧有一个名为build_kernel_objs的文件,那么.PHONY可以防止make失败。最后,echo行前面的@可以防止实际的echo命令被回显,这样看起来会更好。

需要注意的是,它不处理头文件的修改(如前所述,如果一个头文件被更新,依赖于它的c文件将不会被重建)。关于如何解决这个问题,请参阅此处。

下一节link, makefile recipe应该反映目标。

.PHONY: link
link : ${BUILD_DIR}/kernel.bin
${BUILD_DIR}/kernel.bin: ${C_OBJS} ${ASM_OBJS}
${LINKER} -o $@ $^ -Ttext 0x1000 --oformat binary   

这会创建一个虚假的目标链接,因此您可以键入make link。只有当任何C对象或ASM对象被更新时,它才会执行链接。同样的概念也适用于merge_binary目标

对于run,这似乎有些争议,但一个常见的经验法则是,make应该用于使成为可执行文件,而不是运行它。如果您希望使用特定参数调用构建的目标,则使用单独的shell脚本更合适。

最后,您的post_build规则应该重命名为CLEAN,并声明为伪规则。

最新更新