【CSAPP】<Chapter 3>

          编译器承担了生成汇编代码的大部分工作,但是阅读和理解汇编代码仍然是重要能力。

学习意义(1)理解编译器的优化能力,分析代码中隐含的低效率。

      (2)高级语言提供的抽象层会隐藏我们想要了解的程序的运行时行为。

相对于C代码表示的计算操作,优化编译器能够(1)重新排列执行顺序,

                     (2)消除不必要计算,

                     (3)用快速操作替换慢速操作,

                     (4)甚至将递归变换成迭代。

(表述基于x84-64的机器语言。(32位前身是IA-32)(Intel处理器系列俗称x86))

计算机工业已经完成了从32位到64位的过渡。32位机器只能使用4GB的随机访问存储器。(每个后继处理器的设计都是向后兼容的。)

摩尔定律:晶体管数量每26个月就会翻一番。

 

 

          使用Unix命令行编译文件p1.c和p2.c的代码:linux> gcc -Og -o p p1.c p2.c

gcc”:GCC C编译器(linux上默认的编译器),可以简单地用“cc”来启动它。

编译选项“-Og”:说明编译器使用会生符合原始C代码整体结构的机器代码优化等级。较高级别的优化:“-O1”,“-O2”

“gcc”命令调用了一整套程序将源代码——>可执行代码:

  (1)C预处理器,插入所有#include命令指定的文件,扩展所用#define声明指定的宏。

  (2)编译器产生两个源文件的汇编代码p1.s和p2.s。

  (3)编译器将汇编代码——>二进制目标代码文件p1.o和p2.o。(包含所有指令的二进制表示,还没有填入全局值的地址

  (4)链接器将两个目标代码文件与实现库函数的代码合并——>可执行代码文件p(由“-o p”指定)

 

 

          机器级编程中最重要的两种系统抽象:

(1)指令集体系结构ISA(Instruction Set Architecture)定义机器级程序的格式,行为。(处理器状态,指令格式,每条指令对状态的影响)

(2)虚拟地址。使得提供的内存模型看上去是一个很大很大的字节数组。

机器代码对C语言程序员隐藏的处理器状态:

  (1)程序计数器PC(%rip):给出将要执行的下一条指令在内存中的地址。

  (2)整数寄存器文件:包含16个命名的位置,分别存储64位的值,(存储地址,整数数据),记录状态,保存临时数据。

  (3)一组向量寄存器:存放多个整数/浮点数值。

(汇编代码不区分有无符号整数,不同类型指针。)

 

 

          程序内存包括:

(1)程序的可执行机器代码

(2)操作系统需要的一些信息,用来管理过程调用和返回的运行时栈。

(3)用户分配的内存块(如malloc库函数分配的)。

x86-64的虚拟地址是由64位,目前地址的高16位必须设置为0。所以实际地址范围为(0 ~ 64TB)。

 “-S”选项让GCC运行编译器,只产生汇编文件,不做进一步的工作:linux> gcc -Og -S mstore.c

 “-c”选项让GCC编译并汇编该代码:              linux> gcc -Og -c mstore.c

 

 

          展示程序的字节级(二进制目标代码)表示:

用反汇编器确定该过程的代码长度为14字节。在mstore.c上运行GNU调试工具GDBlinux> x/14xb multstore

让GDB(显示为“x”)从函数multstore所处地址开始14个十六进制格式表示(简写为“x”)的字节简写为“b”)

 

 

          查看机器代码文件的内容:

使用反汇编器(根据机器代码产生的一种类似于汇编代码的格式):linux> objdump -d mstore.o(调用程序OBJDUMP,即object dump)

机器代码特性:

  (1)指令长度从1 ~ 15不等。常用指令/操作数少的指令所需字节数少,反之则多

  (2)设计指令格式的方式:从某个给定位置起,可以将字节码唯一地解码成机器指令。(如只有pushq %rbx以字节53开头)(类似哈弗曼编码)

  (3)只基于机器代码文件中的字节序列来确定汇编代码。不需要访问改程序的源代码/汇编代码。

  (4)反汇编代码与汇编代码指令命令规则差别:省略了很多指令结尾的“q”,给“call”和“ret”指令添加了“q”后缀。

 

 

          生成实际可执行的代码需要对一组目标代码文件(必须含有一个main函数)运行链接器。

执行可执行文件prog:linux> gcc -Og -o prog main.c mstore.c

有一部分代码与mstore.c反汇编产生的代码极为相似。不同之处:

  (1)链接器将这段代码的地址移到了一段不同的地址范围中。

  (2)链接器填上了callq指令调用函数mult2需要使用的地址(链接器任务之一:为函数调用找到匹配的函数的可执行代码位置。)

  (3)多了两行代码:插入它们使得函数代码变为16字节,以便更好地放置下一个代码块。(就存储器系统性能而言

 

 

          对于一些应用程序,程序员必须用汇编代码来访问机器的低级特性

  (1)用汇编语言编写完整函数,放入独立的汇编代码文件中,让汇编器和编译器把它和C语言书写的代码合并起来。

  (2)使用GCC内联汇编特性,用asm伪指令可以在C语言中包含简短的汇编代码。

 

 

          C语言编译器无法访问到的机器特性举例:

x86-64处理器执行算术/逻辑运算时,如果运算结果的低8位中有偶数个1,则一名为“PF”(parity flag)的条件码值设为1;

在C语言中,至少需要7次移位,掩码,异或运算。

 

C声明    Intel数据类型    汇编代码后缀    字节数

char      字节        b          1

short        字         w         2      

int       双字         l          4    

long         四字         q            8  

char*        四字         q           8 

float        单精度       s           4

double       双精度       l            8        int和double的“l”后缀不会混淆,因为浮点数使用一组完全不同的指令和寄存器

 

posted @ 2018-01-01 20:01  Leslieeeeee  阅读(277)  评论(0编辑  收藏  举报