做编译器的代码生成器部分的时候一定会使用到汇编代码,特在此总结一些简单的汇编语言知识点。以能看懂常见的汇编代码为目的,不做深入探究。
学习一种的汇编语言,必须了解这种 CPU 的寄存器、寻址方式以及各种指令。我们就先从寄存器开始着手吧。
通用寄存器
32位x86架构的CPU有8个通用寄存器:
通用寄存器 段寄存器
- AX: Accumulator register (累加器 CS 代码段)
- CX: Counter register (计数器 SS 堆栈段)
- DX: Data register (数据 ES 附加段)
- BX: Base register (基址 DS 数据段)
指针寄存器 堆栈寄存器
- SP: Stack pointer register (栈指针,指向栈顶)
- BP: Stack base pointer register (基址指针,指向当前stack frame的底部)
- SI: Source index register (源索引指针)
- DI: Destination index register (目的索引指针)
以上名称对应的寄存器均为16位,在32位模式下,上面的寄存器名称前需要加E,64位模式下需要加R。例如,访问32位的AX寄存器需要使用名称EAX,而访问64位(x86_64)的需要使用RAX。
另外,AX, BX, CX, DX这四个通用寄存器的高8位和低8位还可以分成两个单独的寄存器使用,名称分别是XH和XL(X=A,B,C,D)。
下面是RAX寄存器的示意图:
1 2 3 4 5 6 7 8 9 10 11
| +---------------------------------------------------------------+ | 8bits | 8bits | 8bits | 8bits | 8bits | 8bits | 8bits | 8bits | |---------------------------------------------------------------| | RAX | |---------------------------------------------------------------| | | EAX | |---------------------------------------------------------------| | | AX | |---------------------------------------------------------------| | | AH | AL | +---------------------------------------------------------------+
|
x86_64添加了8个新的通用寄存器,且引入了新的命名规则,详情可参考这里。
其他常用寄存器
- EFLAGS: 32位寄存器,用于保存指令结果和处理器状态,具体说明可参考这里。
- IP: 指令寄存器,用于保存要执行的指令,和通用寄存器类似,32位模式下用EIP,64位模式下用RIP。
语法
x86汇编语言主要有两个语法分支: AT&T和Intel。我们平常接触到的GNU系的工具(包括GCC,OBJDUMP等)都是使用AT&T语法,若无特别说明,下面的汇编代码例子也是如此。
操作 |
AT&T |
Intel |
注释 |
寄存器前缀 |
%eax |
eax |
|
立即数前缀 |
$5 |
5 |
|
指令后缀 |
movl |
mov |
操作数长度4 |
参数次序 |
movl $5, %eax |
mov eax, 5 |
R[eax] = 5 |
取址1 |
var |
[var] |
变量 |
取址2 |
0x8(%eax) |
[eax + 0x8] |
偏移 |
取址3 |
arr(, %eax, 4) |
[eax * 4 + arr] |
数组 |
指令
mov
拷贝数据
1 2 3 4
| # R[eax] = 5 movl $5, %eax # var = 5 movl %eax, var
|
cmp
比较操作符,并将结果暂存于EFLAGS寄存器。
跳转指令
跳转指令可以修改IP寄存器的值,以实现控制流转换。
- jmp (jump):无条件跳转到对应的label地址处。
- je (jump if equal): 如果上一次cmp指令结果是相等的,跳转到label处。
- jne (jump if not equal):和je相反,如果上一次cmp指令结果不相等,跳转到label处。
- jl (jump if less):如果上一次cmp指令结果小于条件成立,跳转到label处。
- jle (jump if less or equal):如果上一次cmp指令结果小于等于条件成立,跳转到label处。
- jg (jump if greater):如果上一次cmp指令结果大于条件成立,跳转到label处。
- jge (jump if greater or equal):如果上一次cmp指令结果大于等于条件成立,跳转到label处。
在这里查看更多跳转指令
call ret
- call: 将下一条指令的地址入栈,然后跳转到proc地址处。
- ret: 将栈顶的数据出栈并载入IP寄存器(即跳转回call的下一条指令处)。
enter leave
- enter: 新建一个stack frame,相当于:
1 2
| push %ebp mov %esp, %ebp
|
- leave: 销毁当前stack frame,恢复上一个stack frame,相当于:
运算指令
1 2
| # R[ebx] += R[eax] add %eax, %ebx
|
1 2
| # R[ebx] -= R[eax] sub %eax, %ebx
|
其他常用指令
- lea (Load effective address):
1 2
| # R[eax] = R[ebx] - 8 lea -0x8(%ebx), %eax
|
注意和mov指令的区别:
1 2
| # mem[R[eax]] = R[ebx] - 8 mov -0x8(%ebx), %eax
|
引用:http://blog.atime.me/note/asm-summary.html
参阅:https://en.wikipedia.org/wiki/Control_flow