可以使用如下命令观看 elf 文件的信息
readelf -a build/ramdisk.img | vim -
在编写 elf loader 的时候,实际上只有下图这一部分 “Program Headers” 是有用的
凡是类型为 “LOAD” 的就是需要加载进内存的部分
所以,只要把这些部分加载进内存里,再跳转到 entrypoint 就完事了,是不是很容易啊?
如下,使用 PA3 中的 elf loader 代码:
static uintptr_t loader(PCB *pcb, const char *filename) {
// 1. read program from ramdisk to mem -- invoke ramdisk_read()
// 2. execute the program -- return the entry
extern size_t ramdisk_read(void *buf, size_t offset, size_t len);
Elf_Ehdr elfheader;
// 从磁盘/elf文件中读取 elf 头,这部分元数据放在 elf 文件的头部
ramdisk_read(&elfheader, 0, sizeof(Elf_Ehdr));
// 检查这个 elf 文件针对的机器架构是否是我们这种架构
assert(elfheader.e_machine == EXPECT_TYPE);
// 检查 elf 魔数
assert(*(uint64_t *)(elfheader.e_ident) == 0x00010102464c457f);
// Elf_Phdr 的格式和大小刚好是 ELF Program Headers 中的一节
Elf_Phdr program_header;
// 从磁盘/elf文件中 读取第一个 program_header
ramdisk_read(&program_header, elfheader.e_phoff, sizeof(Elf_Phdr));
// 进入循环,这个循环会遍历所有的 program_header
for(int i = 1; i < elfheader.e_phnum; i++) {
// 如果当前这节 program_header 的类型是 LOAD,那么把它装载进内存
// 然后再把 program_header.memsize - program_header.filesize 这部分清零
if(program_header.p_type == PT_LOAD) {
ramdisk_read((void *)(program_header.p_vaddr), program_header.p_offset, program_header.p_filesz);
memset((uint8_t *)(program_header.p_vaddr) + program_header.p_filesz, 0, program_header.p_memsz - program_header.p_filesz);
}
// 从磁盘/elf文件中 读取下一个 program_header
ramdisk_read(&program_header, elfheader.e_phoff + i * elfheader.e_phentsize, sizeof(Elf_Phdr));
}
// 返回程序入点
return elfheader.e_entry;
}