当我使用Ubuntu上的gcc 4.6.1编写一个简单的汇编语言程序,并链接C库,尝试打印一个整数时,它能够正常工作:
现在它可以工作,并打印出15.5000。
我的问题是:为什么推送和弹出
.global main
.text
main:
mov $format, %rdi
mov $5, %rsi
mov $0, %rax
call printf
ret
format:
.asciz "%10d\n"
这将会输出5,和预期一致。
但是,如果我进行小改动,并尝试打印一个浮点数值:
.global main
.text
main:
mov $format, %rdi
movsd x, %xmm0
mov $1, %rax
call printf
ret
format:
.asciz "%10.4f\n"
x:
.double 15.5
这个程序出现段错误,没有输出任何信息。仅仅是一个令人悲伤的段错误。
但是我可以通过推入和弹出%rbp
来解决这个问题。
.global main
.text
main:
push %rbp
mov $format, %rdi
movsd x, %xmm0
mov $1, %rax
call printf
pop %rbp
ret
format:
.asciz "%10.4f\n"
x:
.double 15.5
现在它可以工作,并打印出15.5000。
我的问题是:为什么推送和弹出
%rbp
使应用程序工作?根据ABI,%rbp
是调用方必须保留的寄存器之一,因此printf
不能搞砸它。事实上,在第一个程序中,当只传递一个整数给printf
时,printf
可以正常工作。所以问题必须在其他地方?
mov
到%rax
的目的是什么? - NPEprintf
打印float
类型的数据,只能使用double
(使用"%f"
)或long double
。请参考以下链接了解如何使用printf
打印单精度浮点数:如何使用printf打印单精度浮点数。 - Peter Cordesprintf
只在%al != 0
时关心堆栈对齐,因为这是 gcc 编译可能接受 FP 参数的可变参数函数的方式。printf float in nasm assembly 64-bit 显示当使用未对齐的堆栈和 RAX=0 调用printf
时,它恰好不会崩溃,并且答案显示了 gcc 的代码(仅适用于非零 AL),该代码使用movaps
将 xmm0..7 转储到堆栈中(可变参数函数也可以接受__m128
参数,而不仅仅是double
)。 - Peter Cordes