分享
加载器的实现和难点
输入“/”快速插入内容
加载器的实现和难点
飞书用户4347
2024年9月3日修改
如何加载程序
loader函数实现
✅
思考:分析如何实现loader函数
1.
将要加载的程序,放入正确的内存位置。
2.
正确的内存位置在哪里? 每个segment的
[VirtAddr, VirtAddr + MemSiz)
3.
要加载的程序在哪里?
每个segment的
[offset,offset+MemSiz]
✅
resource.S的作用
以及键下
make ARCH=loongarch32r-nemu
后发生的事情
通过重定向命令cat,将在
navy-apps
下编译成功的
dummy-loongarch32r
可执行文件重定向到
nanos-lite
的(ramdisk镜像文件)ramdisk.img中。当我们在
nanos-lite
的目录下键入
make ARCH=loongarch32r-nemu
后,会生成nanos-lite 的可执行文件,编译期间会把ramdisk镜像文件包含进nanos-lite 成为nanos-lite的一部分
(
resource.S所做的工作
)
57%
43%
✅
实现loader函数
在这之前,介绍两个结构体:Elf_Ehdr(
ELF文件头
) 和 Elf_Phdr(
程序头表
)
50%
50%
✅
问题PRO:
那么我们如何通过这两个结构体获取Elf_Ehdr文件头的信息和Elf_Phdr程序头表?
ANS答案:
使用模拟ramdisk的接口:ramdisk_read(
void *buf
,
size_t offset
,
size_t len
),这个函数的本质是将[
文件偏移地址offset
,
文件偏移地址offset
+
文件的大小len
]这大段数据拷贝到buf中
而根据
hexdump
命令查看ramdisk.img我们知道ELF文件数据从0开始。所以我们只需要将0开始偏移
sizeof(Elf_Ehdr)
[文件头结构体的大小]拷贝到Elf_Ehdr这个结构体中,就能够使Elf_Ehdr结构体获取对应的数据。这像是将一大段内存拷贝过去的操作。
难点:
而获取Elf_Phdr结构体的数据也是类似,从结构体Elf_Ehdr获取程序头表的位置开始偏移(下文解释)N*
sizeof(Elf_Phdr)
[程序头表结构体的大小],从而使得Elf_Phdr这个结构体获取到对应的数据,而不同于文件头表只有一个,而程序头表有多个,需要根据文件头表中的
e_phnum
(程序头部表中的条目数量)决定要偏移的长度
e_phnum
*
sizeof(Elf_Phdr)
, 而实例化这个Elf_Phdr结构体的时候需要实例化成结构体数组,这样才能记录好每个程序头的信息,避免覆盖。
✅
将相应的数据获取进来,之后的操作就相对简单。通过判断属性是否是LOAD,如果是就将程序头表中的
[offset,offset+MemSiz]
的数据写到
[VirtAddr, VirtAddr + MemSiz)
这段内存中,程序就能正确加载了。然后为了安全性和正确的内存分配。就是对
[VirtAddr + FileSiz, VirtAddr + MemSiz)
对应的物理区间清零.
✅
然后返回到程序的入口(
Elf_Ehdr文件头中记录
)