Let's say I have a nasm function like this:
inc:
mov rax,[rsp + 8]
add [rax],BYTE 1
ret
And I am calling this function like this:
push some_var
call inc
I want to pass an argument to the function through the stack, so I push some_var
and then call my function. In the function my item is second on the stack so I take it like: mov rax,[rsp+8]
My question is: after calling function should I somehow pop my argument from the stack? If so, can I somehow delete it from the stack, I mean pop it, but not to register? (Because I don't need this argument anymore.)
UPDATE: I found that I can simply add rsp,8
and that's how I can remove item from stack. But is that good practice? To remove the argument from the stack after calling function?
Best practice is to pass args in registers like the standard x86-64 calling conventions that compilers use. e.g. x86-64 System V passes the first 6 integer/pointer args in registers, so your function would be
add byte [rdi], 1
/ ret
, and not need any cleanup.
The caller would only need mov edi, some_var
or lea rdi, [rel some_var]
.
(Basics of user-space function calling documented in What are the calling conventions for UNIX & Linux system calls on i386 and x86-64 even though the title mentions system calls. Full details in https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI. It's also handy to actually look at what compilers do for simple C functions: see How to remove "noise" from GCC/clang assembly output?)
If you do need to pass a stack arg, popping it into a dummy register like pop rcx
can actually be more efficient than add rsp, 8
, for similar reasons to why compilers sometimes use a dummy push
to reserve one qword stack slot / re-align the stack by 16: Why does this function push RAX to the stack as the first operation? But if you have more than 1 stack arg for the caller to clean up, use
add rsp, 8 * n
where n
is the number of stack slots.
It's also possible to have the callee clean the stack by using ret 8
. But that defeats the chance for you to have the caller leave the stack space allocated and do mov
stores into it, e.g. in preparation for another call
.
I listed some ways to remove things from the stack in this answer: Can I POP a value from the stack, but put it nowhere in NASM Assembly?, to summarise:
add rsp, x
lea rsp, [rsp + x]
mov rsp, rbp
(also part of leave
)
lea rsp, [rbp - x]
popping into otherwise unused registers
Other than that, whether you should remove parameters from the stack in the caller is determined by whether your calling convention mandates caller clean-up or the opposite, callee cleanup. Callee cleanup is done by specifying the number of bytes to remove from the stack as an immediate operand to the retn
instruction. For example:
...
; caller code
push rax
push rdi
call testfunction
...
; function code
testfunction:
push rbp
mov rbp, rsp
mov rcx, qword [rbp + 16]
...
mov rsp, rbp
pop rbp
retn 16
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With