• ELF文件格式解析


       ELF(Executable and Linkable Format) 即可执行可链接文件格式,是目前操作系统上最常见的可执行文件格式。不同系统的目标文件不一样,Windows是PE(Portable Executable),linux是ELF(Executable Linkable Format),它们都是COFF(Common file format)格式的变种。

    1、基本格式

          ELF格式的目标文件和可执行文件在结构上没有本质差异,ELF不仅仅描述目标文件,也用于描述可执行文件,Windows下的dll和.lib, Linux下的.so和.a文件都是按照类ELF格式存储,下图描述了ELF链接视图(.o文件、.so文件)和执行视图,链接视图描述了各个段(section)的组成,如.text、.data、bss段。执行视图由segment组成,segment用于表示一个一定长度的区域,按照只读/可读写划分,不区分数据的属性,如代码段、数据段。

          目标文件是未经过链接的,里面的符号和地址没有调整导致无法运行。例如直接运行目标文件,系统提示无法执行该二进制文件。

    $ . hello.o
    bash: .: hello.o: cannot execute binary file
    $ file hello.o
    hello.o: Intel amd64 COFF object file, no line number info, not stripped, 7 sections, symbol offset=0x2a0, 22 symbols, 1st section name ".text"

         ELF文件格式在字节对齐和元素解析时,与系统架构、字长有密切关系,ELF 文件由ELF header和各种段组成,其结构图如下所示。详细文档可以查阅https://elinux.org/Executable_and_Linkable_Format_(ELF)

     

    2、ELF文件头

         ELF 文件的最前面是文件头,描述了ELF文件的基本属性,比如ELF文件版本、目标机器型号、程序入口地址。

         详细的描述可以参考:https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html

    复制代码
    #define EI_NIDENT 16
    
    typedef struct {
            unsigned char   e_ident[EI_NIDENT];
            Elf32_Half      e_type;
            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;
            Elf32_Half      e_phentsize;
            Elf32_Half      e_phnum;
            Elf32_Half      e_shentsize;
            Elf32_Half      e_shnum;
            Elf32_Half      e_shstrndx;
    } Elf32_Ehdr;
    
    
    $ readelf -h /bin/ls 
    ELF Header:
      Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
      Class:                             ELF64
      Data:                              2's complement, little endian
      Version:                           1 (current)
      OS/ABI:                            UNIX - System V
      ABI Version:                       0
      Type:                              DYN (Position-Independent Executable file)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      Entry point address:               0x6180
      Start of program headers:          64 (bytes into file)
      Start of section headers:          145256 (bytes into file)
      Flags:                             0x0
      Size of this header:               64 (bytes)
      Size of program headers:           56 (bytes)
      Number of program headers:         11
      Size of section headers:           64 (bytes)
      Number of section headers:         30
      Section header string table index: 29
    复制代码

    上面结构体里成员类型长度的定义为:

    名称

    大小

    说明

    Elf32_Addr

    4

    无符号程序地址

    Elf32_Half

    2

    无符号中等整数

    Elf32_Off

    4

    无符号文件偏移

    Elf32_SWord

    4

    有符号大整数

    Elf32_Word

    4

    无符号大整数

    unsigned char

    1

    无符号字符型整数

    Elf32_Ehdr 各个成员简介:

    • e_ident magic bytes (0x7fELF), class, ABI version....是一组包含多个标志的数组。
    • e_typeobject file type—ET{REL,DYN,EXEC,CORE} 00-未知, 01--RT_REL,02--ET_EXEC, 03---ET_DY
    • e_machine required architecture—EM X86 64, ... 其中3h=386, 28h=ARM
    • e_version EV CURRENT, always ”1”
    • e_entry virt. addr. of entry point, dl start, jmp *%r12
    • e_phoff program header offset
    • e_shoff section header offset
    • e_flags CPU-specific flags
    • e_ehsize ELF header size
    • e_phentsize size of program header entry, consistency check
    • e_phnum number of program header entries
    • e_shentsize size of section header entry
    • e_shnum number of section header entries
    • e_shstrndx section header string table index

         其中e_ident 对应了对各字段,有MAGIC、Class、Data、Version、 ABI这几个参数。

    Name

    Value

    Purpose

    EI_MAG0

    0

    File identification

    EI_MAG1

    1

    File identification

    EI_MAG2

    2

    File identification

    EI_MAG3

    3

    File identification

    EI_CLASS

    4

    File class

    EI_DATA

    5

    Data encoding

    EI_VERSION

    6

    File version

    EI_OSABI

    7

    Operating system/ABI identification

    EI_ABIVERSION

    8

    ABI version

    EI_PAD

    9

    Start of padding bytes

    EI_NIDENT

    16

    Size of e_ident[]

    1)MAGIC是ELF标志码:魔数,占4字节,byte0固定为0x7F,byte1--byte3是'E', 'L', 'F' 的ASCII码。

    2)EI_CLASS 是CPU字长类型。

    Name

    Value

    Meaning

    ELFCLASSNONE

    0

    Invalid class

    ELFCLASS32

    1

    32-bit objects

    ELFCLASS64

    2

    64-bit objects

    3)EI_DATA

    0:非法格式

    1:小端字节序LSB

    2:大端字节序MSB

    4)EI_VERSION: ELF版本号,为1

    5)EI_OSABI

    Name

    Value

    Meaning

    ELFOSABI_NONE

    0

    No extensions or unspecified

    ELFOSABI_HPUX

    1

    Hewlett-Packard HP-UX

    ELFOSABI_NETBSD

    2

    NetBSD

    ELFOSABI_LINUX

    3

    Linux

    ELFOSABI_SOLARIS

    6

    Sun Solaris

    ELFOSABI_AIX

    7

    AIX

    ELFOSABI_IRIX

    8

    IRIX

    ELFOSABI_FREEBSD

    9

    FreeBSD

    ELFOSABI_TRU64

    10

    Compaq TRU64 UNIX

    ELFOSABI_MODESTO

    11

    Novell Modesto

    ELFOSABI_OPENBSD

    12

    Open BSD

    ELFOSABI_OPENVMS

    13

    Open VMS

    ELFOSABI_NSK

    14

    Hewlett-Packard Non-Stop Kernel

     

    64-255

    Architecture-specific value range

    3、段表--Section

    1)段表的结构

        ELF 文件中有很多段,段表(Section Header Table)就是保存这些段的基本属性的结构。段表描述了ELF各个段的信息,如段名、段的长度、在文件中的偏移、读写权限等。段表在ELF文件中 的偏移是由文件头的e_shoff成员决定的。

    复制代码
    typedef struct{
        Elf32_Word sh_name;
        Elf32_Word sh_type;
        Elf32_Word sh_flags;
        Elf32_Addr sh_addr;
        Elf32_Off  sh_offset;
        Elf32_Word sh_size;
        Elf32_Word sh_link;
        Elf32_Word sh_info;
        Elf32_Word sh_addralign;
        Elf32_Word sh_entsize;
    }Elf32_Shdr
    复制代码

    Elf32_Shdr成员含义:

    • sh_name 段名,但此次只是记录了段名字符串在 .shstrtab 中的偏移
    • sh_type 段的类型
    • sh_flags 段的标志位
    • sh_addr 段的虚拟地址,如果此段可以被加载则表示在进程中的虚拟地址,否则为0
    • sh_offset 如果此段位于文件中则表示此段在文件中的偏移
    • sh_size 段的长度
    • sh_link This member holds a section header table index link, whose interpretation depends on the section type.
    • sh_info This member holds extra information, whose interpretation depends on the section type.
    • sh_addralign 段对齐,以2的n次方表示,如果为0或1,表示没有对齐要求。
    • sh_entsize Section Entry Size段的长度

     sh_type :段的类型。

    Name

    Value

    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

    SHT_REL

    9

    SHT_SHLIB

    10

    SHT_DYNSYM

    11

    SHT_LOPROC

    0x70000000

    SHT_HIPROC

    0x7fffffff

    SHT_LOUSER

    0x80000000

    SHT_HIUSER

    0xffffffff

    sh_flag:段的标志位,表示该段在进程的虚拟地址空间中的属性,如是否可读、写、执行。

    Name

    Value

    notes

    SHF_WRITE

    0x1

    可读

    SHF_ALLOC

    0x2

    需要分配空间

    SHF_EXECINSTR

    0x4

    可执行

    SHF_MASKPROC

    0xf0000000

     

    sh_link 和 sh_info: 如果段是与链接相关的,比如重定位表符号表等,sh_link和sh_info这两个成员所包含的意义如下所示。

    sh_type

    sh_link

    sh_info

    SHT_DYNAMIC

    该段所使用的字符串表在段表中的下标

    0

    SHT_HASH

    该段所使用的符号表在段表中的下标

    0

    SHT_REL

    该段所使用的相应符号表在段表中的下标

    该重定位表所作用的段在段表中的下标

    SHT_RELA

    该段所使用的相应符号表在段表中的下标

    该重定位表所作用的段在段表中的下标

    SHT_SYMTAB

    操作系统相关的

    操作系统相关的

    SHT_DYNAMIC

    操作系统相关的

    操作系统相关的

    other

    SHN_UNDEF

    0

    2)重定位表

        rel.txt段就是重定位表,类型sh_type为SHT_REL(9)。链接器在处理目标文件时,对目标文件某些部位进行重定位,即代码段和数据段中的那部分绝对地址引用。这些重定位信息记录在ELF文件的重定位表中。

    3)字符串表

        ELF中有很多字符串,如变量名段名等,但是字符串长度是不定长的,如果用固定的长度来表示比较困难,常见的做法是把字符串集中起来放到一个表中,然后使用字符串在表中的偏移来引用字符串。

    4)符号表

        在ELF文件中,把函数和变量统称为符号(Sysbol),每个符号有一个相应的值叫做符号值。对函数和变量来说,符号值就是它们的地址。在ELF文件中,用.symtab这个段来记录符号表。

    复制代码
    typedef struct{
        Elf32_Word st_name;
        Elf32_Addr st_value;
        Elf32_Word st_size;
        unsigned char st_info;
        unsigned char st_other;
        Elf32_Half st_shndx;
    }Elf32_Sym
    复制代码
    • st_name 符号名,符号名称在字符串表中的索引
    • st_value 符号相应的值,可能是地址或一个绝对值数
    • st_size 符号大小
    • st_info 符号类型和绑定值
    • st_other 默认0
    • st_shndx 符号所在的段

    st_info 高4位表示符号绑定信息,低4位表示符号类型。

    Symbol Binding, ELF32_ST_BIND

    Name

    Value

    STB_LOCAL

    0

    STB_GLOBAL

    1

    STB_WEAK

    2

    STB_LOPROC

    13

    STB_HIPROC

    15

    Symbol Types, ELF32_ST_TYPE

    Name

    Value

    STT_NOTYPE

    0

    STT_OBJECT

    1

    STT_FUNC

    2

    STT_SECTION

    3

    STT_FILE

    4

    STT_LOPROC

    13

    STT_HIPROC

    15

    5)全局偏移表和跳转表

    .plt和.got 动态链接的跳转表和全局入口表。

     

    6)其它

    代码段(.text): 代码数据

    数据段(.data): 初始化过了的全局变量和局部静态变量

    只读数据段(.rodata) 只读数据如const值、字符串常量。

    .bss段:未初始化的全局变量和局部变量,是否为全局变量和局部变量预留空间和编译器的实现相关。

    自定义段:在变量和函数前加__attribute__((section("name"))) 属性就可以把相应的函数或变量放到以name作为段名的段中。

     

    4、ELF文件加载视图

        虽然ELF把可变数据和不可变数据分的很细,用户也可以自己添加段或命名一个段,但加载器把ELF文件加载到内存时,并不按照ELF的结构读取。例如目标文件.o里的代码段.text是section,链接时多个可重定位文件整合成一个可执行的文件,为了提高程序的效率,链接器把目标文件中相同的section 整合成一个segment,方便运行时加载器加载程序。由于虚拟内存的映射和优化的存在,ELF文件在加载到虚拟内存时,也会合并不同的段来达到节约资源的目的。

     

    5、参考文献

    1、Linux Referenced Specifications https://refspecs.linuxbase.org/

    2、Executable and Linkable Format (ELF) https://elinux.org/Executable_and_Linkable_Format_(ELF)   

    3、Executable and Linkable Format - Wikipedia  https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#Section_header

    4、ELF文件格式 https://zhuanlan.zhihu.com/p/286088470

    尊重原创技术文章,转载请注明: https://www.cnblogs.com/pingwen/p/17323862.html

  • 相关阅读:
    CSS魔法!如何将任意CSS类型转换为数值?
    【C语言】【结构体的位段】位段的内存分配及注意事项
    Java Executors类的9种创建线程池的方法及应用场景分析
    聊聊powerjob的执行机器地址
    开发基于 ChatGPT 分析热点事件并生成文章的网站应用【热点问天】把百度等热点用chatGPT来对热点事件分析海量发文章 开发步骤 多种方式获取利润
    UE导入FBX、GLTF模型
    redis集群
    C++之template可变模板参数应用总结(二百二十八)
    使用vba调用vb.net封装的dll,出现453错误
    【机器学习】GBDT (Gradient Boosting Decision Tree) 深入解析
  • 原文地址:https://www.cnblogs.com/pingwen/p/17323862.html