* 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 修饰符)