理解 current operand-size attribute


current operand-size attribute(当前操作数大小属性)在整个 x86/x64 指令编码体系中相当重要,特别是对一些不规则的 operand-size 来说尤为重要。

某日,我写下一条指令:

fnstenv dword [cs: (state - 0x1000)]                 ; CS:stat = 0x0100:XXXX

很遗憾,这样写法是错误的,fnstenv 指令的是 operand 是一个 14 bytes 28 bytes 内存区域 buffer,用来保存 x87 environment,这是典型的不规则 operand-size(所谓不规则 operand-size 是指:非正常的 byte/word/doubleword/quadword 类 operand-size)。

show the layout in memory of the stored environment, depending on the operating mode of the processor (protected or real) and the current operand-size attribute (16-bit or 32-bit).

这条指令的 operand-size 最终由 current operand-size attribute 来决定,在一般的规则 operand-size 情况下我们可以对 operand 进行 operand-size override 操作,可以在这里我们不能对这个不规则的 operand size 进行 override 操作。

1. 更改 current operand-size attribute

1.1 default operand-size attribute

每条指令都有 default operand-size attribute,某些指令或者某些指令的部分 operand default operand-size 是不可改变的。指令的 default operand-size attribute 是由 processor mode 决定,可是更深一层实际上真正决定 default operand-size 是由 CS.D 标志位,在 long mode 下还将由 CS.L 标志位决定。

关于 default operand-size attribute 更详细的描述,请参见这里:http://www.mouseos.com/x64/operand_size.html

1.2 operand-size override

正如,前面所说的,对于一般的规则 operand-size 我们可以直接对 operand 进行 operand-size override 操作,如下:

         bits 16

... ...

mov dword [bx], 1                     ; operand size override to 32-bit

在 16 位模式下,指令的 default operand-size attribute 是 16 位,我们可以像这样对 operand 进行 override operand-size 操作。在汇编语法(这里使用 nasm 语法)层面上在 operand 前面加了 dword 前缀,在 intel 语法上是这样:mov dword ptr [bx], 1

然而之所以可以直接使用前缀进行 override 是因为前缀词在 operand 的 effective operand size 的合理范围内。但是我们不能对不规则的 operand-size 使用直接的前缀词进行 override 操作,像本文开始的那条指令用法,就是错误的:

fnstenv dword [cs: (state - 0x1000)]                 ; Error: can't override with dword prefix!

fnstenv 指令合理的 operand-size 范围是:14 bytes 以及 28 bytes,想直接使用前缀词改变 operand-size attribute,将会得到的错误是: mismatch in operand sizes(没有匹配的 operand sizes)

可是我们如果确实想更改指令的 default operand-size attribute,怎么办呢?我们可以这样:

db 0x66                                  ; current operand-size override to 32-bit
fnstenv [cs: (state - 0x1000)]           ; the current default operand-size is 16-bit

使用 66H prefix 进行明码的 override 操作,指令的 current operand-size attribute 将会变为 32-bit,那么 fnstenv 指令的执行结果是保存 28 bytes 的 x87 environment,而不是缺省的 14 bytes。

我们依然能找到许多这样的例子,如下面:

lgdt dword [GDT_BASE]                       ; error

正确做法:

db 0x66                               ; OK!
lgdt [GDT_BASE]                       ; OK!

1.3 例外的例子

正如你会想到的,确实,我们也能找到常用的一些例外的例子:

         bits 16

... ...

jmp far dword [bx]                 ; OK! 32 bit far pointer(16:16) override to 48 bit far pointer(16:32)

这情形也是常见的,将 32-bit far pointer 改变为 48-bit far pointer,编译器支持这种语法形式,你不必使用明码进行 override(当然,你也可能使用明码方式)

 


版权所有 mik 2008 - 2011