Compiling_linking
//预编译
gcc -E hello.c -o hello.i //只进行预编译处理
//编译
gcc -S hello.i -o hello.s //对预编译文件进行编译处理,生成相应的汇编代码文件
//汇编
gcc -c hello.s -o hello.o //将汇编文件转化为可执行的机器码文件
as hello.s -o hello.o //与上命令相同效果
gcc -c hello.c -o hello.o //也可以直接将c语言文件转化(经过预编译,编译,汇编)为目标文件(Object File)
编译器前端负责产生机器无关的中间代码
编译器后端(代码生成器,目标代码优化器等)负责将中间代码转换成目标机器代码
链接过程主要包括了地址和空间分配,符号决议(又叫符号绑定等等,决议偏向于静态,绑定偏向于动态),重定位等步骤
Object文件
object文件包括那些编译后但未链接的文件
程序源代码编译后的机器指令经常被放在代码段(.code .text)里
全局变量和局部静态变量数据经常放在数据段(.data)里
.bss段只是为未初始化的全局变量和局部静态变量预留位置而已
程序指令:代码段
程序数据:数据段和.bss段
更多的细节:
.rodata是只读数据段
.comment是注释信息段
.note.GUN-stack是栈堆提示段
__attribute__((section("FOO"))) int a = 42;//将相应的变量和函数(这里是a)放到对应的段(这里是自定义的FOO)中
ELF文件最前部是一个ELF头,里面包含了整个文件的基本属性的描述,其相关常数变量都存在/usr/include/elf.h里
ELF头中包含ELF魔数(用来确认文件的类型,魔数正确操作系统才会正确加载该文件),文件机器字节长度,数据存储方式,版本,运行平台,ABI版本,ELF重定位类型,硬件平台,硬件平台版本,入口地址,程序头入口和长度,段表的位置和长度,段的数量等
ELF魔数
前四个字节是魔数(所有ELF都必须相同的标识码)
第五个字节用来标识文件类,0x01表示是32位,0x02表示是64位的
第六个字节是字节序,规定大端小端
第七个字节是ELF文件版本,一般是1,因为ELF在1.2之后就再也没有更新了
后面的九个字节ELF标准没有定义,一般为0,平台也可对其扩展
文件类型
e_type表示文件类型,有三种ELF文件
ET_REL:1-可重定位文件,一般为.o文件,
ET_EXEC:2-可执行文件,
ET_DYN:3-共享目标文件,一般为.so文件
重定位表
类似.rel.text是针对.text的重定位表,表明该段至少有一个绝对地址的引用
链接
链接的接口——符号
在链接中,我们将变量和函数统称为符号,函数名和变量名就是符号名
每一个目标文件都有一个相应的符号表(一般是一个叫.symtab的段),每个定义的符号都有一个对应的值,叫符号值
符号表里有个符号所在段,表明该符号在段中或者不在本目标文件中
//可以使用以下代码来判断当前编译单元是不是c++代码
#ifdef __cplusplus
extern "C"{
#endif
void *memset( void*, int, size_t );
#ifdef __cplusplus
}
#endif
强符号和弱符号
对于c/c++来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号
规则1:强符号不允许被多次定义
规则2:强符号和弱符号均有的情况下,那么认为符号的定义为强符号
规则3:多个弱符号选择占用空间最大的那一个弱符号
链接步骤
1.空间与地址的分配
2.符号解析与重定位,重定位是核心步骤