为什么 ELF 可执行文件有一个固定的加载地址?

Hey*_*ude 5 linker compilation elf

ELF 可执行文件有一个固定的加载地址(32 位 x86 Linux 二进制文件为 0x804800,64 位 x86_64 二进制文件为 0x40000)。

我阅读了关于这些特定地址的历史原因的 SO 答案(例如,this one)。我仍然不明白的是为什么要使用固定的加载地址而不是随机的地址(给定一些范围内要随机化)?

Emp*_*ian 9

为什么使用固定加载地址而不是随机加载地址

  1. 传统上,这就是可执行文件的工作方式。如果您想要一个随机加载地址,请使用标志构建一个PIE二进制文件(这实际上是具有启动代码的共享库的一种特殊情况)-fPIE并使用-pie标志进行链接。
  2. Build with 会-fPIE引入运行时开销,在某些情况下性能下降高达 10%,如果您有一个大型集群或者您需要每一点性能,这可能是无法容忍的。


DrP*_*tay 5

不确定我是否正确理解了你的问题,但说我理解了,这是一个“遗留”/历史问题,ELF 是 UNIX 派生操作系统使用的文件格式,包括 POSIX (IOS) 和类 Unix (Linux) 。

elf 格式只是表明必须有一些已解析的绝对虚拟地址,代码将加载到该地址并开始运行......这就是文件格式,并且由于历史原因而无法更改......你不能只是将可执行文件“扔”到任何内存地址并让它成功运行,早在 90 年代,当 ELF 格式被引入时,我们提出了诸如使用虚拟表调用函数之类的问题,并决定使用 elf 格式其中会有绝对地址。

还要考虑一下,看看 elf 格式 - https://en.wikipedia.org/wiki/Executable_and_Linkable_Format 你将如何设计一个能够处理可执行文件的操作系统可执行加载程序,将其加载到任何所需的虚拟地址并让代码成功运行,而实际上不必更改二进制文件本身...如果您想做类似的事情,您要么需要极大地更改编译器生成的输出或格式本身,但这又是不可能的

随着时间的推移,位置独立执行(PIE/PIC)的要求已经提出,我们引入了共享对象,以便实现这一点,以及 ASLR(地址空间布局随机化)——这意味着代码可以被扔到任何内存地址中,并且仍然能够执行,这只是通过确保代码本身内的所有调用都相对于所执行指令的当前地址来实现的,并且当加载共享对象时,操作系统加载器必须更改共享对象中的一些数据二进制,因为更改的数据不是可执行指令(RE)而是实际数据(RW,例如.data段),这也是通过调用某些“跳转表”(在加载时会更改)中的函数来实现的,例如PLT / GOT ....这些共享对象允许代码加载到的地址绝对随机化,如果您想执行一些更“安全”的代码,您必须将其编译为共享对象并动态链接它并加载时间或运行时间..

(希望我已经澄清了一些事情:))