Reputation: 15
I have a task to do, call the scanf and printf functions using the char* and double variables. Char* is working, but I have problem with double.
Function: is for scanf/printf char*, function1: is for scanf/printf double. For example, my results after compilation:
(scanf)b
(printf)char: b
(scanf)1.3
(printf)double: 99997200381866062965879955188785948733402760577162787362451212786.000000
It appears the problem is with printf for double variable but I have no idea how to solve it.
.data
STDIN = 0
STDOUT = 1
SYSREAD = 3
SYSWRITE = 4
SYSEXIT = 1
EXIT_SUCCESS = 0
format_inchar: .string "%c"
format_indouble: .string "%lf"
char: .ascii " "
double: .double 0.0
format_string1: .string "char: %c\n"
format_double1: .string "double: %f\n"
.text
.global main
main:
function:
push $char
push $format_inchar
call scanf
push char
push $format_string1
call printf
function1:
push $double
push $format_indouble
call scanf
push double
push $format_double1
call printf
exit:
movl $SYSEXIT, %eax
movl $EXIT_SUCCESS, %ebx
int $0x80
Upvotes: 0
Views: 875
Reputation: 364532
32-bit code can't push
a 64-bit double
from memory with one instruction. 64-bit code doesn't pass args on the stack, so I assume this is intended to be 32-bit code. (Also from using int 0x80
.)
push double
in 32-bit code is pushl
that pushes the low 4 bytes. printf
was taking the high 4 bytes (containing the exponent and most-significant bits of the mantissa) from whatever was above it on the stack. In that case, assuming scanf
didn't clobber its stack args, the high 4 bytes of your double
bit-pattern came from the address $format_indouble
.
You probably want pushl double+4
; pushl double
to push the 8-byte double
in 2 halves.
(The operand-size suffix is optional here, but I'd recommend it to avoid the ambiguity that got you.)
Or you could use SSE2 movsd
to copy 8 bytes to the stack.
sub $8, %esp
movsd double, %xmm0
movsd %xmm0, (%esp)
(push
is special and can copy from memory to memory. Instructions with explicit source and destination can't do that.)
Of course you don't need static storage space for your inputs; you could pass pointers to stack memory to scanf
. Then your double
would be on the stack already, and you could just store a pointer to a format string below it.
BTW, you don't have the stack aligned by 16 when you call scanf
and printf
. The ABI does require this, so you got lucky that your build of glibc happens not to use movaps
on stack memory in these functions, which would segfault.
Before main
is called, the stack is aligned by 16. That call
pushes a return address. So 3 more pushes re-aligns that stack. One dummy push on function entry will set you up for functions with 8 bytes of args.
Also normally you'd add $8, %esp
after a call returns to clear the args from the stack.
Variable names: double
is a type name (and keyword) in C. That makes it a weird name for an assembly program that calls C functions. Something like dbl
might look better.
Upvotes: 0