Pin*_*juh 8 x86 encoding 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
...
Run Code Online (Sandbox Code Playgroud)
(来自我最喜欢的x86指令网站http://siyobik.info/index.php?module=x86&id=147)
所有都是相对跳转,其中每个编码(操作+操作数)的大小在第三列中.
现在我的原始设置(因此因故障而设计)为每条指令保留了最大(5个字节)空间.操作数尚不清楚,因为它跳转到一个未知的位置.所以我实现了一个"重写"机制,如果已知跳转的位置,则将操作数重写在内存中的正确位置,并用NOPs 填充其余部分.在紧密循环中这是一个有点严重的问题.
现在我的问题是以下情况:
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)
Run Code Online (Sandbox Code Playgroud)
问题是我想要一个JMP指令的最小可能编码(并且没有 NOP填充).
我必须知道指令的大小c之前,我可以计算之间的相对距离a,并b在操作数d.这同样适用JMP于c:它需要知道d它之前可以计算e和之间的相对距离的大小a.
现有的汇编程序如何解决这个问题,或者你会怎么做?
这就是我在想的解决问题的方法:
首先将所有指令编码到
JMP它和目标之间的操作码,如果该区域包含可变大小的操作码,则使用最大大小,例如5aJMP.然后JMP通过选择最小可能的编码大小(3,4或5)来编码相对于其目标的相对值并计算距离.如果对任何可变大小的操作码进行编码,则更改之前的所有绝对操作数,以及跳过此编码指令的所有相关指令:当操作数更改为选择最小可能大小时,将对其进行重新编码.保证此方法结束,因为可变大小的操作码只能缩小(因为它使用它们的最大大小).
我想知道,也许这是一个过度设计的解决方案,这就是我问这个问题的原因.
这是我使用过的一种方法,它可能看起来效率低下,但事实证明并不适合大多数现实生活中的代码(伪代码):
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);
Run Code Online (Sandbox Code Playgroud)