assembly-x86

Installation
SKILL.md

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
Related skills

More from killvxk/low-level-dev-skills-zh

Installs
1
First Seen
Mar 21, 2026