64位x86汇编PrintFloat函数参数获取异常及无法正常返回问题
64位x86汇编浮点数打印问题的解决方案
你的核心问题是混淆了32位和64位的调用约定——32位下参数通过栈传递,但64位(不管是System V AMD64还是Windows x64)都采用寄存器传参,栈操作规则也更严格,直接照搬32位的写法必然出错。
原代码的关键错误
- 参数获取方式错误:64位下函数的前几个参数不会压入栈,而是存在指定寄存器中,你用
pop rax/pop rdx从栈里取参数,拿到的必然是垃圾值。 - 栈指针操作混乱:64位要求调用函数前栈必须16字节对齐,你的栈调整逻辑完全不符合这个规则,导致
printf调用异常,栈无法正确恢复。
分平台的正确实现
1. System V AMD64(Linux/macOS)
该平台下,函数前两个参数依次存在rdi(float指针)、rsi(格式字符串);调用可变参数函数(如printf)时,浮点参数需放入XMM寄存器,且栈必须保持16字节对齐。
PrintFloat: # 进入函数时rsp是8字节对齐(call压入8字节返回地址),sub 8后恢复16字节对齐 sub rsp, 8 # 将float转换为double,放入XMM0(printf的%f期望double类型,对应第一个浮点参数XMM0) movd xmm0, dword [rdi] ; 把float加载到XMM0低4字节 cvtss2sd xmm0, xmm0 ; 单精度float转双精度double # 调用printf:第一个参数是格式字符串(移到rdi) mov rdi, rsi call printf # 恢复栈指针 add rsp, 8 ret
2. Windows x64
Windows平台采用fastcall约定,前两个参数存在rcx(float指针)、rdx(格式字符串);函数必须预留32字节的"阴影空间",且栈需保持16字节对齐。
PrintFloat: # 预留32字节阴影空间 + 8字节对齐调整,确保栈16字节对齐 sub rsp, 40 # 将float转换为double,放入XMM0 movd xmm0, dword [rcx] cvtss2sd xmm0, xmm0 # 调用printf:第一个参数是格式字符串(移到rcx) mov rcx, rdx call printf # 恢复栈指针 add rsp, 40 ret
补充说明
- 如果你之前尝试用寄存器传参但仍得到随机值,大概率是没把
float正确转换为double(printf的%f只接受double类型,直接传float会导致解析错误),或者调用printf时参数寄存器传递错误。 - 测试时,C调用代码需匹配汇编的参数顺序,比如System V下的测试代码:
#include <stdio.h> extern void PrintFloat(float* f, const char* fmt); int main() { float val = 2.718f; PrintFloat(&val, "Value: %f\n"); return 0; }
内容的提问来源于stack exchange,提问作者Maxim Ricardo
相关产品推荐
相关产品推荐

