2

我不太了解 x86 ASM,但我对 SHARP-z80 相当满意,而且我根据经验知道每条指令(助记符)都有相应的字节/字值,并通过查看组装好的十六进制转储二进制文件,我可以“读回”我使用助记符编写的相同代码。

在另一个 SO question中,有人声称在某些情况下 ASM 指令没有被翻译成相应的二进制值,而是由汇编程序以不同的方式重新排列

我正在寻找特别是反汇编二进制文件会导致 ASM 代码与原始代码不同的情况。

换句话说,是否存在汇编代码与汇编代码不是 1:1 比例的情况?

MikeKwan链接到另一个问题,其中 GCC 将修改内联 ASM 代码(在 C 项目中),但是,即使这是一个有趣的话题,它也没有回答这个问题,因为 GCC 是一个编译器,并且总是试图优化代码和内联 ASM 翻译受周围 C 代码的影响。

4

1 回答 1

4

在汇编器设计者认为它有帮助的范围内,它可以替代具有其他有用属性的等效指令。

首先,存在具有可变长度值操作数字段的机器。如果一个值/偏移量适合几个变体中的任何一个,汇编器通常会替换最短的那个。(在这样的汇编程序中,能够强制特定大小也很常见)。这适用于涉及立即操作数和索引寻址的指令。

许多机器都有带有 PC 相对偏移量的指令,通常用于 JMP,有时用于加载/存储/算术指令。汇编器在第一遍遇到这样的指令时可以确定寻址操作数在指令之前,或者它还没有看到指令。如果在前面,汇编器可以选择短相对形式或长相对形式,因为它知道偏移量。如果跟随,汇编器不知道大小,并且通常为它在 pass2 期间填充的指令选择一个大的偏移量。类似地,也有强迫汇编程序选择短格式的方法。

有些机器没有跳远相关指令。在这种情况下,如果目标在 jmp 之前并且在附近,则汇编器将相对向后插入一个短 jmp。如果目标在前面但距离很远,或者目标是前向引用,汇编器可能会在目标超过下一条指令的相反分支条件上插入一个短相对 jmp,然后是一个长绝对 jmp。(我亲自构建了这样的汇编程序)。这可确保 jmps 始终可以到达其目标。

关于这些技巧的好消息是,如果你反汇编,你仍然会得到一个有效的汇编程序。

现在让我们来看看那些会让你的反汇编程序感到困惑的东西。

如果机器具有用于加载/存储指令的短相对寻址并且程序员显然指定加载一个很远的常量或值,则可以使用类似的技巧来跳转文字操作数。在这种情况下,汇编器将指令更改为在围绕该常量插入一个短的相对 jmp 之后引用文字或地址常量。反汇编器认为指令流中的所有内容都是指令;在这种情况下,文字值不是,这会使反汇编程序关闭。至少在文字周围有一个无条件的 jmp 来指导反汇编程序。

您可能会在成熟的汇编程序中找到更诡异的技巧,在这些汇编程序中,曾经想象过的每一个特技都得到了支持。在 8 位汇编器上,我最喜欢的一个是“伪”指令 SKIP1、SKIP2,您可以将其视为极短的相对分支。它们实际上只是“CMP #8bits”和“CMP #16bits”指令的操作码字节,分别用于跳转 8 位或 16 位指令。所以,一个“一个字节”的相对跳转而不是两个。当您挤占空间时,每个字节都很重要:-{

      SKIP1
      INC    ; 8 bit instruction
      ...

这在尝试实现一个循环时也很方便,其中某些步骤不应该在循环进入时执行,但需要在进一步的循环迭代中完成:

      SKIP2
LOOP: SHLD  ; 16 bit instruction
      ...
      BNE LOOP

这里的问题是,如果您反汇编 SKIP1 或 SKIP2 指令,您将看不到 INC(或相应的 16 位指令)。

汇编语言程序员用来传递参数的一个技巧是在调用之后将它们内联放置,条件是被调用的例程适当地调整返回地址:

      CALL   foo
      DC     param1
      DC     param2

或 CALL printstring DC "a variable length string",0

反汇编程序没有实际的方法可以知道正在使用这样的约定或该约定是什么,因此反汇编程序必须处理这个错误。

于 2012-05-26T13:19:13.243 回答