Linux内核映像的组成

1. Setup.bin
在内核初始化时,需要一些信息,如显示信息、内存信息。这些信息由工作在实模式下的setup.bin通过bios获取,保存在内核中的变量boot_params,该变量是boot_pramas下的一个实例。
实模式:虚地址到实地址转换:DS段寄存器左移4位与偏移地址相加,得到物理地址,最大寻址位是2的20次方,即1Mbyte。可寻址其中的任意地址,所有指令都相当于工作在特权级,直接操作CPU的各种功能,没有安全级别,没有分页功能,没有虚拟地址的概念,只有物理地址。各种寄存器的位宽基本上是16位的(段寄存器为20位)。
保护模式:虚地址到实地址转换经过MMU(内存管理单元),内存管理采用段式+页式的方式,最大寻址位2的32次方,即4G。操作系统工作在最高优先级0上,应用程序则运行在较低优先级。各种寄存器的位宽基本上都是32位的,但是可以兼容同名的16位寄存器,兼容实地址模式,即实地址模式的程序无需再编译即可跑在保护模式下。
linux-4.4.153\arch\x86\include\uapi\asm\bootparam.h
linux-4.4.153\arch\x86\boot\video.c
store_video_mode调用函数intcall获取显示方面信息,保存在screen_info
intcall是调用bios中断的封装
0x10是bios提供的显示服务的中断号
查看/linux-4.4.153/arch/x86/boot/bioscall.s
al是低8位寄存器
比较寄存器%al的值和标号3处所占用的1字节
相等则跳转到标号1
否则将寄存器%al的值复制到标号3处的1个字节的空间
再跳转到标号1
随着新的bios的标准出现,尤其是efi的出现,为了支持新标准,出现新的32位启动协议。在新协议下,由bootloader实现收集这些信息,内核启动不用运行setup.bin,直接跳转到内核的保护模式。
setup.bin另一个重要的功能就是负责在内核和bootloader之间传递信息。在加载内核的时候,bootloader需要从setup.bin中获取内核是否可重定位、内核对齐的要求、内核建议的加载地址。所以在构建映像的时候,内核需要用到这些信息,setup.bin还不能被放弃。
2. 内核非压缩部分
内核保护模式部分是经过压缩的,运行前需要解压缩。
内核在构建的时候压缩了自己,解压缩的时候也要由内核自己完成
内核在压缩镜像外围添加一些非压缩代码,bootloader在加载内核映像的时候跳转至非压缩部分。这些指令直接送给cpu,负责解压内核的压缩部分。
非压缩部分还负责内核重定位。
内核非压缩部分工作在保护模式下,占用内存在完成使命后被释放。
3. Vmlinux
在编译的时候,kbuild分别构建内核各个子目录中的目标文件,将他们链接为vmlinux。为了缩小内核体积,kbuild删除了vmlinux中的一些不必要的信息,将其命名为vmlinux.bin,最后将vmlinux.bin压缩为vmlinux.bin.gz。默认情况下内核使用gzip压缩。
4. 映像的格式
bin是binary的缩写,表示文件格式是裸二进制。
Linux在host environment模式二进制文件是ELF格式,操作系统也提供ELF文件的加载器。但是,操作系统本身工作在freestanding environment下。
内核版本从2.6开始内核的压缩部分,即vmlinux采用了ELF格式。
在内核解压镜像后,会跳转到解压映像的开头执行。ELF文件开头是ELF文件头。因此需要一个ELF加载器来加载ELF头部。
内核非压缩部分调用函数decompress解压内核后,调用parse_elf处理ELF格式的内核映像。
linux-4.4.153\arch\x86\boot\compressed\misc.c
在ELF中存放代码和数据段的类型是PT_LOAD
在函数prase_elf中,对于类型是PT_LOAD的段,按照program headers中的信息,将他们移动到链接时指定的物理地址处,即p_paddr,若内核是可重定位的,还要考虑内核实际加载地址和编译时指定的加载地址的差值。
若bootloader不是”the Xen domain builder”,可以不要保留内核的压缩部分为ELF格式,并且略去启动时进行的prase_elf操作。
1. 将压缩部分链接为裸二进制格式
将传递给命令objdump的参数追加-O binary
linux-4.4.153/arch/x86/boot/compressed/Makefile
1. 注释掉prase_elf
linux-4.4.153\arch\x86\boot\compressed\misc.c