ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ELF文件基础

2020-10-25 02:32:14  阅读:577  来源: 互联网

标签:文件 exe aarch64 基础 ELF so SHF linux android


ELF (Executable Linkable Format,wiki  chs)是Linux参考COFF(Common Object File Format)规范而定义的可执行文件格式。

可执行文件、共享目标文件(*.so)、目标中间文件(又称可重定位文件,*.o)、核心转储文件(Core Dump File)都是ELF文件。

按位数可分为:elf32和elf64;支持cpu架构有:aarch64(即:arm64)、arm等。

ELF可能按照不同的字节序(Byte Order)来存储。例如:elf32-bigarm是大端(Big-endian)存储;elf64-littleaarch64是小端(Little-endian)存储。

与COFF一样,ELF也是基于段(Segment,有时也被叫节Section)的结构。其文件结构如下:

 

ELF头(ELF Header):ELF魔数、文件机器字节长度、数据存储方式(大小端)、版本、运行平台、ABI版本、ELF重定位类型、硬件平台及版本、入口地址、程序头入口和长度、段表的位置和长度、段的数量。

段表(Section header table):描述了ELF各个段的基本信息,如:每个段的段名、段的长度、在文件中偏移、读写权限及段的其他属性。编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。

 

段的类型

常量 含义
SHT_NULL 0 无效段
SHT_PROGBITS 1 程序段、代码段、数据段都是这种类型
SHT_SYMTAB 2 该段的内容为符号表
SHT_STRTAB 3 该段的内容为字符串表
SHT_RELA 4 重定位表,包含重定位信息
SHT_HASH 5 符号表的哈希表
SHT_DYNAMIC 6 动态链接信息
SHT_NOTE 7 提示性信息
SHT_NOBITS 8 表示该段在文件中没有内容。如:bss段
SHT_REL 9 该段包含了重定位信息
SHT_SHLIB 10 保留
SHT_DNYSYM 11 动态链接的符号表

段的标志位表示该段在进程虚拟空间中的属性。包括:是否可写(SHF_WRITE),是否需要分配空间(SHF_ALLOC)及是否可执行(SHF_EXECINSTR)。

 

常见的段如下:

段名 类型 标志位 说明
.text SHT_PROGBITS

SHF_ALLOC + SHF_EXECINSTR

代码段

 

存放程序的可执行代码

.data

.data1

SHT_PROGBITS

SHF_ALLOC + SHF_EXECINSTR

数据段

 

存放如下内容:

① 显示初始化为非0的局部静态变量

② 显示初始化为非0的全局变量

.rela.text

.rel.text

SHT_RELA  

代码段重定位表

 

存在于目标中间文件(又称可重定位文件,*.o)中,用于链接时重定位(为静态重定位)

.rela.data

.rel.data

SHT_RELA  

数据段重定位表

 

存在于目标中间文件(又称可重定位文件,*.o)中,用于链接时重定位(为静态重定位)

.rodata

.rodata1

SHT_PROGBITS

SHF_ALLOC

只读数据段

 

存放如下内容:

① 字符串常量

② 全局const变量

.bss SHT_NOBITS

SHF_ALLOC + SHF_WRITE

bss段

这个段在程序运行时,在内存中会被清零

在ELF文件中不占用空间

 

存放如下内容:

① 未初始化的全局变量

② 未初始化的局部静态变量

③ 显示初始化为0的局部静态变量

④ 显示初始化为0的全局变量

.tdata   SHF_ALLOC + SHF_WRITE

用来保存线程局部存储的初始化数据。

默认情况下,每次进程启动新的线程时,系统会产生一份.tdata副本。

.tbss   SHF_ALLOC + SHF_WRITE

用来保存线程局部存储的未初始化数据。

默认情况下,每次进程启动新的线程时,系统会产生一份.tbss副本,并将它的内容初始化为0。

.data.rel.ro   SHF_ALLOC(SHF_WRITE) 保存的是程序的只读数据,与.rodata类似,唯一不同的是它在重定位时会被改写,然后将会被置为只读。
.dynamic SHT_DYNAMIC

SHF_ALLOC + SHF_WRITE

注:在某些系统,可能是只读的,没有SHF_WRITE标志位

动态链接信息

 

① 依赖于哪些共享对象

② 动态链接符号表(.dynsym)的位置

③ 动态链接重定位表(.rela.dyn)的位置

④ 共享对象初始化代码的地址

.plt   SHF_ALLOC + SHF_EXECINSTR

用于支持延迟绑定,这些指令是用来计算外部函数的最终地址

.got   SHF_ALLOC + SHF_WRITE

全局偏移表 (Global Offset Table,GOT)

存放外部变量的地址

 

GOT表中的地址需要动态链接器在装载模块,进行地址重定位时进行填充。

在访问外部符号,可以先通过相对地址找到GOT表中相关的项,再从中取出最终地址

.got.plt   SHF_ALLOC + SHF_WRITE

使用PLT(Procedure Linkage Table,过程链接表)来实现对外部函数延迟绑定(第一次用到时才进行绑定)

以此来提升程序的启动速度

 

第一项保存的是“.dynamic”段的地址
第二项保存的是本模块的ID
第三项保存的是_dl_runtime_resolve()的地址

第N项保存的是运用plt段的指令计算得到的外部函数的最终地址(动态链接器重定位得到外部函数的地址后,会写入在这里)

.rela.dyn

.rel.dyn

SHT_RELA SHF_ALLOC

.dynsym段的重定位表

 

对数据引用的修正,它所修正的位置存放在“.got”以及数据段

.rela.plt

.rel.plt

SHT_RELA SHF_ALLOC

.plt段的重定位表

对函数引用的修正,它所修正的位置存放在“.got.plt”

.dynstr   SHF_ALLOC

动态符号字符串表(Dynamic String Table)

 

存放动态链接符号的符号名

.dynsym   SHF_ALLOC

动态符号表(Dynamic Symbol Table)

 

① 导入符号(Import Symbol)

② 导出符号(Export Symbol)

.hash SHT_HASH

SHF_ALLOC 

动态符号哈希表

 

提升动态符号的查找效率

.comment SHT_PROGBITS

 

注释信息段 

 

存放的是编译器版本信息,比如字符串:"GCC:(GNU)4.2.0"

.init   SHF_ALLOC + SHF_EXECINSTR

程序执行前的初始化代码,这些代码早于main函数被执行,多数被用作实现C++全局构造

.fini   SHF_ALLOC + SHF_EXECINSTR 程序退出时执行的代码,这些代码晚于main函数执行,多数被用作实现C++全局析构
.init_array SHT_PROGBITS

SHF_ALLOC + SHF_EXECINSTR

包含一些程序或共享对象刚开始初始化时所须要执行的函数指针

.fini_array SHT_PROGBITS SHF_ALLOC + SHF_EXECINSTR 包含一些程序或共享对象退出时须要执行的函数指针
.preinit_array     保存的是早于初始化阶段执行的函数指针数组,这些函数会在.init_array的函数指针数组之前被执行
.ctors    

保存的是全局构造函数指针

.dtors    

保存的是全局析构函数指针

.interp SHT_PROGBITS  

包含动态链接器的路径(如:/lib/ld-linux.so.2)

interp是interpreter(解释器)的缩写

 

系统在对可执行文件进行加载时,会从该段寻找并装载该可执行文件所需的动态链接器。

.shstrtab SHT_STRTAB  

段表字符串表(Section String Table)

用于存放各个段的名字

.note.GNU-stack SHT_NOTE   堆栈提示段
.note.android.ide SHT_NOTE   记录使用的android ndk的版本
.note.gnu.build-id SHT_NOTE   链接时的唯一标识
.note.ABI-tag SHT_NOTE   指定程序的ABI
.eh_frame SHT_PROGBITS   c++异常处理相关的内容
.eh_frame_hdr SHT_PROGBITS   c++异常处理相关的内容
.gcc_except_table SHT_PROGBITS   语言相关数据
.jcr     Java程序相关
.gnu.version     符号版本相关
.gnu.version_d     符号版本相关
.gnu.version_r     符号版本相关

.debug_str

.debug_loc

.debug_abbrev

.debug_info

.debug_ranges

.debug_macinfo

.debug_line

.debug_aranges

SHT_PROGBITS  

调试信息

 

ELF采用DWARF(Debug With Arbitrary Record Format)的标准调试信息格式来存放调试信息

.symtab SHT_SYMTAB 如果有其他装载的段用到该段,则有SHF_ALLOC标志位

符号表(Symbol Table)

保存链接时所需要的符号信息

.strtab SHT_STRTAB 如果有其他装载的段用到该段,则有SHF_ALLOC标志位

字符串表(String Table)

通常是符号表里的符号名所需要的字符串

 

注:目标中间文件(又称可重定位文件,*.o)也存在该段

 

readelf

aarch64-linux-android-readelf.exe所在目录在:<android-ndk>\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin 

aarch64-linux-android-readelf.exe -S libUE4.so  // 输出libUE4.so段表中各个段的信息

There are 27 section headers, starting at offset 0xf2c1650:

Section Headers:
  [Nr] Name              Type             Address           Offset     
       Size              EntSize          Flags  Link  Info  Align     ;Size为段的长度;Offset为段在文件中偏移位置
  [ 0]                   NULL             0000000000000000  00000000   ;第一个元素是无效的段描述符,类型为NULL
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.android.ide NOTE             0000000000000270  00000270   ;记录使用的android ndk的版本的段
       0000000000000098  0000000000000000   A       0     0     4
  [ 2] .note.gnu.build-i NOTE             0000000000000308  00000308   ;链接时的唯一标识
       0000000000000024  0000000000000000   A       0     0     4
  [ 3] .dynsym           DYNSYM           0000000000000330  00000330
       0000000000dee838  0000000000000018   A       8     1     8
  [ 4] .gnu.version      VERSYM           0000000000deeb68  00deeb68
       000000000012935a  0000000000000002   A       3     0     2
  [ 5] .gnu.version_r    VERNEED          0000000000f17ec4  00f17ec4
       0000000000000080  0000000000000000   A       8     4     4
  [ 6] .gnu.hash         GNU_HASH         0000000000f17f48  00f17f48
       00000000003e61c8  0000000000000000   A       3     0     8
  [ 7] .hash             HASH             00000000012fe110  012fe110
       00000000004a4d70  0000000000000004   A       3     0     4
  [ 8] .dynstr           STRTAB           00000000017a2e80  017a2e80
       000000000326c716  0000000000000000   A       0     0     1
  [ 9] .rela.dyn         RELA             0000000004a0f598  04a0f598
       0000000001a9a510  0000000000000018   A       3     0     8
  [10] .rela.plt         RELA             00000000064a9aa8  064a9aa8
       00000000000041b8  0000000000000018  AI       3    22     8
  [11] .rodata           PROGBITS         00000000064adc80  064adc80   ; 只读数据段
       0000000000b19d08  0000000000000000 AMS       0     0     64
  [12] .gcc_except_table PROGBITS         0000000006fc7988  06fc7988
       000000000005e32c  0000000000000000   A       0     0     4
  [13] .eh_frame_hdr     PROGBITS         0000000007025cb4  07025cb4
       00000000003aed74  0000000000000000   A       0     0     4
  [14] .eh_frame         PROGBITS         00000000073d4a28  073d4a28
       0000000000e29424  0000000000000000   A       0     0     8
  [15] .text             PROGBITS         00000000081fe000  081fe000   ; 代码段
       0000000006464320  0000000000000000  AX       0     0     16
  [16] .plt              PROGBITS         000000000e662320  0e662320
       0000000000002bf0  0000000000000000  AX       0     0     16
  [17] .data.rel.ro      PROGBITS         000000000e665000  0e665000
       0000000000b40f88  0000000000000000  WA       0     0     16
  [18] .fini_array       FINI_ARRAY       000000000f1a5f88  0f1a5f88
       0000000000000010  0000000000000008  WA       0     0     8
  [19] .init_array       INIT_ARRAY       000000000f1a5f98  0f1a5f98
       0000000000002918  0000000000000008  WA       0     0     8
  [20] .dynamic          DYNAMIC          000000000f1a88b0  0f1a88b0
       00000000000002b0  0000000000000010  WA       8     0     8
  [21] .got              PROGBITS         000000000f1a8b60  0f1a8b60
       00000000000e3280  0000000000000000  WA       0     0     8
  [22] .got.plt          PROGBITS         000000000f28bde0  0f28bde0
       0000000000001600  0000000000000000  WA       0     0     8
  [23] .data             PROGBITS         000000000f28e000  0f28e000   ; 数据段
       0000000000033220  0000000000000000  WA       0     0     16
  [24] .bss              NOBITS           000000000f2c1240  0f2c1220   ; BSS段  该段在文件中不存在内容
       000000000060aaa8  0000000000000000  WA       0     0     64
  [25] .comment          PROGBITS         0000000000000000  0f2c1220   ; 注释信息段
       0000000000000328  0000000000000001  MS       0     0     1
  [26] .shstrtab         STRTAB           0000000000000000  0f2c1548
       0000000000000104  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

aarch64-linux-android-readelf.exe -t libUE4.so  // 输出libUE4.so段表中各个段的信息

aarch64-linux-android-readelf.exe -h libUE4.so  // 输出libUE4.so的ELF头的信息

aarch64-linux-android-readelf.exe -l libUE4.so  // 输出libUE4.so的program headers

aarch64-linux-android-readelf.exe -e libUE4.so  // 等价于-h -l -S输出的信息

aarch64-linux-android-readelf.exe -d libUE4.so  // 输出libUE4.so的dynamic段的信息

aarch64-linux-android-readelf.exe -r libUE4.so  // 输出libUE4.so的重定位信息(.rela.dyn段和.rela.plt段)

aarch64-linux-android-readelf.exe --dyn-syms -D -W libUE4.so  // 输出libUE4.so的dynsym段中所有符号(不截断)  注:Ndx为UND时,表示是一个导入符号

aarch64-linux-android-readelf.exe -s -D -W libUE4.so  // 输出libUE4.so的dynsym段和.symtab段(调试符号)中所有符号(不截断)

aarch64-linux-android-readelf.exe -x.data libtestSo.so  // 以16进制的方式输出libtestSo.so的.data段内容

aarch64-linux-android-readelf.exe -p.shstrtab libtestSo.so   // 以字符串的方式输出libtestSo.so的.shstrtab段内容

aarch64-linux-android-readelf.exe -c libssl.a  // 输出静态库libssl.a中包括各个中间目标文件(*.o)中的符号信息

aarch64-linux-android-readelf.exe -n libUE4.so  // 输出libUE4.so中所有note段的信息

 

aarch64-linux-android-readelf.exe -v  //  输出readelf的版本信息

aarch64-linux-android-readelf.exe -H  //  输出readelf的帮助信息

 

objdump

aarch64-linux-android-objdump.exe所在目录在:<android-ndk>\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin 

aarch64-linux-android-objdump.exe -v  //  输出objdump的版本信息

aarch64-linux-android-readelf.exe -H  //  输出objdump的帮助信息

aarch64-linux-android-objdump.exe -i  //  输出支持的ELF的格式和架构(architecture)

 

aarch64-linux-android-objdump.exe -f libUE4.so  //  输出libUE4.so的简要信息

aarch64-linux-android-objdump.exe -h libUE4.so  //  输出libUE4.so段表的信息   注:不输出一些辅助性的段(如:符号表、字符串表、段名字符串表、重定位表等)

aarch64-linux-android-objdump.exe -x libUE4.so  // 输出llibUE4.so整个头的信息   注:包括段表的信息

aarch64-linux-android-objdump.exe -p libUE4.so  // 输出llibUE4.so的program headers和dynsym段基本信息

aarch64-linux-android-objdump.exe -d -s libUE4.so  // 对libUE4.so中的代码段进行反编译,并以16进制的形式输出

 

aarch64-linux-android-objdump.exe -a libpython2.7.a  // 列出.a静态库中所有的目标中间文件(*.o)

aarch64-linux-android-objdump.exe -t libUE4.so   // 显示libUE4.so中的调试符号表

aarch64-linux-android-objdump.exe -T libtestSo.so   // 输出libtestSo.so的动态链接符号表(dynsym段)

aarch64-linux-android-objdump.exe -T -C libtestSo.so  // 输出libtestSo.so的动态链接符号表(dynsym段),并自动对c++符号名进行反修饰(Demangle)

aarch64-linux-android-objdump.exe -R libtestSo.so  // 输出libtestSo.so的动态链接重定位信息(.rela.dyn段和.rela.plt段)

aarch64-linux-android-objdump.exe -R -C libtestSo.so  // 输出libtestSo.so的动态链接重定位信息,并自动对c++符号名进行反修饰(Demangle)

aarch64-linux-android-objdump.exe -d libtestSo.so   // 对libtestSo.so中的包含机器指令的段进行反汇编  

aarch64-linux-android-objdump.exe -S libtestSo.so   // 显示libtestSo.so源代码和机器指令的段的反汇编代码(包含-d参数功能)

aarch64-linux-android-objdump.exe -D libtestSo.so   // 对libtestSo.so所有的段进行反汇编

aarch64-linux-android-objdump.exe -r testSo.o  // 显示中间文件testSo.o中重定位信息

aarch64-linux-android-objdump.exe -s libtestSo.so   // 以16进制的方式输出libtestSo.so的内容

 

// 输出libUE4.debug.so地址范围为[0x0000000008277724, 0x0000000008277850]的机器指令进行反汇编,并自动其中c++符号名进行反修饰(Demangle)

aarch64-linux-android-objdump.exe -d -C libUE4.debug.so --start-address=0x0000000008277724 --stop-address=0x0000000008277850

注1:[0x0000000008277724, 0x0000000008277850]的机器指令对应UAkGameplayStatics::SetState(UAkStateValue const*, FName, FName)函数

注2:可通过命令:aarch64-linux-android-nm.exe -n -C libUE4.debug.so | findstr "UAkGameplayStatics::SetState"来获取该函数的起始地址

 

// 输出libUE4.debug.so地址范围为[0x0000000008277724, 0x0000000008277850]的机器指令进行反汇编(配合源代码),并自动其中c++符号名进行反修饰(Demangle)

aarch64-linux-android-objdump.exe -S -C libUE4.debug.so --start-address=0x0000000008277724 --stop-address=0x0000000008277850 --include="D:\svn\MyGame\Plugins\Wwise\Source\AkAudio\Private"

注:可通过命令:aarch64-linux-android-addr2line.exe -e libUE4.debug.so 0000000008277724来获取地址为0000000008277724对应的源代码文件和行数,得到如下结果

      E:/tiyan/MyGame/Plugins/Wwise/Source/AkAudio/Private/AkGameplayStatics.cpp:377

 

参考

程序员的自我修养

 

标签:文件,exe,aarch64,基础,ELF,so,SHF,linux,android
来源: https://www.cnblogs.com/kekec/p/13829510.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有