如何解决XV6:bootmain-加载内核ELF标头
我一直在分析xv6内核中的bootmain.c代码:
void
bootmain(void)
{
struct elfhdr *elf;
struct proghdr *ph,*eph;
void (*entry)(void);
uchar* pa;
elf = (struct elfhdr*)0x10000; // scratch space
// Read 1st page off disk
readseg((uchar*)elf,4096,0);
// Is this an ELF executable?
if(elf->magic != ELF_MAGIC)
return; // let bootasm.S handle error
// Load each program segment (ignores ph flags).
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
eph = ph + elf->phnum;
for(; ph < eph; ph++){
pa = (uchar*)ph->paddr;
readseg(pa,ph->filesz,ph->off);
if(ph->memsz > ph->filesz)
stosb(pa + ph->filesz,ph->memsz - ph->filesz);
}
// Call the entry point from the ELF header.
// Does not return!
entry = (void(*)(void))(elf->entry);
entry();
}
我了解以下一行
readseg((uchar*)elf,0);
正试图将elf头文件从磁盘复制到 elf 地址处的内存,但我不明白为什么要复制4kb 标头本身是52个字节。
运行readelf -h内核后,我得到以下有关elf标头的信息:
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
(如果我的数学对我有帮助),这意味着elf标头+程序标头表占用的字节数不超过148个字节。
解决方法
您显示的代码是xv6 bootloader的 C 代码。
直到至少读取ELF头文件之前,程序头文件的数量是未知的。而不是从磁盘读取两次(一次用于ELF头,然后一次用于程序头),它们只是读取整个第一页(4KiB)。他们假设合并的程序头和ELF头不超过4 KiB。
最好加载ELF标头;确定程序头的大小并将其读入内存;然后读入与每个程序头相关的段。如果ELF标头和所有其他标头以某种方式超过4 KiB,则此代码将中断。
在阅读了代码和一些xv6文档之后,看来它们简化了加载内核ELF文件的过程,从而使生成的代码可以适合512字节的引导扇区。一次读取数据可能是一项设计决定,但老实说,他们可能并不希望内核本身具有大量的标头。只需阅读第一个4KiB,便非常简单。
第102页上的xv6 documentation可能是一个有趣的事实,它描述了必须简化引导程序代码(和ELF加载程序)以使其适合512字节引导扇区的事实:
本附录中描述的引导加载程序可编译为大约470字节的机器代码,具体取决于编译C代码时使用的优化。 为了适应较小的空间,xv6引导加载程序做了一个简化的重要假设,即内核已从扇区1开始连续写入引导磁盘。更常见的是,存储内核在普通文件系统中,它们可能不是连续的,或者是通过网络加载的。这些复杂性要求引导加载程序能够驱动各种磁盘和网络控制器并了解各种文件系统和网络协议。换句话说,引导加载程序本身必须是小型操作系统。由于如此复杂的引导加载程序肯定无法容纳512字节,因此大多数PC操作系统使用两步引导过程。首先,一个简单的引导加载程序(如本附录中的引导加载程序)从已知的磁盘位置加载功能齐全的引导加载程序,通常依赖空间较少的BIOS进行磁盘访问,而不是尝试驱动磁盘本身。然后,完整的加载器可以摆脱512字节的限制,可以实现定位,加载和执行所需内核所需的复杂性。
我认为可以得出一个简单的假设,即有关引导加载程序中ELF加载程序的设计的假设。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。