返回介绍

2. 函数

发布于 2024-10-13 10:58:57 字数 2737 浏览 0 评论 0 收藏 0

函数执行时,需从栈(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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文