1. 概述

计算机执行机器代码,用字节序列编码低级的操作,包括处理数据、管理内存、读写存储设备上的数据,以及利用网络通信。编译器基于编程语言的规则、目标机器的指令集和操作系统遵循的惯例,经过一系列的阶段生成机器代码。GCC C 语言编译器以汇编代码的形式产生输出,汇编代码是机器代码的文本表示,给出程序中的每一条指令。然后 GCC 调用汇编器和链接器,根据汇编代码生成可执行的机器代码。

2. 程序编码

计算机系统使用多种抽象模型来隐藏实现的细节。对于机器级编程来说,有两种抽象尤为重要。

  • 第一种是由指令集架构(Instruction Set Architecture, ISA)来定义机器级程序的格式和行为,它定义了处理器状态、指令的格式,以及每条指令对状态的影响。
  • 第二种抽象是,机器级程序使用的内存地址是虚拟地址,提供的内存模型看上去是一个非常大的字节数组。存储器系统的实际实现是将多个硬件存储器和操作系统软件组合起来。

在编译过程中,编译器把用 C 语言提供的相对比较抽象的执行模型表示的程序,转化为处理器执行的非常基本的指令。汇编代码表示非常接近于机器代码。与机器代码的二进制格式相比,汇编代码的主要特点是它用可读性更好的文本格式表示。

程序内存包含:程序的可执行机器代码、操作系统需要的一些信息、用来管理过程调用和返回的运行时栈,以及用户分配的内存块。程序内存用虚拟地址来寻址,在任意给定的时刻,只有一部分虚拟地址被认为是合法的。操作系统负责管理虚拟地址空间,将虚拟地址翻译为实际处理器内存中的物理地址。

一条机器指令只执行一个非常基本的操作。机器执行的程序只是一个字节序列,它是对一系列指令的编码。机器对产生这些指令对源代码几乎一无所知。

机器代码与汇编代码

生成实际可执行的代码需要对一组目标代码文件运行链接器,而这一组目标代码文件必须含有一个 main 函数。上图中,callq 指令调用函数 muil2 需要使用地址,链接器的任务之一就是为函数调用找到匹配的函数的可执行的代码的位置。最后的两行 nop 对程序没有影响,插入这些指令的目的是为了使代码变为 16 字节,使得存储器系统更好地放置下一个代码块。

3. 数据格式

C语言数据类型