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

Why ELF executables have a fixed load address?

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

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

why to use a fixed load address and not a randomized one

  1. 传统上这就是可执行文件的工作方式。如果你想要一个随机加载地址,用 -fPIE 和 link 和 -pie 旗帜。
  2. 使用 -fPIE 进行构建会引入运行时开销,在某些情况下会导致性能下降 10%,如果您拥有大型集群或需要每一点性能,这可能是无法容忍的。

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

而 elf 格式简单地声明必须有一些已解析的绝对虚拟地址,代码将加载到该地址并从 运行ning 开始... 简单地说,文件格式就是这样,并且由于无法更改的历史原因...您不能只 "throw" 任何内存地址中的可执行文件并使其 运行 成功,回到90 年代,当 ELF 格式被引入时,我们提出了使用虚拟表调用函数等问题,并决定 elf 格式将在其中包含绝对地址。

也想想,看看elf格式-https://en.wikipedia.org/wiki/Executable_and_Linkable_Format 你将如何设计一个 OS 可执行加载器,它能够处理一个可执行文件,将其加载到任何所需的虚拟地址,并成功地获得代码 运行 而无需实际更改二进制文件本身......如果你想做这样的事情你要么需要大量改变编译器生成的输出或格式本身,这又是不可能的

随着时间的流逝,位置独立执行 (PIE/PIC) 的要求已经提出并共享了我们引入的对象,以允许这一点和 ASLR (Address Space Layout Randomization) - 这意味着代码可以被抛入任何内存地址并且仍然能够执行,这通过确保代码本身中的所有调用都相对于当前地址来简单地实现执行的指令,并且当加载共享对象时,OS 加载程序将不得不更改二进制文件中的一些数据,因为更改的数据不是可执行指令(R E)而是实际数据(RW,例如 .data段),这也是通过从某些 "Jump tables" (将在加载时更改)调用函数来实现的,例如 PLT / GOT ....这些共享对象允许代码加载到的地址的绝对随机化和如果您想执行更多 "secure" 代码,您必须将其编译为共享对象并动态地 link 它和加载时间或 运行 时间..

(希望我已经清除了一些东西:))