zoukankan      html  css  js  c++  java
  • 程序运行之ELF文件结构

    ELF目标文件格式的最前部是ELF文件头。包含了整个文件的基本属性。比如ELF文件版本,目标机器型号,程序入口地址等。然后是ELF的各个段,其中ELF文件中与段有关的重要结构就是段表。段表描述了ELF文件包含的所有段的信息,比如每个段的段名,段的长度,在文件中的偏移,读写权限及段的其他属性。

    一 文件头;

    通过readelf命令来详细查看ELF文件的头信息:

    root@zhf-maple:/home/zhf/c_prj# readelf -h main.o

    ELF 头:

      Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 

      类别:                              ELF64

      数据:                              2 补码,小端序 (little endian)

      版本:                              1 (current)

      OS/ABI:                            UNIX - System V

      ABI 版本:                          0

      类型:                              REL (可重定位文件)

      系统架构:                          Advanced Micro Devices X86-64

      版本:                              0x1

      入口点地址:               0x0

      程序头起点:          0 (bytes into file)

      Start of section headers:          1048 (bytes into file)

      标志:             0x0

      本头的大小:       64 (字节)

      程序头大小:       0 (字节)

      Number of program headers:         0

      节头大小:         64 (字节)

      节头数量:         13

      字符串表索引节头: 12

    我们将Elf32_Ehdr结构与readelf命令输出的信息对比可得如下对应关系:

    成员

    readelf输出结果

    e_ident

    Magic,类别,数据,版本,OS/ABI,ABI

    e_type

    类型

    e_machine

    系统架构

    e_version

    版本

    e_entry

    入口点地址

    e_phoff

    start of program headers

    e_shoff

    start of section headers

    e_flags

    标志

    e_ehsize

    文件头的大小

    e_phentsize

    程序头大小

    e_phnum

    number of program headers

    e_shentsize

    节头大小

    e_shnum

    number of program headers

    e_shstrndx

    字符串表段索引

    下面我来逐步分析各个部分:

    魔数:

    Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 

    7f 454c46分别对应ascii码的Del(删除)、字母E、字母L、字母F。这四个字节被称为ELF文件的魔数,操作系统在加载可执行文件时会确认魔数是否正确,如果不正确则拒绝加载。 
    第五个字节标识ELF文件是32位(01)还是64位(02)的。 
    第六个字节标识该ELF文件字节序是小端(01)还是大端(02)的。 
    第七个字节指示ELF文件的版本号,一般是01。 
    后九个字节ELF标准未做定义。一般为00.

    文件类型:

    e_type成员标识文件类型,ELF文件有三种类型,如下表所示。

    常量标识

    类型

    ET_REL

    1

    可重定位文件,一般位.o文件

    ET_EXEC

    2

    可执行文件

    ET_DYN

    3

    共享目标文件,一般位.so文件

    机器类型:

    ELF文件格式被设计成可以在多个平台下使用,但并不表示同一个ELF文件可以在不同的平台下使用,而是表示不同平台下的ELF文件都遵循同一套ELF标准.e_machine成员就表示该ELF文件的平台属性。

    常量标识

    系统架构

    EM_M32

    1

    AT&T WE 32100

    EM_SPARC

    2

    SPARC

    EM_386

    3

    Intel 80386

    EM_68K

    4

    Motorola m68k family

    EM_88K

    5

    Motorola m88k family

    EM_860

    6

    Intel 80860

    二 段表:

    ELF文件中有各种各样的段,段表就是保存这些段的基本属性的结构。它描述了ELF的各个段的信息,比如每个段的段名,段的长度,在文件中的偏移,读写权限及段的其他属性。编译器,链接器和装载器都是依靠段表来定位和访问各个段的属性的。前面使用objdump -h来显示各个段,但只是显示了关键的几个段。我们用readelf来输出段表的内容。段表是存在Elf32_Shdr中,它是一个结构体数组。各个成员的含义如下表:

    成员

    含义

    sh_name

    段名,位于一个叫“.shstrtab”的字符串表

    sh_type

    段的类型,详细内容看后文

    sh_flags

    段的标志位,详细内容见后文

    sh_addr

    段在被加载后在进程地址空间中的虚拟地址,当段不能被加载时,它为0

    sH_offset

    段在elf文件中的偏移,如果该段不存在于文件中,则它无意义

    sh_szie

    段的长度

    sh_link

    段的链接信息,详细内容见后文

    sh_info

    段的链接信息,详细内容见后文

    sh_addralign

    段地址对齐

    sh_entsize

    项的长度,有的段包含一些固定大小的项,比如符号表,sh_enrsize就是用来指示这些项的大小

    我们用readelf -S main.o来查看内容。

    root@zhf-maple:/home/zhf/c_prj# readelf -S main.o

    共有 13 个节头,从偏移量 0x418 开始:

    节头:

      [名称              类型             地址              偏移量

           大小              全体大小          旗标   链接   信息   对齐

      [ 0]                   NULL             0000000000000000  00000000

           0000000000000000  0000000000000000           0     0     0

      [ 1] .text             PROGBITS         0000000000000000  00000040

           000000000000004c  0000000000000000  AX       0     0     1

      [ 2] .rela.text        RELA             0000000000000000  00000320

           0000000000000060  0000000000000018   I      10     1     8

      [ 3] .data             PROGBITS         0000000000000000  0000008c

           0000000000000008  0000000000000000  WA       0     0     4

      [ 4] .bss              NOBITS           0000000000000000  00000094

           0000000000000004  0000000000000000  WA       0     0     4

      [ 5] .rodata           PROGBITS         0000000000000000  00000094

           0000000000000004  0000000000000000   A       0     0     1

      [ 6] .comment          PROGBITS         0000000000000000  00000098

           0000000000000024  0000000000000001  MS       0     0     1

      [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000bc

           0000000000000000  0000000000000000           0     0     1

      [ 8] .eh_frame         PROGBITS         0000000000000000  000000c0

           0000000000000058  0000000000000000   A       0     0     8

      [ 9] .rela.eh_frame    RELA             0000000000000000  00000380

           0000000000000030  0000000000000018   I      10     8     8

      [10] .symtab           SYMTAB           0000000000000000  00000118

           0000000000000198  0000000000000018          11    11     8

      [11] .strtab           STRTAB           0000000000000000  000002b0

           000000000000006c  0000000000000000           0     0     1

      [12] .shstrtab         STRTAB           0000000000000000  000003b0

           0000000000000061  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),

      l (large), p (processor specific)

    从上面的输出的第一行可以看到,段表从0x418开始,观察其他段的偏移量可以发现,段表位于所有段之后,就是文件的末尾,该ELF文件有12个段,每个段的大小也就是sizeof(Elf32_Shdr)的大小,为40字节,所以整个段表的大小就是12*40 = 480个字节,再加上之前的0x418,总共1048+480=1528个字节.

    段的类型 
    上面输出的第三列就是段的类型,段的类型的相关常量如下表所示:

    常量

    含义

    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

    动态链接的符号表

    段的标志位 
    以上输出的flg一列指出了该段在进程虚拟空间中的属性。

    常量

    含义

    SHF_WRITE

    1

    表示该段在进程空间中可写

    SHF_ALLOC

    2

    表示该段需要在进程空间中分配空间

    SHF_EXECINSTR

    4

    表示该段在进程空间中可以被执行

    段的链接信息

    sh_type

    sh_link

    sh_info

    SHT_DYNAMIC

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

    0

    SHT_HASH

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

    0

    SHT_REL,SHT_RELA

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

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

    SHT_SYMTAB、SHT_DNYSYM

    操作系统相关

    操作系统相关

    other

    SHN_UNDEF

    0

    可重定位表:

    链接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中那些对绝对地址的引用的位置。这些重定位的信息都记录在ELF文件表里面,对于每个须要重定位的代码段和数据段,都会有一个相应的重定位表,例如 .rel.text 表对应.text段。也就是说,重定位表记录了须要被重定位的地址都在相应段的哪些地方。比如.rela.text就是针对.text段的重定位表,因为.text段中至少有一个绝对地址的引用,那就是对printf函数的调用。而.data没有对绝对地址的调用

    字符串表:

    ELF文件中用到了很多字符串。比如段名,变量名等。因为字符串的长度往往是不定的,所以用固定的结构来表示比较困难。常见的做法就是把字符串集中起来存放到一个表。然后使用字符串在表中的偏移来引用字符串。

    下图展示了一个长度为 25 字节的字符串表:

     字符串引用示例:

     

    常见的段名是.strtabString Table)或者是.shstrtabSection Header String Table }

  • 相关阅读:
    A. Vanya and Table
    B. Chris and Magic Square
    A. Bus to Udayland
    关于cout输出精度问题
    1087 1 10 100 1000
    1080 两个数的平方和
    CODE[VS] 1842 递归第一次
    CODE[VS] 1501 二叉树最大宽度和高度
    少年中国说
    CODE[VS] 1475 m进制转十进制
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/9059136.html
Copyright © 2011-2022 走看看