* main.c #include "a.h" #define abc 5 int main() { add(abc, 5); } * a.h int add(int a, int b); * a.c #include "a.h" int add(int a, int b){ return a+b; } ===== 预处理 ===== * 将宏出现的地方进行替换。main.c预处理后 * 命令gcc -E main.c int main() { add(5, 5); // 宏abc替换成了5 } ===== 编译 ===== * 将预处理后的代码编译成汇编 * 命令gcc -S main.c ===== 汇编 ===== * 将汇编代码转成机器码(.o文件),.o文件有如下特点 * 一个功能的机器码 * 自己并不能被执行,常用于链接成可执行文件 * 命令gcc -c main.c a.c 静态链接库(.a文件)就是一堆.o文件的集合 {{:pasted:20190127-055522.png}}{{:pasted:20190127-055547.png}} ===== 链接 ===== * 命令 ld main.o a.o * 汇编之后生成了main.o和c.o 两个文件(这2个文件并不能执行,因为main.o中并不知道add的逻辑在哪里。add的逻辑在a.o中) * 链接的作用是将main.o 和c.o中内容合并成一个可执行文件。步骤大致如下(省略了无关的步骤) * 创建一个新的文件(默认是a.out)写入main.o中的内容 * 发现main.o依赖add方法 * 链接器去其他.o文件中找add方法,找到后将add方法的字节码放入a.out中并记录偏移地址 * 将所有调用add方法的地方,改成a.out中add方法的实际地址 * 如果引用外部的变量也是上面这个过程 链接后就能定位到add方法的实现了 {{:pasted:20190127-055640.png}} ===== 可执行文件结构 ===== 可执行文件。上面的.o 文件 a.out等都是分段存储的 * ELF Header 文件头 * 包含系统类型,程序入口地址等信息 * .text 代码段 * .data 数据段 * .bss 段 * 为全局未定义,静态未定义的变量保留空间 * .rodata 只读数据段(对应编程中const 修饰符)