加载内核
ELF文件由三大部分组成:一个带有加载信息的文件头,程序段表和程序段。每一个程序段是一块连续的代码或数据。bootloader的工作是把程序段加载到内存中。程序段表中列出了要加载到内存中的所有段。
通过命令 objdump -h obj/kern/kernel 考察JOS内核中所有段的名字大小和地址,如下所示。VMA字段是链接地址,指的是这个段希望被存放到的逻辑地址。LMA字段是加载地址,表示的是这个段被加载到内存后的物理地址。
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00005459 f0100000 00100000 00001000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .rodata 0000166c f0105460 00105460 00006460 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .stab 000088c9 f0106acc 00106acc 00007acc 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .stabstr 00002ada f010f395 0010f395 00010395 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .data 0007a114 f0112000 00112000 00013000 2**12
CONTENTS, ALLOC, LOAD, DATA
5 .got 0000000c f018c114 0018c114 0008d114 2**2
CONTENTS, ALLOC, LOAD, DATA
6 .got.plt 0000000c f018c120 0018c120 0008d120 2**2
CONTENTS, ALLOC, LOAD, DATA
7 .data.rel.local 0000100e f018d000 0018d000 0008e000 2**12
CONTENTS, ALLOC, LOAD, DATA
8 .data.rel.ro.local 000000e0 f018e020 0018e020 0008f020 2**5
CONTENTS, ALLOC, LOAD, DATA
9 .bss 00000f10 f018e100 0018e100 0008f100 2**5
ALLOC
10 .comment 00000029 00000000 00000000 0008f100 2**0
CONTENTS, READONLY
每一个ELF文件中都有一个Program Headers Table(PHT),用于指明ELF文件中哪些部分被加载到内存中以及加载到内存中的地址。PHT表中的每一个表项分别对应操作系统的一个段。并且每个表项的内容包括这个段的大小,段起始地址偏移等等信息。可用objdump -x obj/kern/kernel 命令获取PHT。(没看懂PHT,.bss段是否需要加载到内存中)
off字段指的是这段的开头相对于这个elf文件的开头的偏移量。filesz指的是这段在elf文件中的大小。memsz指的是这段装入内存中的实际大小。memsz一定大于或等于filesz,因为段在elf文件中时,许多未定义的变量没有分配内存给他们。
Program Header:
LOAD off 0x00001000 vaddr 0xf0100000 paddr 0x00100000 align 2**12
filesz 0x00011e6f memsz 0x00011e6f flags r-x
LOAD off 0x00013000 vaddr 0xf0112000 paddr 0x00112000 align 2**12
filesz 0x0007c100 memsz 0x0007d010 flags rw-
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
filesz 0x00000000 memsz 0x00000000 flags rwx
BIOS把bootsector加载到内存地址0x7c00处后,执行bootloader程序,在bootmain子程序中执行最后一条语句,把操作系统内核的各个段从外存读入内存中。
((void (*)(void)) (ELFHDR->e_entry))();
BootLoader和内核有关于地址的区别:在boot loader运行时还没有任何的分段处理机制,或分页处理机制,所以boot loader可执行程序中的链接地址就应该等于加载地址。BIOS默认把boot loader加载到0x7C00内存地址处,所以就要求boot loader的链接地址也要在0x7C00处。
内核的加载地址和链接地址是不同的。
================================================================================================
Exercise 6
在这个练习中,我们将尝试使用GDB的x命令(查看内存命令)。 x/Nx ADDR。这个指令将打印出从ADDR地址开始之后的N个字的内容。重启一下Qemu。在Bios进入boot loader之前,内存地址0x00100000处8个字的内容,然后进入boot loader运行到内核开始处停止,再看下这个地址处的值。为什么二者不同?第二次这个内存处所存放的值的含义是什么?
实验过程如图示:
0x7d6b是call entry的地址,是进入内核前执行的最后一条指令,因此将断点设置在这里。0x00100000之后的8个字前后不一样应该是由于bootmain将内核的某个section放在了这个地址。
因为内核的入口地址是0x0010000c,在这个地址范围内,所以在这个内存存放的内容可能是.text段,因为内核最先加载.text段的内容。
================================================================================================
到此,LAB1的part2部分就完成了。