信息安全系统设计基础第四周学习总结

第三章 程序的机器级表示

3.1 历史观点

intel处理器系列俗称x86,经历了一个长期的发展过程。

每个后继处理器的设计都是后向兼容的,即较早版本上编辑的代码可以在较新的处理器上运行。

3.2 程序编码

假设一个c程序,有两个文件p1.c和p2.c,在IA32机器上用unix命令行编译代码如下:

unix> gcc -01 -o p p1.c p2.c

命令gcc是指GCC C编译器

-01告诉编译器使用第一级优化(提高优化级别会使最终程序运行的更快,但是编译时间会更长,调试更难)

实际上gcc命令调用了一系列程序,将源代码转化成可执行代码:

首先,c预处理器扩展源代码

然后,编译器产生两个源代码的汇编代码 .s

接下来,汇编器将汇编代码转化为二进制目标代码 .o

最后,链接器将两个目标代码文件与实现库函数的代码合并,产生最终的可执行代码文件p

3.2.1机器级代码

计算机系统使用了多种不同形式的抽象,利用更简单的抽象模型来隐藏实现的细节。

对于机器级编程来说,有两种重要的抽象:

1.机器级程序的格式和行为,定义为指令集体系结构(ISA),它定义了处理器状态、指令的格式,每条指令对状态的影响

大多数ISA逐条执行指令。

2.机器级程序使用的储存器地址是虚拟地址,提供的存储器模型看上去是一个非常大的字节数组。

 

整个编译过程中编译器完成大部分工作。汇编代码和机器代码的二进制格式相比,有一个主要特点:它用可读性更好的文本格式表示。

IA32机器代码和原始的c代码差别很大,一些通常对c语言程序员隐藏的处理器状态是可见的,一些存储器:

·程序计数器(PC,用%eip表示):指示将要执行的下一条指令在存储器中的地址

·整数寄存器:包含8个命名的位置,分别存储32位的值,可以存储地址(c语言的指针)或整数数据。

·条件码寄存器:保存最近执行的算数或逻辑指令的状态信息,用来实现控制或数据流中的条件变化。

·浮点寄存器:存放浮点数据。

3.2.2代码示例

要查看目标代码文件的内容,最有价值的是反汇编器,在linux中,带-d的命令行标志的程序objdump可以充当这个角色。

机器代码和它的反汇编表示的特性值:

·IA32指令长度从1到15个字节不等

·设计指令格式的方式是,从某个给定位置开始,可以将字节唯一的解码成机器指令

·反汇编器只是基于机器代码文件中的字节序列来确定汇编代码,不需要访问程序的源代码或汇编代码

·反汇编器使用的指令命名规则与GCC生成的汇编代码使用的有些差别

3.3 数据格式

c语言基本数据类型对应的IA32表示:

大多数gcc生成的汇编代码指令都有一个字符后缀,表明操作数的大小,如:

movb 传送字节

movw 传送字

movl 传送双字

3.4 访问信息

一个IA32中央处理单元(CPU)包含一组8个存储32位值的寄存器:

3.4.1操作数指令符

IA32支持多种操作数格式,如下图:

操作数分为三种:

·立即数,即常数值

·寄存器,表示某个寄存器的内容

·存储器,根据有效地址访问某个存储器位置

3.4.2数据传送指令

传送指令分成指令类:一类中的指令执行一样的操作,只不过操作数大小不同。

mov类的指令将源操作数的值复制到目的操作数中

movs和movz指令都是将一个较小的源数据复制到一个较大的数据位置,高位用符号位扩展(movs)或零扩展(movz)

符号位扩展:目的位置的所有高位用源值的最高位数值填充

零扩展:高位用0填充

pushl和popl可以将数据压入程序栈和从程序栈弹出数据。

3.5 算数和逻辑操作

3.5.1加载有效地址

指令 leal S,D,效果 D<-&S

将有效地址写入目的操作数

3.5.2一元操作和二元操作

一元操作:只有一个操作数,既是源又是目的,可以是一个寄存器也可以是一个存储器位置

二元操作:第二个操作数即是源又是目的,两个操作数不能同时是存储器位置

3.5.3移位操作

先给出移位量,再给出移位的数值,可以进行算数和逻辑右移,但只能进行0-31位的移位

3.6 控制

3.6.1条件码

CF 进位标志

ZF 零标志

SF 符号标志

OF 溢出标志

3.6.2访问条件码

条件码通常不会直接读取,常用的使用方法有三种:

1、根据条件码的某个组合,将一个字节设置为0或者1;

2、条件跳转到程序的某个其他的部分;

3、有条件地传送数据。

3.6.3跳转指令及其编码

跳转指令jmp,跳转的目的用一个标号指明,label

跳转是有条件的,根据条件码的某个组合,或跳转或继续执行代码序列的下一条命令

jmp指令:

3.6.4翻译条件分支

将条件表达式和语句从c语言翻译成机器代码,最常用的方式是结合有条件和无条件跳转。

3.6.5循环

1.do-while

先执行才判断

2.while

先判断才执行,即第一次执行就可能终止

3.for

3.6.6条件传送指令

实现条件操作的传统方法是利用控制的条件转移。

数据的条件转移是一种替代的策略,这种方法先计算一个条件操作的两种结果,然后再根据条件是否满足从而选择一个。

3.6.7switch语句

switch语句可以根据一个整数索引值进行多重分支,处理具有多种可能结果的测试时这种语句特别有用,它们不仅提高了c代码的可读性,而且通过使用跳转表这种数据结构使得实现更加高效。

3.7 过程

一个过程调用包括将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分,另外,还必须在进入时为过程的局部变量分配空间,并在退出时释放这些空间。

3.7.1栈帧结构

机器用栈来传递过程参数、存储返回信息、保存寄存器用于以后恢复,以及本地存储。为单个过程分配的那部分栈称为栈帧。

假设过程p(调用者)调用过程q(被调用者),则q的参数放在p的栈帧中,另外,当p调用q时,p中的返回地址被压入栈中,形成p的栈帧的末尾。返回地址就是当程序从q返回时应该继续执行的地方。q的栈帧从保存的帧指针的值开始,后面是保存的其他寄存器的值。

过程q也用栈来保存其它不能存放在寄存器中的局部变量,这样做的原因如下:

·没有足够多的寄存器存放所有的局部变量

·有些局部变量是数组或结构,因此必须通过数组或结构引用来访问

·要对一个局部变量使用地址操作符&,我们必须能够为它生成一个地址

3.7.2转移控制

下表是支持过程调用和返回的指令:

call指令有一个目标,即指明被调用过程起始的指令地址,调用可以是直接的也可以是间接的,汇编代码中,直接调用的目标是一个符号,简介调用的目标是*后跟一个操作数指示符。

call指令的效果是将返回地址入栈,并跳转到被调用过程的起始处。

ret指令从栈中弹出地址,并跳转到这个位置。

3.7.3寄存器使用惯例

必须保证一个调用者调用被调用者时,被调用者不会覆盖某个调用者稍后会用到的寄存器的值。

实现以上要求的两个方式:

·在调用q之前,将y的值存放在自己的栈帧之中,当q返回时,过程p就可以从栈中取出y的值,即调用者保存y的值。

·将y的值保存在被调用者保存寄存器,调用后返回前恢复该值。

 

posted @ 2015-10-11 19:02  20125221银雪纯  Views(183)  Comments(1Edit  收藏  举报