我知道两者之间的关系:
虚拟地址 mod 页面对齐==文件偏移 mod 页面对齐
但有人可以告诉我这两个数字的计算方向是什么?
是否根据上述关系从文件偏移量计算虚拟地址,反之亦然?
以下是一些更详细的信息:当链接器写入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 页面对齐
但我不知道为什么必须满足这种关系。
答案 0 :(得分:6)
为什么它在第一个LOAD段结束后的0x1000字节处开始?
如果没有,则必须从0x08048154
开始,但不能:两个LOAD
段为其映射指定了不同的标记(第一个用PROT_READ|PROT_EXEC
映射,第二个用PROT_READ|PROTO_WRITE
映射。保护(作为页面表的一部分)只能应用于整个页面,而不是页面的一部分。因此,具有不同保护的映射必须属于不同的页面。
虚拟地址mod页面对齐==文件偏移mod页面对齐
但我不知道为什么必须满足这种关系。
LOAD
段直接从文件mmap
编辑。为您的示例执行的第二个LOAD
细分的实际映射看起来像这样(您可以在strace
和看下运行您的程序):
mmap(0x08049000, 0x158, PROT_READ|PROT_WRITE, MAP_PRIVATE, $fd, 0)
如果您尝试将虚拟地址或偏移量设置为非页面对齐,mmap
将失败并显示EINVAL
。使文件数据在虚拟内存中出现在所需地址的唯一方法是使VirtAddr
与Offset
modulo Align
一致,这正是静态链接器的作用。
请注意,对于这么小的第一个LOAD
段,整个第一个段也出现在第二个映射的开头(带有错误的保护) 。但该程序不应该访问[0x08049000,0x08049154)
范围内的任何内容。一般情况下,在第二个LOAD
段中实际数据开始之前存在一些“垃圾”的情况几乎总是如此(除非你真的很幸运并且第一个{{1分段在页面边界上结束)。