ELF程序头的虚拟地址和文件偏移

9

我知道这两者之间的关系:

虚拟地址除以分页对齐数 == 文件偏移量除以分页对齐数

但是有人能告诉我这两个数字是按照哪个方向计算的吗?

是根据上述关系从文件偏移计算虚拟地址,还是反过来?

更新

以下是更多细节:链接器写入ELF文件头时,设置程序头(段)的虚拟地址和文件偏移量。

例如,这是readelf -l someELFfile的输出:

Elf file type is EXEC (Executable file)
Entry point 0x8048094
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x00154 0x00154 R E 0x1000
  LOAD           0x000154 0x08049154 0x08049154 0x00004 0x00004 RW  0x1000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10

我们可以看到有2个LOAD段。
第一个LOAD的虚拟地址以0x8048154结尾,而第二个LOAD从0x8049154开始。
在ELF文件中,第二个LOAD紧随第一个LOAD的后面,文件偏移量为0x00154。但是当这个ELF被加载到内存中时,它会从第一个LOAD段结束后的0x1000字节处开始。
但是,为什么呢?如果我们考虑内存页面对齐,为什么第二个LOAD段不从0x80489000开始呢?为什么它要从第一个LOAD段结束后的0x1000个字节处开始?
我知道第二个LOAD的虚拟地址满足以下关系:
虚拟地址mod页面对齐==文件偏移mod页面对齐
但我不知道为什么一定要满足这个关系。

不太明白这里的问题。最终它必须读取文件内容,所以我想它是从虚拟地址到文件偏移量。你可以使用readelf/objdump/dd等工具来玩弄elf文件并转储其内容。 - Dyno Fu
这两个数字是由谁计算的?它们是朝着哪个方向计算的? - Employed Russian
由链接器在写入头文件时计算。 - user2404501
2个回答

14
为什么它要从第一个 LOAD 段结束后的 0x1000 字节开始?如果不是这样,它就必须从 0x08048154 开始,但它不能这样做:两个 LOAD 段为其映射指定了不同的标志(第一个以 PROT_READ|PROT_EXEC 映射,第二个以 PROT_READ|PROTO_WRITE 映射)。保护(作为页面表的一部分)只能应用于整个页面,而不是页面的部分。因此,具有不同保护的映射必须属于不同的页面。
虚拟地址模页对齐 == 文件偏移模页对齐,但我不知道为什么必须满足这种关系。LOAD 段直接从文件中 mmap。执行示例时,第二个 LOAD 段的实际映射将类似于以下内容(您可以在 strace 下运行程序并查看它):
mmap(0x08049000, 0x158, PROT_READ|PROT_WRITE, MAP_PRIVATE, $fd, 0)
如果您试图使虚拟地址或偏移量非页面对齐,mmap将失败并显示EINVAL。唯一的方法是使文件数据出现在虚拟内存中所需的地址上,就是让VirtAddr模除Align等于Offset,这正是静态链接器所做的事情。
请注意,对于这样一个很小的第一个LOAD段,在第二个映射的开头也会出现整个第一个段(具有错误的保护)。但程序不应该访问[0x08049000,0x08049154)范围内的任何内容。通常情况下,第二个LOAD段中实际数据开始之前会有一些“垃圾”(除非第一个LOAD段恰好结束于页面边界)。
另请参见mmap手册页

0
虚拟地址模页面对齐 == 文件偏移量模页面对齐
但是有人能告诉我这两个数字是如何计算的吗?
我认为虚拟地址被故意设置成这样以遵循文件偏移量。文件本身应该是紧凑的,因此所有段都存储在一起,其边界记录在ELF头中。
虚拟地址模页面对齐 == 文件偏移量模页面对齐
但我不知道为什么必须满足这种关系。
它不需要,第二个段可以映射到0x08049000而没有任何问题。只要具有不同标志的段被映射到不同的虚拟页面,就没问题了。但是当加载结果为ELF可执行文件时,操作系统必须分配另一个物理页面(通常为4 KB)用于映射并将文件偏移量0x154处的4个字节复制到页面的开头,这有点浪费。
如果关系得到满足,操作系统可以分配一个单一的物理页面,并将整个0x158(0x154 + 0x4)字节的文件复制到该页面,并使用不同的标志将物理页面映射到0x08048000和0x08049000。这样可以节省物理内存,并使得像需求分页这样的虚拟内存技术更容易应用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接