内核未加载的问题
problems with kernel not being loaded
我正在创建一个 OS,当我编译代码时什么也没发生,什么都没有(没有错误、警告或任何东西)我认为 make 文件有一些问题。
生成文件:
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 发生了什么,这是将所有代码编译成目标文件和 link 它们的正确方法吗。
任何帮助将不胜感激。
我只是评论你的 make 风格——这并没有回答你为什么什么都不输出(而且你的意思还不清楚——如果你 运行 make
,它应该至少输出 echo "Building kernel..."
...)。就 makefile 风格而言,这似乎是使用脚本思维而不是 make 思维构建的。考虑你的第一部分:
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 $@
注意上面是重复的,如果你有几百个文件,会很快。这也可以使用 static pattern 规则来完成:
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 文件。请参阅 here 了解一些说明关于解决这个问题。
下一节 link
,makefile 配方应反映目标。
.PHONY: link
link : ${BUILD_DIR}/kernel.bin
${BUILD_DIR}/kernel.bin: ${C_OBJS} ${ASM_OBJS}
${LINKER} -o $@ $^ -Ttext 0x1000 --oformat binary
这会创建一个虚假目标 link,因此您可以键入 make link
。如果任何 C 对象或 ASM 对象已更新,它只会执行 link。同样的概念适用于您的 merge_binary
目标
对于run
,这似乎有些争议,但一个普遍的经验法则是 make
应该被用来 制作 一个可执行文件,不是为了 运行 吧。如果您想使用特定参数调用构建的目标,则单独的 shell 脚本更适合。
最后,您的 post_build
规则可能应该重命名为 CLEAN
,并声明为虚假规则。
我正在创建一个 OS,当我编译代码时什么也没发生,什么都没有(没有错误、警告或任何东西)我认为 make 文件有一些问题。
生成文件:
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 发生了什么,这是将所有代码编译成目标文件和 link 它们的正确方法吗。
任何帮助将不胜感激。
我只是评论你的 make 风格——这并没有回答你为什么什么都不输出(而且你的意思还不清楚——如果你 运行 make
,它应该至少输出 echo "Building kernel..."
...)。就 makefile 风格而言,这似乎是使用脚本思维而不是 make 思维构建的。考虑你的第一部分:
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 $@
注意上面是重复的,如果你有几百个文件,会很快。这也可以使用 static pattern 规则来完成:
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 文件。请参阅 here 了解一些说明关于解决这个问题。
下一节 link
,makefile 配方应反映目标。
.PHONY: link
link : ${BUILD_DIR}/kernel.bin
${BUILD_DIR}/kernel.bin: ${C_OBJS} ${ASM_OBJS}
${LINKER} -o $@ $^ -Ttext 0x1000 --oformat binary
这会创建一个虚假目标 link,因此您可以键入 make link
。如果任何 C 对象或 ASM 对象已更新,它只会执行 link。同样的概念适用于您的 merge_binary
目标
对于run
,这似乎有些争议,但一个普遍的经验法则是 make
应该被用来 制作 一个可执行文件,不是为了 运行 吧。如果您想使用特定参数调用构建的目标,则单独的 shell 脚本更适合。
最后,您的 post_build
规则可能应该重命名为 CLEAN
,并声明为虚假规则。