第三章程序的机器级表示

原地址:https://www.cnblogs.com/disturbia/p/4869229.html

第三章 程序的机器级表示

 

3.1 历史观点

  • Intel处理器系列俗称x86,开始时是第一代单芯片、16位微处理器之一。
  • 每个后继处理器的设计都是后向兼容的——较早版本上编译的代码可以在较新的处理器上运行。
  • X86 寻址方式经历三代:

     1  DOS时代的平坦模式,不区分用户空间和内核空间,很不安全

     2  8086的分段模式

     3  IA32的带保护模式的平坦模式

 

3.2 程序编码

gcc -01 -o p p1.c
  • -01 表示使用第一级优化。优化的级别与编译时间和最终产生代码的形式都有关系,一般认为第二级优化-02 是较好的选择。
  • -o 表示将p1.c编译后的可执行文件命名为p

3.2.1机器级代码

  • 计算机系统使用了多种不同形式的抽象,对于机器级编程来说,两种抽象尤为重要。第一种是机器级程序的格式和行为,定义为指令集体系结构(ISA),他定义了处理器状态、指令的格式,以及每条指令对状态的影响。
  • 几个处理器:

    • 程序计数器(CS:IP)
    • 整数寄存器(AX,BX,CX,DX)
    • 条件码寄存器(OF,SF,ZF,AF,PF,CF)
    • 浮点寄存器

 

3.2.2代码示例


在命令行上使用“-S”选项,就能得到C语言编译器产生的汇编代码:
unix> gcc -01 -S code.c
这会使GCC运行编译器产生一个汇编文件code.s,但不做其他进一步工作
如果在命令行上使用“-C”选项,GCC会编译并汇编该代码:
unix> gcc -01 -c code.c
这就会产生目标代码文件code.o,他是二进制格式,无法直接查看。

要查看目标代码文件的内容,需要使用反汇编器

unix> objdump -d code.o

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

  • IA32指令长度从1到15个字节不等。
  • 设计指令格式的方式是,从某个给定位置开始,可以将字节唯一的解码成机器指令。
  • 反汇编器只是基于机器代码文件中的字节序列来确定汇编代码,不需要访问程序的源代码或汇编代码。
  • 反汇编器使用的指令命名规则与GCC生成的汇编代码使用的有些差别。

 

3.3 数据格式

1.Intel中:

8 位:字节
16位:字
32位:双字
64位:四字

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

char	  字节	             1字节
short	  字	             2字节
int	 双字	             4字节
long int  双字               4字节
long long int (不支持)     4字节
char *	双字                 4字节
float	单精度               4字节
double	双精度               8字节
long double 扩展精度     10/12字节

3.数据传送指令的三个变种:

  • movb 传送字节
  • movw 传送字
  • movl 传送双字

3.4 访问信息

3.4.1操作数指示符

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

操作数的三种类型

  • 立即数
  • 寄存器
  • 存储器

 

寻址方式:

1.立即数寻址方式

2.寄存器寻址方式

3.存储器寻址方式

  • 直接寻址方式
  • 寄存器间接寻址方式
  • 寄存器相对寻址方式
  • 基址变址寻址方式
  • 相对基址变址寻址方式

3.4.2 数据传送指令

mov类指令:将源操作数的值复制到目的操作数中。源操作数指定的值是一个立即数,存储在寄存器中或存储器中。目的操作数制指定一个位置,要么是一个寄存器,要么是一个存储器。

  • movb 传送字节
  • movw 传送字
  • movl 传送双字
  • movs 符号位扩展
  • movz 零扩展

push:把数据压入栈中

pop:删除数据

  • 后进先出
  • 栈指针指向栈顶元素
  • 栈朝低地址方向增长

3.4.3 数据传送示例

  • c语言中的指针其实就是地址,间接引用指针就是将该指针放在一个寄存器中,然后在存储器引用中使用这个寄存器
  • 局部变量通常保存在寄存器中,而不是存储器

3.5 算术和逻辑操作

3.5.1 加载有效地址

加载有效地址指令leal实际上就是movl指令的变形。它的指令形式是从存储器读取数据到寄存器,但实际根本没引用存储器。

3.5.2 一元操作和二元操作

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

二元操作:源操作数 目的操作数

  • 第一个操作数可以是立即数、寄存器或者存储器位置

  • 第二个操作数可以是寄存器或者存储器位置

  • 但是不能同时是存储器位置

3.5.3 移位操作

  • SAL 算术左移
  • SHL 逻辑左移
  • SAR 算术右移(补符号位)
  • SHR 逻辑右移(补0)

3.5.5 特殊的算术操作

3.6 控制

3.6.1 条件码

  • CF:进位标志

  • ZF:零标志

  • SF:符号标志

  • OF:溢出标志 

3.6.2 访问条件码

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

  • 可以根据条件码的某个组合,将一个字节设置为0或者1
  • 可以条件跳转到程序的某个其他的部分
  • 可以有条件地传送数据

执行比较指令,根据计算t=a-b设置条件码。

3.6.3 跳转指令及其编码

跳转指令会导致执行切换到程序中一个全新的位置。在汇编代码中,这些跳转的目的地通常用一个标号指明。

跳转指令有几种不同的编码,最常用的是PC(程序计数器)相关的。

jump分为:

  • 直接跳转:后面跟标号作为跳转目标
  • 间接跳转:*后面跟一个操作数指示符

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

3.6.4 翻译条件分支

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

3.6.5 循环

汇编中没有do-while、while和for相应的指令存在,可以用条件测试和跳转组合起来实现循环的效果。大多数汇编器中都要先将其他形式的循环转换成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循环

通用形式:

while (test-expr)
	body-statement

GCC的方法是使用条件分支,在需要时省略循环体的第一次执行:

if(!test-expr)
	goto done;
do
		body-statement
		while(test-expr);
done:

接下来,这个代码可以直接翻译成goto代码:

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

3.for循环

通用形式:

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

do-while形式:

init-expr;
if(!test-expr)
goto done;
do{
  body-statement
  update-expr;
  }while(test-expr);
done:

翻译成goto代码:
init-expr;
t=test-expr;
if(!t)
   goto done;
loop:
   body-statement
update-expr;
t= test-expr;
if(t)
   goto-loop;
done:



3.6.6 条件传送指令

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

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

只有在一些受限制的情况下,这种策略才可行,但如果可行,就可以用一条简单的条件传送指令来实现它。

条件传送指令更好地匹配了现代处理器的性能特性。

3.6.7 Switch语句

3.7 过程

3.7.1 栈帧结构

栈用来传递参数、存储返回信息、保存寄存器,以及本地存储。

1.栈帧:

为单个过程分配的那部分栈称为栈帧。

2.最顶端的栈帧以两个指针界定:

  • 寄存器%ebp-帧指针

  • 寄存器%esp-栈指针

3.7.2转移控制

1.call

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

返回地址是还在程序中紧跟在call后面的那条指令的地址。

2.ret

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

3.leave

这个指令使栈做好返回的准备

3.7.3 寄存器使用惯例

程序寄存器组是唯一能被所有过程共享的资源。

 

3.8 数组分配和访问

 

3.8.1 基本原则

 

   

3.8.2 指针运算

 

   

3.8.3 嵌套的数组

 

 

3.8.5 变长数组

 

3.9 异质的数据结构

创建数据类型机制的两种不同类型的对象:
●结构:用关键字struct声明,将多个对象集合到一个单位中

●联合:用关键字union声明,允许用几种不同的类型来引用一个对象。

3.10 综合:理解指针

(1)每个指针都对应一个类型

(2)每个指针都有一个值

(3)指针用&运算符创建

(4)操作符用于指针的间接引用

(5)数组与指针紧密联系

(6)指针也可以指向函数

3.11 应用:使用 GDB调试器

3.12 存储器的越界引用和缓冲区溢出

!对抗缓冲区溢出攻击

a.栈随机化

b.栈破坏检测

c.限制可执行代码区域

3.13 X86-64: 将IA32扩展到64位(了解知识)

3.14 浮点程序的机器级表示

浮点体系结构——存储模型、指令和传递规则的组合。(x87和SSE)

 

出现的问题:有部分和汇编相关的知识点理解起来有些困难,需要查阅相关学习资料才能理解部分。还有就是课后作业编译只会第一步,接下来的步骤不会,希望老师详解。

课后作业:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值