代码之家  ›  专栏  ›  技术社区  ›  Pindatjuh

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

  •  8
  • Pindatjuh  · 技术社区  · 15 年前

    在为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字节)空间。操作数未知,因为它是到未知位置的跳转。因此,我实现了一个“重写”机制,如果知道跳转的位置,就在内存中的正确位置重写操作数,并用 NOP 在紧环中,这是一个比较严重的问题。

    现在我的问题是以下情况:

    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 在我计算出 a b 对于位于的操作数 d . 同样适用于 JMP C :它需要知道 D 在它能够计算出 e .

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

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

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

    我想知道, 也许这是一个过度设计的解决方案 所以我才问这个问题。

    2 回复  |  直到 15 年前
        1
  •  1
  •   500 - Internal Server Error    15 年前

    下面是我使用的一种方法,它可能看起来效率很低,但事实证明它不适用于大多数实际代码(伪代码):

    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);
    
        2
  •  3
  •   CB Bailey    15 年前

    在第一遍中,你将得到一个非常好的近似值 jmp 对所有跳转指令使用悲观字节计数的代码。

    在第二遍中,您可以用所选的悲观操作码填充跳跃。只有非常接近8/16位或16/32字节跳跃阈值的跳跃才能被重写为使用一个或两个以下的字节。由于候选者都是多字节的跳跃,因此他们不太可能处于关键循环情况下,因此您可能会发现,进一步的传递与两次传递解决方案相比几乎没有好处。