暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

elf格式详解(一)elf格式概述

二进制人生 2020-05-23
1665

微信公众号:二进制人生
专注于嵌入式linux开发。
更新:2020/05/20,内容整理自网络。

目录

1、ELF文件简介可重定位的对象文件(Relocatable file)可执行的对象文件(Executable file)可被共享的对象文件(Shared object file)2、ELF文件格式3、分析ELF文件头4、节头表Section Header Table5、程序头表Program Header Table

1、ELF文件简介

首先,你需要知道的是所谓对象文件(Object files)有三个种类:

可重定位的对象文件(Relocatable file)

这是由汇编器汇编生成的 .o 文件。后面的链接器(link editor)拿一个或一些 Relocatable object files 作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件(Shared object file)。我们可以使用 ar 工具将众多的 .o Relocatable object files 归档(archive)成 .a 静态库文件。另外,可以预先告诉大家的是我们的内核可加载模块 .ko 文件也是 Relocatable object file。

可执行的对象文件(Executable file)

这我们见的多了。文本编辑器vi、调式用的工具gdb、播放mp3歌曲的软件mplayer等等都是Executable object file。你应该已经知道,在我们的 Linux 系统里面,存在两种可执行的东西。除了这里说的 Executable object file,另外一种就是可执行的脚本(如shell脚本)。注意这些脚本不是 Executable object file,它们只是文本文件,但是执行这些脚本所用的解释器就是 Executable object file,比如 bash shell 程序。

可被共享的对象文件(Shared object file)

这些就是所谓的动态库文件,也即 .so 文件。如果拿前面的静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉宝贵的物理内存。如果将静态库换成动态库,那么这些问题都不会出现。动态库在发挥作用的过程中,必须经过两个步骤:

a) 链接编辑器(link editor)拿它和其他Relocatable object file以及其他shared object file作为输入,经链接处理后,生存另外的 shared object file 或者 executable file。
b)在运行时,动态链接器(dynamic linker)拿它和一个Executable file以及另外一些 Shared object file 来一起处理,在Linux系统里面创建一个进程映像。

2、ELF文件格式

首先,ELF文件格式提供了两种视图,分别是链接视图和执行视图。


链接视图是以节(section)为单位,执行视图是以段(segment)为单位。链接视图就是在链接时用到的视图,而执行视图则是在执行时用到的视图。上图左侧的视角是从链接来看的,右侧的视角是执行来看的。整个个文件可以分为四个部分:
  • ELF header:elf文件头部信息,描述整个文件的组织。

  • Program Header Table: 描述文件中的各种segments, 每个segment都用一个表项来描述,表项记录了该segment在文件中的起始地址,以及大小等信息。这些表项(entry)就构成了程序头表。你可以理解成一个结构体数组。用来告诉系统如何创建进程映像。

  • sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出,segments与sections是包含的关系,一个segment包含若干个section。

  • Section Header Table: 每个section都用一个表项来描述,表项记录了该section在文件中的起始地址,以及大小等信息。这些表项(entry)就构成了节头表。你可以理解成一个结构体数组。

备注:下面开始section翻译成节,segment翻译成段。

3、分析ELF文件头

vi /usr/include/elf.h查看elf头数据结构:


可以通过readelf -h查看程序elf头:
root@AI-Machine:/home/hongjh/2019_project# readelf -h a.out  
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048340
  Start of program headers:          52 (bytes into file)
  Start of section headers:          6144 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         31
  Section header string table index28

上面指示了节头表、程序头表在文件的偏移,有多少表项,每个表项的大小,程序的运行平台,大小端,文件类型(可执行文件EXEC、可重定位文件REL、共享目标文件DYN),是32位还是64位。

Entry point address表示该程序在内存的入口地址,即加载地址。

Machine指示了该文件的平台。

ELF header的定义可以在 /usr/include/elf.h 中找到。Elf32_Ehdr是32位 ELF header的结构体。Elf64_Ehdr是64位ELF header的结构体。

typedef struct
{

  unsigned char    e_ident[EI_NIDENT]; /* Magic number和其它信息 */
  Elf32_Half    e_type;         /* Object file type */
  Elf32_Half    e_machine;      /* Architecture */
  Elf32_Word    e_version;      /* Object file version */
  Elf32_Addr    e_entry;        /* Entry point virtual address */
  Elf32_Off    e_phoff;        /* Program header table file offset */
  Elf32_Off    e_shoff;        /* Section header table file offset */
  Elf32_Word    e_flags;        /* Processor-specific flags */
  Elf32_Half    e_ehsize;       /* ELF header size in bytes */
  Elf32_Half    e_phentsize;        /* Program header table entry size */
  Elf32_Half    e_phnum;        /* Program header table entry count */
  Elf32_Half    e_shentsize;        /* Section header table entry size */
  Elf32_Half    e_shnum;        /* Section header table entry count */
  Elf32_Half    e_shstrndx;     /* Section header string table index */
} Elf32_Ehdr;

4、节头表Section Header Table

上面提到节头表就是一个结构体数组,该结构体定义如下:

typedef struct
{

  Elf64_Word    sh_name;                /* Section name (string tbl index) */
  Elf64_Word    sh_type;                /* Section type */
  Elf64_Xword   sh_flags;               /* Section flags */
  Elf64_Addr    sh_addr;                /* Section virtual addr at execution */
  Elf64_Off     sh_offset;              /* Section file offset */
  Elf64_Xword   sh_size;                /* Section size in bytes */
  Elf64_Word    sh_link;                /* Link to another section */
  Elf64_Word    sh_info;                /* Additional section information */
  Elf64_Xword   sh_addralign;           /* Section alignment */
  Elf64_Xword   sh_entsize;             /* Entry size if section holds table */
} Elf64_Shdr;

节头表的每一项描述了该节的详细信息。如该节的名字、类型、大小、在文件的起始地址。

使用readelf -S a.out读取节头表:

root@AI-Machine:/home/hongjh/2019_project# readelf -S a.out  
There are 31 section headers, starting at offset 0x1800:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000020 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481cc 0001cc 000060 10   A  6   1  4
  [ 6] .dynstr           STRTAB          080482200022000052 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0804827000270000002   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         080482800028000020 00   A  6   1  4
  [ 9] .rel.dyn          REL             080482ac 0002ac 000008 08   A  5   0  4
  [10] .rel.plt          REL             080482b4 0002b4 000018 08  AI  5  24  4
  [11] .init             PROGBITS        080482cc 0002cc 000023 00  AX  0   0  4
  [12] .plt              PROGBITS        080482f0 0002f0 000040 04  AX  0   0 16
  [13] .plt.got          PROGBITS        08048330 000330 000008 00  AX  0   0  8
  [14] .text             PROGBITS        08048340 000340 0001a2 00  AX  0   0 16
  [15] .fini             PROGBITS        080484e4 0004e4 000014 00  AX  0   0  4
  [16] .rodata           PROGBITS        080484f8 0004f8 000008 00   A  0   0  4
  [17] .eh_frame_hdr     PROGBITS        08048500 000500 0000200   A  0   0  4
  [18] .eh_frame         PROGBITS        0804852000520000cc 00   A  0   0  4
  [19] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4
  [20] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4
  [21] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
  [22] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4
  [23] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
  [24] .got.plt          PROGBITS        0804a000 001000 000018 04  WA  0   0  4
  [25] .data             PROGBITS        0804a018 001018 000008 00  WA  0   0  4
  [26] .bss              NOBITS          0804a020 001020 000004 00  WA  0   0  1
  [27] .comment          PROGBITS        00000000 001020 000035 01  MS  0   0  1
  [28] .shstrtab         STRTAB          00000000 0016f6 0001000      0   0  1
  [29] .symtab           SYMTAB          00000000 001058 000460 10     30  47  4
  [30] .strtab           STRTAB          00000000 0014b8 0002300      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

该程序一共有31个节,第0个节通常是个空节。这里有我们熟悉的:

  [14] .text             PROGBITS        08048340 000340 0001a2 00  AX  0   0 16
  [16] .rodata           PROGBITS        080484f8 0004f8 000008 00   A  0   0  4
  [25] .data             PROGBITS        0804a018 001018 000008 00  WA  0   0  4
  [26] .bss              NOBITS          0804a020 001020 000004 00  WA  0   0  1

还有很多其他不认识的节,后面再介绍。

5、程序头表Program Header Table

程序头部(Program Header)描述与程序执行直接相关的目标文件结构信息。用来在文件中定位各个段的映像。同时包含其他一些用来为程序创建映像所必须的信息。
可执行文件或者共享目标文件的程序头部是一个结构数组,每个结构描述了一个段或者系统准备程序执行所必须的其他信息。目标文件的“段”包含一个或者多个“节区”,也就是“段内容(Segment Contents)”。程序头部仅对可执行文件和共享目标文件有意义。
对于可重定位的对象文件不包含程序头表。
程序头部的数据结构如下:

typedef struct {  
    Elf32_Word p_type;           //此数组元素描述的段的类型,或者如何解释此数组元素的信息。 
    Elf32_Off  p_offset;           //此成员给出从文件头到该段第一个字节的偏移
    Elf32_Addr p_vaddr;         //此成员给出段的第一个字节将被放到内存中的虚拟地址
    Elf32_Addr p_paddr;        //此成员仅用于与物理地址相关的系统中。System V忽略所有应用程序的物理地址信息。
    Elf32_Word p_filesz;         //此成员给出段在文件映像中所占的字节数。可以为0。
    Elf32_Word p_memsz;     //此成员给出段在内存映像中占用的字节数。可以为0。
    Elf32_Word p_flags;         //此成员给出与段相关的标志。
    Elf32_Word p_align;        //此成员给出段在文件中和内存中如何对齐。
} Elf32_phdr;

p_type指示了程序头的类型,类型定义如下:

/* Special value for e_phnum.  This indicates that the real number of
   program headers is too large to fit into e_phnum.  Instead the real
   value is in the field sh_info of section 0.  */


#define PN_XNUM         0xffff

/* Legal values for p_type (segment type).  */

#define PT_NULL         0               /* Program header table entry unused */
#define PT_LOAD         1               /* Loadable program segment */
#define PT_DYNAMIC      2               /* Dynamic linking information */
#define PT_INTERP       3               /* Program interpreter */
#define PT_NOTE         4               /* Auxiliary information */
#define PT_SHLIB        5               /* Reserved */
#define PT_PHDR         6               /* Entry for header table itself */
#define PT_TLS          7               /* Thread-local storage segment */
#define PT_NUM          8               /* Number of defined types */
#define PT_LOOS         0x60000000      /* Start of OS-specific */
#define PT_GNU_EH_FRAME 0x6474e550      /* GCC .eh_frame_hdr segment */
#define PT_GNU_STACK    0x6474e551      /* Indicates stack executability */
#define PT_GNU_RELRO    0x6474e552      /* Read-only after relocation */
#define PT_LOSUNW       0x6ffffffa
#define PT_SUNWBSS      0x6ffffffa      /* Sun Specific segment */
#define PT_SUNWSTACK    0x6ffffffb      /* Stack segment */
#define PT_HISUNW       0x6fffffff
#define PT_HIOS         0x6fffffff      /* End of OS-specific */
#define PT_LOPROC       0x70000000      /* Start of processor-specific */
#define PT_HIPROC       0x7fffffff      /* End of processor-specific */

可以通过readelf -l a.out查看程序头表:

root@AI-Machine:/home/hongjh/2019_project# readelf -l a.out    

Elf file type is EXEC (Executable file)
Entry point 0x102b4
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x000474 0x00010474 0x00010474 0x00008 0x00008 R   0x4
  PHDR           0x000034 0x00010034 0x00010034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x00010154 0x00010154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.3]
  LOAD           0x000000 0x00010000 0x00010000 0x00480 0x00480 R E 0x10000
  LOAD           0x000f0c 0x00020f0c 0x00020f0c 0x00118 0x0011c RW  0x10000
  DYNAMIC        0x000f18 0x00020f18 0x00020f18 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x00010168 0x00010168 0x00020 0x00020 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x000f0c 0x00020f0c 0x00020f0c 0x000f4 0x000f4 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     .ARM.exidx 
   01     
   02     .interp 
   03     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .ARM.exidx .eh_frame 
   04     .init_array .fini_array .jcr .dynamic .got .data .bss 
   05     .dynamic 
   06     .note.ABI-tag 
   07     
   08     .init_array .fini_array .jcr .dynamic 

这验证了第一张图中所述,segment是section的一个集合,sections按照一定规则映射到segment。那么为什么需要区分两种不同视图?

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


喜欢点个在看或者转发…

图 二进制人生公众号



数据库
文章转载自 二进制人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

玻璃钢生产厂家抚顺玻璃钢雕塑生产厂家长春玻璃钢雕塑推荐贺州玻璃钢商场美陈沈阳玻璃钢雕塑设计制作沧州江西玻璃钢造型雕塑甘肃园林玻璃钢雕塑制作重庆雕塑玻璃钢工厂有哪些商场儿童创意美陈镇江玻璃钢仿铜雕塑定做杭州玻璃钢花盆设计乳山玻璃钢卡通雕塑花朵玻璃钢景观雕塑定做价格玻璃钢雕塑包运费吗重庆北碚区玻璃钢雕塑广州玻璃钢抽象人物雕塑江西玻璃钢造型雕塑河源玻璃钢大白菜雕塑园林玻璃钢雕塑代理沈阳个性化玻璃钢雕塑优势临沧玻璃钢雕塑厂家直销商场广场美陈道定制水果玻璃钢雕塑订做价格贺兰玻璃钢雕塑制作湖北玻璃钢雕塑生产厂家购买玻璃钢花盆深圳大型商场美陈山东仿铜玻璃钢雕塑批发贵州人物玻璃钢雕塑订做价格玻璃钢雕塑摆件施工大型不锈钢玻璃钢仿铜雕塑生产香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化