内核主程序相当于应用程序的主函数。负责调用各个系统模块的初始化函数,在这些模块初始化结束后,它会创建处系统的第一个init进程,并将控制权交给init进程。

当前主程序不具备任何功能,只是为了让内核执行头程序有目标跳转而已。

start_kernel函数实现:

1
2
3
4
5
void start_kernel(void) {
while (1) {
;
}
}

编译脚本:

1
2
$(BUILD_KERNEL)/main.o: $(KERNEL)/main.c
gcc -mcmodel=large -fno-builtin -m64 -c $< -o $@

使用kernel.lds负责将编译生成的main.o文件和header.o文件链接成可执行程序,输出名为system

1
2
3
$(BUILD_KERNEL)/system: $(BUILD_KERNEL)/header.o \
$(BUILD_KERNEL)/main.o
ld -b elf64-x86-64 -o $@ $^ -T $(KERNEL)/kernel.lds

经过编译后生成的文件system依然不是最终的内核程序,还需要将system文件中的二进制提取出来:

1
2
$(BUILD_KERNEL)/kernel.bin: $(BUILD_KERNEL)/system
objcopy -I elf64-x86-64 -S -R ".eh_frame" -R ".comment" -O binary $< $@

此段Makefile脚本命令的作用是剔除system程序中多余的段信息,并提取出二进制程序段数据(textdatabss段等)。

反汇编system文件:

1
objdump -D system

必须反编译system,只有这个文件记录内核程序的各个段信息。

可看到start_kernel的信息如下:

1
2
3
4
5
6
7
8
9
10
......
ffff800000104000 <start_kernel>:
ffff800000104000: 55 push %rbp
ffff800000104001: 48 89 e5 mov %rsp,%rbp
ffff800000104004: 48 8d 05 f9 ff ff ff lea -0x7(%rip),%rax # ffff800000104004 <start_kernel+0x4>
ffff80000010400b: 49 bb 94 11 00 00 00 movabs $0x1194,%r11
ffff800000104012: 00 00 00
ffff800000104015: 4c 01 d8 add %r11,%rax
ffff800000104018: eb fe jmp ffff800000104018 <start_kernel+0x18>
......

jmp ffff800000104018这条指令是一个死循环语句。

使用bochs虚拟机运行后,输出寄存器信息如下:

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
......
00567966634i[CPU0 ] CPU is in long mode (active)
00567966634i[CPU0 ] CS.mode = 64 bit
00567966634i[CPU0 ] SS.mode = 64 bit
00567966634i[CPU0 ] EFER = 0x00000500
00567966634i[CPU0 ] | RAX=ffff800000105198 RBX=0000000000000000
00567966634i[CPU0 ] | RCX=00000000c0000080 RDX=0000000000000000
00567966634i[CPU0 ] | RSP=ffff800000007df8 RBP=ffff800000007df8
00567966634i[CPU0 ] | RSI=00000000000080ae RDI=000000000000c800
00567966634i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00567966634i[CPU0 ] | R10=0000000000000000 R11=0000000000001194
00567966634i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00567966634i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00567966634i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf SF zf af pf cf
00567966634i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00567966634i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 00000000 0 0
00567966634i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 00000000 0 0
00567966634i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 00000000 0 0
00567966634i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 00000000 0 0
00567966634i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 00000000 0 0
00567966634i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 00000000 0 0
00567966634i[CPU0 ] | MSR_FS_BASE:0000000000000000
00567966634i[CPU0 ] | MSR_GS_BASE:0000000000000000
00567966634i[CPU0 ] | RIP=ffff800000104018 (ffff800000104018)
00567966634i[CPU0 ] | CR0=0xe0000011 CR2=0x0000000000000000
00567966634i[CPU0 ] | CR3=0x0000000000101000 CR4=0x00000020
......

RIP=ffff800000104018记录RIP寄存器的值,与编译处的jmp ffff800000104018指令地址一致。