一个最基本的汇编程序如下所示:
.section .data .section .text
.globl _start
_start:
movl $, %eax # the number 1 is the number of the exit system call movl $, %ebx int $0x80
1、.globl表示汇编器不能在汇编之后就丢弃该标识,因为接下来的连接器还会使用它。_start是一个特殊的标识,它必须用.globl修饰,因为它代表了程序开始的入口
否则在链接的时候会报“ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078”的错误
2、几个通用寄存器如下所示:(但事实上,对每个通用寄存器,都至少在一条指令中它们是用于特殊用途的)
- %eax
- %ebx
- %ecx
- %edx
- %edi
- %esi
另外几个用于特殊目的的寄存器如下所示:
- %ebp
- %esp
- %eip
- %eflags
3、在每次系统调用过程中,%eax总是用于存放系统调用的编号,而对于其他寄存器,在不同的系统调用中有不同的用途。例如在上述例子中,%ebx就用于存储返回值
4、"int $0x80"中的int代表interrupt,而interrupt表示中断当前正常的程序执行流,并且将控制权转交给内核,让它能执行系统调用
5、几种寻址模型:
- direct addressing mode:
- movl ADDRESS, %eax ----- 将地址ADDRESS中的内容放到%eax中
- indexed addressing mode:
- movl string_start(, %ecx, 1), %eax ---- 将地址string_start + %ecx * 1地址中的内容放到%eax中
- indirect addressing mode:
- movl (%eax), %ebx ----- 将地址%eax中的内容放到%ebx中
- base pointer addressing mode:
- movl 4(%eax), %ebx ----- 将地址%eax + 4中的内容放到%ebx中
- immediate mode:
- movl $12, %eax ----- 将立即数12放到%eax中,若没有$符号,则意为将地址12中的值放到%eax内
6、汇编函数调用规则
在调用一个函数之前,首先将所有参数以相反的声明顺序入栈,之后再调用call指令
call主要做两件事:1、它将下一条指令的地址入栈 2、将%eip设置为函数的初始地址、
接着进入函数内部,首先要做的就是将%ebp入栈,并将当前%esp的值赋值给%ebp。事实上,我们就是利用%ebp作为基准来获取函数的参数和局部变量
Parameter #N <--- N*4+4(%ebp)
...
Parameter 2 <--- 12(%ebp)
Parameter 1 <--- 8(%ebp)
Return Address <--- 4(%ebp)
Old %ebp <--- (%esp) and (%ebp)
最后在函数返回时,应该让%ebp恢复到上一个函数的状态,并调用ret返回,ret的作用是将上图的Return Address装入%eip中,恢复上一个函数的执行。具体的指令如下:
movl %ebp, %esp
popl %ebp
ret
7、mov指令不能用于内存和内存之间或者段寄存器和段寄存器之间的数据转移。不过其实内存和内存之间的数据转移是可以的,通过字符串的转移指令movs
8、pusha将8个通用寄存器入栈,入栈的顺序为:eax, ecx, edx, ebx, esp, ebp, esi, edi
9、inc/dec指令并不影响CF标志位
10、cr0中包含了系统控制的flag,包括标志位PE置位,则系统处于保护模式,标志位PG置位,则处理器利用页表将线性地址转换为物理地址
cr2用于保存导致缺页异常的线性地址,cr3用于保存页目录的地址