main.c
预编译 main.i
删除注释
头文件引入
宏展开
编译 main.s
代码优化
汇总所有的符号
汇编 main.o 二进制可重定位文件
根据特定平台 将汇编指令转换成机器码
构建 .o(obj) 文件
链接 可执行文件
1 合并所有obj文件的段,并调整段偏移和段长度,合并符号表
2 链接二点核心:符号重定位 注意强弱符号
符号处理只对所有obj文件的global符号进行处理
local 的符号不做处理 static 修饰的就是local
符号解析完成后 就分配内存地址(虚拟地址)
符号解析就是所有obj符号表中对符号引用的地方都要找到该符号定义的地方
3 所有相同属性的段进行合并组织在一个页面,调整段偏移和长度
然后符合并号表,
然后给符号分配内存地址
函数符号地址存的是偏移量,因为函数调用要跳到别的代码段。
函数符号的真实地址是,下一行指令 + 偏移量
然后给符号重定位
objdump -h main.o 打印obj文件的段
readelf -h main.o 打印 elf 文件头
信息: 平台 体系 入口地址 文件头大小 52 字节
bss 段不占elf文件的空间,占虚拟内存的空间
bss段的初始都为0 不需要存储,存储是为了记录初始值
每个段都会记录在段表中
file main.o 查看文件属性
readelf -S main.o 查看所有段
.text AX 可读可执行
.data WA 可读可写
.bss WA 可读可写
.comment
.note.GNU-stack
.shstrtab
.symtab 符号表段
.strtab
objdump -d main.o 查看机器码 和汇编
objdump -s main.o 打印段的内容
char *p = "hello world"; 常量字符串在 .rodata (只读数据段)
C语言种有强符号和弱符号的概念 初始化的是强符号 未初始化的是弱符号
C语言:多个强符号 编译出错
强符号和弱符号 选强符号
多个弱符号选 占用内存最大的弱符号
objdump -t main.o 查看符号所在的段
A.c 文件要引用 B.c 文件中的变量, 需要在A.c 或A.h 中 extern int data;
汇编中 数据的地方地址都是 00 00 00 00
函数的地方都是 fc ff ff ff
pc 寄存器保持下一条执行的指令
程序的运行
1 创建虚拟地址空间到物理内存的映射(创建内核地址映射结构体)创建页目录和页表
2 加载代码段和数据段
3 把可执行文件的入口地址写到CPU的pc寄存器