Board logo

标题: 理解 x87 FPU 的执行环境 [打印本页]

作者: yuyang911220    时间: 2016-10-19 20:23     标题: 理解 x87 FPU 的执行环境

在这里,我们来了解一下 x86/x64 体系中的 x87 FPU 结构,包括下面几个物理资源:
然而,x87 Environment 包括下面的要素:
也就是说 x87 Environment 中并不包括 x87 FPU 的 data registers

1. last instruction pointer 与 last data pointerx87 last instruction pointer 是存储最后一条非控制指令的地址值,x87 last data pointer 是存储最后一条非控制指令的内存操作数地址值,如果最后一条非控制指令的操作数不是 memory 操作数,那么 x87 last data pointer 的值是无效的,未定义的。

2. 观察 last instruction pointer 与 last data pointer 表现现在我们来关注一下 last instruction pointer 与 last data pointer:
也就是说:48 位的寄存器高 16 位存放 segment 值,低 32 位存放 offset 值。
在 64-bit 模式下:
在这种情况下,将不保存 last instruction selector 和 last data pointer selector 值。
下面的小节中,我将分别在 real mode,protected mode 以及 64-bit mode 下做实验,在实验中我将使用 fstenv 指令进行保存 x87 Environment
2.1 real mode 下的表现由于我们根据 fstenv 指令来观察 x87 Environment 因此根据 fstenv 指令的 memory 操作数,我们可以分为:
2.1.1 real mode 下的 16 位内存模式实际上 Intel 已经给出答案了,下图是摘自 Intel Manual:

现在我们可以用代码来验证这个图的正确性。
2.1.1.1 为测试做准备
        jmp 0x0100next - 0x1000)        ; for test ...
next:
        mov ax, 0x1000                        ; for test ...
        mov ds, ax
我在这里将 CS 设为 0x0100,而 DS 设为 0x1000 目的是想清楚地观察 last instruction pointer 和 last data pointer
2.1.1.2 执行 x87 指令
        fild word [mem16int]            ; for test DS:mem16int = 0x1000:XXXX
        fstenv [cs: state - 0x1000]              ; CS:stat = 0x0100:XXXX
这里首先执行一条 x87 指令,内存 [mem16int] 的位置在哪无关紧要,在这个例子中并不影响指令的执行效果。指令执行目的是获得 last instruction pointer 和 last data pointer,接着使用 fstenv 指令来保存 x87 Environment,值得注意的是:这里使用的是 16 位的内存模式。
我们可以使用 bochs 的调试功能,使用 fp 命令来输出 float point 寄存器
<bochs:7> fp
status  word: 0x82c1: B c3 TOS0 c2 C1 c0 ES SF pe ue oe ze de IE
control word: 0x0040: inf RC_NEAREST PC_32 pm um om zm dm im
tag word:     0x5555
operand:      0x0706
fip:          0x0000000000006c28
fcs:          0x0100
fdp:          0x0000000000007c45
fds:          0x1000
上面命令输出:
然后,我们观察 fstenv 指令执行结果,验证上面是否正确,我们可以打印 state 内存点的值:
(0) [0x0000000000007c2d] 0100:6c2d (unk. ctxt): fnstenv cs:0x6c47         ; 2ed936476c
<bochs:10> x /7h 0x1000+0x6c47
[bochs]:
0x0000000000007c47 <bogus+       0>: 0x0040 0x82c1 0x5555 0x7c28 0x0706 0x7c45 0x1000
fnstenv 指令在 cs:0x6c47(即:0x7c47) 的位置上保存 x87 Environment,我们验证得到:
接下来 20 位是 last instruction pointer 被拆分两部分,高 4 位在下一个字节的高 4 位,结果 last instruction pointer 是 0x7c28,接下来是 11 位的 last opcode 为:
真实的 x87 opcode 是 d8 + 7 06 = df 06,接下来是 last data pointer 组成原理与 last instruction 是一样的,为 0x17c45
本例的源码和映像下像:x87environment_demo1.zip
下图是在 bochs 2.4.6 下运行的截图:

上图中打印出了 last data pointer 和 last instrcution pointer,以及根据 last instrcution pointer 获取的 x87 instruction encodes
这里有完整的源代码以及映像下载:x87environment_demo_real16.zip

2.1.2 real mode 下的 32 位内存模式下面是 x87 Environment 在 real mode 的 32 位内存模式下的布局:

在 real mode 下缺省的操作数大小是 16 位的,在缺省情况下使用指令 fstenv/fnstenv 可以获得 16 位的 state image,为了获得 32 位的 state image,我在代码里修改如下:
        db 0x66                                       ; override to 32-bit
        fnstenv [cs: (state - 0x1000)]                ; CS:stat = 0x0100:XXXX
我手工加上 operand-size override prefix 将操作数调整为 32 位,运行结果如下:

上面是在 bochs 中的运行结果,得到的是 32 位的 real mode 的 state image,我们可以看出,图上红色标记的是分别是:
这些寄存器的高 16 位,在 32 位的的 state image 中这些高 16 位被写入 0xFFFF 值。对应在 intel 给出的图中的灰色部分。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0