x86 汇编知识补充

做编译器的代码生成器部分的时候一定会使用到汇编代码,特在此总结一些简单的汇编语言知识点。以能看懂常见的汇编代码为目的,不做深入探究。

学习一种的汇编语言,必须了解这种 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

1
cmpl $2, count.0

比较操作符,并将结果暂存于EFLAGS寄存器。

跳转指令

跳转指令可以修改IP寄存器的值,以实现控制流转换。

  • jmp (jump):无条件跳转到对应的label地址处。
1
jmp label
  • je (jump if equal): 如果上一次cmp指令结果是相等的,跳转到label处。
1
je label
  • jne (jump if not equal):和je相反,如果上一次cmp指令结果不相等,跳转到label处。
1
2
cmp $5, $eax
jne label
  • jl (jump if less):如果上一次cmp指令结果小于条件成立,跳转到label处。
1
2
cmp $5, $eax
jl label
  • jle (jump if less or equal):如果上一次cmp指令结果小于等于条件成立,跳转到label处。
1
2
cmp $5, $eax
jle label
  • jg (jump if greater):如果上一次cmp指令结果大于条件成立,跳转到label处。
1
2
cmp $5, $eax
jg label
  • jge (jump if greater or equal):如果上一次cmp指令结果大于等于条件成立,跳转到label处。
1
2
cmp $5, $eax
jge label

这里查看更多跳转指令

call ret

  • call: 将下一条指令的地址入栈,然后跳转到proc地址处。
1
cal proc
  • ret: 将栈顶的数据出栈并载入IP寄存器(即跳转回call的下一条指令处)。
1
ret

enter leave

  • enter: 新建一个stack frame,相当于:
1
2
push %ebp
mov %esp, %ebp
1
enter arg
  • leave: 销毁当前stack frame,恢复上一个stack frame,相当于:
1
2
mov %ebp, %esp
pop %ebp
1
leave

运算指令

  • add: 加法
1
2
# R[ebx] += R[eax]
add %eax, %ebx
  • sub: 减法
1
2
# R[ebx] -= R[eax]
sub %eax, %ebx
  • inc: 自增
1
2
# R[eax] += 1
inc %eax
  • dec: 自减
1
2
# R[eax] -= 1
dec %eax

其他常用指令

  • 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