免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12
最近访问板块 发新帖
楼主: mik

【x64 指令系统】之 指令编码内幕 [复制链接]

论坛徽章:
0
发表于 2008-12-09 18:01 |显示全部楼层
10、 Displacement 与 Immediate



  Displacement 与 Immediate 是嵌入指令编码中的。在编码组合序列里 Displacement 先于 Immediate。
  Displacement 与 immediate 最大 4 个字节。可是在 64 位 Long 模式下,一些应用场合下 displacement 与 immediate 可以是 8 个字节。
  Immediate 是符号数;大部分情况下 displacement 是符号数。可是在直接内存寻址下 displacement 不是符号数。




10.1、 displacement

  Displacement 意即基于基址的偏移量,意同 offset。常见于基址+disp 的形式。



10.1.1、 displacement 的符号数

  在两种寻址模式下,displacement 是符号值,有符号数的语义。

(1)基址+displacement 的寻址模式下

  这种寻址是使用 ModRM 进行,相对于基址的偏移量。这时 displacement 是符号数,当 displacement 为 8 位时,会符号扩展至 32 位。
  典型的指令如:lea eax, [ebp - 0x0c]


(2)相对转移寻址
  这种寻址是基于 rip(instruction pointer)的相对寻址,这时 displacement 是个基于 rip 的符号数偏移量。如:call Jz 和 Jmp Jz 等。同样,displacement 为 8 位时,会符号扩展至 32 位来计算结果。

  典型的指令如:jmp $+0xc(短跳转)




相反,在直接内存寻址下,displacement 不是符号值,它不含符号数的语义。能提供直接内存寻址有两种方式:

(1)基于 ModRM 寻址中的 [disp32] 寻址
  在 ModRM = 00-XXX-101 能提供直接内存寻址。或者:ModRM = 00-XXX-100 + SIB = 00-100-101 也能提供直接内存寻址。
  典型的编码是:c7 04 25 44 33 22 11 01 00 00 00
  它的指令是:mov dword ptr [0x11223344], 1

(2)寻址模式直接嵌入 Opcode 中的情况
  另一种是在 Opcode 中就提供直接内存寻址模式。这种 Opcode 为数少,典型的编码是:a1 44 33 22 11
  它的指令形式是:mov eax, [0x11223344]



10.1.2、 64 位的 displacement 值

  64 位 Long 模式下,只有一种情况 displacement 是 64 位。前面已经提到:采用嵌在 Opcode 中的直接内存寻址,displacement 是可以为 64 的,仅止而已。


  mov rax, Ov 这条指令中它的内存寻址属性字符是 Ov,它是不依赖于 ModRM 的直接内存寻址。

  典型的编码是:48 a1 88 77 66 55 44 33 22 11
  指令形式是:mov rax, qword ptr [0x1122334455667788]
  它仅能对 rax 寄存器寻址提供 64 位的 displacement 值。





10.2、immediate

  Immediate 相对简单多了,采用 immediate 寻址的 Opcode 要么就是嵌入 Operand,要么就是 Group 属性的 Opcode。
  Immediate 是个符号数,32 位下 imme8 会符号扩展到 32 位。64 位下会符号扩展到64位。


10.2.1、采用 Immediate 的寻址模式

(1)、 采用 Immediate 单 Operand 的指令寻址模式

  只有 1 个操作数下,Immediate 寻址模式在 Opcode 嵌入寻址模式,典型的如:push Iv 或 push Ib 指令,仅此而已。


(2)、两个以上的 Operands 的 Immediate 寻址模式
  一种是与 GPRs 之间的寻址,如指令:mov eax,0x10 这条指令的目标操作数是 rax,源操作数是 Iv。其中 rax 的寻址已嵌入到Opcode中。
  它的编码是:b8 10 00 00 00

  还有就是与 mem 之间的寻址,如指令:mov dword ptr [eax], 1 这种指令是 Group 属性的指令。



10.2.2、 64 位 Immediate 值

  64 位下,只有与 GPRs 之间的传送,才可能是 64 位,仅止而已。

  但 push Iv 和 push Ib 这类 push 指令它的缺省操作数是 64 位的。虽然指令编码不能有 64 位 immediate 值,但它会符号扩展至 64 位,其结果是 64 位的。


记住:
  1、64 位下 displacement / immediate 与 GPRs 之间的寻址才可能是 64 位,仅止而已。
  2、displacement 含有偏移的语义时,它是符号数。它是直接内存寻址时,它是非符号数;immediate 则是符号数。



x86 & x64 平台的指令编码已经讲解完毕。

论坛徽章:
0
发表于 2008-12-09 22:44 |显示全部楼层
11、 解析指令


  现在,回头看看序言里的两个例子,应该怎样解析呢?




1、 mov word ptr es:[eax + ecx * 8 + 0x11223344], 0x12345678


分析:

(1) 这条指令有两个操作数,目标操作数是 mem 寻址。源操作数是 imme 寻址。

(2) 目标操数的大小是 word,源操作数的大小是 0x12345678,这是一个 32 位值。这样出现了操作数不匹配,大部分编译器会将 imme 截断为 16 位,即结果为:imme = 0x5678。
  最后,两个操作数属性是: mem16, imme16

(3) 这条指令是 mov,那么就有: mov mem16, imme16, 是典型的:mov Ev, Iv 具 Group 属性 
  所以:Opcode = c7

(4) 分析 mem16 中,  eax 是 base 寄存器,ecx 是 index 寄存器,8 是 scale,还有一个 32 位 displacement 值。
  那么其 SIB.scale = 11 (8)、SIB.index = 001(ecx)、SIB.base = 000(eax),disp = 0x11223344
    SIB = c8

(5)现在来看看 ModRM 字节怎么来分解。
  SIB 需要 ModRM 引导出来,ModRM = 10-XXX-100,ModRM.mod = 10 是选择 [SIB+disp32] 寻址
  关键是 ModRM.reg 的值是多少?

(6) mov Ev, Iv 是 Group 属性,ModRM.reg = 000 时,分配给 mov Ev, Iv 这个 Opcode 码
  最终 ModRM = 10-000-100 = 84

--------------------------------------------------------------------------------------------------
  现在,基本的编码已经出来了:c7 84 c8 44 33 22 11 78 56
  由于 x86 平台是 little-endian 机器,所以 disp 与 imme 要按 little-endian 排列。

  但是,这个 encode 还不是最终的,因为操作数需要调整大小和段,因此还要加上 26h 和 66h,

  所以最终的 encode 是:26 66 c7 84 c8 44 33 22 11 78 56





2、 解析 encode:FF 15 D4 81 DF 00

(1) 提取第 1 个字节 FF ,这是个 Group 属性的 Opcode。

(2) 提取第 2 个字节 15,这必定是个 ModRM 寻址。
  它是结构是:00-010-101 = 15。 mod = 00、reg = 010、r/m = 101

(3) 分析 ModRM 字节:
  mod = 00:将提供 [base] 寻址
  reg = 010:将对补充 Opcode 码,在 FF 里选择第 3 列,它是 call Ev 指令
  r/m = 101:提供 [disp32] 直接内存寻址模式。

(4) 由以上第 3 点分析得出:ModRM 字节后是个 32 位的 displacement 值。
  也就是:D4 81 DF 00 是个 displacement,且是个直接内存寻址。
  所以内存操作数是:[0x00DF81D4]
  同样,在这里要注意 little-endian 排列。


(5) 最终这个 encode 的指令形式是:call [0x00DF81D4]

  那么,在这里需要注指令格式的问题:在 32 位下 call 指令的操作数必然是 32 位。eip = [0x00DF81D4]。所以对有些编译器来说,可以不用加 Operand-Size 修饰符。而 Microsoft 的编译器则需要加上 call dword ptr [0x00DF81D4],对于 Microsoft 的编译器来说,这样还不够。还需要明确指定段。
  对 Microsoft 的编译器来说,最终形式是 call dword ptr ds:[0x00DF81D4]
  对 nasm 来说:call [0x00DF81D4] 已经可以了,对 nasm 来说 [0x00DF81D4] 是缺省 DS 段
  



  到此,这篇 【x64 指令编码内幕】 已接近尾声。
 
  有机会再探讨更高级的应用话题,包括:assembler(汇编器)、disassembler(反汇编器) 以及 decoder(解码器)。



  
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP