欢迎大家来我的博客做客:re1ife.top
知识回顾
程序、指令
计算机采取的是“存储程序”工作方式。
- 如何工作?——程序有指令组成
- 程序执行前:数据和指令实现存放在存储器中,每条指令与每个数据都有地址,指令按序存放,指令由OP、ADDR字段组成,程序起始地址PC
- 开始执行程序
- 根据PC取得指令 (从五号架子取菜谱
- 指令译码 ( 看菜谱
- 取操作数 (从架子或盘中取原材料)
- 指令执行 (洗、切…)
- 回写结果(装盘或直接送桌)
- 改写PC值 (6 = 5 + 1 就是找下一个菜谱在哪儿)
指令执行中,指令与数据从存储器取到CPU、存放在CPU内的寄存器中,指令在IR中,数据在GRP.
指令要给出的数据:
- 操作性质: 操作码
- 源操作数1 或/和 源操作数2 (立即数、寄存器编号、存储地址)
- 目的操作数地址(寄存器编号、存储地址)
- 存储地址的描述与操作数的数据结构有关
不同层次语言之间的等价转换
程序转化概述
指令
计算机中的指令包括:微指令、机器指令以及伪(宏)指令之分。
- 机器指令: 处于硬件和软件的交界面,相当于一个菜谱指定的一个完整做菜过程。
[!NOTE]
本章中提到的的指令都是 机器指令
- 微指令: 是指微程序级别指令,属于硬件范畴。相当于洗、切、煮等做菜的微过程。
- 伪指令:若干机器指令组成的指令序列,属于软件范畴。相当于多个菜谱合成的一个大菜谱。
- 汇编指令: 是机器指令的汇编表示形式,即符号表示。。
- 机器指令与汇编指令一一对应,都与具体机器结构有关,属于机器级别指令。
机器级指令
机器指令是一个0/1序列,由若干自度那组成。
汇编指令是机器指令的符号表示(允许有不同的格式):
b是一个长度后缀表示一个字节。
指令功能为: M[R[bx] + R[di] - 6] <- R[cl]。
这就是一个寄存器传送语言RTL(Register Transfer Language)
-
R: 寄存器内容
-
M: 存储单元内容
- 也可以用(x), 表示地址x中内容。
mov、bx、movb、%bx都是助记符
- 也可以用(x), 表示地址x中内容。
-
用GCC编译器套件进行转换的过程
- 预处理:在高级语言源程序中插入所有用#include命令指定的文件和用#define声明指定的宏。
- 编译:将预处理后的源程序文件编译生成相应的汇编语言程序。
- 汇编:由汇编程序将汇编语言源程序文件转换为可重定位的机器语言目标代码文件。
- 链接:由链接器将多个可重定位的机器语言目标文件以及库例程(如printf()库函数)链接起来,生成最终的可执行目标文件。
- GCC使用举例:
- gcc -E xxx.c -o xxx.i 生成预处理的文件
- gcc -S xxx.c -O xxx.s 生成汇编源程序
- 后面更了 l、w等字长符号。
- gcc -c test.s -o test.o 生成可重定位目标程序
- objdump -d test.o反汇编(与源代码生成的汇编不同):
- objdump -d test 反汇编可执行文件
- 与可重定位的反汇编相比,他的地址就不是从0开始,是从真实地址开始。
- 与可重定位的反汇编相比,他的地址就不是从0开始,是从真实地址开始。
可执行文件的存储映像
回顾ISA
ISA(Instruction Set Architecture)位于软件和硬件之间
ISA规定如何使用硬件:
- 可执行的指令的集合,包括指令格式、操作种类以及每种操作对应的操作数的相应规定;
- 指令可以接受的操作数的类型;
- 操作数所能存放的寄存器组的结构,包括每个寄存器的名称、编号、长度和用途;
- 操作数所能存放的存储空间的大小和编址方式;
- 操作数在存储空间存放时按照大端还是小端方式存放;
- 指令获取操作数的方式,即寻址方式;
- 指令执行过程的控制方式,包括程序计数器、条件码定义等。
ISA与计算机组成(微结构)之间关系:不同ISA规定的指令集不同,例如:IA-32,MIPS、ARM。计算机组成必须要能实现ISA规定的功能。例如GRP、标志等。
IA-32
在IA-32体系中,有8个GRP(0-7)通用寄存器,一个EFLAGs 标志寄存器、程序计数器PC为EIP。
可寻址的空间是4GB, (编号为0 ~ 0xFFFFFFFF)
指令格式变长(长度可变)、操作码变长、指令由若干字段(OP、Mod、SIB等)组成
计算机中的数据存放在寄存器文件、通用寄存器组GRPs. (宿舍书架)、存储器。
IA-32 支持的数据结构
主要是定点书与浮点数:
IA-32 架构有16位架构发展而来,因此虽然字长32位火更大、但是一个字16位,长度后缀位w, 32 位为双字,长度为I。 long double 实际长度为80位,但是分配96位 = 12B按照4B对齐。
寄存器组织
IA-32 有
- 8个通用寄存器(32位的分别为:EAX、EBX、…)
- 两个专用寄存器 EIP程序计数器和EFLAGs 标志寄存器。
- 6个段寄存器
但是IA-32是通过扩展方式实现的。
-
8个8位寄存器(AH、AL、BH、BL、…)
-
8个16位寄存器(AX、BX…)
-
IA-32 标志寄存器
6个条件标志: -
OF、SF、ZF、CF ([[计算机系统基础#n位加法器]])
-
AF: 辅助进位标志(BCD码运算有意义)
-
PF:奇偶标志
3个控制标志:
- DF: 方向标志(自动变址方向是增还是减)
- IF: 中断允许标志
寻址方式
寻址方式:如何根据指令给定信息得到操作数或者操作数地址。
操作数所在位置:
- 指令中:立即寻址
- 寄存器中:寄存器寻址
- 存储单元: 其他寻址方式
存储器操作数寻寻址方式与微处理器工作模式有关:实地址模式(基本用不到)与保护模式
-
实地址模式:寻址空间为1MB, 20位地址: CS << 4 + (IP)
-
保护模式: 加点进入后、采用虚拟存储管理方式。多任务情况下隔离、保护。
寻址空间: 232B = 4G , 32 位线性地址分段(段基址+ 段内偏移量)
保护模式下的寻址方式:
寻址举例子:
int x;
float a[100];
short b[4][4];
char c;
double d[10];
double 对齐:
-
Linux 按 4B 边界对齐
-
windows 按8B边界对齐
-
a[i] 的地址如何计算?
- 104 + i*4 (因为是float ,4Byte)
- i = 99时, 104+99x4 = 500
-
b[i][j]地址如何计算:
- 504 + i * 8(i * 8 是因为 一个short 是2Byte。然后按照一个行有4个short 4 * 2 = 8)
-
d[i] 地址计算: 544 + i * 8
- 为什么起始是 544 而不是 537 因为是8B对齐的。要能被8整除。
其他变量寻址方式:
- x、c:位移 / 基址
- a[i]:104+i×4,比例变址+位移
- d[i]:544+i×8,比例变址+位移
- b[i][j]: 504+i×8+j×2,基址+比例变址+位移
算完地址,我们可以举例将b[i][j]取到AX中的指令是:
movx 504(%ebp, %esi, 2), %ax
// i x 8 在EBP中, j在esi中,2 为比例因子
机器指令格式
- 位移量和立即数都可以是1B、2B、4B
- SIB中基址B和变址I 都可是 8个GRS中任意一个。SS给出比例因子(数字1、2、4、8 因此只需要两位)
- 操作码(1或2个字节):opcode ;w :与机器模式(16/32位)一起确定寄存器的位数(AL/AX/EAX)D操作方向(确定源和目标)
- 寻址方式(ModRM字节): mod, r/m ,reg/op三个字段与w字段和机器模式(16/32)一起确定操作数所在寄存器编号或有效地址的计算方式。
常用指令类型
-
传送指令
- 通用数据传送指令
- MOV:一般传送,包括movb、movw和movl等
- MOVS:符号扩展传送,如movsbw、movswl等
- MOVZ:零扩展传送,如movzwl、movzbl等
- XCHG:数据交换
- PUSH/POP:入栈/出栈,如pushl,pushw,popl,popw等
- 地址传送指令
- LEA:加载有效地址,如leal (%edx,%eax), %eax”的功能为R[eax]←R[edx]+R[eax],执行前,若R[edx]=i,R[eax]=j,则指令执行后,R[eax]=i+j
- 输入输出指令
- IN和OUT:I/O端口与寄存器之间的交换
- 标志传送指令
- PUSHF、POPF:将EFLAG压栈,或将栈顶内容送EFLAG
- 通用数据传送指令
-
定点算术运算指令
- 加 / 减运算(影响标志、不区分无/带符号)
- ADD:加,包括addb、addw、addl等
- SUB:减,包括subb、subw、subl等
- 增1 / 减1运算(影响除CF以外的标志、不区分无/带符号)
- INC:加,包括incb、incw、incl等
- DEC:减,包括decb、decw、decl等
- 取负运算(影响标志、若对0取负,则结果为0且CF=0, 否则CF=1)
- NEG:取负,包括negb、negw、negl等
- 比较运算(做减法得到标志、不区分无/带符号)
- CMP:比较,包括cmpb、cmpw、cmpl等
- 乘 / 除运算(区分无/带符号)
- MUL / IMUL:无符号乘 / 带符号乘(影响标志OF和CF)
- DIV/ IDIV:无符号除 / 带符号除
- 加 / 减运算(影响标志、不区分无/带符号)
栈操作
栈(Stack)是一种采用“先进后出”方式进行访问的一块存储区,用于嵌套过程调用。从高地址向低地址增长
[!NOTE]
栈不等于堆栈(堆栈由“堆”和“栈”组成)
- 入栈(pushw %ax)
要把AL这一低数据放在低地址(小端).
0xFFFFH是栈地。
入栈前: R[sp] <- R[sp] - 2、M[R[sp]] <- R[ax] ()
- 出栈(popw %ax)
汇编操作:R[ax] <- M[R[sp]]、[sp] <- R[sp] + 2
整数乘除指令
- 乘法指令:可给出一个、两个或三个操作数
- 若给出一个操作数SRC,则另一个源操作数隐含在AL/AX/EAX中,将SRC和累加器内容相乘,结果存放在AX(16位)或DX-AX(32位)或EDX-EAX(64位) 中。DX-AX表示32位乘积的高、低16位分别在DX和AX中。 n位× n位=2n位
- 若指令中给出两个操作数DST和SRC,则将DST和SRC相乘,结果在DST中。n位× n位=n位
- 若指令中给出三个操作数REG、SRC和IMM,则将SRC和立即数IMM相乘,结果在REG中。n位× n位=n位
- 除法指令:只明显指出除数
- 若为8位,则16位被除数在AX寄存器中,商送回AL,余数在AH
- 若为16位,则32位被除数在DX-AX寄存器中,商送回AX,余数在DX
- 若为32位,则被除数在EDX-EAX寄存器中,商送EAX,余数在EDX
以上内容不要死记硬背,遇到具体指令时能查阅到并理解即可
举例子
- 无符号乘法:
- 有符号乘法(布斯乘法):
- 三个操作数乘法:
按位运算指令
- 逻辑运算(仅NOT不影响标志,其他指令OF=CF=0,而ZF和SF根据结果设置:若全0,则ZF=1;若最高位为1,则SF=1 )
- NOT:非,包括 notb、notw、notl等
- AND:与,包括 andb、andw、andl等
- OR:或,包括 orb、orw、orl等
- XOR:异或,包括 xorb、xorw、xorl等
- TEST:做“与”操作测试,仅影响标志
- 移位运算(左/右移时,最高/最低位送CF)
- SHL/SHR:逻辑左/右移,包括 shlb、shrw、shrl等
- SAL/SAR:算术左/右移,左移判溢出,右移高位补符(移位前、后符号位发生变化,则OF=1 )
- ROL/ROR: 循环左/右移,包括 rolb、rorw、roll等
- RCL/RCR: 带循环左/右移,将CF作为操作数一部分循环移位
以上内容不要死记硬背,遇到具体指令时能查阅到并理解即可
举例子:
假设short型变量x被编译器分配在寄存器AX中,R[ax]=FF80H,则以下汇编代码段执行后变量x的机器数和真值分别是多少?
movw %ax, %dx
salw $2, %ax
addl %dx, %ax
sarw $1, %ax
解:$2和$1分别表示立即数2和1 。
x是short型变量,故都是算术移位指令,并进行带符号整数加。
假设上述代码段执行前R[ax]=x,则执行((x<<2)+x)>>1后,R[ax]=5x/2。算术左移时,AX中的内容在移位前、后符号未发生变化,故OF=0,没有溢出。最终AX的内容为FEC0H,解释为short型整数时,其值为-320。验证:x=-128,5x/2=-320。经验证,结果正确。
控制转移指令
-
指令执行可按顺序 或 跳转到转移目标指令处执行
- 无条件转移指令
- JMP DST:无条件转移到目标指令DST处执行
- 条件转移
- Jcc DST:cc为条件码,根据标志(条件码)判断是否满足条件,若满足,则转移到目标指令DST处执行,否则按顺序执行
- 条件设置
- SETcc DST:将条件码cc保存到DST(通常是一个8位寄存器 )
- 调用和返回指令 (用于过程调用)
- CALL DST:返回地址RA入栈,转DST处执行
- RET:从栈中取出返回地址RA,转到RA处执行
- 无条件转移指令
-
条件转移指令的分类:
- 根据单个标志的值转移
- 按无符号整数比较转移
- 按带符号整数比较转移