【问题标题】:Does a linker generate absolute virtual addresses when linking链接器在链接时是否生成绝对虚拟地址
【发布时间】:2019-08-07 12:48:12
【问题描述】:

假设C 中有一个简单的hello world,使用gcc -c 编译为目标文件并使用objdump 反汇编,如下所示:

_main:
       0:   55  pushq   %rbp
       1:   48 89 e5    movq    %rsp, %rbp
       4:   c7 45 fc 00 00 00 00    movl    $0, -4(%rbp)
       b:   c7 45 f8 05 00 00 00    movl    $5, -8(%rbp)
      12:   8b 05 00 00 00 00   movl    (%rip), %eax

你可以看到内存地址是0, 1, 4, ..等等。它们不是实际地址。

链接目标文件并反汇编如下所示:

_main:
100000f90:  55  pushq   %rbp
100000f91:  48 89 e5    movq    %rsp, %rbp
100000f94:  c7 45 fc 00 00 00 00    movl    $0, -4(%rbp)
100000f9b:  c7 45 f8 05 00 00 00    movl    $5, -8(%rbp)
100000fa2:  8b 05 58 00 00 00   movl    88(%rip), %eax

我的问题是,100000f90 是虚拟内存字节的实际地址还是偏移量?

链接器如何在执行之前给出实际地址?如果执行时该内存地址不可用怎么办?如果我在另一台内存少得多的机器上执行它会怎样(可能在这里开始分页)。

分配实际地址不是加载器的工作吗?

链接器是否为最终的可执行文件生成实际地址?

【问题讨论】:

  • 是虚拟内存。无论您拥有多少物理内存,它始终在给定架构上可用。可能有特殊情况,例如如果您调整您的 32 位操作系统以使用 3G/1G 拆分,那么理论上您可以将您的东西放在普通操作系统无法加载的 2G 以上。 x86-64 也有不同的 VM 大小,但有已知的安全范围。
  • 是的,在位置依赖可执行文件中,静态代码/数据地址是链接时常量。请参阅32-bit absolute addresses no longer allowed in x86-64 Linux? 了解更多关于 PIE 可执行文件的信息,其中不是,需要使用 RIP 相对寻址的位置无关代码,即使将地址放入寄存器,而不是 5 字节 mov $symbol, %edi .
  • 这是 OS X,对吧?链接器选择了低 2GiB 虚拟地址空间之外的基地址,但看起来有 是默认加载地址。
  • 链接器链接到它被告知的地址。由您直接或间接进行。如果在您的计算机上使用非交叉编译器,并且编译器可以工作,那么这就是您的可执行文件的地址空间,这可能是虚拟的,因为这是在操作系统上运行的应用程序,是吗?链接器并不聪明,就像编译器一样,他们做你告诉他们的事情,他们非常愚蠢。
  • otool -lV 应用于您的可执行文件可能很有启发性。这将显示链接器加载命令。代码在__TEXT 段中。加载命令为段指定一个“加载(虚拟)地址”。这可能会受到链接命令选项的影响或留给链接器默认值。对于位置无关的可执行文件,加载器可以加载到不同的地址;否则,它将加载到指定的地址。

标签: macos assembly linker executable virtual-memory


【解决方案1】:

(以下答案假定链接器没有创建与位置无关的可执行文件。)

我的问题是,100000f90 是虚拟内存字节的实际地址还是偏移量?

这是实际的虚拟地址。严格来说,它是从代码段基址的偏移量,但由于现代操作系统总是将代码段的基址设置为 0,因此它实际上是实际的虚拟地址。

链接器如何在执行之前给出实际地址?如果执行时该内存地址不可用怎么办?如果我在另一台内存少得多的机器上执行它会怎样(可能在这里开始分页)。

每个进程都有自己独立的虚拟地址空间。因为它是虚拟内存,所以机器中的物理内存量无关紧要。分页是虚拟地址映射到物理地址的过程。

分配实际地址不是加载器的工作吗?

是的,在创建进程时,操作系统加载程序会为进程分配物理页框,并将页映射到进程的虚拟地址空间。但虚拟地址是由链接器分配的。

【讨论】:

  • 回复the virtual addresses are those assigned by the linker:代码可能是可重定位的,因此链接器生成的虚拟地址可能会在加载时发生额外的变化。
  • @Alexey,我在第一句话中就提到了这一点。 (除非你要区分可重定位和位置无关;我没有——它们是实现相同结果的不同技术。)
  • x86-64 长模式将 CS/DS/ES/SS 基数固定为 0,因此操作系统甚至别无选择。 IDK 如果指出 x86 仍然将其视为偏移量是有用的。
【解决方案2】:

链接器在链接时是否生成绝对虚拟地址

这取决于链接器设置和输入源。对于一般编程,链接器通常会努力创建与位置无关的代码。

我的问题是,100000f90 是虚拟内存字节的实际地址还是偏移量?

很可能是偏移量。

链接器如何在执行前给出实际地址?

想想操作系统的加载器。它期望事物位于特定的地址位置。任何体面的链接器都将允许程序员以某种方式指定绝对地址。

如果执行时该内存地址不可用怎么办?如果我在另一台内存少得多的机器上执行它会怎样(可能在这里开始分页)。

这就是位置相关代码的问题。

分配实际地址不是加载器的工作吗?

加载器的工作是遵循可执行文件中给它的指令。在创建可执行文件时,链接器可以指定地址或在某些情况下推迟到加载器。

【讨论】:

  • 这不是一个偏移量,除了技术意义上的“相对于固定为 0 的段基数”。如果它只是一个偏移量,它会比 ~4GB 小很多。此外,x86-64 的所有主流操作系统都使用虚拟内存,所以不,位置相关的可执行文件不是问题。在 Linux 上,这是 ld 的默认值,直到大约一年前,大多数发行版上的 gcc 还是默认值。 (现在 PIE 可执行文件是默认的,将可执行文件放入动态链接器知道如何加载的 ELF 共享对象中。)
  • 您的最后一段是正确的,但是:在某些类型的可执行文件中,链接器可以选择是否设置固定的绝对地址。在其他类型中,例如共享库,不允许使用不可重定位的绝对地址。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
  • 2023-03-21
  • 2011-02-21
  • 2021-01-07
  • 2012-04-02
  • 2011-12-11
相关资源
最近更新 更多