用途
本技能引导代理处理 x86-64 汇编:阅读编译器输出、理解 ABI、编写内联汇编,以及掌握常见汇编模式。
适用触发场景包括:如何阅读 GCC 生成的汇编?x86-64 有哪些寄存器?Linux/macOS 上的调用约定是什么?如何在 C 中编写内联汇编?如何使用 SSE/AVX 内联函数?汇编中的 %rsp、%rbp 是什么意思?
生成与阅读汇编
- 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)
x86-64 寄存器
资料给出寄存器在各宽度下的名称与用途:
- %rax / %eax / %ax / %ah / %al:返回值/累加器
- %rbx / %ebx / %bx / %bh / %bl:被调用者保存
- %rcx / %ecx / %cx / %ch / %cl:第 4 个参数/计数
- %rdx / %edx / %dx / %dh / %dl:第 3 个参数/第二个返回值
- %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(及对应 32/16/8 位形式):被调用者保存
- %rip:指令指针
- %rflags / %eflags:状态标志
- %xmm0–%xmm7:浮点/SIMD 参数与返回值
- %xmm8–%xmm15:调用者保存的 SIMD
- %ymm0–%ymm15:AVX 256 位
- %zmm0–%zmm31:AVX-512 512 位
System V AMD64 ABI(Linux、macOS、FreeBSD)
- 整数/指针参数寄存器(按顺序):%rdi, %rsi, %rdx, %rcx, %r8, %r9
- 浮点参数寄存器:%xmm0–%xmm7
- 返回值:整数用 %rax(低位)、%rdx(128 位时的高位);浮点用 %xmm0(低位)、%xmm1(高位)
- 调用者保存(scratch):%rax, %rcx, %rdx, %rsi, %rdi, %r8–%r11, %xmm0–%xmm15
- 被调用者保存(必须保留):%rbx, %rbp, %r12–%r15
- 栈:调用前 16 字节对齐;call 压入 8 字节,因此函数入口在序言后 16 字节对齐
- 红区:叶子函数可不调整 %rsp 而使用 %rsp 以下 128 字节;内核/信号处理器中不可用
常见指令模式
资料列出以下模式及其含义:
mov %rdi, %rax:把 rdi 复制到 raxmov (%rdi), %rax:从 rdi 中的地址加载 8 字节mov %rax, 8(%rdi):把 rax 存储到 rdi+8lea 8(%rdi), %rax:把有效地址 rdi+8 加载到 rax(不访问内存)push %rbx:压入 rbx;rsp -= 8pop %rbx:弹出到 rbx;rsp += 8call foo:压入返回地址;跳到 fooret:弹出返回地址;跳转到它xor %eax, %eax:清零 rax(编码比mov $0, %rax更小)test %rax, %rax:rax == 0 时设置 ZF(比cmp $0, %rax更省)cmp $5, %rdi:为 rdi - 5 设置标志jl label:有符号小于时跳转
AT&T 与 Intel 语法
资料给出对照:
- 操作数顺序:AT&T 为 source, dest;Intel 为 dest, source
- 寄存器前缀:AT&T 为 %rax;Intel 为 rax
- 立即数前缀:AT&T 为 $42;Intel 为 42
- 内存操作数:AT&T 为 8(%rdi);Intel 为 [rdi+8]
- 大小后缀:AT&T 为 movl、movq;Intel 无(推断得出)
GCC 默认输出 AT&T;使用 -masm=intel 切换为 Intel 语法。
内联汇编(GCC 扩展 asm)
资料给出三个示例:
基本寄存器递增:
int x = 5;
__asm__ volatile (
"incl %0"
: "=r"(x) // outputs: =r means write-only register
: "0"(x) // inputs: 0 means same as output 0
: // clobbers: none
);
CPUID 示例:
uint32_t eax, ebx, ecx, edx;
__asm__ volatile (
"cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(1) // input: leaf 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:告知编译器内存可能被修改(屏障)
SSE/AVX 内联函数(优先于内联汇编)
#include <immintrin.h> // includes all x86 SIMD headers
// Add 8 floats at once with AVX
__m256 a = _mm256_loadu_ps(arr_a); // load 8 floats (unaligned)
__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)
限制
资料未说明是否需要特定平台、工具版本或额外依赖,也未提供收费、价格或效果相关信息。