更短的 x86 调用指令

Shorter x86 call instruction

就上下文而言,我正在打 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 个字节,即使偏移量适合一个字节!有什么办法可以用 GNU assembler 编写 call cube 和 assemble 并获得更小的偏移量吗?我知道可以使用 16 位偏移量,但理想情况下我会使用像 call reg 这样的 2 字节指令。

没有 call rel8,也没有任何方法可以将 return 地址和 jmp 推送到少于 5 个字节。

要在 call reg 中脱颖而出,您需要在寄存器中生成一个少于 3 个字节的完整地址。即使 RIP-relative LEA 也无济于事,因为它仅以 rel32 形式存在,而不是 rel8.

单人call,显然不值得。 如果您可以为多个 2 字节 call reg 指令重用相同的函数指针寄存器,那么即使只有 2 calls(5 字节 mov reg, imm32 加上 2x 2 字节 call reg 总共是 9 个字节,而 2x 5 字节 call 是 10 个。但它确实要花你一个寄存器。


大多数操作系统不允许您在最低页面映射任何内容(因此 NULL-pointer deref 错误),因此可用地址大于 16 位,在 16 位模式之外。

在 32 位/64 位代码中,我认为将您的代码映射到零页所必需的链接器选项是您 code-golf 答案的 byte-count 的一部分。 (And also the /proc/sys/vm/mmap_min_addr kernel setting,或其他操作系统上的等效项)


一般情况下,如果可以,请避免在 code-golf 中使用 call。通常最好构建循环以避免需要 code-reuse。例如jmp 进入循环的中间,使循环的一部分达到 运行 正确的次数,而不是多次调用一个块。

我想我通常会看 code-golf 自然适合机器代码的问题,并且可以避免从多个地方需要相同的代码块。我已经可以花几个小时来调整一个简短的函数,所以开始回答一个需要更多代码的问题(因此在它的各个部分之间/之间有更多的优化空间)对我来说是很少见的。