assembly-x86
x86-64 汇编
用途
指导代理学习 x86-64 汇编:阅读编译器输出、理解 ABI、编写内联汇编,以及常见汇编模式。
触发场景
- "如何阅读 GCC 生成的汇编?"
- "x86-64 有哪些寄存器?"
- "Linux/macOS 上的调用约定是什么?"
- "如何在 C 中编写内联汇编?"
- "如何使用 SSE/AVX 内联函数?"
- "这段汇编用了
%rsp/%rbp— 是什么意思?"
工作流程
1. 生成和阅读汇编
# AT&T 语法(GCC 默认)
gcc -S -O2 -fverbose-asm foo.c -o foo.s
# Intel 语法
gcc -S -masm=intel -O2 foo.c -o foo.s
# 使用 GDB
(gdb) disassemble /s main # 带源码
(gdb) x/20i $rip
# 使用 objdump
objdump -d -M intel -S prog # Intel 语法 + 源码(需 -g)
2. x86-64 寄存器
| 64 位 | 32 位 | 16 位 | 高 8 位 | 低 8 位 | 用途 |
|---|---|---|---|---|---|
%rax |
%eax |
%ax |
%ah |
%al |
返回值 / 累加器 |
%rbx |
%ebx |
%bx |
%bh |
%bl |
被调用者保存 |
%rcx |
%ecx |
%cx |
%ch |
%cl |
第 4 个参数 / 计数 |
%rdx |
%edx |
%dx |
%dh |
%dl |
第 3 个参数 / 第 2 个返回值 |
%rsi |
%esi |
%si |
— | %sil |
第 2 个参数 |
%rdi |
%edi |
%di |
— | %dil |
第 1 个参数 |
%rbp |
%ebp |
%bp |
— | %bpl |
帧指针(被调用者保存) |
%rsp |
%esp |
%sp |
— | %spl |
栈指针 |
%r8–%r11 |
%r8d–%r11d |
%r8w–%r11w |
— | %r8b–%r11b |
第 5–8 个参数 / 调用者保存 |
%r12–%r15 |
%r12d–%r15d |
%r12w–%r15w |
— | %r12b–%r15b |
被调用者保存 |
%rip |
指令指针 | ||||
%rflags |
%eflags |
状态标志 | |||
%xmm0–%xmm7 |
FP/SIMD 参数和返回值 | ||||
%xmm8–%xmm15 |
调用者保存的 SIMD | ||||
%ymm0–%ymm15 |
AVX 256 位 | ||||
%zmm0–%zmm31 |
AVX-512 512 位 |
3. System V AMD64 ABI(Linux, macOS, FreeBSD)
整数/指针参数寄存器(按顺序):
%rdi, %rsi, %rdx, %rcx, %r8, %r9
浮点参数寄存器:
%xmm0–%xmm7
返回值:
- 整数:
%rax(低位),%rdx(高位,128 位时) - 浮点:
%xmm0(低位),%xmm1(高位)
调用者保存(临时): %rax, %rcx, %rdx, %rsi, %rdi, %r8–%r11, %xmm0–%xmm15
被调用者保存(必须保留): %rbx, %rbp, %r12–%r15
栈: 在 call 之前必须 16 字节对齐;call 压入 8 字节 → 函数入口处序言后仍 16 字节对齐。
红区(Red zone): 叶子函数可以不调整 %rsp 而直接使用其下方 128 字节。内核/信号处理函数中不可用。
4. 常见指令模式
| 模式 | 含义 |
|---|---|
mov %rdi, %rax |
将 rdi 复制到 rax |
mov (%rdi), %rax |
从 rdi 所指地址加载 8 字节 |
mov %rax, 8(%rdi) |
将 rax 存储到 rdi+8 |
lea 8(%rdi), %rax |
将有效地址 rdi+8 加载到 rax(无内存访问) |
push %rbx |
压入 rbx;rsp -= 8 |
pop %rbx |
弹出到 rbx;rsp += 8 |
call foo |
压入返回地址;跳转到 foo |
ret |
弹出返回地址;跳转到该地址 |
xor %eax, %eax |
将 rax 清零(比 mov $0, %rax 编码更短) |
test %rax, %rax |
若 rax == 0 则设置 ZF(比 cmp $0, %rax 更快) |
cmp $5, %rdi |
为 rdi - 5 设置标志位 |
jl label |
有符号小于则跳转 |
5. AT&T 与 Intel 语法
| 特性 | AT&T | Intel |
|---|---|---|
| 操作数顺序 | 源操作数, 目的操作数 | 目的操作数, 源操作数 |
| 寄存器前缀 | %rax |
rax |
| 立即数前缀 | $42 |
42 |
| 内存操作数 | 8(%rdi) |
[rdi+8] |
| 大小后缀 | movl, movq |
— (由上下文推断) |
GCC 默认生成 AT&T 语法。使用 -masm=intel 切换到 Intel 语法。
6. 内联汇编(GCC 扩展 asm)
// 基础示例:递增一个寄存器
int x = 5;
__asm__ volatile (
"incl %0"
: "=r"(x) // 输出:=r 表示只写寄存器
: "0"(x) // 输入:0 表示与输出 0 相同
: // clobbers:无
);
// CPUID 示例
uint32_t eax, ebx, ecx, edx;
__asm__ volatile (
"cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(1) // 输入:叶子 1
);
// 原子递增
static inline int atomic_inc(volatile int *p) {
int ret;
__asm__ volatile (
"lock; xaddl %0, %1"
: "=r"(ret), "+m"(*p)
: "0"(1)
: "memory"
);
return ret + 1;
}
约束代码:
"r"— 任意通用寄存器"m"— 内存操作数"i"— 整数立即数"a","b","c","d"— 特定寄存器(%rax, %rbx, %rcx, %rdx)"="前缀 — 输出(只写)"+"前缀 — 读写"memory"clobber — 告知编译器内存可能被修改(屏障)
7. SSE/AVX 内联函数(优先于内联汇编)
#include <immintrin.h> // 包含所有 x86 SIMD 头文件
// 使用 AVX 同时对 8 个浮点数求和
__m256 a = _mm256_loadu_ps(arr_a); // 加载 8 个浮点数(非对齐)
__m256 b = _mm256_loadu_ps(arr_b);
__m256 c = _mm256_add_ps(a, b);
_mm256_storeu_ps(result, c);
编译时检查 CPU 支持:-mavx2 或 -march=native。
运行时检查:__builtin_cpu_supports("avx2")。
完整的寄存器和指令参考,请参阅 references/reference.md。
相关技能
- 使用
skills/low-level-programming/assembly-arm了解 AArch64/ARM 汇编 - 使用
skills/compilers/gcc了解-S -masm=intel标志详情 - 使用
skills/debuggers/gdb逐步执行汇编(si,ni,x/i)
More from killvxk/low-level-dev-skills-zh
binutils
GNU binutils 二进制操作与分析技能。适用场景:使用 ar 管理静态库、使用 strip 或 objcopy 处理二进制文件、使用 addr2line 将地址转换为源码位置、使用 strings 提取文本、或使用 c++filt 对 C++ 名称进行反混淆。触发条件:涉及 ar、strip、objcopy、addr2line、strings、c++filt、ranlib 或二进制后处理任务的查询。
1ebpf
Linux 可观测性和网络的 eBPF 技能。适用场景:使用 libbpf 或 bpftrace 编写 eBPF 程序、挂载 kprobe/tracepoint/XDP 钩子、调试验证器错误、使用 eBPF map,或实现跨内核版本的 CO-RE 可移植性。触发条件:查询 eBPF、bpftool、bpftrace、XDP 程序、libbpf、验证器错误、eBPF map 或使用 BPF 进行内核追踪相关问题。
1clang
C/C++ 项目的 Clang/LLVM 编译器技能。适用场景:使用 clang 或 clang++ 进行诊断、sanitizer 插桩、优化备注、通过 clang-tidy 进行静态分析、通过 lld 实现 LTO,或从 GCC 迁移到 Clang。触发条件:涉及 clang 标志、clang-tidy、clang-format、更好的错误信息、Apple/FreeBSD 工具链或 LLVM 特定优化的查询。涵盖标志选择、诊断调优及与 LLVM 工具的集成。
1gcc
C/C++ 项目的 GCC 编译器技能。适用场景:选择优化级别、警告标志、调试构建、LTO、sanitizer 插桩,或诊断 GCC 编译错误。涵盖调试与发布构建的标志选择、ABI 问题、预处理器宏、配置引导优化(PGO)及与构建系统的集成。触发条件:涉及 gcc 标志、编译错误、性能调优、警告抑制或跨标准编译的查询。
1cmake
C/C++ 项目的 CMake 构建系统技能。适用场景:编写或重构 CMakeLists.txt、配置源外构建、选择生成器(Ninja、Make、VS)、使用 target_link_libraries 管理目标和依赖、通过 find_package 或 FetchContent 集成外部包、启用 Sanitizer、为交叉编译配置工具链文件,或导出 CMake 包。触发条件:涉及 CMakeLists.txt、cmake 配置错误、目标属性、安装规则、CPack 或 CMake Presets 的查询。
1cpp-modules
现代 C++ 项目的 C++20 模块技能。适用场景:使用命名模块、模块分区、头文件单元、CMake MODULE_SOURCES、Clang -fmodules-ts、BMI 缓存问题,或从头文件迁移到模块。触发条件:涉及 C++20 模块、import 语句、模块接口单元、头文件单元或 BMI 文件的查询。
1