0

Say I have the following code written in (AT&T) Assembly:

push qword 0
push qword 0
mov rax, 2                ;Tell rax we receive 2 floats
mov rdi, floatformat      ;floatformat db "%lf %lf",0

mov rsi, rsp              ;I am assuming my logic flaw is in these two lines
mov rdx, rsp

call scanf
pop rax                   ;Clean up the stack
pop rax

movsd xmm0, [rsi]         ;This does not give the value I want

As stated in the comments above, I want xmm0 to hold the first float the user types in when call scanf is performed, but only receive the second float. I am aware that this is most likely due to the mov rdx, rsp operation, but if that is not performed, my program does not operate correctly to read in the user inputs.

How can I get the first float the user types in? I have tried researching scanf calling conventions but have yet to find a clear answer.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
lawgik
  • 87
  • 3
  • 10

1 Answers1

2

%lf is a double, not a single precision float. And no, the 2 args you're passing to scanf are double *, not double, so you should set AL=0 not 2.

Anyway, your problem is that you passed the same pointer for both output operands. And that scanf destroys its arg-passing registers, like the calling convention allows it to. (What registers are preserved through a linux x86-64 function call)

The C equivalent is like scanf("%lf %lf", &b, &b) after reserving stack space for double a,b;

    ; assuming stack is 16-byte aligned to start with,
    ; e.g. if your function started with an odd number of pushes

    sub   rsp, 16
    mov   rsi, rsp        ; pointer to low slot
    lea   rdx, [rsp+8]    ; pointer to high slot
    lea   rdi, [rel format_string]    ; use RIP-relative LEA for 64-bit static addresses
    xor   eax,eax         ; 0 FP args in regs to a variadic function
    call  scanf           ; scanf(fmt, &tmp0, &tmp1)

    movaps xmm0, [rsp]     ; load both doubles
    add    rsp, 16         ; now tmp0 and tmp1 are below RSP, in the red-zone

    movsd   xmm1, [rsp-8]  ; or shuffle xmm0 to get tmp1 from the high half.

dummy push/pop is usually only worth it (instead of add rsp, imm8) for one stack slot, and then mostly if you're going to call right away instead of explicitly reference RSP. Doing that will insert a stack-sync uop.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Thanks for the detailed answer. One question, however: is there any way to do the same action WITHOUT the `lea` operation? I only ask because the textbook I am learning from has yet to mention it, yet has given what I am working on as a practice problem. – lawgik Nov 01 '19 at 04:03
  • 2
    @lawgik: Yes of course, you can emulate it using `mov` and `add`. It's not magic, just a shift-and-add instruction that can add and put the result in a different register. Or something like `push rax` / `mov rdx, rsp` / `push rax` / `mov rsi, rsp`. Other possible sequences to end up with RDI and RDX pointing at 2 different qwords above RSP should be straightforward to come up with, with or without LEA. – Peter Cordes Nov 01 '19 at 04:17
  • 1
    @lawgik: oh BTW, you can't emulate a RIP-relative LEA for position-independent code, except with 32-bit-style `call`/`mov rdi,[rsp]` to read the current program-counter and add some offset. But you can use inefficient 64-bit absolute addressing like you were doing with `mov rdi, format_string` on most OSes. [How to load address of function or label into register in GNU Assembler](//stackoverflow.com/q/57212012) – Peter Cordes Nov 02 '19 at 01:30