2

I'm trying to get my head around printing doubles in assembly but I'm failing miserably. I'm getting segfault when calling my own function that I'm planning to use as a helper to print doubles for debugging purposes. I was following these printf examples: https://www.csee.umbc.edu/portal/help/nasm/sample.shtml

My code currently looks like this:

section .data
    formatStrf: db `The number is %f\n`,0

section .text

extern printf

printfcallfloat:

    ;Pass in RDI
    PUSH RDI ;Preserve value of rdi
    PUSH RAX ;Preserve value of RAX

    PUSH RDI;The value we want to print
    PUSH DWORD formatStrf
    CALL printf ;Segfault

    POP RAX;Pop the stack back (too lazy to manually change the RSP value)
    POP RAX

    POP RAX;Restore the RAX and RDI
    POP RDI
    RET

I'm passing the floating point value to the RDI reg as follows:

MOVSD QWORD [RSP], XMM0 ;Copy to stack
MOV RDI, QWORD [RSP]
CALL printfcallfloat

EDIT: I'm running this on linux.

The amateur programmer
  • 1,238
  • 3
  • 18
  • 38
  • @prl yes I'm running this on linux indeed. Added that infoermation to the question also. – The amateur programmer Feb 23 '20 at 21:24
  • 2
    The page you linked has 32-bit examples, which you seem to have converted to 64-bit in a simple way, but this won't work as the calling conventions are completely different. There is a link on that page for 64-bit examples; you should be following those instead. – Nate Eldredge Feb 23 '20 at 21:37

2 Answers2

4

On x86_64, arguments are passed in registers, and not on the stack (the stack is used only if the size of the arguments to too large to fit in the registers). All the gory details1 are laid out in the SYSV ABI for x86_64

The basics of that is that the first 6 integer/pointer arguments are passed in RDI/RSI/RDX/RCX/R8/R9, while the first 8 float/double arguments are passed in XMM0..XMM7. In addition, you need to specify the number of XMM registers used for arguments in AL2. So in your case, you want the format in RDI, the double value in XMM0 and 1 in AL

The wikipedia page also has lots of good (concise) info about this.


1For non-Microsoft systems -- MS being MS they do things in their own incompatible way

2You only actually need to set this for varargs functions that use at least one XMM register. For non-varargs functions it will be ignored, and if it is set too large for a varargs function, the result will be a few wasted cycles (saving uneeded XMM regs), but wont actually break anything.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • The OP also seems to be unaware of the ABI requirement that RSP is 16-byte aligned before a `call`. (Except for private helper functions.) – Peter Cordes Feb 24 '20 at 08:03
1

Edit: as pointed out in the comments, this is only for Windows architectures. See Chris' answer for the convention on Linux systems.


The page you linked to has examples for x86, the 32-bit architecture. The x86_64 architecture differs in how arguments are passed to functions.

The first four integer arguments are passed in registers. Integer values are passed in left-to-right order in RCX, RDX, R8, and R9, respectively. Arguments five and higher are passed on the stack.

source

Any floating-poing arguments would normally be passed in XMM0...3 instead, but for varargs this is different:

If parameters are passed via varargs (for example, ellipsis arguments), then the normal register parameter passing convention applies, including spilling the fifth and subsequent arguments to the stack. It's the callee's responsibility to dump arguments that have their address taken. For floating-point values only, both the integer register and the floating-point register must contain the value, in case the callee expects the value in the integer registers.

So, in other words, before your call to printf, you should set RCX to the format string, and set both RDX and XMM1 to the number.

Aurel Bílý
  • 7,068
  • 1
  • 21
  • 34
  • 2
    Your quotes are from the Microsoft documents, which uses different registers from everyone else (being microsoft). For anything non-MS you want the [SYSV ABI for x86_64](https://web.archive.org/web/20160801075146/http://www.x86-64.org/documentation/abi.pdf) – Chris Dodd Feb 23 '20 at 21:15
  • The Microsoft ABI also requires an extra 32 bytes of unused space on the top of the stack before the call and alignment of the stack to 16 byte alignment. – prl Feb 23 '20 at 21:19
  • Okay so this isn't working for me obviously as I'm running this on linux. – The amateur programmer Feb 23 '20 at 21:23