文章目录
知识回顾
gcc过程
gcc -v 查看完整的gcc过程
ld 为链接器
as 为汇编器
gcc相关命令:
选项 | 功能 | 文件后缀 |
---|---|---|
-o | 指定输出文件的文件名称 | |
-E | 生成预处理之后的文件 | xxxx.i |
-S | 生成编译之后的文件(汇编代码) | xxxx.s |
-c | 将汇编代码转换成机器语言,暨生成目标文件 | xxxx.o |
-g | 生成可供gdb调试的文件 |
注意: 总结:在编译过程中。除非使用了"-c",“-S”,或"-E"选项(或者编译错误阻止了完整的过程),否则统一完整链接步骤。
GCC -O 四级优化
-O0 | 不做任何优化,这是默认的编译选项。 |
-O1 | 优化会消耗少多的编译时间,它主要对代码的分支,常量以及表达式等进行优化。 |
-O2 | 会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。 |
-O3 | 在O2的基础上进行更多的优化 |
gcc相关文件
.o文件: object file 目标文件
动态库文件
他们有一个共同的特点: ELF
虚拟地址空间分布
一张图
ELF文件有哪些
使用file 命令 查看相关文件
ELF结构
优秀博客:https://blog.youkuaiyun.com/daide2012/article/details/73065204
使用readelf 命令 读取elf文件
ELF文件分为4部分
- ELF首部
ELF的文件头中定义了ELF魔数 、 文件机器字节长度、数据存储方式、
版本、 运行平台、 ABI版本、 ELF重定位类型、 硬件平台、
硬件平台版本、入口地址、 程序头入口和长度、 段表的位置和长度及段的数量等
- 程序首部表(段表): "段"是程序运行时的概念
段表是从加载器角度出发查看的表`
段表是ELF文件中除了文件头以外最重要的结构,
它描述了 ELF的各个段的信息,比如每个段的段名、段的长度、
在文件中的偏移、读写权限及段的其他属性。
也就是说,ELF文件的段结构就是由段表决定的,
编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。
- 节
- 节首部表(节表): 节是程序连接时的概念
节表是才能够汇编器和链接器角度出发查看的表
注意: ELF首部位置固定,其余各部分的位置 有ELF首部信息指定查找地址
ELF首部
百度百科 ELF首部
#define EI_NIDENT 16
typedef struct{
unsigned char e_ident[EI_NIDENT]; 16字节魔数字
Elf32_Half e_type; elf文件类型
Elf32_Half e_machine; 机器类别
Elf32_Word e_version; 版本
Elf32_Addr e_entry; 程序入口虚拟地址
Elf32_Off e_phoff; 段表地址偏移
Elf32_Off e_shoff; 节表地址偏移
Elf32_Word e_flags;
Elf32_Half e_ehsize; elf头长度
Elf32_Half e_phentsize; 段表长度
Elf32_Half e_phnum; 段表项目数量
Elf32_Half e_shentsize; 节表长度
Elf32_Half e_shnum; 节表项目数量
Elf32_Half e_shstrndx; 节名称字符串在节表中的索引
}Elf32_Ehdr;
readelf -h
ELF魔数
unsigned char e_ident[EI_NIDENT]; 16字节魔数字
总计16个字节: 4+1+1+1+9=16
- 前4字节: 固定 0x7f 0x45 0x4C 0x46
- 第五字节:ELF文件类别 1表示32位 2表示 64位
- 第6字节: 表示机器大小端
- 第7字节: ELF文件版本号
段表
段:是从程序运行时的角度出发的概念
readelf -l hellow
上述各段组成了最终在内存中执行的程序 其还提供了各段在虚拟地址空间和物理地址空间中的大小、位置、标志、访问授权和对齐方面的信息。各段语义如下:
PHDR: 保存程序头表
INTERP: 指定程序从可行性文件映射到内存之后,必须调用的解释器,它是通过链接其他库来满足未解析的引用,用于在虚拟地址空间中插入程序运行所需的动态库。
LOAD: 表示一个需要从二进制文件映射到虚拟地址空间的段,其中保存了常量数据(如字符串),程序目标代码等。
DYNAMIC: 段保存了由动态连接器(即INTERP段中指定的解释器)使用的信息
节表
节;是从程序连接时的角度出发的概念
readelf -s hellow
节表的结构
节表的本质是一个数组
所以在elf头那里有
Elf32_Off e_shoff; 节表地址偏移
Elf32_Half e_shentsize; 节表长度
Elf32_Half e_shnum; 节表项目数量
节表元素的结构
节的name
常见的
- .bss: 未初始化全局变量,和未初始化静态变量
- .text: 代码段
- .date: 存放已初始化全局变量和已初始化静态变量
不常见的
相关命令行
readse:
1.readelf -h :查看elf头的内容
2.readelf -l :查看段表
3. readelf -S : 查看节表
size:它可以用来查吞 ELF 文件的代码段、数据段和 BSS的长度
objdump
objdump -s:参数可以将所有段的内容以十六进制的方式打印出来,
objdump -d :可以将所有包含指令的段反汇编。
查看某个节的内容来看变量
##coredump文件
当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core Dump(中文有的翻译成“核心转储”)。core dump 对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而 core dump 文件可以再现程序出错时的情景。
回顾 wait/waitpid 韩那书返回值中, 低八位 的最高位 有一个coredump标志位
使用readelf查看coredump文件
readelf -S gdb
可以看到多了很多.debug段
使用strip 命令 可以去除掉调试信息. 并且文件大小也会明显减小
ELF装载流程
举例: 即将装载程序 A
- 读取A文件,检查前四个字节 ,看是否是ELF文件
ELF头,在ELF文件的最开始,并且ELF文件的最开始16字节是ELF魔数
- 读取程序A的段表,并在A的段表中查找解释器段
段表是加载器查找的表
- 从解释器段读取到解释器文件名,并打开解释器,然后读取并检查解释器文件的ELF首部
解释器也是一个ELF文件
- 读取解释器的段表
- 调用相关函数,终止线程组其他线程,设置内存映射布局,确定用户栈空间.
- 把所有可以加载的段,加载到进程的虚拟地址空间
- 得到程序入口
elf头部,有程序入口