5.3 加载内核
5.3.1 用C语言写内核
首先是编译:
在64位环境中编译32位文件。
第一个C语言代码:
//main.c
int main(){
while(1);
return 0;
}
编译:
//加上-m32是为了在64位的情况下编译出32位的main.o
gcc -m32 -c -o main.o main.c
file查看main.o:
$ file main.o
main.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
nm 查看 main.o:
$ nm main.o
U _GLOBAL_OFFSET_TABLE_
00000000 T __x86.get_pc_thunk.ax
00000000 T main
在linux中ld用于链接。
使用-Ttext 0xc0001500 -e main指定 main符号为入口加载到0xc0001500处。如果用_start代替main就直接默认_start为起点了。
//书中
ld main.o -Ttext 0xc0001500 -e main -o kernel.bin
//因为我的笔记本是64位,所以我为了将其转换为32位。加上-m elf_i386
ld main.o -m elf_i386 -Ttext 0xc0001500 -e main -o kernel.bin
5.3.2 二进制程序的运行方法
程序肯定有格式的。
比方开头有程序入口地址,和程序体长度等信息。
5.3.3 ELF格式的二进制模式
Linux下的可执行文件格式是ELF(可执行链接格式)。
ELF文件是经过编译链接后的二进制可执行文件。
e_type占2字节,用来指定elf目标文件的类型。
取值如下:
e_machine占用2字节,表明在什么硬件平台上才能运行:
结构属性名 | 字节 | 作用 |
---|---|---|
e_version | 4 | 表示版本信息 |
e_entry | 4 | 指明操作系统运行该程序时,控制权交给虚拟地址 |
e_phoff | 4 | 指明程序头表在文件中的字节偏移量,没有表则为0 |
e_shoff | 4 | 指明节头表在文件内的字节偏移量,没有表则为0 |
e_flags | 4 | 用来指明和处理器相关的标志 |
e_ehsize | 2 | 用来指明elf header的字节大小 |
e_phentsize | 2 | 用来指明程序头表中每个条目的字节大小,即每个用来描述段信息的数据结构的字节大小,该结构是struct Elf32_Phdr |
e_phnum | 2 | 用来指明程序头表中每个条目的数量。实际上就是段的个数。 |
e_shentsize | 2 | 用来指明节头表中每个条目的字节大小,每个用来描述节信息的数据结构的字节大小 |
e_shnum | 2 | 用来指明节头表中条目的数量。实际上就是节的个数 |
e_shstrndx | 2 | 用来指明string name table在节头表中的索引index. |
struct Elf32_Phdr是用来描述位于磁盘上的程序中的一个段
接下来是属性介绍:
5.3.4 elf文件实例分析
书上很详细。
5.3.5 将内核载入内存
作者选的给kernel.bin的地址为0x70000~0x9fbff有0x2fbff=190KB字节空间。
关于几个数:
入口虚拟地址0xc0001500 | loader.bin的加载地址为0x900,预计loader.bin大小不超过2000字节,所以选的kernel的骑士物理地址为0x900+2000=0x10d0。为了凑个整数选择了0x1500,也就是使用链接命令ld时候指定的代码段的起始虚拟地址。ld main.o -Ttext 0xc0001500 -e main -o kernel.bin |
目前的布局
boot.inc、mbr.asm、loader.asm代码链接
加载kernel.bin
dd if=kernel.bin of=hd60M.img bs=512 count=200 seek=9 conv=notruce