在为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教学网站)
它们都是相对跳转,其中每个编码(操作+操作数)的大小都在第三列中。
现在,我的原始设计(因此是错误的)设计为每条指令保留了最大(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 at c也是如此:它需要知道d的大小,然后才能计算e和a之间的相对距离。
现有的汇编程序如何解决这个问题,或者如何解决?
我就是这么想的,解决了这个问题:
首先对
JMP与其目标之间的所有操作码指令进行编码,如果该区域包含可变大小的操作码,则使用最大大小,例如5用于JMP。然后通过选择最小的编码大小(3、4或5)并计算距离,将相对JMP编码到它的目标。如果任何可变大小的操作码被编码,请更改之前的所有绝对操作数,以及跳过此编码指令的所有相关指令:当操作数更改以选择最小的大小时,它们将被重新编码。此方法保证结束,因为可变大小的操作码只能缩小(因为它使用它们的最大大小)。
我想知道,也许这是一个过度设计的解决方案,这就是我问这个问题的原因。
发布于 2010-05-11 22:21:32
下面是我使用过的一种方法,它看起来效率很低,但实际上并不适用于大多数现实生活代码(伪代码):
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);发布于 2010-05-11 21:40:53
在第一遍,您将有一个非常好的近似,jmp代码使用悲观字节计数所有跳转指令。
在第二次传递时,您可以使用所选的悲观操作码填充跳转。只有非常接近8/16位或16/32字节跳转阈值的跳转才能被重写为少用一两个字节。由于候选文件都是许多字节的跳转,所以它们不太可能处于关键的循环情况,因此您可能会发现,进一步的传递比两次传递解决方案几乎没有什么好处。
https://stackoverflow.com/questions/2814556
复制相似问题