Program crashes after stack allocation and call in C

Asked

Viewed 184 times

2

I’m trying to make a simple program: allocate on the stack an integer, calls scanf from the C library and print it on the screen. I’ve done the same Disassembly in C, and I try to reimplement it, without success. I have tested the allocation without making any call in C, without any error. The problem is Microsoft’s x64 convention. What’s wrong with the following program?

bits 64
section .data
local1 db 'Hello. Input a number, please.',10,'> ', 0
local2 db '%u', 0
local3 db 10,'You input: %u', 0

section .text
extern printf
extern scanf
extern exit
extern getch

global WinMain
WinMain:
    push rbp
    mov rbp, rsp
    mov dword[rbp - 4], 0
    ; = push dword 0 em 64 bits 
    mov rcx, local1 
    call printf ;imprime o "pedido"
    mov rcx, local2
    lea rdx, [rbp - 4] ;endereço da variável alocada
    call scanf
    mov rcx, local3
    call printf
    call getch
    xor rcx, rcx ;coloca o valor de saída como 0
    call exit ;sai
    mov rsp, rbp
    pop rbp ;stack frame de saída
    leave

PS.: (Consider | a line break) I’ve tried switching mov dword[rbp - 4], 0 for sub rsp, 4 | mov dword[rsp-4], 0 and push word 0 | push word 0 (push dword is illegal in 64 bits).

1 answer

1


Did you compare the code generated by the compiler to the code you wrote? If you paste the generated Assembly here, it may help.

Anyway before calling a function you must allocate space in the stack to 4 parameters, those that are passed in rcx, Rdx, R8, R9, even if there are fewer parameters.

So the called function will have space to "spill" (Spill) into the stack the values passed and use these registers for other things (to call other functions, get pointers to these parameters, etc.)

I saw you just said sub rsp, 4, that advances the stack by 4 bytes (for its integer, and nothing for the parameters), which is insufficient. Also, the stack has to stay aligned in 16 bytes between function calls. Remembering that call pushes 8 bytes, then Each function must adjust the stack with 16n+8 bytes to restore alignment, being n the number of 64-bit variables you want.

Then I suggest starting the function with sub rsp, 88 (16*5 + 8, thus leaving space for 5 values, 4 for the calls and 1 for your local variable) and forget all the rest of the stack manipulation (use only mov thereafter).

If you push the rbp, it should serve as the + 8 of the above formula, then only need sub rsp, 80.

Obs.: untested code snippets.

  • Marcus, that’s exactly what it was: sub rsp, 80. I didn’t know why, and I thought it was the compiler’s thing (there were several compiler-only Abels)

  • Ah, I forgot to say that to balance the stack at the end of the function would need to undo all the steps in the opposite order. In case it would be a add rsp, 80, although in this case it should not be mandatory because the program ends before that, already in Exit.

  • how do I handle my variable? Should I use rbp - 4 or rbp - 76 ?

  • The parameters should be at the top of the stack (smaller memory addresses) when a call is made. Then the local variables of each function should have larger addresses (Obs.: I’m running out of time to give a more detailed answer, then tell me if you got it)

  • As expected, rbp - 76. The only fact I didn’t understand is: why do I have to allocate a local variable of size 16? I can’t just allocate a 4?

  • Whoa, are you sure it’s rbp - 76? Take a look at the diagrams of these sites: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/ and https://msdn.microsoft.com/en-us/magazine/cc300794.aspx (the first speaks of both Linux and Windows, beware of confusing) Local variables are "closer" to rbp (so rbp - 4) and then (then it means "smaller addresses", not shown in the diagrams) comes the location of the parameters (I didn’t answer this yesterday because I was in a hurry and would confuse everything).

  • The stack has to be aligned in 16 bytes. Probably because it allows better performance (ensures that the least significant bits will always be 0). Unless your function does not call any other (ie if your function is a Function Leaf), in which case it can do whatever you want with the stack! Of course you can find another place to store your variable. Remember that: the function that called yours must have allocated 16*4 bytes in rbp + XXXX, and that you are not required to use rbp (you can use rsp for everything), you can allocate these 8 bytes to something else.

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.