• ELF文件基础


    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)

    编译选项开启:-fvisibility=hidden -fvisibility-inlines-hidden,默认不导出so中的符号

    .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++异常处理相关的内容

    异常时栈帧unwind信息(Exception Handling Frame unwinding information)

    Call Frame Information(CFI)信息,保存了函数的调用关系,可以用于异常发生后的栈回溯

    编译时,带上-funwind-tables,可以去除该段

    -fno-asynchronous-unwind-tables

    .eh_frame_hdr SHT_PROGBITS   c++异常处理相关的内容
    .gcc_except_table SHT_PROGBITS   语言相关数据
    .jcr     Java类注册信息
    .gnu.version    

    符号版本相关

    所有动态符号的版本信息,它和.dynsym是一一对应的

    为0表示内部定义的local的符号,1表示内部定义的global符号

    其他的值则来自于.gnu_version_d的Index和.gnu_version_r的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> oolchainsaarch64-linux-android-4.9prebuiltwindows-x86_64in 

    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 -W libUE4.so  // 输出libUE4.so的.symtab段(调试符号)中所有符号(不截断)

    aarch64-linux-android-readelf.exe --debug-dump=frames-interp libUE4.so  // 输出libUE4.so的eh_frame段的信息

    aarch64-linux-android-readelf.exe -V libUE4.so // 输出libUE4.so的符号版本信息

    aarch64-linux-android-readelf.exe -x.data libc++_shared.so  // 以16进制的方式输出libc++_shared.so的.data段内容  注:libc++_shared.so来自于strip后的<android-ndk>sourcescxx-stlllvm-libc++libsarm64-v8alibc++_shared.so

    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> oolchainsaarch64-linux-android-4.9prebuiltwindows-x86_64in 

    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:svnMyGamePluginsWwiseSourceAkAudioPrivate"

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

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

    参考

    程序员的自我修养

    Executable and Linkable Format (ELF) 

    ELF(Special Sections) 

  • 相关阅读:
    js 导航栏多项点击显示下拉菜单代码
    当你工作与生活迷茫时可以来看看 shuke
    vs code使用指南
    两列
    三列浮动中间宽度自适应
    两列右列宽度自适应
    word文档巧替换(空行替换、空格替换、软回车替换成硬回车)
    统计单元格内指定的字符数方法 ,方法 一好用
    umi ui 构建时出现 spawn sh ENOENT 报错的解决方法
    新的博客,声明一下以前的域名作废了
  • 原文地址:https://www.cnblogs.com/kekec/p/13829510.html
Copyright © 2020-2023  润新知