ELF文件浅析

本文介绍了ELF文件的相关知识,包括gcc编译过程、虚拟地址空间分布、ELF文件的组成部分如首部、段表和节表的详细解析,以及如何使用readelf等命令进行查看。还探讨了ELF装载流程和coredump文件的分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

知识回顾

gcc过程

上一篇博客讲述过c程序的启动和终止:

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部分

  1. ELF首部
ELF的文件头中定义了ELF魔数 、 文件机器字节长度、数据存储方式、
 版本、 运行平台、 ABI版本、 ELF重定位类型、 硬件平台、
  硬件平台版本、入口地址、 程序头入口和长度、 段表的位置和长度及段的数量等
  1. 程序首部表(段表): "段"是程序运行时的概念
    段表是从加载器角度出发查看的表`
段表是ELF文件中除了文件头以外最重要的结构,
它描述了 ELF的各个段的信息,比如每个段的段名、段的长度、
在文件中的偏移、读写权限及段的其他属性。
也就是说,ELF文件的段结构就是由段表决定的,
编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。
  1. 节首部表(节表): 节是程序连接时的概念
    节表是才能够汇编器和链接器角度出发查看的表

注意: 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

  1. 前4字节: 固定 0x7f 0x45 0x4C 0x46
  2. 第五字节:ELF文件类别 1表示32位 2表示 64位
  3. 第6字节: 表示机器大小端
  4. 第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

常见的

  1. .bss: 未初始化全局变量,和未初始化静态变量
  2. .text: 代码段
  3. .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

  1. 读取A文件,检查前四个字节 ,看是否是ELF文件

ELF头,在ELF文件的最开始,并且ELF文件的最开始16字节是ELF魔数

  1. 读取程序A的段表,并在A的段表中查找解释器段

段表是加载器查找的表

  1. 从解释器段读取到解释器文件名,并打开解释器,然后读取并检查解释器文件的ELF首部

解释器也是一个ELF文件

  1. 读取解释器的段表
  2. 调用相关函数,终止线程组其他线程,设置内存映射布局,确定用户栈空间.
  3. 把所有可以加载的段,加载到进程的虚拟地址空间
  4. 得到程序入口

elf头部,有程序入口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值