本篇主要包括以下内容:
1. 为什么还要学汇编?
1.1 Linux下C语言转汇编及反汇编
1.2 汇编语言相对于高级语言的特点
2. 学会汇编
2.1 学习汇编要先了解的基本知识 —— 本节及其以上都是学来吹牛逼的
2.2 汇编的数据类型 —— 相对于其他语言,类型少的可怜
2.3 汇编的常用指令 —— 这是个假的重点,看看就行了
2.4 高级语言中条件循环等语句在汇编层怎么实现的 —— 没错,我觉得这个才是重点
2.5 高级语言中函数调用在汇编层的实现过程 —— 重点,重点,重点,专治各种不会传参
2.6 数组在汇编层实现 —— 重点,数组和指针,还在傻傻分不清楚?
2.7 结构体、联合等结构在汇编层实现 —— 重点,或者说深度探索c结构模型,解释字节对齐的原理等
2.8 x86_64汇编补充 —— 就是64位机下的汇编,64位机是趋势,你确定你不看看
3.用汇编做点事
3.1 汇编和gdb —— 下次面试官问你平时怎么找错就不虚了
3.2 汇编和黑客 —— 还记得多年前的梦想吗
鉴于这篇内容较多,所以我根据章节将内容分成了三篇,以下是三篇的地址:
一、为什么还要学习汇编?
网上有很多人讨论,在现在各种高级语言争芳斗艳,各显其能的时候,我们还有必要学习汇编吗?
作为一个应届生,关于这个问题我自己一开始想的是,有时间的话肯定要学一学,为的是在校招场上能有一项与众不同的技能,说不定可以惊艳一下面试官。但是,当我经历了几场面试后,我发现很多面试官并不在乎你会不会汇编或者更底层的知识,会这些知识好像并没有引起他们的好奇。说白了,企业只是各取所需,在你掌握的知识领域有他们想要的,并且你的掌握程度达到了他们的要求,他们才会对你感兴趣,很少会因为你了解了某个尖钻并且无人问津的知识但是对于他们企业无用的知识而对你感兴趣。所以,学习汇编的这个原因好像已经很难说服别人了。
但是,即使是明白了这点之后,我还是坚持并且信心满满的学习汇编,因为我发现自己以前的想法过于短视,汇编是一门独特的语言,汇编和其他高级语言不一样,它不是一门用来创造生产的语言,这也不是它的长处,我们不能用评价普通高级语言的角度去评价汇编。
我认为,汇编是一门研究性的语言,汇编语言很强大,它强大的背后是它和操作系统,计算机硬件的直接交流,我们学习操作系统,计算机体系结构,学完之后我们建立了对于计算机一个大概的认识,知道了对计算机内部结构高度抽象形成的一些概念,但是就算知道了这些概念,对于我们来说又有什么大的改变呢,当你敲下一行行c代码,你真的懂得你所敲下的c代码在计算机底层做了什么,比如一个经常用的printf函数,你只知道你调用这个函数,传入几个参数,屏幕上就会出现字符,但是这个过程真正是怎样的呢?我们在写程序的时候,被告知不能这样写,那样写会出现内存错误,我们用经验解决了这些问题,但是我们真正对这些问题产生的原因了解多少?
对于计算机系统这个黑盒子,我们一无所知,而真正能打开这个黑盒子,我觉得就是汇编语言,学习汇编语言会将我们之前学过的操作系统知识和真正的开发工作联系起来,当我们清楚的知道自己敲下的每个字符在计算机底层的活动时,发生的错误就会更少,我们面对计算机也会更加自信。
当担心程序会不会出错不再成为每天的主要活动时,限制你的就不是技术,而是想法。
1.1 Linux下C语言转汇编及反汇编
在弄清楚汇编语言的好处之前,我们有必要先看看汇编语言长什么样子?汇编语言存在在哪里?
众所周知,c语言经过预处理、编译、汇编、链接才形成可执行文件,C语言转换为汇编代码就是在编译这一阶段产生的。
示例
假如我们给定一个code.c的c语言源程序,在Linux下可以使用下面的命令让编译过程停留在编译阶段并且指定产生的汇编文件为code.s:
gcc -O1 -S code.c -o code.s
编译选项-O1告诉编译器使用第一级优化,关于gcc的编译优化等级,可以参考这篇文章:GCC编译器优化选项分析及具体优化了什么
-S选项表示将编译过程截止到编译,这是产生的输出文件就是源代码对应的汇编代码,使用-o选项制定输出文件
示例的hello.c内容如下:
int accum = 0;int sum(int x, int y)
{int t = x + y;accum += t;return t;
}
通过上条命令产生的汇编代码如下:
sum:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eaxaddl 8(%ebp), %eaxaddl $eax, accumpopl %ebpret
在上面的汇编代码中,pushl,movl,popl,ret等都是汇编指令,它们每个都对应一条机器指令,每条指令都都只会处理一个非常简单的任务。
同时,在c语言对应的汇编语言中已经除去了所有局部变量名和数据类型,并将其转换为相应的寄存器名称和地址引用。但是对于全局变量accum,暂时采用c代码中的变量名,这是因为编译器还不确定这个变量在存储器的哪个位置,一旦知道这个变量的存储器地址,就会将变量名替换为相应的地址信息。
反汇编
我们还可以使用-c命令行选项,使编译过程停留在汇编阶段。产生经过汇编器汇编过得.o文件
gcc -O1 -c code.c -o code.o
目标代码文件code.o是二进制格式,所以无法直接查看。这时候我们就要使用我们的反汇编器(disassembler),Linux下objdump命令就可以对目标程序进行反汇编,使用objdump的-d项进行反汇编示例如下:
objdump -d code.o
关于objdump命令的更多用法,可以参考这篇文章:objdump命令
结果如下:(遵照原书作者,斜体字并不是命令的输出,而是为了理解加上的注释)
上面输出的offset栏表示该指令的机器码在.o文件中的位置,Bytes栏表示该指令的机器码表示,Equivalent assembly language表示机器码等价的汇编指令。
值得说明的是反汇编出来的汇编指令与我们编译出来的汇编指令有些许差距,编译出来的汇编指令每个指令后面会跟着该指令所处理的的字节长度,如pushl中的‘l’,这些大小指示符在反汇编中被忽略,反汇编的结果是保证我们看清楚程序的逻辑,所以这些后缀的忽略对我们的分析影响不大。
上面列出的编译之后的汇编语言只包含了真正编译结果的一部分,其中还有一部分未列出,这些命令主要都是以’.’开头的汇编器和链接器的命令,这些都不是我们研究的重点,所以我们暂且忽略这些命令。
1.2 汇编语言相对于高级语言的特点
对于汇编语言,我们已经有了一个大概的认识,下来我们看看汇编语言和高级语言的特点:
汇编语言和高级语言各有千秋。首先,我们看看高级语言的相对于汇编语言的优点:
1.高级语言相对于汇编语言提供的抽象级别高,会带来更高的工作效率和可靠性。
2.编译器为我们提供了类型检查等一系统防御措施,避免很多程序错误。
3.现代的优化编译器产生的代码至少与一个熟练的汇编语言程序员手工编写的代码一样有效。
4.高级语言的可移植性。
汇编语言相对于高级语言的优点:
1.我们可以通过分析程序的汇编代码来分析优化代码中隐含的低效率。
2.高级语言的抽象隐藏了我们想要了解的程序运行时的行为信息。
3.程序在遭受病毒时,很多程序漏洞都是汇编级的。
所以学习汇编在很多时候还是很有必要的。
程序员学习汇编代码的需求随着时间的推移也发生了变化,开始时只要求程序员能直接用汇编语言编写程序,现在则是要求他们能够阅读和理解编译器产生的代码。