在为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 )
所有都是相对跳转,其中每个编码(操作+操作数)的大小位于第三列。
现在我的原始设置(因此故障)设计为每条指令保留了最大(5个字节)空间。操作数尚不清楚,因为它跳转到一个未知的位置。所以我实现了一个“重写”机制,如果已知跳转的位置,则将操作数重写在内存中的正确位置,并用NOP
填充其余部分。这在紧密循环中有点严重。
现在我的问题出在以下情况:
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
处操作数的b
和d
之间的相对距离。这同样适用于JMP
c
:它需要知道d
的大小才能计算e
和a
之间的相对距离。< / p>
现有的汇编程序如何解决此问题,或者您将如何解决此问题?
这就是我在想的解决问题的方法:
首先将所有指令编码到
JMP
及其目标之间的操作码,如果此区域包含可变大小的操作码,则使用最大大小,例如5
JMP
。然后通过选择最小可能的编码大小(3,4或5)将相对JMP
编码到其目标,并计算距离。如果对任何可变大小的操作码进行编码,则更改之前的所有绝对操作数,以及跳过此编码指令的所有相关指令:当操作数更改为选择最小可能大小时,将对其进行重新编码。保证此方法结束,因为可变大小的操作码只能缩小(因为它使用了它们的最大大小)。
我想知道,也许这是一个过度设计的解决方案,这就是我提出这个问题的原因。
答案 0 :(得分:3)
在第一遍中,您将非常接近使用对所有跳转指令使用悲观字节计数的jmp
代码。
在第二遍中,您可以使用选择的悲观操作码填充跳转。然后可以重写很少的跳转以使用一个或两个以下的字节,只有那些非常接近8/16位或16/32字节跳转阈值的跳转。由于候选者都是许多字节的跳跃,因此它们不太可能处于关键循环情况中,因此您可能会发现进一步的传递比两次传递解决方案几乎没有任何好处。
答案 1 :(得分:1)
这是我使用过的一种看似效率低下的方法,但结果却不适用于大多数现实代码(伪代码):
IP := 0;
do
{
done = true;
while (IP < length)
{
if Instr[IP] is jump
if backwards
{ Target known
Encode short/long as needed }
else
{ Target unknown
if (!marked as needing long encoding) // see below
Encode short
Record location for fixup }
IP++;
}
foreach Fixup do
if Jump > short
Mark Jump location as requiring long encoding
PC := FixupLocation; // restart at instruction that needs size change
done = false;
break; // out of foreach fixup
else
encode jump
} while (!done);