1.汇编指令介绍
1.1 寄存器
- x86中有个32位的寄存器。
- EAX、EBX、ECX、EDX的高两位字节和低两位字节可单独使用。如EAX的低两位称为AX,而AX的高字节又称为AH,低字节称为AL。
- 除EBP与ESP外,其他寄存器均可用于任意用途。
2.汇编指令格式
2.1 分类
2.1.1 AT&T格式
2.1.1.1 说明
- 指令只能用小写字母。
- 第一个为源操作数,第二个为目的操作数,方向由左向右。
- 寄存器前需加%表示,立即数前需加$表示。
- 内存寻址需要使用()。
- 为表明操作数大小,指令操作码部分后面紧跟一个字符。例如,b表示byte、w表示word(字)、l表示long(双字)。
2.1.2 Intel格式
2.1.2.1 说明
- 指令对大小写不敏感。
- 第一个为目的操作数,第二个为源操作数,方向由右向左。
- 寄存器和立即数前不需要加任何前缀。
- 内存寻址需要使用[]。
- 为表明操作数大小,指令操作部分后面显式注明byte ptr、word ptr、dword ptr。
由于32或64位体系结构都是由16位拓展来的,因此用word(字)表示16位。
2.2 两种格式的指令对比
2.3 常用指令(以Intel格式的指令为例)
2.3.1 数据传送指令
mov
说明
- 将第二个操作数(可以是寄存器中的内容、内存中的内容、常数值)复制到第一个操作数(寄存器或内容)中去。
- 两个操作数不能同时为内存地址!
语法
Ps:代表寄存器编号;代表内存地址;代表常数,后面跟的数字代表常数的位数。
栗子🌰
push
说明
- 将操作数压入内存的栈,常用于函数调用。
- ESP是栈顶指针,再将操作数压入栈之前,先将ESP = ESP - 4(栈增长方向与内存地址增长方向相反),然后再将操作数压入栈。
语法
栗子🌰
pop
说明
- 与push指令截然相反,其工作是将操作数出栈。
- 出栈前,先将ESP指向的内容出栈,然后ESP = ESP + 4。
栗子🌰
2.3.2 算术和逻辑运算指令
add/sub
说明
将两个操作数相加/减,结果保存在第一个操作数中。
语法
栗子🌰
inc/dec
说明
操作数自增1/自减1。
语法
栗子🌰
imul
说明
- 带符号整数相乘。
- 有两种指令格式
- 指令具有两个操作数,两个操作数相乘,结果存放在第一个操作数中,但第一个操作数必须为寄存器。
- 指令具有三个操作数,后两个操作数相乘,结果存放在第一个操作数中,但第一个操作数必须为寄存器。
语法
栗子🌰
idiv
说明
- 带符号整数相除。
- 指令中只有一个操作数,即除数。被除数为 edx:eax 中的内容(64位整数)。
- 操作结构有两部分,商送到eax,余数送到edx。
语法
栗子🌰
and/ox/xor
说明
分别是逻辑与、或、异或操作。用于操作操作数的位,操作结果存放在第一个操作数中。
语法
栗子🌰
not
说明
将操作数中的每一位翻转,即 0 -> 1,1 -> 0。
语法
栗子🌰
neg
说明
对操作数取负。
语法
栗子🌰
shl/shr
说明
- 逻辑移位指令。shl表示逻辑左移,shr表示逻辑右移。
- 第一个操作数是被操作数,第二个操作数是移位的位数。
语法
栗子🌰
2.3.3 控制流指令
jmp
说明
控制IP转移到label所指示的指令地址处。
Ps
x86处理器中维持一个指示当前执行指令的指针–IP。当一条指令执行后,IP会自动指向下一条指令。
IP寄存器无法直接操作,但可以用控制流更新。通常用标签(label)指示程序中的指令地址。
在x86汇编代码中,可在任意一条指令前添加标签:
语法
栗子🌰
jcondition
说明
条件转移指令。依据CPU状态字中的一系列条件进行状态转移。
语法
栗子🌰
cmp/test
说明
- cmp用于比较两个操作数的值;test用于对两个操作数进行逐位运算。
- cmp/test指令均不保存运算结果,而是依据运算结果去设置CPU状态字中的条件码。
语法
栗子🌰
call/ret
说明
- 用于实现子函数/子过程的调用与返回。
- call先将当前指令地址入栈,然后无条件转移到由标签指示的指令。
- call会保存指令调用前的地址信息,以便call指令结束后,返回到该地址继续执行程序。
- ret用于实现子程序的返回。
- ret会弹出栈中保持的指令地址,然后无条件返回到该地址处继续执行程序。
语法
2.过程调用的机器级表示
call/ret主要用于过程调用,且均属于无条件转移指令。
下面以过程P调用过程Q为例,则其过程调用的步骤如下:
2.1 说明
- 栈是用来存储局部变量、返回结果的存储区域,栈从高地址向低地址增长。
- 寄存器EAX、ECX、EDX是调用者保存寄存器;寄存器EBX、ESI、EDI是被调用者保存寄存器,Q必须先将它们的值保存在栈中,才能使用它们。
- 栈帧指每个程序自己的栈区。
- 帧指针寄存器EBP指示栈帧的起始位置(栈底),ESP则指示栈顶。
- 当前栈帧的范围介于EBP与ESP指向的区域之间。
3.查找语句的机器级表示
3.1 说明
- 编译器通过条件码(标志位)设置指令和各类转移指令来实现程序中的选择结构语句。
- 常见的算术逻辑运算指令(add、sub、imul、idiv、or、and、shl等)会设置条件码。
- cmp行为与sub一样;test指令与and指令行为一样。但是他们都只设置标志位,而不更新目的寄存器。
条件码/标志位
CPU维护的一组条件码(标志位)寄存器,用于描述最近的算术运算和逻辑运算操作的属性。
常用的标志位:
标志位 | 名称 | 用途 | 标志位为1时的意义 | 说明 |
---|---|---|---|---|
CF | 进(借)位标志 | 记录最近的无符号整数加/减运算后的进/借位情况。 | 存在进/借位 | 对带符号运算无意义 |
ZF | 零标志 | 记录最近的操作的运算结果是否为0 | 最近的运算结果为0 | |
SF | 符号标志 | 记录最近的带符号数运算结果的符号 | 最近的运算结果为负 | 对无符号运算无意义 |
OF | 溢出标志 | 记录最近的带符号数运算结果是否溢出 | 最近的运算结果发生溢出 | 对无符号运算无意义 |
jcondition指令就是依据ZF和SF来实现跳转的。
4.循环语句的机器级表示
4.1 说明
- 汇编中没有对应的指令,只能通过条件测试+跳转组合起来实现循环的效果。
- 多数编译器都会将循环结果转换为do-while形式来生成机器代码。