在为x86平台构建汇编程序时,我遇到了编码 JMP
指令的一些问题:
OPCODE INSTRUCTION SIZE
EB cb JMP rel8 2
E9 cw JMP rel16 4 (because of 0x66 16-bit prefix)
E9 cd JMP rel32 5
...
(来自我最喜欢的x86指令网站,http://siyobik.info/index.php?module=x86&id=147)
所有都是 relative 跳转,其中每个编码(操作操作数)的大小在第三列中 .
现在我的原始设置(因此因故障而设计)为每条指令保留了最大(5个字节)空间 . 该操作数尚不清楚,因为它实现了一个"rewrite"机制,如果已知跳转的位置,则将操作数重写在内存中的正确位置,并用 NOP
s填充其余部分 . 在紧密循环中这是一个有点严重的问题 .
现在我的问题是以下情况:
b: XXX
c: JMP a
e: XXX
...
XXX
d: JMP b
a: XXX (where XXX is any instruction, depending
on the to-be assembled program)
问题是我想要 JMP
指令的最小可能编码(并且没有 NOP
填充) .
我必须知道 c
处指令的大小才可以计算 a
的操作数 a
和 b
之间的相对距离 . 这同样适用于 c
c
:它需要知道 d
的大小才能计算 e
和 a
之间的相对距离 .
How do existing assemblers solve this problem, or how would you do this?
这就是我在想的解决问题的方法:
首先将所有指令编码为JMP与其目标之间的操作码,如果该区域包含可变大小的操作码,则使用最大大小,例如: 5为JMP . 然后通过选择最小可能的编码大小(3,4或5)将相对JMP编码到其目标,并计算距离 . 如果对任何可变大小的操作码进行编码,则更改之前的所有绝对操作数,以及跳过此编码指令的所有相关指令:当操作数更改为选择最小可能大小时,将对其进行重新编码 . 保证此方法结束,因为可变大小的操作码只能缩小(因为它使用它们的最大大小) .
我想知道,也许这是一个过度设计的解决方案,这就是我问这个问题的原因 .
2 回答
在第一遍中,您将非常接近地使用对所有跳转指令使用悲观字节计数的
jmp
代码 .在第二遍中,您可以使用选择的悲观操作码填充跳转 . 然后可以重写很少的跳转以使用一个或两个以下的字节,只有那些非常接近8/16位或16/32字节跳转阈值的跳转 . 由于候选者都是许多字节的跳跃,因此它们不太可能处于关键循环情况中,因此您可能会发现进一步的传递在两次传递解决方案上几乎没有任何好处 .
这是我使用过的一种看起来效率低下的方法,但结果却不适用于大多数现实代码(伪代码):