汇编概述#

架构(ISA)是 CPU 设计的一部分,决定了汇编语言的形式。指令集充当了软件和硬件的接口,定义了

  • 系统状态(state),例如寄存器、内存、程序计数器等

  • CPU 可以执行的指令(instruction)

  • 每个指令对系统状态的影响(effect)

不同的处理器家族有不同的指令集架构,常见的例如 x86-64、ARM 以及 Risk-V。

微架构(Microarchitecture)是 ISA 的某种实现,比如高通的晓龙和苹果的 M1 处理器,虽然都是 ARM 架构,但其内部实现截然不同。

数据以二进制形式表示,代码也同样以二进制形式表示。GCC 将 C 代码转换成二进制机器码。而二进制的机器码一般较难以阅读,所以有了汇编语言——这是一种比较容易阅读的形式(human-readable machine language)。一条 C 代码,通常会转化成多条汇编指令。

一般情况下,我们不会直接编写汇编程序,通过现代编译器可以将 C 程序转化为不同平台的汇编指令,甚至可以自动优化指令。即便如此,理解汇编程序对理解机器级的执行模式依然很有帮助:

  • 更好地了解程序的漏洞是如何出现的以及防御措施

  • 分析代码中隐含的低效部分,优化程序的性能

  • 在分布式系统中,可以通过底层直接观察数据的形式

利用在线网站 godbolt.org 也可以很方便地查看 C 代码转换后的汇编形式,注意编译选项需要添加 -Og -std=gnu99。通过命令行工具使用 objdump -d 也可以得到类似的结果。

int sum_array(int arr[], int nelems) {
    int sum = 0;
    for (int i = 0; i < nelems; i++) {
        sum += arr[i];
    }
    return sum;
}
0000000000401136 <sum_array>:
  401136:       b8 00 00 00 00          mov    $0x0,%eax
  40113b:       ba 00 00 00 00          mov    $0x0,%edx
  401140:       eb 09                   jmp    40114b <sum_array+0x15>
  401142:       48 63 c8                movslq %eax,%rcx
  401145:       03 14 8f                add    (%rdi,%rcx,4),%edx
  401148:       83 c0 01                add    $0x1,%eax
  40114b:       39 f0                   cmp    %esi,%eax
  40114d:       7c f3                   jl     401142 <sum_array+0xc>
  40114f:       89 d0                   mov    %edx,%eax
  401151:       c3                      ret
  • 第 1 行是函数的名称和函数的内存地址,这是函数指令的起点。如果定义一个函数指针,则可以指向这个位置。

  • 第 2~11 行冒号之前的部分,是函数每一条指令的内存地址,按序依次存储在内存中。

  • 中间部分则是字节的位模式,以十六进制显示,这是真实的机器指令。本质是 0/1 组成的 ISA 指令,不同指令,长度不一。

  • 为了便于阅读,后半部分显示了每一条机器指令对应的汇编指令