1

I have a simple questions but couldn't find the answer to it. I am working on a programming language which translates to NASM and of course I need to have compatibility with the x64 calling convention Microsoft uses. My test codes just calls puts() with the string "Hello", sets RAX to 0 and returns. I know that xor rax, rax would be smaller but optimization is a job for later on.

extern puts

global main

section .data

section .rdata
    constp_main:
        .c0: db 72,97,108,108,111,0

section .text
    main:
        push rbp
        mov rbp, rsp

        lea rcx, [constp_main.c0]
        call puts
        mov dword eax, dword 0

    .return:
        pop rbp
        ret

This code does not work and produces a crash. If I reserve 24 bytes or more on stack, code like this works:

extern puts

global main

section .data

section .rdata
    constp_main:
        .c0: db 72,97,108,108,111,0

section .text
    main:
        push rbp
        mov rbp, rsp
        sub rsp, 24

        lea rcx, [constp_main.c0]
        call puts
        mov dword eax, dword 0

    .return:
        mov rsp, rbp
        pop rbp
        ret

Can anybody tell me why? I thought the callee has to do all that stuff?

(Editor's note: these examples aren't actually safe because not enough stack space is reserved, even if they happen to work in some cases. See comments.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • What OS? What is the calling convention from the ABI? – Frank C. Jul 12 '16 at 10:08
  • Windows 8.1 64bit so I took a look at the docs by microsoft but I couldn't find the answer there:/ When I want to call a method which calls puts or printf I need to reserve even more space although my method is not using it... – Roman Meusch Jul 12 '16 at 11:14
  • 7
    You should actually be allocating 32 bytes right before the call because of the shadow space requirement on [Microsoft's 64-bit calling convention](https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention). The fact it works with just 24 is mostly luck in this case. – Michael Petch Jul 12 '16 at 16:23
  • 3
    Although this will encode correctly `mov dword eax, dword 0` the dwords aren't necessary. Since the destination is a register with a known size you can use `mov eax, 0`. That will move the 32-bit immediate value of 0 to the 32-bit register _EAX_. In 64-bit mode writing to a 32-bit register will zero extend through the upper 32-bits of the 64-bit register. – Michael Petch Jul 12 '16 at 19:45
  • Thank you @MichaelPetch I didn't knew about the shadow space!:) And I know the dword's are unnecessary but this is generated code by my compiler so I don't care. – Roman Meusch Jul 13 '16 at 08:23
  • `xor eax,eax` is smaller still than `xor rax,rax`, unless you're using an assembler that does that optimization for you. – Peter Cordes Jul 13 '16 at 20:23
  • [How to write hello world in assembler under Windows?](https://stackoverflow.com/a/1032422) has a decent example of using shadow space. – Peter Cordes Jan 14 '21 at 10:05

0 Answers0