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

问题描述 投票:2回答:2

假设我有一个像这样的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,这就是我可以从堆栈中删除项目的方式。但这是好习惯吗?要在调用函数后从堆栈中删除参数?

assembly x86-64 nasm callstack calling-convention
2个回答
3
投票

最佳实践是在寄存器中传递args,例如编译器使用的标准x86-64调用约定。例如x86-64系统V在寄存器中传递了前6个整数/指针args,因此您的功能应为add byte [rdi], 1/ ret,并且不需要任何清理。呼叫者只需要mov edi, some_varlea rdi, [rel some_var]

What are the calling conventions for UNIX & Linux system calls on i386 and x86-64中记录了用户空间函数调用的基础,尽管标题中提到了系统调用。https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI中的完整详细信息。实际上查看编译器对简单C函数的操作也很方便:请参阅How to remove "noise" from GCC/clang assembly output?

如果您do需要传递一个堆栈arg,则将其弹出到一个像pop rcx这样的虚拟寄存器中实际上比add rsp, 8效率更高[[more,原因与为何编译器有时会使用一个虚拟变量类似push保留一个qword堆栈插槽/按16:重新对齐堆栈。Why does this function push RAX to the stack as the first operation?但是,如果您有多个堆栈arg供调用者清理,请使用add rsp, 8 * n其中n是堆栈插槽的数量。

也可以使被呼叫者使用ret 8来清洁堆栈。但这使您失去了让调用方离开分配的堆栈空间并在其中进行mov存储的机会,例如正在准备另一个call

2
投票
我在此答案中列出了一些从堆栈中删除内容的方法: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还是执行相反的被调用者清理。通过将要从堆栈中删除的字节数指定为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
© www.soinside.com 2019 - 2024. All rights reserved.