20135223何伟钦—信息安全系统设计基础第五周学习总结

第三章 程序的机器级表示

一、历史观点

     Intel处理器(X86)

二、程序编码

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

      ①编译选项-01 表示编译器使用第一级优化

     ②编译选项-02 表示编译器使用第二级优化(较好的选择)

     ③-o 表示分别将p1.c和p2.c编译后的可执行文件命名为p

     GCC将源代码转化为可执行代码的步骤:

  C预处理器:扩展源代码,插入所有#include命令指定的文件,并扩展生成.i文件
  编译器:产生两个源代码的汇编代码,生成.s文件
  汇编器:将汇编代码转化成二进制目标代码——生成.o文件
  链接器:产生可执行代码文件p

回顾:gcc命令编译运行C语言
  1. 预处理阶段:将*.c文件转化为*.i预处理过的C程序。

  2. 编译阶段:将*.i文件编译为汇编代码*.s文件。

  3. 汇编阶段:将*.s文件转化为*.o的二进制目标代码文件。

  4. 链接阶段:将*.o文件转化为可执行文件。

  5. 生成可执行文件:将*.o转换为可执行文件。

  6. 执行可执行C语言文件。

(1)机器级代码

    两种抽象

(1)指令集体系结构ISA

   (2)机器级程序使用的存储器地址是虚拟地址(多个硬件存储器和操作系统软件组合起来)


 处理器状态:
  • 程序计数器(CS:IP)
  • 整数寄存器(AX,BX,CX,DX)
  • 条件码寄存器(OF,SF,ZF,AF,PF,CF)
  • 浮点寄存器
 (2)代码示例
C语言代码文件code.c:

int accum = 0;
int sum(int x, int y) 
{
   int t = x + y; 
   accum += t; 
   return t;
 }

产生汇编代码code.s:
gcc -01 -S code.c
编译并汇编该代码,产生目标代码code.o:gcc -01 -c code.c

反汇编器——查看目标代码文件
abjdump -d code.o


三、数据格式
数据类型:
字:16位
双字:32位
四位:64位

基本数据类型:

C 声明 Intel 汇编代码后缀 大小(字节)
char 字节 b 1
short w 2
int 双字 l 4
long int 双字 l 4
long long int 4
char * 双字 l 4
float 单精度 s 4
double 双精度 l 8
long double 扩展精度 t 10/12
 

数据传送指令


movb 传送字节

movw 传送字

movl 传送双字


四、访问信息

 

(一)操作数指示符


操作数:指示出执行一个操作中要引用的源数据值,以及放置结果的目标位置。


操作数类型:

  1. 立即数 (常数值)  例:$0x1F
  2. 寄存器  例:%ax
  3. 存储器 有效地址

寻址方式:

(1)立即数寻址方式

(2)寄存器寻址方式

(3)存储器寻址方式

(4)直接寻址方式

(5)寄存器间接寻址方式

(6)寄存器相对寻址方式

(7)基址变址寻址方式

(8)相对基址变址寻址方式

 

 

(二)数据传送指令


1.mov指令: 把源操作数的值复制到目的操作数上

movb、movw、movl

2.堆栈

  • 遵循“后进先出”的原则
  • 栈指针指向栈顶元素
  • 栈顶元素的地址在栈中元素地址是最低的

压栈push:将数据压入栈中
出栈pop:弹出数据

 

3.数据传送示例

c语言中的指针其实就是地址,间接引用指针就是将该指针放在一个寄存器中,然后在存储器引用中使用这个寄存器

局部变量通常保存在寄存器


(五)算术和逻辑操作

字节加法:addb
字加法;addw
双字加法:addl

1、加载有效地址


加载有效地址指令——leal,是movl指令的变形,对比汇编中的LEA指令学习。


将有效地址写入到目的操作数,目的操作数必须是寄存器


2、一元操作和二元操作


1.一元操作


只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。

例:加法运算符(++)和减1运算符


2.二元操作

第二个数既是源又是目的

  • 第一个操作数可以是立即数、寄存器或者存储器位置
  • 第二个操作数可以是寄存器或者存储器位置
  • 两个操作数但是不能同时是存储器位置。

三、移位操作


右边填0:SAL 算术左移
         SHL 逻辑左移
填上符号位:SAR 算术右移
填上0:SHR 逻辑右移

移位操作源操作数可以是立即数或CL

移位操作目的操作数可以是寄存器或存储器


4、特殊的算术操作


 


(六) 控制


一、条件码

CF:进位标志
ZF:零标志
SF:符号标志
OF:溢出标志


注意:

  1. leal不改变条件码寄存器
  2. CMP与SUB的区别:CMP也是根据两个操作数之差设置条件码,但只设置条件码而不更新目标寄存器
  3. 有条件跳转的条件看状态寄存器(教材上叫条件码寄存器)

常用指令:
MOV 不影响标志位
PUSH POP 不影响标志位
XCHG 交换指令 不影响标志位
XLAT 换码指令 不影响标志位
LEA 有效地址送寄存器指令 不影响标志位
PUSHF 标志进栈指令 不影响标志位
POPF 标志出栈指令 标志位由装入值决定

2、访问条件码
  1. 根据条件码的某个组合,将一个字节设置成0或1;
  2. 跳转到程序某个其他的部分
  3. 有条件的传送数据。

    SET指令根据t=a-b的结果设置条件码


SET指令:


3、跳转指令及其编码


JUMP指令会导致执行切换到程序一个全新的位置,通常用一个标号指明

jmp指令是无条件跳转,可分为直接跳转和间接跳转:

直接跳转:后面跟标号作为跳转目标

间接跳转:*后面跟一个操作数指示

区别:

jmp *%eax 用寄存器%eax中的值作为跳转目标

jmp *(%eax) 以%eax中的值作为读地址,从储存器中读取跳转目标

控制中最核心的是跳转语句:

  • 有条件跳转(实现if,switch,while,for)

  • 无条件跳转jmp(实现goto)

当执行PC相关的寻址时,程序计数器的值是跳转指令后面那条指令的地址,而不是跳转指令本身的地址。

4、翻译条件分支

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

无条件跳转:例如goto。书上的例子就是把if-else语句翻译成了goto形式,然后再由这个形式翻译成汇编语言

C语言中if-else语句的通用形式:

if(test-expr)
	then-statement
else
	else-statement

汇编结构:

	t=test-expr;
	if!(t)
		goto false;
	then-statement
	goto done;
false:
	else-statement
done:

5、循环


汇编中可以用条件测试和跳转组合起来实现循环的效果,但是大多数汇编器中都要先将其他形式的循环转换成do-while格式。


1.do-while循环


通用形式:


do
	body-statement
	while(test-expr);

循环体body-statement至少执行一次。


可以翻译成如下所示的条件和goto语句:


loop:
	body-statement
	t = test-expr;
	if(t)
		goto loop;

即先执行循环体语句,再执行测试表达式。


2.while循环

C语言中while语句的通用形式:

while(test-expr)
	body-statement

汇编结构:

	t=test-expr;
	if(!t)
		goto done;
loop:
	body-statement
	t=test-expr;	
	if(t)
		goto loop;
done:

for循环

C语言中for语句的通用形式:

for(init-expr;test-expr;update-expr)
	body-statement

汇编结构:

	init-expr
	t=test-expr;
	if(!t)
		goto done;
loop:
	body-statement
	update-expr;
	t=test-expr;
	if(t)
		goto loop;
	done:

3.for循环


for循环

C语言中for语句的通用形式:

for(init-expr;test-expr;update-expr)
	body-statement

汇编结构:

	init-expr
	t=test-expr;
	if(!t)
		goto done;
loop:
	body-statement
	update-expr;
	t=test-expr;
	if(t)
		goto loop;
	done:

6、条件传送指令(详细看书本)


7、Switch语句


Switch语句是多重分支的典型(这个已经掌握的比较好,详细见书本)



(七) 过程

栈帧结构
  • 为单个过程分配的栈叫做栈帧,寄存器%ebp为帧指针,而寄存器指针%esp为栈指针,程序执行时栈指针移动,大多数信息的访问都是相对于帧指针。

  • 栈向低地址方向增长,而栈指针%esp指向栈顶元素。

  转移控制

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

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

函数返回值存在%eax中

1.call

call指令和转移指令相似,同样分直接和间接,直接调用的目标是标号,间接调用的目标是*后面跟一个操作数指示符,和JMP一样。

CALL指令的效果是将返回地址入栈,并跳转到被调用过程的起始处。返回地址是还在程序中紧跟在call后面的那条指令的地址。

然后就会用到ret了。

2.ret

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

在上学期的汇编语言学习中,call和ret常被用来进行子函数、子模块的调用。

3.leave

这个指令可以使栈做好返回的准备,等价于:

movl %ebp,%esp
popl %ebp

查看函数调用栈信息的GDB命令

Ÿ   backtrace/bt n

n是一个正整数,表示只打印栈顶上n层的栈信息。

-n表一个负整数,表示只打印栈底下n层的栈信息。

Ÿ   frame n

n是一个从0开始的整数,是栈中的层编号。这个指令的意思是移动到n指定的栈帧中去,并打印选中的栈的信息。如果没有n,则打印当前帧的信息。

Ÿ   up n

表示向栈的上面移动n层,可以不打n,表示向上移动一层。

Ÿ   down n

表示向栈的下面移动n层,可以不打n,表示向下移动一层。


 寄存器使用惯例


             寄存器%eax、%edx和%ecx被划分为调用者保存寄存器。


                 寄存器%ebx、%esi、%edi被划分为被调用者保存寄存器。


过程示例


          GCC坚持一个函数使用的所有栈空间必须是16字节的整数倍,包括保存%ebp值的4个字节和返回值的4个字节。


递归过程


          递归调用自身和调用其它函数是一样的。栈规则提供了一种机制,每次函数调用都有它自己私有的状态信息存储(保存的返回位置,栈指针和被调用者保存寄存器的值)


补充汇编语言命令:
算术指令
ADD 加法指令 影响标志位
ADC 带进位加法指令 影响标志位
INC 加一指令 不影响CF,影响别的标志位
SUB 减法指令 影响标志位
SBB 带借位减法指令 影响标志位
DEC 减一指令 不影响CF,影响其他标志位
NEG 求补指令 影响标志位 只有操作数为0,例如字运算对-128求补,OF=1,其他时候OF=0
CMP 比较指令 做减法运算但不存储结果,根据结果设置条件标志位
MUL 无符号数乘法指令
IMUL 有符号数乘法指令   均对CF和OF位以外的条件码位无定义(即状态不定)
DIV 无符号数除法指令
IDIV 带符号数除法指令 除法指令对所有条件码位均无定义
位操作指令AND 逻辑与
OR 逻辑或
NOT 逻辑非 不影响标志位
XOR 异或
TEST 测试指令   除NOT外的四种,置CF、OF为0,AF无定义,SF,ZF,PF根据运算结果设置
移位指令SHL 逻辑左移指令
SHR 逻辑右移指令 移位指令根据结果设置SF,ZF,PF位
ROL 循环左移指令
ROR 循环右移指令 循环移位指令不影响除CF,OF之外的其他条件位
串处理指令:
MOVS 串传送指令
STOS 存入串指令
LODS 从串取指令 均不影响条件位
CMPS 串比较指令
SCAS 串扫描指令 均不保存结果,只根据结果设置条件码
控制转移指令:
JMP 无条件转移指令 不影响条件码

 

参考资料:20135202闫佳欣的《第三章 程序的机器级表示》总结

 
posted on 2015-10-11 22:38  20135223  阅读(289)  评论(1编辑  收藏  举报