Encoding 如何在汇编程序中实现相对JMP(x86)?
在为x86平台构建汇编程序时,我遇到了一些编码Encoding 如何在汇编程序中实现相对JMP(x86)?,encoding,x86,instruction-set,Encoding,X86,Instruction Set,在为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教学网站) 所有跳转都是相对跳转,其中每个编码(操作+操作数)的大小在第三列中 现在,我的原始设计(因此也是错误的)为每条指
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
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
处指令的大小,然后才能计算d
处操作数的a
和b
之间的相对距离。这同样适用于JMPatc
:它需要知道d
的大小,然后才能计算e
和a
之间的相对距离
现有的汇编程序如何解决此问题,或者您将如何解决此问题?
这就是我解决问题的想法:
首先对JMP
与其目标之间的所有操作码指令进行编码,如果此区域包含可变大小的操作码,请使用最大大小,例如JMP
的5
。然后选择可能的最小编码大小(3、4或5),将相对JMP
编码到它的目标,并计算距离。如果对任何可变大小的操作码进行编码,请更改之前的所有绝对操作数,以及跳过此编码指令的所有相对指令:当操作数更改为选择尽可能最小的大小时,它们将重新编码。这种方法保证会结束,因为可变大小的操作码只会收缩(因为它使用它们的最大大小)
我想知道,也许这是一个过度设计的解决方案,这就是我提出这个问题的原因。在第一个过程中,您将获得一个非常好的近似值,对于所有跳转指令使用悲观字节计数的jmp
代码
在第二步中,您可以使用所选的悲观操作码填充跳转。然后可以重写很少的跳转以使用一个或两个以下的字节,只有那些最初非常接近8/16位或16/32字节跳转阈值的跳转。由于候选者都是许多字节的跳转,它们不太可能处于关键循环情况,因此您可能会发现进一步的传递与两次传递解决方案相比几乎没有好处。我使用的一种方法似乎效率低下,但对于大多数实际代码(伪代码)来说却不是这样:
IP:=0;
做
{
完成=正确;
while(IP短
将跳转位置标记为需要长编码
PC:=FixupLocation;//在需要更改大小的指令处重新启动
完成=错误;
break;//退出foreach fixup
其他的
编码跳转
}而(!完成);
+1关于漂亮的asm文档链接很好的答案:但是为什么128字节的边界(8/16位之间)不太可能处于关键循环状态?我可以想象一个128字节的临界循环情况,它比使用16位跳转运行得更快。或者这是过早的优化?我所说的关键循环是指循环的测试和跳转是循环代码的重要部分。128字节对于这样一个循环来说是非常长的,最关键的循环只有几个字节,从循环中跳出的任何一个字节都可能很小!虽然,但您不知道,我的汇编程序和编译器是并行运行的,所以代码可能插入到目标和向后相对跳转之间。但是循环两通是一个非常好的方法,谢谢。(我想也是修复循环中的PC=IP吗?)实际上,我刚刚记得这有一点复杂:当你返回并调整跳转的大小时,你还必须考虑到你将要扩展并且现在不能再达到目标的位置上的任何短前跳转。
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);