delvian
delvian

Reputation: 15

Scanf and printf functions in assembly, example on char* and double

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

Answers (1)

Peter Cordes
Peter Cordes

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

Related Questions