The amateur programmer
The amateur programmer

Reputation: 1308

NASM printf print 64 bit double segfaults

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.

Upvotes: 2

Views: 740

Answers (2)

Aurel Bílý
Aurel Bílý

Reputation: 7981

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.

Upvotes: 1

Chris Dodd
Chris Dodd

Reputation: 126526

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.

Upvotes: 4

Related Questions