为什么我的反汇编C++代码要使用指令指针和偏移量来获取字符串字面值?

10

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

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

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

编译器为什么要使用指令指针来获取字符串字面值?这似乎会给任何人类程序员带来很大的麻烦,尽管对于编译器来说可能不是那么难。

然而,我的问题是——为什么呢? 是有某些基于机器或历史原因,还是编译器作者只是随意使用了 %rip


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

9

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


我知道这一点,但我的问题是为什么代码使用%rip寄存器?为什么不能使用另一个寄存器(%rax%rbx等)或者只是使用一个固定地址? - Dovahkiin
3
“位置无关代码”。如果使用一个固定地址,那么动态加载器在将库加载到非首选基址时将不得不更改(“修复”)所有“常量”地址,这也会使代码段不可共享。此外,相对地址往往比完整指针更小。 - Ben Voigt
2
@Dovahkiin 因为编译器可能不知道代码段实际加载的地址。这可能是操作系统为了挫败攻击而采取的措施,或者是因为代码位于动态加载的库中。 - Some programmer dude
@Dovahkiin 因为在编译期间,字符串字面量被放置在代码段的某个位置,编译器只知道这些字符串距离当前指令或该段落的起始位置有多远,而不知道它们的绝对地址。 - riodoro1
1
有关为什么对于静态存储(.rodata和.data)使用RIP相对定位的更多信息,请参见为什么这个MOVSS指令使用RIP相对寻址? / 为什么静态变量的地址与指令指针相关? - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接