打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
2.2.2 基本的浮点数指令
2.2.2 基本的浮点数指令
2013-07-12 11:21:39     我来说两句 
收藏    我要投稿   
本书既是一本全面而系统地讲解反汇编与逆向分析技术的安全类专著,又是一部深刻揭示C++内部工作机制的程序设计类著作。理论与实践并重,理论部分系统地讲解了C++的各种语法特性和元素的逆向分析方法和流程,重在...  立即去当当网订购

前面学习了浮点数的编码方式,下面来学习浮点数指令。浮点数的操作指令与普通数据类型不同,浮点数操作是通过浮点寄存器来现实的,而普通数据类型使用的是通用寄存器,它们分别使用两套不同的指令。

浮点寄存器是通过栈结构来实现的,由ST(0)~ST(7)共8个栈空间组成,每个浮点寄存器占8字节。每次使用浮点寄存器都是率先使用ST(0),而不能越过ST(0)直接使用ST(1)。浮点寄存器的使用就是压栈、出栈的过程。当ST(0)存在数据时,执行压栈操作后,ST(0)中的数据将装入ST(1)中,如无出栈操作,将顺序地向下压栈,直到将浮点寄存器占满。常用浮点数指令的介绍如表2-1所示,其中,IN表示操作数入栈,OUT表示操作数出栈。


 

其他运算指令和普通指令类似,只需在前面加F即可,如FSUB和FSUBP等。

在使用浮点指令时,都要先利用ST(0)进行运算。当ST(0)中有值时,便会将ST(0)中的数据顺序向下存放到ST(1)中,然后再将数据放入ST(0)中。如果再次操作ST(0),则会先将ST(1)中的数据放入ST(2)中,然后将ST(0)中的数据放入ST(1)中,最后才将新的数据存放到ST(0)。以此类推,在8个浮点寄存器都有值的情况下继续向ST(0)中存放数据,这时会丢弃ST(7)中数据信息。

下面通过一个简单的示例来了解各指令的使用流程,熟悉数据传送类型指令FLD、FLD1、FST、FSTP、FISTP等的使用方法。MicrosoftVisualC++6.0通过函数__ftol将float型转换为int型,见代码清单2-3。
// C++ 源码对比,argc 为命令行参数
float fFloat = (float)argc;
; 将地址ebp+8 处的整型数据转换成浮点型,并放入ST(0) 中,对应变量argc
0040E9D8   fild        dword ptr [ebp+8]
; 从ST(0) 中取出数据以浮点编码方式放入地址ebp-4 中,对应变量fFloat
0040E9DB   fst         dword ptr [ebp-4]
// C++ 源码对比,浮点数作为函数参数进行传递
printf("%f", fFloat);
; 这里对esp 执行减8 操作是由于浮点数作为变参函数的参数时需要转换为双精度浮点值
; 这步操作是提前准备8 字节的栈空间,以便于存放double 数据
0040E9DE   sub         esp,8
; 将ST(0) 中的数据传入esp 中,并弹出ST(0)
0040E9E1   fstp        qword ptr [esp]
; 以下为printf 函数调用,略
0040E9E4   push        offset string "%f" (0042302c)
0040E9E9   call        printf (0040e940)
0040E9EE   add         esp,0Ch
// C++ 源码对比,将float 类型转换成int 类型
argc = (int)fFloat;
; 将地址ebp-4 处的数据以浮点型压入ST(0) 中
0040E9F1   fld         dword ptr [ebp-4]
; 调用函数__ftol 进行浮点数转换,__ftol 的实现见代码清单2-4
0040E9F4   call        __ftol (0040e688)
; 转换后结果放入eax 中,并传递到ebp+8 地址处
0040E9F9   mov         dword ptr [ebp+8],eax
// C++ 源码对比,printf 函数调用略
printf("%d", argc);
0040E9FC   mov         eax,dword ptr [ebp+8]
0040E9FF   push        eax
0040EA00   push        offset string "%d" (0042301c)
0040EA05   call        printf (0040e940)
0040EA0A   add         esp,8

代码清单2-3通过浮点数与整数、整数与浮点数间的互相转换演示了数据传送类型的浮点指令的使用方法。从示例中可以发现,float类型的浮点数虽然占4字节,但都是以8字节方式进行处理。当浮点数作为参数时,并不能直接压栈。PUSH指令只能传入4字节数据到栈中,这样会丢失4字节数据。这就是为什么使用printf函数以整数方式输出浮点数时会产生错误的原因。printf以整数方式输出时,将对应参数作为4字节数据,按补码方式解释;而真正压入的参数为浮点类型时,数据长度为8字节,需要按浮点编码方式解释。浮点数作为返回值的情况也是如此,同样需要传递8字节数据,如代码清单2-4所示。
代码清单2 - 4  浮点数作为返回值
// C++ 源码对比,返回值为浮点数的函数调用
fFloat = GetFloat();
; 调用函数GetFloat
0040EA3D   call        @ILT+5(GetFloat) (0040100a)
; 由于浮点数需要特殊处理,浮点数占8 字节,无法使用eax 进行传递
; 使用浮点寄存器ST(0) 作为返回值
0040EA42   fst         dword ptr [ebp-4]
;...
// C++ 源码对比,GetFloat 函数实现
float GetFloat()
{
  ; Debug 附加代码略
  // C++ 源码对比,返回浮点数12.25f
  return 12.25f;
  ; 将浮点数保存在ST(0) 中,在返回值为浮点数的情况下,无法使用eax
  ; 使用ST(0) 作为返回值进行传递
  0040E9D8   fld         dword ptr [__real@4@4002c400000000000000 (0042301c)]
}
; Debug 附加代码略
; 返回,结束函数调用
0040E9E4   ret
 

在代码清单2-3中,float型数据被强制转换为int型,编译器通过_ftol实现了转换过程,如代码清单2-5所示。
代码清单2 - 5  类型转换函数_ f t o l 的实现
; 保存环境,预留语句变量空间
0040E688  push    ebp
0040E689  mov    ebp,esp
0040E68B  add    esp,0F4h
; 浮点异常检查、CPU 与FPU 的同步工作
0040E68E  wait
0040E68F  fnstcw    word ptr [ebp-2]
0040E692  wait
0040E693  mov    ax,word ptr [ebp-2]
0040E697  or    ah,0Ch
0040E69A  mov    word ptr [ebp-4],ax
0040E69E  fldcw    word ptr [ebp-4]
; 从ST(0) 中取出8 字节数据转换成整型并存入ebp-0Ch 中
; 将ST(0) 从栈中弹出
0040E6A1  fistp    qword ptr [ebp-0Ch]
0040E6A4  fldcw    word ptr [ebp-2]
; 使用eax 保存整型数据的低4 字节,用于返回
0040E6A7  mov    eax,dword ptr [ebp-0Ch]
; 使用edx 保存整型数据的高4 字节,用于返回
0040E6AA  mov    edx,dword ptr [ebp-8]
; 释放栈
0040E6AD  leave
0040E6AE  ret

在代码清单2-5中,将浮点数转换为长整型的关键指令FISTP,此函数的开始部分只是做了一些浮点异常检查、CPU与FPU的同步工作,最后通过调用FISTP来实现浮点数与整数之间的转换的。由于浮点数都占8字节,而在32位下的整型只占4字节长度,所以使用EDX来保存多出的4字节数据。
 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
[原创]从反汇编的角度学C/C++之浮点数,结构体与联合体
恶斗TCC(Tiny C Compiler)
程序员需要了解的硬核知识之汇编语言(全)
C++虚函数调用的反汇编解析
栈溢出漏洞原理详解与利用
CPU阿甘:函数调用的秘密
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服