x86 汇编语言详细笔记
1. 高级语言与机器代码的对应关系
汇编语言是机器代码的符号化表示,直接对应处理器指令。高级语言(如 C/C++)通过编译器生成汇编代码,再转换为机器码(二进制)。
1.1 编译过程示例
以 C 语言代码为例:
c
int add(int a, int b) {
return a + b;
}
GCC 生成的 AT&T 格式汇编(32 位):
assembly
add:
push %ebp
mov %esp, %ebp
mov 0x8(%ebp), %eax ; 取第一个参数a
add 0xc(%ebp), %eax ; 加上第二个参数b
pop %ebp
ret
对应的机器码(十六进制):
55 89 E5 8B 45 08 03 45 0C 5D C3
55
→push %ebp
89 E5
→mov %esp, %ebp
8B 45 08
→mov 0x8(%ebp), %eax
1.2 反汇编工具
- objdump:
objdump -d 可执行文件
- GDB:
disassemble 函数名
2. AT&T 格式与 Intel 格式的对比
x86 汇编有两种主流语法:AT&T(GCC 默认)和 Intel(NASM/MASM)。
特性 | AT&T 格式 | Intel 格式 |
---|---|---|
操作数顺序 | 源操作数在前,目标在后 | 目标在前,源在后 |
寄存器前缀 | % (如%eax ) | 无(如eax ) |
立即数前缀 | $ (如$0x10 ) | 无(如10h ) |
内存引用符号 | () (如(%eax) ) | [] (如[eax] ) |
指令后缀 | 操作数大小(如movl 表示 32 位) | 无(通过操作数推断,如dword ptr ) |
示例对比
assembly
; AT&T格式
movl $0x10, %eax ; 将立即数0x10加载到eax
addl 4(%ebx), %eax ; eax += *(ebx + 4)
; Intel格式
mov eax, 10h ; 同AT&T第一行
add eax, [ebx + 4] ; 同AT&T第二行
3. 常用 x86 汇编指令
3.1 数据传送指令
指令 | 功能 | 示例 |
---|---|---|
mov | 数据移动 | mov %eax, %ebx (AT&T) |
push | 压栈 | push $0x20 |
pop | 弹栈 | pop %eax |
lea | 加载有效地址 | lea (%eax,%ebx,4), %ecx |
3.2 算术运算
指令 | 功能 | 示例 |
---|---|---|
add | 加法 | add $5, %eax |
sub | 减法 | sub %ebx, %eax |
mul | 无符号乘法(结果在 EDX:EAX) | mul %ebx |
div | 无符号除法(除数在 EDX:EAX) | div %ebx |
3.3 逻辑运算
指令 | 功能 | 示例 |
---|---|---|
and | 按位与 | and $0x0F, %al |
or | 按位或 | or %ebx, %eax |
xor | 按位异或 | xor %eax, %eax (清零 eax) |
not | 按位取反 | not %eax |
3.4 控制流指令
指令 | 功能 | 示例 |
---|---|---|
jmp | 无条件跳转 | jmp label |
call | 调用函数 | call func |
ret | 函数返回 | ret |
cmp | 比较操作数 | cmp $10, %eax |
test | 位测试(常用于逻辑判断) | test %eax, %eax |
3.5 条件跳转指令
指令 | 触发条件 | 说明 |
---|---|---|
je/jz | 相等/结果为 0 | je label |
jne/jnz | 不等/结果非 0 | jne label |
jg | 有符号大于 | jg label |
jl | 有符号小于 | jl label |
ja | 无符号大于 | ja label |
jb | 无符号小于 | jb label |
4. 选择语句的机器级表示
4.1 if-else 语句
C 代码:
c
if (a > b) {
max = a;
} else {
max = b;
}
对应的 AT&T 汇编:
assembly
movl a, %eax ; 加载a到eax
movl b, %ebx ; 加载b到ebx
cmp %ebx, %eax ; 比较eax和ebx(a - b)
jle else_block ; 若a <= b,跳转到else
movl %eax, max ; max = a
jmp end_if ; 跳过else块
else_block:
movl %ebx, max ; max = b
end_if:
4.2 switch-case 语句
编译器通常将switch
转换为跳转表(Jump Table)或连续cmp
指令。
5. 循环语句的机器级表示
5.1 while 循环
C 代码:
c
int i = 0;
while (i < 10) {
sum += i;
i++;
}
对应的 AT&T 汇编:
assembly
movl $0, %eax ; eax = i = 0
movl $0, %ebx ; ebx = sum = 0
loop_start:
cmp $10, %eax ; 比较i和10
jge loop_end ; 若i >= 10,跳出循环
add %eax, %ebx ; sum += i
inc %eax ; i++
jmp loop_start ; 继续循环
loop_end:
5.2 for 循环
C 代码:
c
for (int i=0; i<10; i++) {
sum += i;
}
汇编实现与while
完全相同(编译器优化后)。
6. 内存寻址模式
x86 支持多种寻址方式,用于访问复杂数据结构(如数组、结构体)。
6.1 常见寻址模式
模式 | 示例(AT&T) | 说明 |
---|---|---|
直接寻址 | movl 0x1000, %eax | 访问固定地址 |
寄存器间接寻址 | movl (%ebx), %eax | 地址由寄存器给出 |
基址+偏移 | movl 4(%ebx), %eax | 地址 = ebx + 4 |
基址+变址+比例因子 | movl (%ebx,%ecx,4), %eax | 地址 = ebx + ecx*4 |
6.2 数组访问示例
C 代码:
c
int arr[3] = {10, 20, 30};
int val = arr[2];
AT&T 汇编:
assembly
movl $2, %ecx ; 索引ecx=2
movl arr(,%ecx,4), %eax ; eax = arr[2](地址 = arr + ecx*4)
7. 栈帧与函数调用
7.1 栈帧结构
- ebp:栈基址指针(指向当前栈帧底部)。
- esp:栈顶指针。
7.2 函数调用示例
C 代码:
c
int add(int a, int b) {
return a + b;
}
int main() {
add(3, 5);
}
AT&T 汇编:
assembly
main:
push $5 ; 参数b
push $3 ; 参数a
call add ; 调用函数
add $8, %esp ; 清理栈(平衡堆栈)
add:
push %ebp
mov %esp, %ebp
mov 8(%ebp), %eax ; 取a
add 12(%ebp), %eax ; 加b
pop %ebp
ret
8. 高级主题
8.1 SIMD 指令集
- SSE/AVX:用于向量化计算(如并行处理浮点数)。
- 示例:
movaps
(对齐加载)、addps
(浮点向量加法)。
8.2 内联汇编
在 C 代码中嵌入汇编(GCC 语法):
c
int a = 10, b;
asm("movl %1, %%eax; add $5, %%eax; movl %%eax, %0"
: "=r"(b) // 输出
: "r"(a) // 输入
: "%eax" // 破坏的寄存器
);
9. 调试与分析工具
- GDB:单步调试、查看寄存器和内存。
- strace:跟踪系统调用。
- perf:性能分析。
总结
x86 汇编语言是理解计算机底层运行机制的关键。通过分析高级语言与汇编的对应关系、掌握常用指令和控制结构,能够深入优化代码、分析二进制程序或进行逆向工程。实践中建议结合反汇编工具和调试器逐步验证逻辑。