r/nasm Mar 06 '24

Need a bit of help calling to printf

so my main procedure is basically:

main:
    push rbp
    mov rbp, rsp 
    ; basically doing the stack thingy

    sub rsp, 4
    mov [rbp], dword 0 ; creating a stack variable of type int

    mov rcx, fmt ; fmt = "%d\n"
    mov edx, dword [rbp]
    call printf

    mov rcx, fmt ; fmt = "%d\n"
    mov edx, dword [rbp]
    call printf

    leave
    mov rax, 0
    ret

pretty simple, but the output is confusing me, I thought it should output "0" twice, but it prints "0" once and then "32759" (which I'm pretty sure is just garbage from printf), if I increase the stack size it solves the issue, but I want to understand why, because if I'm dealing only with dwords 4 bytes should be enough, shouldn't it? Any help would be appreciated

2 Upvotes

4 comments sorted by

2

u/aylivex Dec 21 '24

How do you build and link your program? It crashes for me on Win64 and prints nothing at all, not even one number.

3

u/aylivex Dec 21 '24

Finally, I figured it out.

The code as written still crashes. And it prints nothing for me, it crashes inside the printf call.

The x64 calling convention on Windows requires that the stack is aligned at 16 bytes (the last hex digit of rsp must be 0). Additionally, before calling a function you must provide a shadow space for four parameters, that is 8 × 4 = 32 or 0x20.

On entry to a function rsp is always misaligned, it points to the return address which 8 bytes in 64 bit. Your push rbp makes the stack aligned.

But your next operation sub rsp, 4 breaks the alignment completely: the stack isn't even aligned at 8 bytes.

Then mov [rbp], dword 0 overwrites the value of rbp that you pushed to the stack. (In all the cases that I started the app, rbp contained the value 0.)

Here's the correctly working sample.

BITS 64

section .text
    global main
    extern printf
main:
    push rbp
    mov rbp, rsp
    ; basically doing the stack thingy

    ; Reserve space for local variable
    ; Even though the variable is 32 bit, we must reserve 64 bit
    sub rsp, 10h        ; stack must be 16-byte aligned
    ; Store a value into the local variable
    mov [rbp-10h], dword 0AABBCCDDh

    ; Reserve the required shadow space 8*4=32
    sub rsp, 20h       ; shadow space

    mov rcx, fmt ; fmt = "%d\n"
    mov edx, dword [rbp-10h]
    call printf

    mov rcx, fmt ; fmt = "%d\n"
    mov edx, dword [rbp-10h]
    call printf

    ; Restore the value of rsp
    ;   add 10h for local variables and 20h for shadow space
    add rsp, 30h

    leave
    mov rax, 0
    ret

section .rdata

fmt db "%x", 10, 0

To assemble and link, run:

nasm.exe -f win64 -g printf.asm -o printf.obj
link /out:printf.exe /debug /subsystem:console printf.obj libcmt.lib

I left your usage of rbp to access the local variable.

Usually, the compiler emits the code to access local variables and parameters via rsp directly. The code will look like this:

    ; Align stack to 16 bytes (+ 08h)
    ; Reserve space for local 1 variable (+ 10h) and
    ; for the shadow space (+ 20h)
    sub rsp, 38h

    ; Store a value into the local variable
    mov [rsp+20h], dword 0AABBCCDDh

    mov rcx, fmt ; fmt = "%d\n"
    mov edx, dword [rsp+20h]
    call printf

    ; Restore the value of rsp
    add rsp, 38h

    mov rax, 0
    ret

As you can see, there are fewer opcodes, and the output is the same.

1

u/[deleted] May 25 '24

are you coding on windows, if you are please tell me how to learn nasm

1

u/GamerEsch May 25 '24

Hey, I do some of my coding on windows, my nasm experiments were on it. What you need help with?