NASM 我应该在调用函数后弹出函数参数吗?

NASM should I pop function argument after calling a function?

假设我有一个这样的 nasm 函数:

inc:
    mov rax,[rsp + 8]
    add [rax],BYTE 1
    ret

我是这样调用这个函数的:

push some_var
call inc

我想通过堆栈将参数传递给函数,所以我压入 some_var 然后调用我的函数。在函数中,我的项目在堆栈中排在第二位,所以我把它当作:mov rax,[rsp+8]

我的问题是:在调用函数之后,我是否应该以某种方式从堆栈中弹出我的参数?如果是这样,我可以以某种方式从堆栈中删除它,我的意思是弹出它,但不注册吗? (因为我不再需要这个参数了。)

更新:我发现我可以简单地 add rsp,8 这就是我从堆栈中删除项目的方法。但这是好的做法吗?要在调用函数后从堆栈中删除参数?

最佳做法是在寄存器中传递参数,就像编译器使用的标准 x86-64 调用约定一样。例如x86-64 System V 在寄存器中传递前 6 个 integer/pointer 参数,因此您的函数将是
add byte [rdi], 1 / ret,不需要任何清理。
调用者只需要 mov edi, some_varlea rdi, [rel some_var].

(用户基础知识-space 函数调用记录在 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 中)

如果你需要传递一个堆栈arg,将它弹出到一个像pop rcx这样的虚拟寄存器实际上可以是更多 效率高于 add rsp, 8,原因与编译器有时使用虚拟 push 保留一个 qword 堆栈槽/按 16 重新对齐堆栈的原因类似: 但是如果你有超过1 个堆栈参数供调用者清理,使用
add rsp, 8 * n 其中 n 是堆栈槽的数量。

也可以使用 ret 8 让被调用者清理堆栈。但这让你失去了让调用者离开堆栈 space 分配并在其中存储 mov 的机会,例如准备另一个 call.

我在这个答案中列出了一些从堆栈中删除内容的方法:Can I POP a value from the stack, but put it nowhere in NASM Assembly?,总结一下:

  • add rsp, x

  • lea rsp, [rsp + x]

  • mov rsp, rbp(也是leave的一部分)

  • lea rsp, [rbp - x]

  • 突然进入未使用的寄存器

除此之外,您是否应该 从调用者的堆栈中删除参数取决于您的调用约定是否要求 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 operandretn操作说明。例如:

    ...
    ; 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