x86-64汇编从入门到精通

📅 2026/8/3 5:14:24 👁️ 阅读次数 📝 编程学习
x86-64汇编从入门到精通

x86-64汇编从入门到精通

x86-64(也称AMD64或Intel 64)是目前个人电脑和服务器领域最主流的指令集架构。无论是进行底层性能优化、操作系统开发、逆向工程,还是深入理解计算机体系结构,掌握x86-64汇编都是一项关键技能。本文将系统性地带你从零基础走向实战。


第一部分:为什么学习x86-64汇编?

汇编语言是连接软件与硬件的桥梁。学习x86-64汇编的价值体现在多个层面:首先,它让你能够写出极致高性能的代码,在关键路径上榨干硬件潜能;其次,它是安全研究和逆向工程的必备工具;最后,深入理解汇编能让你写出更高质量的C/C++/Rust代码,因为你清楚编译器在背后做了什么。

x86-64架构统治了数十年桌面和服务器市场,掌握了它就能深入理解绝大多数生产环境的底层运行机制。


第二部分:前置知识与环境准备

2.1 前置知识要求

学习本系列内容前,建议具备以下基础:

  • 能看懂C语言编写的程序
  • 了解计算机体系结构的基本概念(寄存器、内存、栈等)
  • 能够简单使用命令行进行操作

2.2 开发环境搭建

Linux环境(推荐):

  • 编译器:GCC(gcc)或Clang
  • 汇编器:GAS(GNU Assembler,as)或NASM
  • 调试器:GDB
  • 反汇编工具:objdump

macOS环境

  • 编译器:Clang(Xcode自带)
  • 汇编器:AS(GNU assembler,随Xcode安装)
  • 调试器:LLDB

Windows环境

  • 可使用MinGW、Cygwin或WSL2(推荐)
  • MASM(微软宏汇编器)或NASM

验证环境:

gcc--versionas--versiongdb--version

第三部分:x86-64编程模型

3.1 寄存器一览

x86-64的寄存器体系需要重点掌握:

通用寄存器(64位):

64位32位16位8位(低)用途
RAXEAXAXAL累加器,返回值
RBXEBXBXBL被调用者保存
RCXECXCXCL计数器,第4个参数
RDXEDXDXDL第3个参数
RSIESISISIL第2个参数
RDIEDIDIDIL第1个参数
RBPEBPBPBPL帧指针(被调用者保存)
RSPESPSPSPL栈指针
R8-R15R8D-R15DR8W-R15WR8B-R15B扩展寄存器

特殊寄存器

寄存器用途
RIP指令指针(程序计数器)
RFLAGS状态标志寄存器
XMM0-XMM15128位SIMD寄存器(浮点/向量)
YMM0-YMM15256位SIMD寄存器(AVX)
ZMM0-ZMM15512位SIMD寄存器(AVX-512)

宽度变体

  • 写32位寄存器(EAX)会自动将高32位清零
  • 写16位/8位寄存器不影响高48/56位

3.2 System V AMD64 ABI调用约定

理解调用约定是编写正确汇编函数的关键(Linux/macOS适用):

参数传递

  • 第1个参数:RDI
  • 第2个参数:RSI
  • 第3个参数:RDX
  • 第4个参数:RCX(Linux)/ R10(macOS)
  • 第5个参数:R8
  • 第6个参数:R9
  • 第7个及以后参数:通过栈传递
  • 浮点/SIMD参数:XMM0-XMM7
  • 返回值:RAX(整数),XMM0(浮点)

保存规则

  • 被调用者保存(需在函数中保护):RBX, RBP, R12-R15
  • 调用者保存(不用保护):RAX, RCX, RDX, RDI, RSI, R8-R11, XMM0-XMM15

栈规则:执行CALL前RSP必须16字节对齐

3.3 与AArch64的主要区别

特性x86-64AArch64
寄存器数量16个通用寄存器31个通用寄存器
指令编码变长(1-15字节)固定32位
栈增长方向向下(地址减小)向下(地址减小)
参数传递RDI, RSI, RDX, RCX, R8, R9X0-X7
返回值RAXX0
帧指针RBP(可选)X29(固定)
分支指令JMP, JccB, B.cond

第四部分:x86-64指令集入门

4.1 指令格式概览

x86指令采用变长编码,典型格式为:

[opcode] [dest], [src1], [src2]

示例(AT&T语法):

movq %rax, %rbx ; RBX = RAX addq $10, %rax ; RAX += 10

示例(Intel语法):

mov rbx, rax ; RBX = RAX add rax, 10 ; RAX += 10

两种语法对比

AT&T语法Intel语法
源操作数在前,目标在后目标在前,源在后
寄存器前加%寄存器不加%
立即数前加$立即数不加$
内存地址用()内存地址用[]
movl $1, (%rax)mov dword [rax], 1

本文主要使用Intel语法(更直观)。

4.2 寻址模式

模式示例有效地址
立即数mov rax, 42直接使用42
寄存器mov rax, rbxRBX的值
直接内存mov rax, [0x1000]地址0x1000的内存
寄存器间接mov rax, [rbx]RBX指向的内存
基址+偏移mov rax, [rbx+16]RBX+16
基址+索引mov rax, [rbx+rcx*8]RBX+RCX*8
基址+索引+偏移mov rax, [rbx+rcx*4+8]RBX+RCX*4+8
RIP相对mov rax, [rip+offset]RIP+offset

4.3 数据宽度后缀

x86指令需要指明操作数宽度:

后缀宽度示例
b8位(字节)movb al, bl
w16位(字)movw ax, bx
l32位(双字)movl eax, ebx
q64位(四字)movq rax, rbx

第五部分:常用指令大全

5.1 数据传送指令

指令描述示例
MOV传送数据mov rax, rbx
MOVZX零扩展传送movzx rax, byte [rsi]
MOVSX符号扩展传送movsx rax, byte [rsi]
MOVSXD符号扩展32位到64位movsxd rax, eax
LEA加载有效地址lea rax, [rbx+rcx*4]
XCHG交换数据xchg rax, rbx
PUSH压栈push rax
POP出栈pop rax
PUSHF压入标志寄存器pushf
POPF弹出标志寄存器popf

LEA的妙用:不访问内存,只计算地址,常用于快速算术运算:

lea rax, [rcx+rcx*4] ; RAX = RCX * 5 lea rax, [rax+rax*2] ; RAX = RAX * 3

5.2 算术运算指令

指令描述示例
ADD加法add rax, rbx
ADC带进位加法adc rax, rbx
SUB减法sub rax, rbx
SBB带借位减法sbb rax, rbx
INC自增1inc rax
DEC自减1dec rax
IMUL有符号乘法imul rax, rbx
MUL无符号乘法mul rbx
IDIV有符号除法idiv rbx
DIV无符号除法div rbx
NEG取负neg rax
CMP比较cmp rax, rbx

注意MULDIV使用RAX和RDX:

  • MUL rbx:RDX:RAX = RAX * RBX
  • DIV rbx:RAX = RDX:RAX / RBX,RDX = 余数

5.3 逻辑运算指令

指令描述示例
AND按位与and rax, rbx
OR按位或or rax, rbx
XOR按位异或xor rax, rax
NOT按位取反not rax
TEST测试(与AND同,不写结果)test rax, rax
SHL逻辑左移shl rax, 2
SHR逻辑右移shr rax, 2
SAR算术右移(符号扩展)sar rax, 2
ROL循环左移rol rax, 1
ROR循环右移ror rax, 1

5.4 分支与跳转指令

指令描述示例
JMP无条件跳转jmp label
CALL调用函数call func
RET从函数返回ret
JE/JZ相等/零跳转je label
JNE/JNZ不相等/非零跳转jne label
JG/JNLE有符号大于跳转jg label
JGE/JNL有符号大于等于跳转jge label
JL/JNGE有符号小于跳转jl label
JLE/JNG有符号小于等于跳转jle label
JA/JNBE无符号大于跳转ja label
JAE/JNB无符号大于等于跳转jae label
JB/JNAE无符号小于跳转jb label
JBE/JNA无符号小于等于跳转jbe label

条件跳转的反向:了解对立条件便于代码优化

条件对立条件
JE (相等)JNE (不相等)
JG (有符号大于)JLE (有符号小于等于)
JL (有符号小于)JGE (有符号大于等于)
JA (无符号大于)JBE (无符号小于等于)
JB (无符号小于)JAE (无符号大于等于)

5.5 标志寄存器与条件码

RFLAGS寄存器中的关键标志位:

标志含义设置条件
ZF(零标志)结果为零运算结果等于0
SF(符号标志)结果为负最高位为1
CF(进位标志)无符号溢出运算产生进位/借位
OF(溢出标志)有符号溢出符号位错误
PF(奇偶标志)低8位含偶数个1偶数个1

5.6 位操作与条件传送指令

指令描述示例
BSWAP字节序反转bswap rax
BT位测试bt rax, 3
BTS位测试并置1bts rax, 3
BTR位测试并清零btr rax, 3
BTC位测试并取反btc rax, 3
CMOVcc条件传送cmovg rax, rbx
SETcc条件设置setg al

CMOV示例

cmp rax, rbx cmovl rax, rbx ; if (rax < rbx) rax = rbx(取最大值)

5.7 栈操作指令

指令描述等效操作
PUSH reg压栈sub rsp, 8; mov [rsp], reg
POP reg出栈mov reg, [rsp]; add rsp, 8
PUSHQ imm压入立即数sub rsp, 8; mov [rsp], imm
ENTER创建栈帧少用
LEAVE销毁栈帧mov rsp, rbp; pop rbp

第六部分:第一个汇编程序

6.1 Hello World(Linux)

文件:hello.s

.section .text .globl _start _start: ; write(1, msg, 14) mov $1, %rax ; 系统调用号1 = write mov $1, %rdi ; 文件描述符1 = stdout lea msg(%rip), %rsi ; 消息地址 mov $14, %rdx ; 消息长度 syscall ; 系统调用 ; exit(0) mov $60, %rax ; 系统调用号60 = exit xor %rdi, %rdi ; 返回值0 syscall .section .data msg: .ascii "Hello, x86-64!\\n"

编译与运行

as-ohello.o hello.s ld-ohello hello.o ./hello

6.2 简单加法函数

汇编文件 add.s(Linux/macOS):

.section .text .globl add add: ; int add(int a, int b) -> a在RDI, b在RSI mov %edi, %eax ; EAX = a add %esi, %eax ; EAX += b ret

C文件 main.c

#include<stdio.h>externintadd(inta,intb);intmain(){intresult=add(3,5);printf("Result: %d\\n",result);return0;}

编译与运行

gcc-cadd.s-oadd.o gcc-oprog main.c add.o ./prog

第七部分:栈帧管理

7.1 函数入口与出口模式

标准函数序言(Prologue):

func_name: push rbp ; 保存旧的帧指针 mov rbp, rsp ; 设置新的帧指针 sub rsp, frame_size ; 分配局部变量空间

标准函数尾声(Epilogue):

mov rsp, rbp ; 释放局部变量空间 pop rbp ; 恢复帧指针 ret

7.2 局部变量访问

func: push rbp mov rbp, rsp sub rsp, 32 ; 分配32字节局部空间 mov dword [rbp-4], 10 ; 局部变量1 = 10 mov dword [rbp-8], 20 ; 局部变量2 = 20 mov eax, [rbp-4] ; 加载局部变量1 add eax, [rbp-8] ; 加上局部变量2 mov rsp, rbp pop rbp ret

7.3 保存被调用者保存寄存器

func: push rbp mov rbp, rsp sub rsp, 64 push rbx ; 保存被调用者保存寄存器 push r12 push r13 push r14 push r15 ; 使用RBX, R12-R15寄存器... pop r15 ; 恢复(注意顺序相反) pop r14 pop r13 pop r12 pop rbx mov rsp, rbp pop rbp ret

第八部分:SIMD与浮点运算

8.1 XMM/YMM/ZMM寄存器

x86-64支持多种SIMD扩展:

扩展寄存器宽度数据类型
SSEXMM0-XMM15128位单精度浮点(4个)
SSE2XMM0-XMM15128位双精度浮点(2个)
AVXYMM0-YMM15256位8个单精度/4个双精度
AVX-512ZMM0-ZMM15512位16个单精度/8个双精度

8.2 浮点运算指令

指令描述示例
MOVSS加载/存储单精度movss xmm0, [mem]
MOVSD加载/存储双精度movsd xmm0, [mem]
ADDSS单精度加法addss xmm0, xmm1
ADDSD双精度加法addsd xmm0, xmm1
SUBSS单精度减法subss xmm0, xmm1
SUBSD双精度减法subsd xmm0, xmm1
MULSS单精度乘法mulss xmm0, xmm1
MULSD双精度乘法mulsd xmm0, xmm1
DIVSS单精度除法divss xmm0, xmm1
DIVSD双精度除法divsd xmm0, xmm1
SQRTSS单精度平方根sqrtss xmm0, xmm1
SQRTSD双精度平方根sqrtsd xmm0, xmm1
CVTSI2SS整数转单精度浮点cvtsi2ss xmm0, eax
CVTSS2SI单精度浮点转整数cvtss2si eax, xmm0
CVTSI2SD整数转双精度浮点cvtsi2sd xmm0, eax
CVTSD2SI双精度浮点转整数cvtsd2si eax, xmm0
UCOMISS比较单精度ucomiss xmm0, xmm1
UCOMISD比较双精度ucomisd xmm0, xmm1

8.3 浮点条件分支

ucomisd xmm0, xmm1 ; 比较xmm0和xmm1 ja greater ; 无符号大于(实际用于浮点比较) jb less je equal

8.4 SSE/AVX向量运算

128位SSE向量示例

movaps xmm0, [rsi] ; 加载4个单精度到xmm0 movaps xmm1, [rdi] ; 加载4个单精度到xmm1 addps xmm0, xmm1 ; 4个单精度同时相加 movaps [rdx], xmm0 ; 存储结果

256位AVX示例

vmovaps ymm0, [rsi] ; 加载8个单精度到ymm0 vmovaps ymm1, [rdi] ; 加载8个单精度到ymm1 vaddps ymm0, ymm0, ymm1 ; 8个单精度同时相加 vmovaps [rdx], ymm0 ; 存储结果

向量点积示例

xorps xmm0, xmm0 ; 清零累加器 mov rcx, 8 ; 循环8次 loop: movss xmm1, [rsi] ; 加载a[i] mulss xmm1, [rdi] ; a[i] * b[i] addss xmm0, xmm1 ; sum += 乘积 add rsi, 4 add rdi, 4 loop loop ret

第九部分:条件分支与循环

9.1 if-else结构

C代码

if(a>b){result=a;}else{result=b;}

对应汇编

cmp eax, ebx jle else ; if (a <= b) goto else mov ecx, eax ; result = a jmp done else: mov ecx, ebx ; result = b done: mov eax, ecx ; 返回result

9.2 for循环

C代码

intsum=0;for(inti=0;i<100;i++){sum+=i;}

对应汇编

xor eax, eax ; sum = 0 xor ecx, ecx ; i = 0 loop_start: cmp ecx, 100 jge loop_end ; if i >= 100, exit add eax, ecx ; sum += i inc ecx ; i++ jmp loop_start loop_end: ret

9.3 while循环

C代码

while(n>0){result+=n;n--;}

对应汇编

loop_start: cmp rdi, 0 jle loop_end add rax, rdi dec rdi jmp loop_start loop_end: ret

9.4 循环优化:使用LOOP指令

LOOP指令等价于dec rcx; jnz target

mov rcx, 100 loop_start: ; 循环体 loop loop_start

注意:LOOP在现代CPU上性能通常不如显式的DEC+JNZ,不推荐在性能敏感代码中使用。


第十部分:实战示例

10.1 字符串长度计算

.globl strlen strlen: xor rax, rax ; len = 0 mov rcx, -1 ; 计数器 xor al, al ; 搜索NULL repne scasb ; 扫描字符串 not rcx ; 补码 dec rcx ; 减去终止符 mov rax, rcx ret

10.2 数组求和

.globl array_sum ; int64_t array_sum(int64_t *arr, int64_t len) array_sum: xor rax, rax ; sum = 0 xor rcx, rcx ; i = 0 loop: cmp rcx, rsi jge done add rax, [rdi+rcx*8] ; sum += arr[i] inc rcx jmp loop done: ret

10.3 冒泡排序

.globl bubble_sort ; void bubble_sort(int *arr, int n) bubble_sort: push rbp mov rbp, rsp push rbx mov rcx, rsi ; outer = n dec rcx ; outer = n-1 outer_loop: cmp rcx, 0 jle done xor rdx, rdx ; i = 0 inner_loop: cmp rdx, rcx jge next_outer mov eax, [rdi+rdx*4] ; arr[i] mov ebx, [rdi+rdx*4+4] ; arr[i+1] cmp eax, ebx jle no_swap mov [rdi+rdx*4], ebx ; 交换 mov [rdi+rdx*4+4], eax no_swap: inc rdx jmp inner_loop next_outer: dec rcx jmp outer_loop done: pop rbx pop rbp ret

10.4 SIMD优化数组点积

.globl dot_product ; float dot_product(float *a, float *b, int len) dot_product: xorps xmm0, xmm0 ; sum = 0 xor rax, rax ; i = 0 loop: cmp rax, rdx jge done movss xmm1, [rdi+rax*4] mulss xmm1, [rsi+rax*4] addss xmm0, xmm1 inc rax jmp loop done: ret ; SSE向量化优化版本 .globl dot_product_sse dot_product_sse: xorps xmm0, xmm0 ; 累加器清零 xor rax, rax ; i = 0 mov rcx, rdx shr rcx, 2 ; 每次处理4个 jz remainder loop4: movaps xmm1, [rdi+rax*4] mulps xmm1, [rsi+rax*4] addps xmm0, xmm1 add rax, 4 loop loop4 ; 水平相加 haddps xmm0, xmm0 haddps xmm0, xmm0 ; 处理剩余元素 remainder: ; ... 处理余数 ret

第十一部分:系统调用(Linux)

11.1 Linux系统调用速查表

x86-64系统调用使用syscall指令,参数通过寄存器传递:

参数寄存器
系统调用号RAX
第1个参数RDI
第2个参数RSI
第3个参数RDX
第4个参数R10
第5个参数R8
第6个参数R9
返回值RAX

常用系统调用

调用名RAX值说明
read0读取文件
write1写入文件
open2打开文件
close3关闭文件
stat4获取文件状态
fstat5获取文件状态(文件描述符)
mmap9内存映射
mprotect10修改内存保护
brk12调整数据段大小
socket41创建socket
connect42连接socket
sendto44发送数据
recvfrom45接收数据
exit60退出进程
getpid39获取进程ID
fork57创建子进程
execve59执行程序

11.2 使用系统调用读取文件

.section .text .globl _start _start: ; open("test.txt", O_RDONLY) mov $2, %rax lea filename(%rip), %rdi xor %rsi, %rsi syscall ; read(fd, buffer, 128) mov %rax, %rdi ; fd = 返回值 mov $0, %rax lea buffer(%rip), %rsi mov $128, %rdx syscall ; write(1, buffer, bytes_read) mov %rax, %rdx mov $1, %rax mov $1, %rdi lea buffer(%rip), %rsi syscall ; exit mov $60, %rax xor %rdi, %rdi syscall .section .data filename: .asciz "test.txt" .section .bss buffer: .space 128

第十二部分:调试技巧

12.1 使用GDB调试

gcc-g-oprog main.c add.s gdb ./prog(gdb)breakadd(gdb)run(gdb)info registers(gdb)p$rax(gdb)set$rax=42(gdb)disassemble(gdb)stepi(gdb)continue

常用GDB命令

命令描述
info registers显示所有寄存器
info registers rax显示RAX
p $rax打印RAX值
set $rax = 42设置RAX=42
x/10x $rsp以16进制显示栈上10个字
x/s $rdi显示字符串
disassemble反汇编当前函数
disassemble main反汇编main函数
stepi单步执行一条指令
nexti单步执行(跳过子调用)
break *0x401000在地址设断点
watch *0x1000监视内存地址

12.2 查看汇编输出

gcc-S-O2main.c# 生成汇编代码objdump-dmain# 反汇编可执行文件objdump-d-Mintel main# Intel语法反汇编gdb-batch-ex"disassemble main"./prog

第十三部分:常用伪指令与汇编器指令

13.1 数据定义(GAS)

指令描述示例
.byte定义8位数据.byte 0x01, 0x02
.short定义16位数据.short 0x1234
.word定义32位数据.word 0x12345678
.long定义32位数据.long 0x12345678
.quad定义64位数据.quad 0x1234567890ABCDEF
.ascii定义字符串.ascii "Hello"
.asciz定义字符串(自动添加NULL).asciz "Hello"
.space预留空间.space 100
.rept重复定义.rept 4; .byte 0; .endr

13.2 分段指令

指令描述
.text代码段
.data初始化数据段
.rodata只读数据段
.bss未初始化数据段
.section .note.GNU-stack声明栈不需要可执行权限

13.3 其他常用指令

指令描述
.globl symbol导出符号(全局可见)
.hidden symbol导出为隐藏符号
.align n按n字节对齐
.p2align n按2^n字节对齐
.size symbol, size设置符号大小
.type symbol, @function设置符号类型为函数

第十四部分:性能优化技巧

  1. 减少内存访问:尽量使用寄存器,将频繁访问的变量保存在寄存器中
  2. 指令并行:避免指令间数据依赖,提升流水线效率
  3. 使用SIMD:批量处理数据,单指令多数据提升吞吐量
  4. 分支预测优化:将常见路径放在前面,减少分支误预测
  5. 缓存对齐:数据按64字节(缓存行大小)对齐避免伪共享
  6. 减少函数调用开销:内联小函数或使用__attribute__((always_inline))
  7. 使用LEA进行简单算术:比ADD+MUL组合更快
  8. 编译器优化:使用-O2-O3让编译器自动优化

附录A:x86-64指令速查表

类别指令示例功能
传送MOV, LEA, XCHG数据传送、地址计算
算术ADD, SUB, MUL, DIV加、减、乘、