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_var
或 lea 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 operand 到 retn
操作说明。例如:
...
; 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
假设我有一个这样的 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_var
或 lea 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 重新对齐堆栈的原因类似:
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 operand 到 retn
操作说明。例如:
...
; 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