Apple Silicon M1上NASM x86_64代码输出0.0而非正确浮点结果的问题
在Apple Silicon M1上正确编写调用printf传递双精度参数的NASM汇编程序
问题背景
在Apple Silicon M1的macOS上编写x86_64架构的NASM汇编程序,计算0.1+0.2的双精度浮点和并通过printf打印,但运行后输出0.1 + 0.2 = 0.000000,同时收到链接器警告ld: warning: no platform load command found in 'main.o', assuming: macOS。
原代码
section .data fmt db "0.1 + 0.2 = %f", 10, 0 num1 dq 0.1 num2 dq 0.2 result dq 0.0 section .text extern _printf global _main _main: fld qword [rel num1] fadd qword [rel num2] fstp qword [rel result] lea rdi, [rel fmt] movsd xmm0, [rel result] xor eax, eax call _printf ret
原编译链接命令
nasm -f macho64 main.asm -o main.o clang -arch x86_64 main.o -o main ./main
LLDB调试信息
(lldb) breakpoint set --name main Breakpoint 2: where = float_bug`main, address = 0x0000000100000f6f (lldb) run Process 31221 launched: '/Users/dmitroparhomenko/FlowyROS/float_bug' (x86_64) warning: libobjc.A.dylib is being read from process memory. This indicates that LLDB could not read from the host's in-memory shared cache. This will likely reduce debugging performance. Process 31221 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 frame #0: 0x0000000100000f6f float_bug`main float_bug`main: -> 0x100000f6f <+0>: movsd 0x1099(%rip), %xmm0 ; num1, xmm0 = mem[0],zero 0x100000f77 <+8>: movsd 0x1099(%rip), %xmm1 ; num2, xmm1 = mem[0],zero 0x100000f7f <+16>: addsd %xmm1, %xmm0 0x100000f83 <+20>: movsd %xmm0, 0x1095(%rip) ; result Target 0: (float_bug) stopped. (lldb) register read xmm0 xmm0 = {0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00} (lldb) expr *(double*)&$xmm0 (double) $0 = 0
问题分析
- printf参数传递错误:x86_64的System V ABI(macOS遵循该规范)中,调用
printf这类变参函数时,若有浮点参数通过XMM寄存器传递,必须在eax中设置传递的浮点参数数量。原代码中xor eax, eax表示没有浮点参数,导致printf忽略xmm0中的值,输出0.0。 - 链接器警告:NASM的
macho64输出格式默认不添加平台加载命令,clang链接时会自动补全,警告本身不影响功能,但可通过指定NASM参数消除。
解决方案
方案1:修复x86_64版本代码
修改代码中调用printf前的eax设置,同时添加NASM参数消除链接警告:
修复后的代码
section .data fmt db "0.1 + 0.2 = %f", 10, 0 num1 dq 0.1 num2 dq 0.2 result dq 0.0 section .text extern _printf global _main _main: fld qword [rel num1] fadd qword [rel num2] fstp qword [rel result] lea rdi, [rel fmt] movsd xmm0, [rel result] mov eax, 1 ; 告知printf有1个浮点参数在XMM寄存器中 call _printf ret
编译链接命令(消除警告)
nasm -f macho64 -DMACOS main.asm -o main.o ; -DMACOS让NASM添加平台加载命令 clang -arch x86_64 main.o -o main ./main
运行后会正确输出(因浮点精度,实际显示类似0.30000000000000004):
0.1 + 0.2 = 0.300000
方案2:编译为ARM64架构(原生运行,无需Rosetta)
若想直接在M1上以原生ARM64运行,需编写ARM64版本的NASM代码,并使用对应编译命令:
ARM64版本代码
section .data fmt db "0.1 + 0.2 = %f", 10, 0 num1 dq 0.1 num2 dq 0.2 section .text extern _printf global _main _main: ; 保存栈帧 stp x29, x30, [sp, -16]! mov x29, sp ; 加载浮点数并相加 ldr d0, [rel num1] ldr d1, [rel num2] fadd d0, d0, d1 ; 调用printf:第一个参数x0是格式字符串,d0是浮点参数 adrp x0, fmt add x0, x0, :lo12:fmt bl _printf ; 恢复栈帧并返回 ldp x29, x30, [sp], 16 ret
编译链接命令
nasm -f macho64 main.asm -o main.o ; ARM64的macho64格式 clang main.o -o main ; 默认编译为ARM64 ./main
关键说明
- x86_64的System V ABI要求:调用变参函数时,必须用
eax声明传递到XMM寄存器的浮点参数数量。 - ARM64的macOS ABI中,浮点参数通过
d0-d7寄存器传递,变参函数无需额外声明数量,直接传递即可。
内容的提问来源于stack exchange,提问作者Dmytro Parkhomenko
相关产品推荐
相关产品推荐

