When I write a simple assembly language program, linked with the C library, using gcc 4.6.1 on Ubuntu, and I try to print an integer, it works fine:
.global main
.text
main:
mov $format, %rdi
mov $5, %rsi
mov $0, %rax
call printf
ret
format:
.asciz "%10d\n"
This prints 5, as expected.
But now if I make a small change, and try to print a floating point value:
.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
This program seg faults without printing anything. Just a sad segfault.
But I can fix this by pushing and popping %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
Now it works, and prints 15.5000.
My question is: why did pushing and popping %rbp
make the application work? According to the ABI, %rbp
is one of the registers that the callee must preserve, and so printf
cannot be messing it up. In fact, printf
worked in the first program, when only an integer was passed to printf
. So the problem must be elsewhere?
mov
into%rax
? – Necrosefloat
withprintf
, onlydouble
(with"%f"
) orlong double
because of C promotion rules for variadic functions: How to print a single-precision float with printf. – Projectionistprintf
only cares about stack alignment when%al != 0
, because that's how gcc compiles variadic functions that might accept FP args. printf float in nasm assembly 64-bit shows thatprintf
happens to not crash when called with a misaligned stack and RAX=0, and the answer shows gcc's code (which runs only for non-zero AL) that dumps xmm0..7 to the stack withmovaps
(variadic functions can accept__m128
args, too, not justdouble
.) – Projectionist