7

在为 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字节)空间。操作数尚不为人所知,因为它是到一个未知位置的跳转。所以我实现了一种“重写”机制,如果跳转的位置已知,则将操作数重写到内存中的正确位置,并用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)

问题是我想要JMP指令的最小编码(并且没有 NOP填充)。

在计算操作数 at之间的c相对距离之前,我必须知道指令的大小。这同样适用于at :它需要知道 的大小才能计算 和 之间的相对距离。abdJMPcdea

现有的汇编程序如何解决这个问题,或者您将如何解决这个问题?

这就是我想解决问题的方法:

首先将所有指令编码为JMP和它的目标之间的操作码,如果该区域包含可变大小的操作码,则使用最大大小,5例如JMP. JMP然后通过选择可能的最小编码大小(3、4 或 5)并计算距离,对相对于它的目标进行编码。如果对任何可变大小的操作码进行编码,则更改之前的所有绝对操作数,以及跳过此编码指令的所有相对指令:当它们的操作数更改以选择可能的最小大小时,它们将被重新编码。此方法保证结束,因为可变大小的操作码只能缩小(因为它使用它们的最大大小)。

我想知道,也许这是一个过度设计的解决方案,这就是我问这个问题的原因。

4

2 回答 2

3

在第一遍中,您将非常近似地jmp使用对所有跳转指令使用悲观字节计数的代码。

在第二遍中,您可以使用选择的悲观操作码填充跳转。然后可以重写很少的跳转以使用更少的一个或两个字节,只有那些最初非常接近 8/16 位或 16/32 字节跳转阈值的跳转。由于候选对象都是多字节的跳转,因此它们不太可能处于关键循环情况,因此您可能会发现与两次通过的解决方案相比,进一步的通过提供很少或没有好处。

于 2010-05-11T21:40:53.943 回答
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);
于 2010-05-11T22:21:32.033 回答