【问题标题】:Why does my disassembled C++ code use the instruction pointer and an offset to get string literals?为什么我的反汇编 C++ 代码使用指令指针和偏移量来获取字符串文字?
【发布时间】:2017-05-31 15:09:06
【问题描述】:

我有一个我已经反汇编的 C++ 程序,看起来程序集正在使用指令指针来获取字符串文字。例如:

leaq    0x15468(%rip), %rsi ## literal pool for: "special"

leaq    0x15457(%rip), %rsi ## literal pool for: "ordinary"

为什么编译器使用指令指针来获取字符串字面量?这似乎会让任何人类程序员都非常头疼,尽管对编译器来说可能并不那么难。

不过,我的问题是 为什么? 是否存在基于机器或历史原因的原因,还是编译器编写者只是随意决定使用 %rip

【问题讨论】:

  • 它允许您通过使引用相对于指令指针而不是固定的内存地址来创建与位置无关的代码。
  • 但这只是 amd64 RIP 相对寻址。 x64 中的大多数指令都使用它。在这种情况下,有效地址是通过将位移添加到下一条指令的 64 位 RIP 中形成的。在您的示例中 0x154680x15457 这是位移。您的反汇编程序以这种形式向您显示说明。另一个disasm可以以另一种形式显示相同的指令-显示计算出的有效地址而不是位移-但这只是不同形式的可视化。
  • 这种形式可以节省 4 个字节 - 如果我们在 x64 长模式下使用绝对地址作为有效地址 - 我们需要 8 个字节(64 位)。但是使用 rip-addressing 我们只使用 4 字节(32 位)有符号偏移量来 rip - 所以我们可以评估 [rip-0x80000000, rip+0x7fffffff] 内存范围。这为我们节省了 4 个字节的指令
  • 因为您的文字字符串位于您的二进制文件中,并且如果您的二进制文件在内存中小于 2GB - 文字将在[rip-0x80000000, rip+0x7fffffff] 范围内

标签: c++ assembly x86-64


【解决方案1】:

请记住,C++ 中的字符串文字是 常量 且不可修改的。确保这一点的一种方法是将它们与代码段中的代码放在一起,该代码段被加载到标记为只读的内存页面中。

【讨论】:

  • 我知道,但我的问题是为什么代码使用%rip 寄存器?为什么它不能使用另一个寄存器(%rax%rbx 等)或者只使用一个常量地址?
  • @Dovahkiin:“位置无关代码”。如果它使用常量地址,那么动态加载器在将库加载到非首选基地址时,将不得不更改(“修复”)所有“常量”地址,这也使得代码段不共享。此外,相对地址往往小于完整指针。
  • @Dovahkiin 因为编译器可能不知道代码段实际加载的确切地址。这可能是操作系统试图阻止攻击的一部分,或者因为代码位于动态加载的库中。
  • @Dovahkiin 因为在编译过程中,字符串文字被放置在代码部分的某处,编译器只知道这些字符串与当前指令或本节开头的距离,而不是它们的绝对地址。
猜你喜欢
  • 1970-01-01
  • 2021-12-23
  • 2013-01-26
  • 2012-08-25
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多