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

sta*_*lit 2 linux binary gcc elf

我知道两者之间的关系:

虚拟地址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
Run Code Online (Sandbox Code Playgroud)

我们可以看到2个LOAD段.

第一个LOAD的虚拟地址以0x8048154结束,而第二个LOAD从0x8049154开始.

在ELF文件中,第二个LOAD紧跟在第一个LOAD后面,文件偏移量为0x00154,但是当这个ELF加载到内存中时,它在第一个LOAD段结束后的0x1000字节处开始.

但为什么?如果我们必须考虑内存页面对齐,为什么第二个LOAD段不是从0x80489000开始?为什么它在第一个LOAD段结束后的0x1000字节处开始?

我知道第二个LOAD的虚拟地址满足关系:

虚拟地址mod页面对齐==文件偏移mod页面对齐

但我不知道为什么必须满足这种关系.

Emp*_*ian 7

为什么它在第一个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)
Run Code Online (Sandbox Code Playgroud)

如果您尝试使虚拟地址或偏移量非页面对齐,mmap将失败EINVAL.使文件数据在虚拟内存中出现在所需地址的唯一方法是使其 VirtAddrOffset模数一致Align,这正是静态链接器的作用.

请注意,对于这样小的LOAD第一段,整个第一段也出现在第二个映射的开头(具有错误的保护).但该程序不应该访问该[0x08049000,0x08049154)范围内的任何内容.通常,在第二段中实际数据开始之前几乎总是存在一些"垃圾" LOAD(除非你真的很幸运并且LOAD第一段在页面边界上结束).

另请参见mmap手册页.