有了之前的 Makefile 基础, 相信你也可以很快总结出构建 EOS 的思路:
找到生成这些文件的规则, 并且指定生成时依赖的文件;
使用变量和函数让 Makefile 脚本更为灵活.
那么我们接下来就用这种自顶向下的思路来编写构建 EOS 的 Makefile.
生成的文件
根据 EOS 官方教程, EOS 编译完成之后会生成以下文件:
boot.bin
: EOS 的引导程序, 将会被写入软盘的引导扇区, 负责执行最初的引导工作;
loader.bin
: EOS 的内核加载程序, 被 boot.bin
加载后, 负责切换处理器状态并加载内核;
kernel.dll
: EOS 的内核, 操作系统本体;
libkernel.a
: EOS 内核的, 用于给 EOS 用户应用程序提供内核接口信息.
其中, boot.bin
可以由 boot/boot.asm
直接汇编得到; loader.bin
可以由 boot/loader.asm
直接汇编得到; 其余文件需要单独编译为目标文件, 然后使用链接器链接为 DLL 同时生成导入库.
在 EOS 的构建过程中, 所有 .asm
文件均需要使用 nasm
汇编器进行汇编; .c
文件需要使用 MinGW GCC (i686-w64-mingw32-gcc
) 进行编译; .o
文件需要使用 MinGW LD (i686-w64-mingw32-ld
) 进行链接.
构建规则示例
为了方便调试, 我们需要为编译器和汇编器指定 -g
选项来生成调试信息.
将 .asm
构建为 .bin
例如将 boot.asm
构建为 boot.bin
:
nasm -g boot.asm -o boot.bin -l boot.lst
-l *.lst
表示生成列表文件, 其中包含了生成的二进制与汇编源文件的对应关系.
将 .asm
构建为 .o
例如将 cpu.asm
构建为 cpu.o
:
nasm -g cpu.asm -o cpu.bin -l cpu.lst -f win32
-f win32
表示指定输出的目标格式为 win32
.
将 .c
构建为 .o
例如将 start.c
构建为 start.o
:
i686-w64-mingw32-gcc -g start.c -o start.o \
-c -m32 -nostdlib -nostdinc \
-fsigned-char -pipe -fno-builtin \
-fno-omit-frame-pointer -ffreestanding \
-D_KERNEL_ -D_I386 -D_DEBUG \
-Isrc/inc -Isrc/ke -Isrc/ob -Isrc/io \
-Isrc/io/driver -Isrc/ps -Isrc/mm \
-Isrc/mm/i386
其中:
-m32
, ..., -ffreestanding
: 自行 RTFM;
-D_KERNEL_
, ...: 预定义宏, 相当于在源码中 #define _KERNEL_
, 目的是控制源码的表达;
-Isrc/inc
, ..., -Isrc/mm/i386
: 设置头文件搜索目录, 告诉编译器到这些目录中寻找头文件, 因为 EOS 的头文件 (*.h
) 只出现在了这些地方.
将 .o
构建为 .dll
, 同时生成导入库:
i686-w64-mingw32-ld *.o -o kernel.dll \
-nostdlib -shared \
--image-base 0x80010000 \
-e _KiSystemStartup \
--out-implib libkernel.a
其中:
-shared
: 生成 shared library;
--image-base 0x80010000
: EOS 内核将会被 loader.bin
加载到虚地址 0x80010000
处, 所以此处指定 DLL 映像的基地址为 0x80010000
. 详情请参考官方教程并 RTFSC;
-e _KiSystemStartup
: 指定 DLL 入口点为 _KiSystemStartup
, 即 EOS 内核的初始化函数 (位于 ke/start.c
);
--out-implib libkernel.a
: 生成导入库 libkernel.a
.
编写 Makefile
可以得到编译 EOS 内核的 Makefile 如下:
export CROSS_PREFIX = i686-w64-mingw32-
DEBUG_ARG = -D_DEBUG -g
# directories
export TOP_DIR = $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)
TARGET_DIR = $(TOP_DIR)/build
OBJ_DIR = $(TOP_DIR)/build/obj
LST_DIR = $(TOP_DIR)/build/lst
KERNEL_SRC_DIR = $(TOP_DIR)/src
INCLUDE_ARG := -I$(KERNEL_SRC_DIR)/inc
INCLUDE_ARG += -I$(KERNEL_SRC_DIR)/ke
INCLUDE_ARG += -I$(KERNEL_SRC_DIR)/ob
INCLUDE_ARG += -I$(KERNEL_SRC_DIR)/io
INCLUDE_ARG += -I$(KERNEL_SRC_DIR)/io/driver
INCLUDE_ARG += -I$(KERNEL_SRC_DIR)/ps
INCLUDE_ARG += -I$(KERNEL_SRC_DIR)/mm
INCLUDE_ARG += -I$(KERNEL_SRC_DIR)/mm/i386
# files
KERNEL_TARGETS := $(patsubst $(KERNEL_SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(wildcard $(KERNEL_SRC_DIR)/**/*.c))
KERNEL_TARGETS += $(patsubst $(KERNEL_SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(wildcard $(KERNEL_SRC_DIR)/**/**/*.c))
KERNEL_TARGETS += $(patsubst $(KERNEL_SRC_DIR)/%.asm, $(OBJ_DIR)/%.o, $(wildcard $(KERNEL_SRC_DIR)/**/**/*.asm))
# C compiler
CFLAGS := $(DEBUG_ARG)
CFLAGS += -m32 -c -nostdlib -nostdinc -fsigned-char -pipe
CFLAGS += -fno-builtin -fno-omit-frame-pointer -ffreestanding
CFLAGS += -D_KERNEL_ -D_I386
CFLAGS += $(INCLUDE_ARG)
CC = $(CROSS_PREFIX)gcc $(CFLAGS)
# assembler
NASMFLAGS := $(DEBUG_ARG) -f win32
NASM = nasm $(NASMFLAGS)
# linker
LDFLAGS := -nostdlib
LDFLAGS += -shared --image-base 0x80010000 -e _KiSystemStartup
LDFLAGS += --out-implib $(TARGET_DIR)/libkernel.a
LD = $(CROSS_PREFIX)ld $(LDFLAGS)
.PHONY: all kernel clean
all: $(TARGET_DIR)/boot.bin $(TARGET_DIR)/loader.bin kernel
kernel: $(TARGET_DIR) $(OBJ_DIR) $(LST_DIR) $(TARGET_DIR)/kernel.dll
clean:
-rm -rf $(OBJ_DIR)/*
-rm -rf $(LST_DIR)/*
-rm -f $(TARGET_DIR)/boot.bin
-rm -f $(TARGET_DIR)/loader.bin
-rm -f $(TARGET_DIR)/kernel.dll
-rm -f $(TARGET_DIR)/libkernel.a
# directories
$(TARGET_DIR):
mkdir $(TARGET_DIR)
$(OBJ_DIR):
mkdir $(OBJ_DIR)
$(LST_DIR):
mkdir $(LST_DIR)
# EOS kernel
$(TARGET_DIR)/kernel.dll: $(KERNEL_TARGETS)
$(LD) -o $@ $(KERNEL_TARGETS)
# EOS object
$(OBJ_DIR)/%.o: $(KERNEL_SRC_DIR)/%.c
-mkdir -p $(dir $@)
$(CC) -o $@ $^
$(OBJ_DIR)/%.o: $(KERNEL_SRC_DIR)/%.asm
-mkdir -p $(dir $@)
$(NASM) -o $@ -l $(LST_DIR)/$(basename $(notdir $<)).lst $<
# bootloaders
$(TARGET_DIR)/%.bin: $(KERNEL_SRC_DIR)/boot/%.asm
nasm $(DEBUG_ARG) -o $@ -l $(LST_DIR)/$(basename $(notdir $<)).lst $<
将其放入 eos
目录中:
eos
├── build
├── src
├── vm
├── Makefile # 构建脚本
└── License.txt
尝试构建 EOS 内核:
可以看到 build
目录中已经生成了我们需要的四个文件.