如何在汇编程序中实现相对JMP(x86)?

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.这同样适用JMPc:它需要知道d它之前可以计算e和之间的相对距离的大小a.

现有的汇编程序如何解决这个问题,或者你会怎么做?

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

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

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

500*_*ror 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);
Run Code Online (Sandbox Code Playgroud)