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 操作。
每条指令都有 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
正如,前面所说的,对于一般的规则 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 |
使用 66H prefix 进行明码的 override 操作,指令的 current operand-size attribute 将会变为 32-bit,那么 fnstenv 指令的执行结果是保存 28 bytes 的 x87 environment,而不是缺省的 14 bytes。
我们依然能找到许多这样的例子,如下面:
lgdt dword [GDT_BASE] ; error
|
正确做法:
db 0x66 ; OK! |
正如你会想到的,确实,我们也能找到常用的一些例外的例子:
|
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(当然,你也可能使用明码方式)