美文网首页linux深入c++库
Linux[ELF]: ELF文件结构简单梳理

Linux[ELF]: ELF文件结构简单梳理

作者: 马小藤 | 来源:发表于2019-05-15 23:21 被阅读0次

    Linux[ELF]: ELF文件结构简单梳理

    一.编译过程-ELF文件生成

    在这里插入图片描述

    二.ELF的文件概述

    2.1 4种ELF文件类型

    ELF文件类型 说明 实例
    Relocatable File 包含例代码和数据,可以被链接成可执行文件或共享目标文件 Linux下的.o文件
    Executable File 包含可以直接执行的程序,ELF可执行文件,一般没有扩展名 /bin/bash文件
    Shared Object File 包含代码和数据,和其他目标文件链接成新的目标文件,和可执行文件链接作为进程映像的一部分来允许 Linux下的.so文件
    Core Dump File 进程意外终止时可以产生的文件,存储着该进程的内存空间中的内容等信息 Linux下的core dump

    2.2 ELF文件简单结构

    在这里插入图片描述

    两种视图:


    在这里插入图片描述

    三.ELF重要的结构

    3.1 文件结构详细学习:

    ELF文件格式解析: https://blog.csdn.net/dddxxxx/article/details/80347610

    3.2 详细结构整理:

    在这里插入图片描述

    3.3 注意点:

    1. 除了 ELF 头部表以外,其他节区和段都没有规定的顺序
    2. 目标文件中的每个节区都有对应的节区头部描述它,反过来,有节区头部不意味着有节区
    3. 每个节区占用文件中一个连续字节区域(这个区域可能长度为 0)。
    4. 文件中的节区不能重叠,不允许一个字节存在于两个节区中的情况发生。
    5. 目标文件中可能包含非活动空间(INACTIVE SPACE)。这些区域不属于任何头部和节区,其内容未指定。
    6. 以“.”开头的节区名称是系统保留的。应用程序可以使用没有前缀的节区名称,以避免与系统节区冲突。
    7. 目标文件中也可以包含多个名字相同的节区。
    8. Section和Segment的区别和联系
      可执行文件中,一个program header描述的内容称为一个段(segment)。Segment包含一个或者多个section
    9. 可执行程序中的几个段:
    名称 内容
    代码段 可执行代码、字符串常量
    数据段 已初始化全局变量、已初始化全局静态变量、局部静态变量、常量数据
    BSS段 未初始化全局变量,未初始化全局静态变量
    而当程序被加载到内存单元时,则需要另外两个域:堆域和栈域
    局部变量、函数参数
    动态内存分配

    3.4 section类型:

    名称 类型 属性 含义
    .bss SHT_NOBITS SHF_ALLOC SHF_WRITE 包含将出现在程序的内存映像中的为初始化数据。根据定义,当程序开始执行,系统将把这些数据初始化为 0。此节区不占用文件空间。
    .data SHT_PROGBITS (无) 包含版本控制信息。
    .data1 SHT_PROGBITS SHF_ALLOC SHF_WRITE 这些节区包含初始化了的数据,将出现在程序的内存映像中。
    .debug SHT_PROGBITS (无) 此节区包含用于符号调试的信息。
    .dynamic SHT_DYNAMIC 此节区包含动态链接信息。节区的属性将包含 SHF_ALLOC 位。是否 SHF_WRITE 位被设置取决于处理器。
    .dynstr SHT_STRTAB SHF_ALLOC 此节区包含用于动态链接的字符串,大多数情况下这些字符串代表了与符号表项相关的名称。
    .dynsym SHT_DYNSYM SHF_ALLOC 此节区包含了动态链接符号表。
    .fini SHT_PROGBITS SHF_ALLOCSHF_EXECINSTR 此节区包含了可执行的指令,是进程终止代码的一部分。程序正常退出时,系统将安排执行这里的代码。
    .got SHT_PROGBITS 此节区包含全局偏移表。
    .hash SHT_HASH SHF_ALLOC 此节区包含了一个符号哈希表.
    .init SHT_PROGBITS SHF_ALLOCSHF_EXECINSTR 此节区包含了可执行指令,是进程初始化代码的一部分。当程序开始执行时,系统要在SHF_EXECINSTR 开始调用主程序入口之前(通常指 C 语言的 main 函数)执行这些代码。
    .interp SHT_PROGBITS 此节区包含程序解释器的路径名。如果程序包含一个可加载的段,段中包含此节区,那么节区的属性将包含 SHF_ALLOC 位,否则该位为 0。
    .line SHT_PROGBITS 此节区包含符号调试的行号信息,其中描述了源程序与机器指令之间的对应关系。其内容是未定义的。
    .note SHT_NOTE 此节区中包含注释信息,有独立的格式。
    .plt SHT_PROGBITS 此节区包含过程链接表(procedure linkage table)。
    .relname SHT_REL 这些节区中包含了重定位信息。如果文件中包含可加载的段,段中有重定位内容,节区的属性将包含 SHF_ALLOC 位,否则该位 置 0。传统上 name 根据重定位所适用的节区给定。例如 .text 节区的重定位节区名字将是:.rel.text 或者 .rela.text。
    .rela name SHT_RELA
    .rodata SHT_PROGBITS SHF_ALLOC 这些节区包含只读数据,这些数据通常参与进程映像的不可写段。
    .rodata1 SHT_PROGBITS SHF_ALLOC
    .shstrtab SHT_STRTAB 此节区包含节区名称。
    .strtab SHT_STRTAB 此节区包含字符串,通常是代表与符号表项相关的名称。如果文件拥有一个可加载的段,段中包含符号串表,节区的属性将包含 SHF_ALLOC 位,否则该位为 0。
    .symtab SHT_SYMTAB 此节区包含一个符号表。如果文件中包含一个可加载的段,并且该段中包含符号表,那么节区的属性中包含SHF_ALLOC 位,否则该位置为 0。
    .text SHT_PROGBITS 此节区包含程序的可执行指令。

    3.5 segment类型:

    程序段类型 取值 说明
    PT_NULL 0 此数组元素未用。结构中其他成员都是未定义的。
    PT_LOAD 1 此数组元素给出一个可加载的段,段的大小由 p_filesz 和 p_memsz描述。文件中的字节被映射到内存段开始处。如果 p_memsz 大于p_filesz,“剩余”的字节要清零。p_filesz 不能大于 p_memsz。可加载的段在程序头部表格中根据 p_vaddr 成员按升序排列。
    PT_DYNAMIC 2 数组元素给出动态链接信息。Dynamic Segment 是很重要的一个程序头,里面存储着函数名、使用过的动态库名、重定位表、函数代码偏移等重要信息,不过不是直接记录,而是通过一定的方法查询得到,这个查询过程是elf设计中巧妙且关键的核心所在。
    PT_INTERP 3 该记录的是链接器linker的路径.数组元素给出一个 NULL 结尾的字符串的位置和长度,该字符串将被当作解释器调用。这种段类型仅对与可执行文件有意义(尽管也可能在共享目标文件上发生)。在一个文件中不能出现一次以上。如果存在这种类型的段,它必须在所有可加载段项目的前面。
    PT_NOTE 4 此数组元素给出附加信息的位置和大小。
    PT_SHLIB 5 此段类型被保留,不过语义未指定。包含这种类型的段的程序与 ABI 不符。
    PT_PHDR 6 此类型的数组元素如果存在,则给出了程序头部表自身的大小和位置,既包括在文件中也包括在内存中的信息。此类型的段在文件中不能出现一次以上。并且只有程序头部表是程序的内存映像的一部分时才起作用。如果存在此类型段,则必须在所有可加载段项目的前面。
    PT_LOPROC 0x70000000 此范围的类型保留给处理器专用语义。
    PT_HIPROC 0x7ffffffff

    四. 举个例子

    1. Cpp代码:
    //abc.cpp
    #include<iostream>
    using namespace std;
    
    int main(){
            int sum=0,value=0;
            while(cin>>value){
                    sum+=value;
              cout<<"sum:"<<sum;
            }
            return 0;
    }
    
    1. g++ abc.cpp生成.out文件,file看一下是ELF文件类型:


      在这里插入图片描述
    2. 使用readelf命令简单查看该a.out这个elf文件的program header,可以看出一个segment对应着多个section,readelf加参数可以查看更多其他信息:


      在这里插入图片描述

      当ELF文件被加载到内存之后,系统会将多个具有相同权限的section合并到一个segment。
      操作系统往往以页为基本单位来管理内存分配,一般页的大小位4KB。同时,内存的权限管理的粒度也是以页为单位,页内的内存是具有同样的权限,并且操作系统对内存的管理往往追求高效和高利用率这样的目标。
      ELF文件在被映射时,是以系统的页长度位单位的,那么每个section在映射时的长度都是系统页长度的整数倍,如果section的长度不是其整数倍,则导致多余部分也将占用一个页。
      而我们从上面的例子中知道,一个ELF文件具有很多section,那么会导致内存浪费。将sections映射到segment可以减少页面内部的碎片,节省了空间,显著提高内存利用率。

    详细参考:

    Linux ELF文件格式分析 https://blog.csdn.net/xj178926426/article/details/72825630
    程序或-内存区域分配(五个段)--终于搞明白了:https://blog.csdn.net/love_gaohz/article/details/41310597
    C程序内存区域分配(5个段作用): http://www.cnblogs.com/bigbigtree/archive/2012/11/23/2784137.html
    学习资料:
    specific 中英文版及 Learning Linux Binary Analysis 英文版百度云盘下载:
    链接: https://pan.baidu.com/s/1vt5clx862dqmelYP8PWyLA 提取码: amgs

    相关文章

      网友评论

        本文标题:Linux[ELF]: ELF文件结构简单梳理

        本文链接:https://www.haomeiwen.com/subject/xzuvaqtx.html