【问题标题】:Shorter x86 call instruction更短的 x86 调用指令
【发布时间】:2018-09-16 22:54:38
【问题描述】:

对于上下文,我是 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 字节指令。

【问题讨论】:

  • 当前 x86 指令集中没有 2 字节 call 等价于 jmp。替代方案(弄乱堆栈)会更长或更长。
  • @usr2564301 我可以将我的标签地址移动到寄存器中并使用FF 调用吗?
  • 一次性使用也不会更短。您不能在call reg 中编码相对 地址,因此加载寄存器本身将以完全相同的长度开始——然后您需要调用它。如果这种跳跃发生得更多,它可能会在第 5 次或第 6 次调用左右时开始获得回报。
  • 是的,如果您可以在寄存器中生成少于 3 个字节的完整地址...即使是相对于 RIP 的 LEA 也无济于事,因为它仅以 rel32 形式存在,而不是rel8。大多数操作系统不允许您在最低页面中映射任何内容(因此 NULL 指针取消引用错误),因此可用地址大于 16 位,在 16 位模式之外。
  • @PeterCordes 我多次使用这个调用,所以即使将地址放入寄存器需要几个字节,我们也会整体保存。您可以发布答案吗?

标签: assembly x86 call micro-optimization machine-code


【解决方案1】:

没有call rel8,也没有任何方法可以在少于 5 个字节内推送返回地址和jmp

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

对于单个call,显然不值得。 如果您可以为多个 2 字节 call reg 指令重复使用相同的函数指针寄存器,那么即使只有 2 个 calls 也能领先(5 个字节 mov reg, imm32 加上 2 个 2 字节call reg 共有 9 个字节,而 2x 5 字节 call 则为 10 个字节。但它确实需要您注册。


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

在 32 位/64 位代码中,我会考虑将您的代码映射到零页中所必需的链接器选项作为代码高尔夫答案的字节数的一部分。 (And also the /proc/sys/vm/mmap_min_addr kernel setting,或其他操作系统上的等效项)


如果可以的话,通常避免在代码高尔夫中使用call。通常最好构造循环以避免需要代码重用。例如jmp 进入循环中间,让循环的一部分运行正确的次数,而不是多次调用一个块。

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

【讨论】:

  • 我认为mov/lea 标签到注册是我会去的(我不会使用call 进行单个呼叫)。我也很喜欢循环的想法。
  • @qwr:有趣的技巧:您可以跳过循环的前 4 个指令字节,使用 1 个字节而不是 2 个字节 jmp。使用db 0x3D,循环顶部cmp eax, imm32 的操作码。在循环进入时,它将立即消耗 4 个字节。但是当你分支回到循环的顶部时,那是在立即数字节内,所以它们作为指令运行。
  • 我认为我还没有准备好将立即数作为代码执行......!虽然它可能是一种重要的安全技术。
猜你喜欢
  • 2012-01-15
  • 2011-10-27
  • 1970-01-01
  • 2021-11-13
  • 2012-02-27
  • 1970-01-01
  • 2015-10-27
  • 2018-03-12
相关资源
最近更新 更多