文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
2. 函数
函数执行时,需从栈(stack)划出一块空间,称作栈帧(frame)。用于局部变量存储和调用其他函数。
相关寄存器:
BP
: Frame Pointer,栈帧开始位置。SP
: Stack Pointer,栈顶。IP
: Instruction Pointer,下一条准备执行指令地址。(也称作 PC)
ADDR SP +===========+ -----+ | ... | | [sp + offset] low +-----------+ +- FRAME [bp - offset] ^ | ... | | | BP +===========+ -----+ | | caller.bp | high +-----------+ | | caller.ip |
相关指令:
push rbp
;mov rbp, rsp
:保存调用方开始位置;BP 指向栈顶,作为当前起始位置。mov rsp, rbp
;pop rbp
:将栈顶指向开始位置,释放栈帧;恢复调用方栈帧起始位置。call
:将 IP 寄存器内容入栈,跳转到目标函数。ret
:弹出值到 IP 寄存器,跳转回调用方,继续执行。
保存和恢复现场,可以使用
enter/leave
指令。
注意函数调用和宏的区别。宏在原地展开,属于所在函数的一部分,没有所谓调用方,也不需要保存和恢复现场。
global _start section .text _start: jmp main main: mov rdi, 11 mov rsi, 22 call add mov rdi, rax ; error_code = add.ret call exit add: ; long add(long x, long y) mov rax, rdi ; arg.x add rax, rsi ; + arg.y ret exit: mov rax, 60 syscall ret
$ make; ./test; echo $? 33
(gdb) b add (gdb) r (gdb) x/xg $rsp ; main.rip 0x7fffffffe698: 0x0000000000401011 (gdb) disass main Dump of assembler code for function main: 0x0000000000401002 <+0>: mov edi,0xb 0x0000000000401007 <+5>: mov esi,0x16 0x000000000040100c <+10>: call 0x401019 <add> 0x0000000000401011 <+15>: mov rdi,rax 0x0000000000401014 <+18>: call 0x401020 <exit> End of assembler dump.
局部变量版本
global _start section .text _start: jmp main main: push rbp mov rbp, rsp sub rsp, 24 mov qword [rsp] , 11 ; void main() { mov qword [rsp+8], 22 ; long x = 11; ; long y = 22; mov rdi, [rsp] ; long z = add(x, y); mov rsi, [rsp+8] ; exit(z); call add ; } mov [rsp+16], rax mov rdi, [rsp+16] ; error_code = z call exit mov rsp, rbp pop rbp ret add: ; long add(long x, long y) { push rbp ; long z = x + y; mov rbp, rsp ; return z; sub rsp, 8 ; } mov [rsp], rdi ; arg.x add [rsp], rsi ; + arg.y mov rax, [rsp] ; ret mov rsp, rbp pop rbp ret exit: mov rax, 60 ; xor rdi, rdi syscall ret
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论