float数据分别以int和float类型打印的反汇编分析
目录
1.源代码
2.VS配置
3.反汇编代码
3.分析
0.movss指令
1.cvtss2sd指令
资料出处
简介
描述
翻译
提炼要点
2.movsd指令
资料出处
简介
描述
翻译
提炼要点
3.关键指令分析
1.
2.
由double在内存中的存储形式可以推测xmm0寄存器中存储的数据
1.求S
2.将十进制数字转换为二进制
3.规范化
4.求E
5.操作M补0
6.结果
4.题外话
提示
分析
5.推荐阅读
1.源代码
#include <stdio.h>
int main()
{float value = 1.0;printf("value_int = %d\n", value);printf("value_float = %f\n", value);
}
2.VS配置
3.反汇编代码
#include <stdio.h>
int main()
{push ebp mov ebp,esp sub esp,44h push ebx push esi push edi mov ecx,offset _21126C67_FileName@c (0D6C008h) call @__CheckForDebuggerJustMyCode@4 (0D6132Ah) nop float value = 1.0;movss xmm0,dword ptr [__real@3f800000 (0D67B30h)] movss dword ptr [value],xmm0 printf("value_int = %d\n", value);cvtss2sd xmm0,dword ptr [value] sub esp,8 movsd mmword ptr [esp],xmm0 push offset string "value_int = %d\n" (0D67BD4h) call _printf (0D610D2h) add esp,0Ch printf("value_float = %f\n", value);cvtss2sd xmm0,dword ptr [value] sub esp,8 movsd mmword ptr [esp],xmm0 push offset string "value_float = %f\n" (0D67BE4h) call _printf (0D610D2h) add esp,0Ch
}xor eax,eax pop edi pop esi pop ebx mov esp,ebp pop ebp ret
3.分析
push ebp mov ebp,esp sub esp,44h push ebx push esi push edi mov ecx,offset _21126C67_FileName@c (0D6C008h) call @__CheckForDebuggerJustMyCode@4 (0D6132Ah) nop
栈区初始化,非本文重点,详细参见36.【C语言】函数栈帧的创建和销毁
0.movss指令
注:有关movss和XMM寄存器的详细内容参见float强制类型转换为int类型的反汇编分析
这里介绍另外8086汇编没有的指令cvtss2sd和movsd
1.cvtss2sd指令
资料出处
CVTSS2SD— Convert Scalar Single Precision Floating-Point Value to Scalar Double PrecisionFloating-Point Value
CVTSS2SD — ConVerT Scalar Single precision floating-point value to Scalar Double precision floating-point value
题外话:缩写里的2对应全称的to,原因:two的音标:/tuː/ to的音标:/tu/,两者的发音类似,因而用2
简介
把标量(Scalar)单精度(Single precision)浮点值(floating-point value)转换(ConVerT)为标量双精度(Double precision )浮点值
描述
Converts a single precision floating-point value in the “convert-from” source operand to a double precision floating-point value in the destination operand. When the “convert-from” source operand is an XMM register, the single precision floating-point value is contained in the low doubleword of the register. The result is stored in the low quadword of the destination operand.
128-bit Legacy SSE version: The “convert-from” source operand (the second operand) is an XMM register or memory location. Bits (MAXVL-1:64) of the corresponding destination register remain unchanged. The destination operand is an XMM register.
翻译
将"convert-from"源操作数中的单精度浮点值转换为目的操作数中的双精度浮点值。当"convert-from"源操作数是一个XMM寄存器时,单精度浮点值存储在寄存器的低双字中.结果存储在目的操作数的低四字中.
128位传统SSE版本:"convert-from"源操作数(第二个操作数)是一个XMM寄存器或内存位置.相应目标寄存器的位(MAXVL-1:64)保持不变.目标操作数是一个XMM寄存器.
备注:SSE是x86的扩展,是Streaming SIMD Extension的缩写,SIMD是Single Instruction Multiple Data的缩写
提炼要点
格式:cvtss2sd dest(存储双精度浮点值),convert-from(存储单精度浮点值)
convert-from可以是XMM寄存器或内存位置;dest可以是XMM寄存器
2.movsd指令
资料出处
CVTSS2SD— Convert Scalar Single Precision Floating-Point Value to Scalar Double PrecisionFloating-Point Value
MOVSD — MOVe or merge Scalar Double precision floating-point value
简介
移动(MOVe)或合并(merge)标量(Scalar)双精度(Double precision)浮点值(floating-point value)
描述
Moves a scalar double precision floating-point value from the source operand (second operand) to the destination operand (first operand). The source and destination operands can be XMM registers or 64-bit memory locations. This instruction can be used to move a double precision floating-point value to and from the low quadword of an XMM register and a 64-bit memory location, or to move a double precision floating-point value between the low quadwords of two XMM registers. The instruction cannot be used to transfer data between memory locations.
翻译
将源操作数(第二个操作数)中的标量双精度浮点值移动到目的操作数(第一个操作数),源操作数和目的操作数可以为XMM寄存器或64位内存地址.指令可以来回在XMM寄存器的低四字和64位内存地址移动一个双精度浮点值,或者在XMM寄存器的低四字之间移动.这个指令不能被用来在两个内存地址之间转移数据
提炼要点
格式:movsd dest(存储双精度浮点值),src(存储双精度浮点值)
dest和src可以为XMM寄存器或64位内存地址,但不能同时为64位内存地址
3.关键指令分析
1.
float value = 1.0;movss xmm0,dword ptr [__real@3f800000 (0D67B30h)] movss dword ptr [value],xmm0
由于存储单精度浮点值(float类型)要使用xmm寄存器,这里选择xmm0做中转寄存器来存储到value变量的地址dword ptr [value]处
(x86不支持由地址[...]到地址[...]的指令: movss dword ptr [value],dword ptr [__real@3f800000 (0D67B30h)])
2.
printf("value_int = %d\n", value);cvtss2sd xmm0,dword ptr [value] sub esp,8 movsd mmword ptr [esp],xmm0 push offset string "value_int = %d\n" (0D67BD4h) call _printfadd esp,0Ch
注:call _printf(0D610D2h)处理的细节非本文重点,在此不做分析
注意到: cvtss2sd xmm0,dword ptr [value]
疑问:value变量是float类型,在cvtss2sd这一行时,为什么存储在xmm0寄存器的小数要转换为double类型呢?
答:这是编译器的一种默认机制,执行printf("%d\n", value); value是以double传递参数的
回忆IEEE 754标准(具体参见62.【C语言】浮点数的存储)
由double在内存中的存储形式可以推测xmm0寄存器中存储的数据
1.求S
1.0为正数,因此S = 0
2.将十进制数字转换为二进制
3.规范化
4.求E
十进制1023=二进制011 1111 1111(11位)
5.操作M补0
由于M为0,占1位,因此在M的右边增加51个0
6.结果
每4位二进制数转换为1位十六进制数,为3F F0 00 00 00 00 00 00
由于VS默认以小端序存储,因此在内存中的存储形式为00 00 00 00 00 00 F0 3F
打印%d时,只取左侧4字节(即低32位)(int类型4字节),前4字节比特位都是0,所以最终打印为0
打印%f(float)时,和上面的double小数无关,正常打印为1.000000(默认保留小数点后6位)
4.题外话
画出以下汇编指令的栈区内存图
cvtss2sd xmm0,dword ptr [value] sub esp,8 movsd mmword ptr [esp],xmm0 push offset string "value_int = %d\n" (027BE4h) call _printfadd esp,0Ch
提示
1.call指令不改变esp寄存器的值
2.初始esp寄存器的值为0x009DFD70
分析
1.cvts2sd指令不改变esp
2.sub esp,8指令使得esp减8,为0x009DFD68
3.movsd指令不改变esp
4.push指令压栈,压入0x00027BE4操作了esp,使esp指向栈顶(0x009DFD64)
5.add esp,0Ch指令使esp变为初始值(0x009DFD70)
因此内存图为(注:压栈从高地址向低地址压)
5.推荐阅读
float强制类型转换为int类型的反汇编分析