首页 > 解决方案 > NASM 调用函数后我应该弹出函数参数吗?

问题描述

假设我有一个这样的 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从堆栈中删除项目。但这是好的做法吗?调用函数后从堆栈中删除参数?

标签: assemblyx86-64nasmcallstackcalling-convention

解决方案


最佳实践是在寄存器中传递参数,例如编译器使用的标准 x86-64 调用约定。例如 x86-64 System V 在寄存器中传递前 6 个整数/指针参数,因此您的函数将是
add byte [rdi], 1/ ret,并且不需要任何清理。
调用者只需要mov edi, some_varor lea rdi, [rel some_var]

(用户空间函数调用的基础知识记录在What are the call conventions for UNIX & Linux system calls on i386 and x86-64即使标题提到了系统调用。完整的细节在https://github.com/hjl-tools/ x86-psABI/wiki/X86-psABI。实际查看编译器对简单 C 函数的作用也很方便:请参阅如何从 GCC/clang 程序集输出中删除“噪声”?

如果您确实需要传递一个堆栈 arg,将其弹出到一个虚拟寄存器实际上pop rcx可能此函数是否将 RAX 作为第一个操作推入堆栈? 但是,如果您有超过 1 个堆栈参数供调用者清理,则使用where是堆栈槽的数量。add rsp, 8push
add rsp, 8 * nn

也可以让被调用者使用ret 8. 但这会让您失去让调用者离开分配的堆栈空间并对其进行mov存储的机会,例如为另一个call.


推荐阅读