CSAPP要点总结 第3章 程序的机器级表示 part1 basic

隐藏

前言

强烈建议学习CSAPP第三章前先学一下王爽的《汇编语言》,虽然其中讲解的8086指令有些过时,但很多内容都很有用,且内容很细。

为什么需要学习汇编

通常,现代优化的编译器产生的高级语言的代码至少和一个熟练的汇编语言程序员手写的汇编代码一样高效,且比汇编有更好的可移植性。

然而能阅读理解汇编代码仍是一项重要技能。因为这样可以:

  1. 理解编译器的优化能力,并分析隐含的低效率。用于优化关键代码。
  2. 有些高级语言的抽象层会隐藏有关程序的运行时行为信息。例如当用线程包写并发程序时,知道存储器保持不同程序变量的区域很重要,这在汇编中可见。
  3. 程序漏洞都涉及程序存储运行时控制信息方式的细节,被病毒利用漏洞重写信息。

C语言嵌入汇编的方法:

  1. 整个文本用汇编,编译,然后链接阶段与C结合
  2. 在C语言中,直接利用GCC对嵌入汇编代码的支持

用SSE进行浮点运算

SSE指令是为支持多媒体应用而开发的。 但SSE2以后,配合较新的GCC,已经成为将浮点运算映射到IA32和x86-64的首选方法。

Intel CPU重要的历史发展

8086

16位微处理器之一,8088是8086的变体,增加了8位外部总线。用于最初的IBM的PC机。 详情参考王爽著《汇编语言》

i386

扩展到32位,增加了平坦寻址(flat addressing model),Linux和现代Windows都用的平坦寻址。第一次支持UNIX系统。

Pentium III

Pentium Pro 开始,引入称为P6的微体系结构。 引入SSE,每个数据可以1、2或者4字节,打包成128位的向量。芯片上包括了二级缓存。

Pentium 4

SSE扩展到SSE2,增加了新的数据类型(包括double float),即针对新数据的指令。可用SSE指令编译浮点代码。 引入NetBurst微体系结构

Pentium 4E

增加了超线程(hyperthreading),增加了EM64T,即Intel开始支持AMD提出的64位扩展,即x86-64。 NetBurst微体系结构

Core 2

回归类似P6的微体系结构多核处理器,但不支持超线程。

Core i7

支持超线程也支持多核

以上每一代都向后兼容,成为x86系列。

x86加入很多处理小整数和浮点数的向量格式和指令,提高了多媒体应用的性能, 例如图像处理、音频视频的编码解码。 现代GCC为32位执行编译时,仍然默认假设为i386机器,因此要用扩展指令,需要指定编译选项,或者用64位。

程序编码

机器级代码

计算机系统使用了多种不同形式的的抽象,利用抽象来隐藏实现细节。 对于机器级编程来说,有两个重要的抽象:

指令集体系结构ISA

机器级程序的格式和行为。 定义为**指令集体系结构(Instruction Set Architecture,ISA)。

包括处理器的状态、指令的格式和每条指令对状态的影响(类似于8086中指令对标志位寄存器的影响)。

大多数ISA(包括IA32,x86-64)抽象上假设了指令是一条一条按顺序执行,实际复杂的硬件可并行处理。但保证结果一致。

虚拟的存储器地址

参见第一章内容,也就是说将所用内存,硬件缓存,指令等等都统一映射到同一个虚拟地址上,这样只需要操作一个虚拟地址就行了,实现对不同硬件平台的统一处理。

汇编代码

机器码就是二进制, 汇编代码是对应的文本表示。

汇编指令与原始的C代码差别较大,很多对C隐藏的处理器细节都可见,包括:

1)程序计数器 在IA32中称为PC,也是一个寄存器。用%eip表示,指向下一条执行的指令地址,参见王爽《汇编语言》第二章内容。类似8086的ip寄存器。

2)整数寄存器文件 32位寄存器,有8个,可存地址(对应C的指针)或整数数据。

3)条件码寄存器 保存最近执行的算术或逻辑指令的状态信息。用来实现控制流或数据流的条件变化,例如C中的if或者while语句。

4)浮点寄存器 一组浮点寄存器存放浮点数据。

机器码、汇编代码的区别

机器码就是一个大的按照字节读取数据的二进制数组序列。 不区分有符号/无符号整数, 不区分数组还是单个数 不区分各类指针等。

机器码是二进制的片段,当程序载入内存后,占用内存的一片连续空间(连续空间是猜测的,应该是)这一段程序内存空间包含几个段:

  1. 可执行的机器代码,类似于8086的code segment
  2. 操作系统需要的一些信息,类似8086的psp(程序段前缀)
  3. 用于函数调用/返回的栈
  4. 以及一块供用户使用的内存block

上述片段寻址是虚拟地址, 操作系统管理虚拟地址空间,并将虚拟地址空间映射到实际内存的物理地址

一条机器指令只执行非常基本的元操作。

汇编代码生成与反汇编

汇编代码生成:

~ $ gcc -O1 -S code.c -o codeO1.s

-S即生成汇编代码.s文件的命令行选项

生成目标文件

~ $ gcc -O1 -c code.c -o codeO1.o

-c compile命令即生成目标二进制文件

要想查看二进制目标文件的汇编代码,就需要反汇编:

~ $ objdump -d codeO1.o

assembly与dump似乎正好是一对反义词, assembly意为集成、装配,所以是“汇”编,装配成机器码。 dump意为拆卸,“反汇”编,把机器码拆卸成汇编语言。

反汇编后,结果类似于:

asm.o:     文件格式 pe-i386

Disassembly of section .text:

00000000 <_sum>:
   0:   8b 44 24 08             mov    0x8(%esp),%eax
   4:   03 44 24 04             add    0x4(%esp),%eax
   8:   01 05 00 00 00 00       add    %eax,0x0
   e:   c3                      ret
   f:   90                      nop

IA32指令长度为1~15个字节不等。 常用指令,以及操作数较少的指令长度较短; 不常见的。操作数多的则指令较长。

通常反汇编的代码不能完全逆向出原始的汇编代码,但主要内容一致。

-----EOF-----

Categories: csapp Tags: 汇编 computer system