x86-64 ISA 系统梳理
x86-64(又称 AMD64、Intel 64、x64)是目前桌面与服务器领域最主流的 64 位指令集架构,由 AMD 于 2000 年提出,2003 年随 Opteron/Athlon 64 发布。它向后兼容 16 位(8086)和 32 位(IA-32)x86 指令集。
1. 用例图 — x86-64 ISA 的参与者与功能
不同使用者以不同方式与 x86-64 ISA 交互:
- 编译器开发者 — 将高级语言编译为 x86-64 机器码,需要理解指令编码、寄存器分配、调用约定
- 操作系统内核 — 通过 ISA 控制 CPU 特权级、页表、中断描述符表、MSR 等
- 汇编程序员 — 直接编写 x86-64 汇编指令进行底层优化或系统编程
- 调试器/反汇编器 — 读取并解析 x86-64 字节码,映射回汇编助记符
- 固件(BIOS/UEFI) — 在实模式 / 保护模式 / 长模式下初始化 CPU
2. 活动图 — 指令执行流水线
指令在 CPU 内部的执行过程分为多个阶段,现代 x86-64 CPU 采用深度流水线、超标量、乱序执行(Tomasulo 算法)和推测执行。上图展示简化的经典五级流水线:取指 → 译码 → 执行 → 访存 → 写回。
3. 状态机图 — CPU 运行模式
x86-64 兼容多种历史运行模式。CPU 从上电复位开始于实模式,通过设置 CR0.PE 进入保护模式(32 位),然后通过设置 EFER.LME 和启用分页进入长模式(64 位)。系统管理模式(SMM)用于固件级电源管理和系统控制。
4. 序列图 — 系统调用流程
x86-64 提供两套快速系统调用指令:Intel 的 SYSCALL/SYSRET 和 AMD 的 SYSENTER/SYSEXIT。用户态程序将系统调用号放在 RAX 中,参数放在 RDI/RSI/RDX/R10/R8/R9 中,执行 syscall 指令即可陷入内核。RCX 保存返回地址,R11 保存 RFLAGS,内核通过 sysret 返回用户态。
5. 序列图 — 中断处理
中断处理是操作系统核心机制。外部设备通过 APIC 向 CPU 发送中断向量号,CPU 在中断描述符表(IDT)中查找对应的门描述符,切换到内核栈,保存上下文,调用中断服务例程(ISR),最后通过 iretq 返回。
6. 活动图 — 函数调用约定
x86-64 主要有两种调用约定:
- System V AMD64 ABI(Linux/macOS/BSD):前 6 个整数参数用 RDI, RSI, RDX, RCX, R8, R9;前 8 个浮点参数用 XMM0-XMM7;返回值在 RAX(整数)或 XMM0(浮点)
- Microsoft x64(Windows):前 4 个参数用 RCX, RDX, R8, R9;返回值在 RAX
7. 组件图 — x86-64 整体架构
x86-64 ISA 由多个核心子系统组成:指令集(含基础指令、扩展指令集、向量指令)、寄存器组、内存管理(分段/分页)、异常与中断机制、系统管理模式等。
8. 包图 — 指令集分类
x86-64 指令集非常丰富,可归为以下大类。`REX` 前缀(0x40-0x4F)用于在 64-bit 模式下扩展寄存器访问到 R8-R15 和 XMM8-XMM15,以及扩展操作数大小为 64 位。
9. 类图 — 寄存器文件
x86-64 相对于 IA-32 的主要变化是通用寄存器从 8 个扩展到 16 个,宽度从 32 位扩展到 64 位,并新增了 8 个 XMM 寄存器(XMM8-XMM15)。
10. 部署图 — x86-64 在计算机系统中的位置
11. 寄存器速查
通用寄存器(GPR)
| 64 位 | 低 32 位 | 低 16 位 | 低 8 位 | 用途(System V ABI) |
|---|---|---|---|---|
RAX | EAX | AX | AL | 返回值 / 累加器 |
RBX | EBX | BX | BL | 被调用者保存 |
RCX | ECX | CX | CL | 第 4 参数(syscall: 返回地址) |
RDX | EDX | DX | DL | 第 3 参数 / 除法高位 |
RSI | ESI | SI | SIL | 第 2 参数 / 源指针 |
RDI | EDI | DI | DIL | 第 1 参数 / 目标指针 |
RSP | ESP | SP | SPL | 栈指针 |
RBP | EBP | BP | BPL | 基址指针(被调用者保存) |
R8 | R8D | R8W | R8B | 第 5 参数 |
R9 | R9D | R9W | R9B | 第 6 参数 |
R10 | R10D | R10W | R10B | 第 4 参数(syscall 时) |
R11 | R11D | R11W | R11B | syscall 时保存 RFLAGS |
R12–R15 | R12D–R15D | — | — | 被调用者保存 |
特殊寄存器
| 寄存器 | 用途 |
|---|---|
RIP | 指令指针(64 位,不能直接访问) |
RFLAGS | 状态与控制标志寄存器(64 位) |
CS/DS/SS/ES/FS/GS | 段寄存器(64 位模式下 FS/GS 仍用于 TLS) |
SIMD 寄存器
| 寄存器 | 数量 | 宽度 | 说明 |
|---|---|---|---|
MMX0–MMX7 | 8 | 64 bit | 别名映射到 x87 FPU 栈(已过时) |
XMM0–XMM15 | 16 | 128 bit | SSE / SSE2 / SSE3 / SSSE3 / SSE4 |
YMM0–YMM15 | 16 | 256 bit | AVX / AVX2(低 128 bit 即 XMM) |
ZMM0–ZMM31 | 32 | 512 bit | AVX-512(需要 CPU 支持) |
系统寄存器(内核专用)
| 寄存器 | 用途 |
|---|---|
CR0 | PE(保护启用)、PG(分页启用)、WP、TS、EM、MP、NE 等 |
CR2 | 页错误线性地址(#PF 异常时自动加载) |
CR3 | 页表物理基址(PML4 基址) |
CR4 | PAE、OSXSAVE、SMEP、SMAP、FSGSBASE 等 |
CR8 | 任务优先级寄存器(TPR) |
IDTR | 中断描述符表寄存器(基址 + 界限) |
GDTR | 全局描述符表寄存器 |
EFER | MSR 0xC0000080:LME(长模式启用)、LMA(激活)、SCE(syscall 启用)、NXE |
STAR/LSTAR/CSTAR/SFMASK | MSR:syscall/sysret 相关配置 |
x87 FPU 栈
| 寄存器 | 数量 | 宽度 | 说明 |
|---|---|---|---|
ST(0)–ST(7) | 8 | 80 bit | x87 浮点栈寄存器;80 位扩展双精度 |
12. 调用约定
System V AMD64 ABI(Linux / macOS / FreeBSD)
| 方面 | 约定 |
|---|---|
| 整数参数 | RDI, RSI, RDX, RCX, R8, R9(左到右),超出 6 个走栈 |
| 浮点参数 | XMM0–XMM7(左到右),超出走栈或 RAX 整型传 |
| 返回值 | RAX(≤8 字节整数/Ptr)、RDX:RAX(128 位整数)、XMM0(float/double)、ST(0)(long double 80-bit) |
| 栈对齐 | 16 字节对齐(调用 call 前 RSP % 16 == 0) |
| 红色区域 | RSP 以下 128 字节可直接被函数使用(不需移动 RSP),仅限 leaf function |
| 被调用者保存 | RBX, RBP, R12–R15 |
| 调用者保存 | RAX, RCX, RDX, RSI, RDI, R8–R11, XMM0–XMM15, ST(0)–ST(7) |
Microsoft x64 Calling Convention(Windows)
| 方面 | 约定 |
|---|---|
| 整数参数 | RCX, RDX, R8, R9(左到右),超出 4 个走栈 |
| 浮点参数 | XMM0–XMM3(左到右),超出走栈 |
| 返回值 | RAX(整数/Ptr)、XMM0(float/double) |
| 栈对齐 | 16 字节对齐(call 前 RSP % 16 == 0) |
| 影子空间 | 每个函数调用必须预留 32 字节栈空间(即使参数 ≤ 4 个),用于被调用者保存寄存器参数(RCX/RDX/R8/R9) |
| 被调用者保存 | RBX, RBP, RDI, RSI, R12–R15, XMM6–XMM15 |
| 调用者保存 | RAX, RCX, RDX, R8–R11, XMM0–XMM5 |
13. 寻址模式
x86-64 在 64-bit 模式下支持以下寻址模式。基址和变址可以用任意 GPR(16 个均可),比例因子为 1/2/4/8。
| 模式 | 语法 | 有效地址计算 |
|---|---|---|
| 立即数 | imm | — |
| 寄存器 | reg | — |
| 直接寻址 | [disp32] | disp32 (RIP-relative in 64-bit mode) |
| 基址 | [base] | base |
| 基址+偏移 | [base + disp] | base + disp |
| 基址+变址 | [base + index] | base + index |
| 变址×比例 | [index * scale] | index × scale |
| 基址+变址×比例 | [base + index * scale] | base + index × scale |
| RIP 相对 | [rip + disp32] | RIP + disp32(64-bit 模式独有,用于位置无关代码 PIC) |
REX 前缀编码
REX 前缀(1 字节,0x40-0x4F)用于 64-bit 模式扩展寄存器寻址:
+---+---+---+---+---+---+---+---+ | 0 | 1 | 0 | 0 | W | R | X | B | +---+---+---+---+---+---+---+---+ W: 操作数宽度 0=32bit 1=64bit R: 扩展 ModRM.reg(与 ModRM reg 构成 4 位) X: 扩展 SIB.index B: 扩展 ModRM.rm 或 SIB.base REX.W=1: 使用 64-bit 操作数 REX.R=1: reg 字段扩展至 R8-R15 REX.X=1: index 字段扩展至 R8-R15 REX.B=1: rm/base 字段扩展至 R8-R15
14. EFLAGS/RFLAGS 标志位
| 位 | 名称 | 含义 |
|---|---|---|
| 0 | CF | 进位标志(无符号溢出) |
| 2 | PF | 奇偶标志(结果低字节中 1 的个数为偶数) |
| 4 | AF | 辅助进位标志(BCD 运算) |
| 6 | ZF | 零标志(结果为零) |
| 7 | SF | 符号标志(结果最高位) |
| 8 | TF | 单步陷阱标志(调试用) |
| 9 | IF | 中断使能标志 |
| 10 | DF | 方向标志(字符串操作方向) |
| 11 | OF | 溢出标志(有符号溢出) |
| 12–13 | IOPL | I/O 权限级别 |
| 14 | NT | 嵌套任务标志 |
| 16 | RF | 恢复标志(屏蔽调试异常) |
| 17 | VM | 虚拟 8086 模式 |
| 18 | AC | 对齐检查 |
| 19 | VIF | 虚拟中断标志 |
| 20 | VIP | 虚拟中断挂起 |
| 21 | ID | CPUID 支持标识 |
15. 汇编示例
Hello World(Linux, System V ABI)
; hello.asm — Linux x86-64, NASM syntax ; nasm -f elf64 hello.asm -o hello.o && ld hello.o -o hello section .data msg db "Hello, x86-64!", 10 len equ $ - msg section .text global _start _start: mov rax, 1 ; syscall: write mov rdi, 1 ; fd = stdout mov rsi, msg ; buf mov rdx, len ; count syscall mov rax, 60 ; syscall: exit xor rdi, rdi ; status = 0 syscall
函数调用(C 调用约定)
; 调用 C 函数 add(a, b) 并返回结果
; int add(int a, int b) { return a + b; }
extern printf
section .data
fmt db "sum=%d", 10, 0
section .text
global main
main:
push rbp
mov rbp, rsp
mov edi, 42 ; a = 42
mov esi, 58 ; b = 58
call sum
mov rsi, rax ; 返回值 -> printf 第2参数
mov rdi, fmt ; 格式串 -> 第1参数
xor eax, eax ; AL=0 表示无浮点参数传给 varargs
call printf
xor eax, eax
pop rbp
ret
sum:
; int sum(int a, int b) — a=edi, b=esi
lea eax, [rdi + rsi]
ret
SIMD 向量加法(SSE2)
; 四个单精度浮点数并行相加 ; c[i] = a[i] + b[i], i=0..3 section .data align 16 a dd 1.0, 2.0, 3.0, 4.0 b dd 5.0, 6.0, 7.0, 8.0 c dd 0, 0, 0, 0 section .text global _start _start: movaps xmm0, [a] ; 加载 4 个 float → XMM0 movaps xmm1, [b] ; 加载 4 个 float → XMM1 addps xmm0, xmm1 ; 并行加法(4 个 float 一次性完成) movaps [c], xmm0 ; 存结果 mov rax, 60 xor rdi, rdi syscall
原子操作(lock cmpxchg)
; 原子地比较并交换 — 实现无锁同步原语 ; lock cmpxchg [mem], reg section .text global atomic_inc atomic_inc: ; void atomic_inc(uint64_t *ptr); ; ptr 在 rdi 中 mov rax, [rdi] ; 读取旧值 .retry: mov rdx, rax ; 保存旧值 inc rdx ; 新值 = 旧值 + 1 lock cmpxchg [rdi], rdx ; [rdi] == rax ? [rdi] = rdx, ZF=1 : rax = [rdi], ZF=0 jnz .retry ; 失败则重试 ret
RDTSC 性能计时
; 读取时间戳计数器 — 高精度性能测量 rdtscp_benchmark: ; 返回时钟周期数到 rax:rdx rdtsc ; 读 TSC → EDX:EAX shl rdx, 32 or rax, rdx ; 合并为 64 位值 ret