如何在汇编程序中实现相对 JMP (x86)? [英] How is a relative JMP (x86) implemented in an Assembler?
问题描述
在为 x86 平台构建汇编程序时,我遇到了一些编码 JMP
指令的问题:
While building my assembler for the x86 platform I encountered some problems with encoding the JMP
instruction:
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)
都是相对跳转,其中每个编码(操作+操作数)的大小在第三列.
All are relative jumps, where the size of each encoding (operation + operand) is in the third column.
现在我的原始设计(因此错误)为每条指令保留了最大(5 个字节)空间.操作数尚不清楚,因为它跳转到未知位置.所以我实现了一个重写"机制,如果跳转的位置已知,它会在内存中的正确位置重写操作数,并用 NOP
填充其余部分.这在紧密循环中是一个有点严重的问题.
Now my original (and thus fault because of this) design reserved the maximum (5 bytes) space for each instruction. The operand is not yet known, because it's a jump to a yet unknown location. So I've implemented a "rewrite" mechanism, that rewrites the operands in the correct location in memory, if the location of the jump is known, and fills the rest with NOP
s. This is a somewhat serious concern in tight-loops.
现在我的问题是以下情况:
Now my problem is with the following situation:
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
填充).
The problem is that I want the smallest possible encoding for a JMP
instruction (and no NOP
filling).
在计算 a
和 b
之间操作数的相对距离之前,我必须知道 c
处指令的大小d
.这同样适用于 c
处的 JMP
:它需要知道 d
的大小,然后才能计算 e 之间的相对距离
和 a
.
I have to know the size of the instruction at c
before I can calculate the relative distance between a
and b
for the operand at d
. The same applies for the JMP
at c
: it needs to know the size of d
before it can calculate the relative distance between e
and a
.
现有的汇编程序如何解决这个问题,或者您将如何解决?
这就是我想解决的问题:
This is what I am thinking which solves the problem:
首先将所有指令编码为 JMP
和它的目标之间的操作码,如果该区域包含可变大小的操作码,则使用最大大小,例如5
表示 JMP
.然后通过选择最小可能的编码大小(3、4 或 5)并计算距离,将相对 JMP
编码到它的目标.如果任何可变大小的操作码被编码,则更改之前的所有绝对操作数,以及跳过此编码指令的所有相关指令:当它们的操作数更改以选择可能的最小大小时,将重新编码它们.这种方法肯定会结束,因为可变大小的操作码只会缩小(因为它使用了它们的最大大小).
First encode all the instructions to opcodes between the
JMP
and it's target, if this region contains a variable-sized opcode, use the maximum size, e.g.5
for aJMP
. Then encode the relativeJMP
to it's target, by choosing the smallest possible encoding size (3, 4 or 5) and calculate the distance. If any variable-sized opcode is encoded, change all absolute operands before, and all relative instructions that skips over this encoded instruction: they are re-encoded when their operand changes to choose the smallest possible size. This method is guaranteed to end, as variable-sized opcodes only may shrink (because it uses the maximum size of them).
我想,也许这是一个过度设计的解决方案,这就是我问这个问题的原因.
I wonder, perhaps this is an over-engineered solution, that's why I ask this question.
推荐答案
这是我使用过的一种方法,它可能看起来效率低下,但结果证明不适用于大多数现实生活中的代码(伪代码):
Here's one approach I've used that may seem inefficient but turns out not to be for most real-life code (pseudo-code):
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);
这篇关于如何在汇编程序中实现相对 JMP (x86)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!