mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
1376 字
4 分钟
asm04
2025-11-13

寄存器的使用约定#

寄存器主要用途调用约定特殊指令
RAX返回值、系统调用、算术运算调用者保存mul, div, in, out
RBX基地址、数据存储被调用者保存无特殊指令
RCX循环计数、字符串计数调用者保存loop, rep, 移位计数
RDXI/O端口、乘除法高位调用者保存mul, div, in, out
RDI第一个参数、目标索引调用者保存字符串指令目标
RSI第二个参数、源索引调用者保存字符串指令源

RFLAGS 寄存器#

这个寄存器用于存储CPU 操作状态和控制信息

寄存器布局

63 32 16 8 0
+-----------------+-----------------+-----+-----+
| 保留位 | 扩展标志 | FLAGS | 状态标志 |
+-----------------+-----------------+-----+-----+

常用标志位

  1. CF - 进位标志
  2. PF - 奇偶标志
  3. AF - 辅助进位
  4. ZF - 零标志
  5. SF - 符号标志
  6. OF - 溢出标志

程序栈#

程序栈(通常简称为”栈”) 是进程内存布局中的一块连续区域,用于存储函数的调用上下文局部状态 。它是一种后进先出 的数据结构,可以想象成一摞盘子,你总是把新盘子放在最上面(压入),也总是从最上面拿走盘子(弹出)。

在x86_64 架构中:

  • 栈的地址从高地址像低地址生长
  • 栈指针寄存器 rsp 始终指向栈的顶部
  • 基指针寄存器 rbp 通常用于指向当前函数栈帧的开始,以便稳定地访问参数和局部变量

程序栈的核心作用#

  1. 控制流管理
  • call/ret 依靠栈来记住返回地址,实现了函数的嵌套调用和返回
  1. 局部变量存储
  • 函数的非静态局部变量在栈上分配。当函数返回的时,这些变量占用的空间会被释放
  1. 参数传递:
  • 在x86_64 的 System V ABI (1) 中,前6个整型/指针 参数通过寄存器传递,如果参数多余6个,或某些参数是大型结构体那么额外的参数会通过栈传递
  1. 保存调用的上下文:
  • 在调用一个新函数面前,调用者可能需要将一些”被调用者保存的寄存器”(rbx, r12-r15) 压栈,因此为新函数承诺不会改变这些寄存器的值
  • 被调用函数也会保存 rbp 等寄存器以便返回时恢复
  1. 提供临时的存储空间:
  • 编译器可能会使用分配的空间来存储中间计算的结果

栈的操作与指令#

  1. push <src>
  • 机理:先将 rsp 减去 8(在 x86_64 架构中 一个单位的栈空间为 8位), 然后将操作数存入 [rsp]
  • 等价于
sub rsp, 8
mov <src>, rsp
  1. pop <dest>
  • 机理: 先将 [rsp] 的值存入目标操作数,然后将 rsp 加上8
  • 等价于
mov <dest>, rsp
add rsp, 8
  1. call <target>
  • 机理:将下一条指令的地址(返回地址)压栈,然后跳转到目标地址
  • 等价于:
push rip
jmp <target>
  1. ret
  • 机理: 从栈顶弹出返回地址,并跳转到该地址
  • 等价于:
pop rip

栈帧 Stack Frame#

每一个函数在栈上占用的区域称为一个栈帧。rbprsp 共同界定了一个栈帧.

leave 指令#

x86_64leave 指令是一个符合指令,他的作用等同以下两条指令序列:

mov rsp, rbp
pop rbp

在函数执行完毕后,准备返回。在调用ret 之前,需要先将栈恢复到调用前的状态

标准的函数序言#

my_function:
# === 函数序言 ===
push %rbp # 1. 保存旧的帧指针
mov %rsp, %rbp # 2. 建立新的帧指针(新栈帧开始)
sub $16, %rsp # 3. 在栈上为局部变量分配空间
# === 函数体 ===
movl $0, -4(%rbp) # 使用 rbp 偏移量来访问局部变量
... # 函数逻辑
# === 函数尾声 ===
leave # 1. 等效于 `mov rsp, rbp` 和 `pop rbp`
# (这步会销毁当前栈帧,恢复旧的 rbp 和 rsp)
ret # 2. 返回到调用者

System V ABI#

System B ABI 是一套详尽的规范,其定义了:

  1. 在特定额硬件和操作系统上,编译后的程序应该如何运行
  2. 不同的编译单元之间该如何交互

System V ABI包含内容#

  1. 函数调用约定
  • 参数传递(整数/指针):

    • 前六个参数按顺序使用以下寄存器传递:
      • rdi, rsi, rdx, rcx, r8, r9
      • 第七个及以后的参数通过栈来传递
    • 返回值:
      • 整数或者指针类型的返回值存放在rax寄存器中
      • 如果返回值是较大的结构体(无法放入寄存器),调用者会分配一块内存,并将地址作为隐藏的第一个参数 通过 rdi 传递
    • 栈对齐:
    • 寄存器使用约定:
      • 被调用者保存的寄存器:rbp, rbx, r12, r13, r14, r15
        • 如果一个函数要使用这些寄存器,他必须在修改他们之前将他的原始值压栈,并在返回前恢复。调用者可以放心认为这些寄存器的值在函数调用前不变
      • 调用者保存的寄存器:rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11
        • 如果一个函数希望在函数调用后还能使用这些寄存器的值,他必须在调用前自己保存他们,因为被调用者可以随意修改这些寄存器

程序寻址基本方式#

  1. 顺序寻址: 指令在内存中的存储顺序执行
mov rax, 10 ; 指令地址: 0x400100
add rax, 20 ; 指令地址: 0x400107
mov rbx, rax ; 指令地址: 0x40010e

机理:

  • CPU 通过 rip 指令寄存器跟踪当前指令地址
  • 执行完每条指令后, rip 自动增加,指向下一条指令
  • 增加的大小取决于当前指令的长度
  1. 直接跳转寻址: 直接指定目标地址
jmp 0x400150 ; 无条件跳转到绝对地址 0x400150
call my_function ; 调用函数(函数名在链接时解析为绝对地址)
je error_handler ; 条件跳转到标签
  1. 间接跳转寻址:通过寄存器或者内存地址来跳转
jmp rax ; 跳转到 RAX 寄存器中的地址
call [rsi] ; 调用 RSI 指向的地址处的函数
jmp [jump_table + rdx*8] ; 通过跳转表跳转
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

asm04
https://yoyolp.github.io/posts/c_asm/asm04/
作者
超级玉米人
发布于
2025-11-13
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录