计算机执行机器代码时,实际上就是用字节编码低级的操作
计算机在发展过程中,x86-64处理器系列的指令是向后兼容的,即支持过去的指令集,即使是过去的指令集在现在的平台上也是可以运行的,IA-32的64位扩展“intel64”,我们现在称他为“x86-64”
程序编码
gcc编译器
指的是GCC C他是linux上默认的编译器,我们实际调用gcc命令实际就是执行了一系列的流程,将源代码转换为可执行代码,首先,gcc预处理,插入include命令指定的文件,然后扩展define,将使用宏定义的值全部展开。第二步,将产生了两个源文件的汇编代码,然后汇编器会把这两个汇编代码的文件转换为二进制形式,二进制目标代码文件后缀名为.o。他是机器代码一种。最后链接器将多个目标代码文件同实现库函数的代码结合,最后生成可执行代码的文件
机器级代码
计算机系统使用了多种不同形式的抽象,即利用更简单的抽象模型来对实现隐藏实现的细节
第一种是ISA(指令集体系结构,指令集架构)
定义了机器级程序格式和行为,其定义了处理器状态,指令的格式以及每种指令对处理器状态的影响,多数ISA是把程序行为描述成好像每条指令按顺序执行的,实际上机器硬件运算更加复杂,他们并发的执行多条指令,可以采取措施确保整体行为和ISA指定的顺序一致。
第二种就是虚拟地址
把内存看成一个巨大的字节数组的集合,内存地址就是这个巨大字节数组的元素,存储器系统实际就是将多个硬件和操作系统软件相结合
计算机的机器代码是cpu可以直接进行处理的代码,汇编代码和机器代码一一对应,但是汇编代码与我们的源程序是息息相关的,汇编预言是一种使用助记符的语言,不同的机器上有不同的机器语言,但是汇编语言大多数结构却是差不多的,把c语言源代码转换为比较基本的指令,汇编代码十分接近机器代码,甚至可以说是一一对应的,与机器代码二进制格式相比,汇编代码具有良好的可读性的文本表示,我们理解好汇编代码是理解计算机如何执行程序的关键。
汇编代码特点:
不区分有符号数和无符号数,不区分各种类型的指针,甚至是不区分指针和整数。但是C语言中提供的一些数据类型,他们可以在内存中声明各种数据类型的对象,机器代码就是把内存看成一个很大地、可连续寻址的字节数组,C语言之中的数据类型在机器代码中实际就是使用一些连续的字节保存的
程序内存包含:程序的可执行机器代码,操作系统可能会需要的信息,用来管理过程调用和返回时的栈,用户分配的内存块(堆),在虚拟地址中并不是所有的位都可以使用,地址中的高16位设为0所以实际可以访问的字节范围是2^48,操作系统负责管理虚拟地址空间,用来将虚拟地址转换为实际的地址。一个指令只能完成一个非常基本的操作编译器产生一个指令的序列,然后在这个序列上面完成各种操作结构(算术表达式求值,循环或过程调用)
机器代码和他的反汇编表示的特性:
- x86-64指令长度从1到15个字节,一般是越常用的字节数越少,因为使用的频率大,所以可以节省空间
- 设计指令格式方式:某个给定位置开始,我们可以将字节唯一解码位机器指令
- 反汇编是使用字节序列来确定汇编代码的,不需要知道原码或汇编代码
- 反汇编器和高层次、生成的指令由细微不同,即后缀“-q”,但是省略或者加上这些后缀对程序的影响并不是很大,大多数情况下可以省略
要生成实际可执行的代码就必须要对多个目标代码使用连接器,这一组目标文档也必须有一个main函数,实际一个文件生成的字节数要多得多,他包含多个过程的字节,和操作系统交互的代码,程序运行和终止的代码,为提高存储器系统性能,有时候也会进行字节补全的操作
汇编语言程序员写代码风格:在汇编代码右边写下汇编语言对应的源代码操作之间的关系
在c中插入汇编
为了让程序运行更加高效,我们避免不了在c语言中使用汇编语言来对机器进行低级的操作,所以会有下面这几种在c中插入汇编的方法:
- 汇编代码编写整个函数,连接器把它和c函数组合起来。具体操作是,我们编写一个独立函数放进汇编文件中,利用c的链接器和汇编器,把C语言写的代码合并起来。
- 利用gcc的支持,在C语言中嵌入汇编代码,具体操作是利用asm伪指令在c中书写简短的汇编代码,减少了 与机器相关的代码量。
- 在c中包含汇编代码使得这一段代码和某类机器相关,只应该在想要的特性只能以此种方式才能访问到时才能使用它
ATT和intel
汇编代码的格式也有多种种类,现在学习的是ATT格式,但是还有Intel格式的汇编代码:
- intel省略了只是大小“-q”的后缀
- intel省略了在ATT中寄存器之前的“%”
- intel使用不同的方式来描述内存中的位置
- 多个操作数指令的情况下,顺序相反