如何在汇编程序中实现相对 JMP (x86)? [英] How is a relative JMP (x86) implemented in an Assembler?

查看:89
本文介绍了如何在汇编程序中实现相对 JMP (x86)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在为 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 NOPs. 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).

在计算 ab 之间操作数的相对距离之前,我必须知道 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 a JMP. Then encode the relative JMP 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆