【问题标题】:How to simplify algorithm of pasting addresses of data to immediate fields in machine code?如何简化将数据地址粘贴到机器代码中的立即字段的算法?
【发布时间】:2021-11-06 03:38:51
【问题描述】:

在开发我的汇编程序时,我遇到了一个问题。在汇编语言中,我们可以定义数据值(例如msg db 'Hi')并将该数据的地址粘贴到任何地方,甚至在该数据上方。然而,当汇编代码时,汇编器不知道数据的地址,直到没有处理这些数据的行。

当然,汇编器可以记住我们使用已定义数据地址的机器代码地址,并在处理代码后将记住的地址中的值替换为我们的数据地址。但是,如果我们在 2 字节地址中定义数据(例如在 01 F1),汇编程序会将记忆地址中的 1 个字节替换为地址(01 F1)的 2 个字节,因此立即字段大小将被更改(imm8 -> imm16)和汇编程序应在同一地址重写指令(更改操作码处的 w 和 s 位,并可能设置前缀 0x66)。如果汇编程序将设置前缀 0x66 并且我们的数据定义在该指令之后,则应重写立即字段字节(增加地址值)。

此算法的说明:

以下代码:

mov dh, 09h
add dx, msg

;...

msg db 'Hello$'

将按照以下原则组装:

  1. 准备代码:
Comment :                       |===> Remember address of this byte (0x0004)
Comment :           |  ADD DX,MSG  |
Address : 0000 0001 |0002 0003 0004| ... 01F1 01F2 01F3 01F4 01F5 01F6
Code    :  B4   09  | 83   C2   00 | ...  48   65   6C   6C   6F   24
Comment :           ----------------      H    e    l    l    o    $
  1. 在记住的地址中重写代码:
Comment :                         |=============|-This address (msg)
Comment :           |    ADD DX,01F1    |       v  
Address : 0000 0001 |0002 0003 0004 0005| ... 01F2 01F3 01F4 01F5 01F6 01F7
Code    :  B4   09  | 83   C2   F1   01 | ...  48   65   6C   6C   6F   24
Comment :           ---------------------      H    e    l    l    o    $
  1. 重写指令的操作码83h -> 81h10000011b -> 10000001b:位s=0):
Comment :                         |=============|-This address (msg)
Comment :           |    ADD DX,01F1    |       v  
Address : 0000 0001 |0002 0003 0004 0005| ... 01F2 01F3 01F4 01F5 01F6 01F7
Code    :  B4   09  | 81   C2   F1   01 | ...  48   65   6C   6C   6F   24
Comment :           ---------------------      H    e    l    l    o    $
  1. 将数据的新地址 (0x01F2) 写入立即字段:
Comment :                         |=============|-This address (msg)
Comment :           |    ADD DX,01F2    |       v  
Address : 0000 0001 |0002 0003 0004 0005| ... 01F2 01F3 01F4 01F5 01F6 01F7
Code    :  B4   09  | 81   C2   F2   01 | ...  48   65   6C   6C   6F   24
Comment :           ---------------------      H    e    l    l    o    $

我认为这个算法很难。可以简化吗?

【问题讨论】:

    标签: algorithm assembly x86 machine-code


    【解决方案1】:

    如果汇编器没有发出平面二进制文件(即也是链接器),则汇编器必须假设符号地址可能是 2 个字节,因为最终的绝对地址要到链接时间才能知道,在汇编程序完成。 (因此它只会为 2 字节地址和重定位留出空间供链接器填充)。

    但是,如果您直接组装成一个平面二进制文件并希望进行此优化,大概您会将其视为使用 start-small 算法的分支位移,并进行多遍优化,直到一切都适合为止。 strong> 事实上,你会在jmp/jcc rel8jmp/jcc rel16 的相同通道中执行此操作。 (Why is the "start small" algorithm for branch displacement not optimal? - 如果你没有像 align 8 这样的东西,这是最佳的,否则在某些极端情况下它可以但不是最佳的。)

    这些优化过程只是循环代表代码的内部数据结构,而不是在每一步实际编写最终的机器代码。无需计算或查找实际的操作码和 ModRM编码直到最后一次优化传递之后。

    您只需要让优化器了解指令大小的规则,例如add reg, imm8 是 3 个字节,add reg, imm16 是 4 个字节(AX 除外,其中 add ax, imm16 具有 3 字节特殊编码,与 add ax, imm8 相同,因此添加到 AX 不需要是 multi - 完全通过优化,当我们知道所有符号地址后,它可以选择一种编码。)


    请注意,使用地址作为mov 的立即数更为常见,这根本不允许窄立即数(mov reg, imm16 始终为 3 个字节)。但是这种优化也与寻址模式中的 disp8 与 disp16 相关,例如对于xor cl, [di + msg] 可以使用 reg+disp8 来处理小地址,因此值得进行此优化。

    同样,您的优化器通过会知道 [di + disp8] 在 ModRM 之后占用 1 个字节,[di + disp16] 占用额外的 2 个字节。

    [msg]在ModRM之后总是占用2个字节,没有[disp8]编码。如果 asm 源想要利用 disp8 处理小地址,则它需要有一个归零的寄存器。


    当然,一个简单的或一次性的汇编程序总是可以假设地址是 16 位的,并相应地对机器代码的其他部分进行编码,只有在看到未解析的符号时才返回填充数字地址。 (或者在最后发出重定位信息以供链接器执行。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-11
      • 2019-02-04
      • 2020-01-28
      • 2012-05-01
      • 1970-01-01
      • 2018-01-15
      • 1970-01-01
      • 2018-11-21
      相关资源
      最近更新 更多