#define ELFHDR ((struct elfhdr *)0x10000)
void
bootmain(void) {
// read the 1st page off disk
readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0);
// is this a valid ELF?
if (ELFHDR->e_magic != ELF_MAGIC) {
goto bad;
}
struct proghdr *ph, *eph;
// load each program segment (ignores ph flags)
ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff);
eph = ph + ELFHDR->e_phnum;
for (; ph < eph; ph ++) {
readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
}
// call the entry point from the ELF header
// note: does not return
//
((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();
bad:
outw(0x8A00, 0x8A00);
outw(0x8A00, 0x8E00);
/* do nothing */
while (1);
}
注意上边的两个0xFFFFFF,都是用于地址转换的,再找到kernel.ld的如下代码,可以看出,我们给程序的位置是ELFHDR,这是内核被加载到的物理地址,但是其线性地址却是0xC0100000,注意,不同的lab这个地址会不同,因此,程序入口也是一个0xC0100000+一定的偏移的值off,此时,我们的临时gdt转换完的地址仍是原值(0xC0100000+off),这样的物理地址是不对的,
与上一个0xFFFFFF就是正确的物理地址了,因为我们只是为了进入32位,临时建立了一个全局描述符表,这个描述符表的内容为了方便瞎整的。
SECTIONS {
/* Load the kernel at this address: "." means the current address */
. = 0xC0100000;
# Bootstrap GDT
.p2align 2 # force 4 byte alignment
gdt:
SEG_NULLASM # null seg
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel
gdtdesc:
.word 0x17 # sizeof(gdt) - 1
.long gdt # address gdt
要问我为什么不一开始就整个正确的,我也不知道为啥,反正就写成这样了,
.align 4
__gdt:
SEG_NULL
SEG_ASM(STA_X | STA_R, - KERNBASE, 0xFFFFFFFF) # code segment
SEG_ASM(STA_W, - KERNBASE, 0xFFFFFFFF) # data segment
__gdtdesc:
.word 0x17 # sizeof(__gdt) - 1
.long REALLOC(__gdt)