对于上下文,我是x86高尔夫。
00000005 <start>:
5: e8 25 00 00 00 call 2f <cube>
a: 50 push %eax
后来打了很多电话...
0000002f <cube>:
2f: 89 c8 mov %ecx,%eax
31: f7 e9 imul %ecx
33: f7 e9 imul %ecx
35: c3 ret
call
占用了5个字节,即使偏移量适合单个字节!有没有办法编写call cube
并使用GNU汇编器进行汇编,从而获得更小的偏移量?我知道可以使用16位偏移量,但理想情况下,我会使用像call reg
这样的2字节指令。
1条答案
按热度按时间iecba09b1#
没有
call rel8
,也没有任何方法可以将返回地址和jmp
压入少于5个字节。要想领先
call reg
,需要在寄存器中生成少于3个字节的完整地址,即使是RIP相关的莱亚也无济于事,因为它只以rel32
的形式存在,而不是rel8
。对于一个单一的
call
,显然不值得。如果您可以为多个2字节
call reg
指令重用同一个函数指针寄存器,那么即使只使用2个call
指令,您也会取得成功。(5字节mov reg, imm32
加上2个2字节call reg
总共是9字节,而2个5字节call
总共是10字节)。但这确实会花费您一个寄存器。大多数操作系统不允许你在最低页Map任何东西(所以NULL指针deref错误),所以在32或64位模式下可用地址大于16位。
66 E8 rel16
(4字节callw
)即使在32位模式下也不是一个选项;这会将EIP截短为IP.https://www.felixcloutier.com/x86/call在32位/ 64位代码中,我会考虑将代码Map到零页所需的链接器选项作为代码高尔夫答案的字节计数的一部分。(And also the
/proc/sys/vm/mmap_min_addr
kernel setting,或其他操作系统上的等效项)通常我们认为在代码高尔夫中根本不计算ELF元数据是合理的,只计算.text
部分的字节,因此特殊的链接器技巧会在那里打开一个蠕虫罐头。如果可以的话,一般避免在代码高尔夫中使用
call
。通常最好是构造循环以避免代码重用。例如,将jmp
插入循环中间,以使循环的一部分运行正确的次数,而不是多次调用一个块。我想我通常会看代码高尔夫问题,这些问题很自然地适合机器代码,并且可以避免需要来自多个地方的相同代码块。我已经可以花几个小时调整一个短函数,所以对我来说,开始回答一个需要更多代码的问题(因此在部分之间/跨部分优化的空间更大)是罕见的。