从高级语言到汇编

hello world

从你写出hello world的C程序,到shell打出hello world发生了什么?

现在有个hello.c的c文件

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

现在让我们分析这个程序的编写好以后,编译和执行过程:

One

每个步骤对应的 GCC 执行命令如下:

  1. 预处理(Preprocessing):

    gcc -E hello.c -o hello.i

    这个命令将源文件 hello.c 进行预处理并将结果保存为 hello.i,在这个阶段会处理#include指令,将标准库头文件的内容插入到程序中。

  2. 编译(Compilation):

    gcc -S hello.i -o hello.s

    这个命令将预处理后的文件 hello.i 编译成汇编代码并将结果保存为 hello.s,在这个阶段将C代码转换成机器可以理解的低级指令。

  3. 汇编(Assembly):

    gcc -c hello.s -o hello.o

    这个命令将汇编代码文件 hello.s 汇编成目标文件 hello.o,这个目标文件包含了机器指令。

  4. 链接(Linking):

    gcc hello.o -o hello

    这个命令将目标文件 hello.o 与任何所需的库一起链接,并生成一个可执行文件 hello,这就是可以运行的程序。

这些命令将 hello.c 编译成一个名为 hello 的可执行文件。

Two

此时hello 已经是一个只有1和0的二进制文件

./hello

可执行文件执行(Execution): 该进程的执行过程可以从以下几个方面来看:

  • 加载(Loading): 操作系统负责将可执行文件加载到内存中,准备执行。

  • 解释(Interpretation): 处理器(CPU)开始执行加载到内存中的指令。这些指令来自于汇编代码,它们被解释成底层的机器指令。

  • 输出(Output): 当程序执行到printf函数时,它将"Hello, World!\n"字符串写入标准输出流(通常是终端)。这就是为什么你会在Shell中看到"Hello, World!"的原因。

现在你可能看到上面的一大堆名词很头疼,但这都没有关系,因为你会在ICS的课程中慢慢学到相关的知识。而这么一大段只是告诉你C语言从编写到编译到执行有一系列复杂的过程,而并非在IDE里面点击运行就好了,这也是为什么要学会使用gcc


著名的拆炸弹 (Bomb Lab)

在上面一小节产生的中间文件hello.s就是我们需要重点学习的汇编语言

现在让我们打开这个文件

现在你可能还无法完全理解里面全部的指令

但我们专注于这么几行

leaq    L_.str(%rip), %rdi
   
callq   _printf

L_.str:                                 ## @.str
    .asciz  "Hello, World!\n"
  • L_.str:: 这是一个标签,用于标识字符串 "Hello, World!\n" 的位置,它只是一个相对位置的符号名称

  • L_.str(%rip) 是一个汇编语言中的地址引用,用于表示字符串 "Hello, World!\n" 在内存中绝对的位置

  • leaq L_.str(%rip), %rdi: 将字符串 "Hello, World!\n" 的地址加载到寄存器 %rdi 中,以供后续的 printf 函数使用。

  • callq _printf: 调用 printf 函数来打印字符串,这个汇编代码中的 printf 调用中,%rdi 寄存器包含了指向格式化字符串 "Hello, World!\n" 的地址。printf 函数会根据这个格式化字符串来打印相应的内容,即 "Hello, World!\n"。

OK,如果这几行也没有看懂也没有关系,在ICS的学习中,我们将一个个学习这里指令和寄存器。简单来说,这些指令的目的就是获取

"Hello, World!\n"在内存中的地址并打印出来

现在请假想一下,没有C的源代码

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

现在只有汇编语言的这些指令,

  1. 你是否能倒退推理出C的源代码的逻辑呢?

  2. 在这个汇编文件中 .asciz "Hello, World!\n",字符串的内容是给出的,假如没有这一行字符串,你是否能从汇编语言中找到字符串的地址,并通过调试工具打印出来呢?

这正是Bomb Lab要做的,当然实际的Lab还要复杂地多。

Last updated