24 Oct 2019

ELF静态结构

ELF静态结构

ELF

ELF 是洋文Executable and Linking Forma的缩写,即可执行可连接格式,具有这种格式的文件成为ELF文件

在 ELF 规范中,所有符合 ELF 格式规范的都称为 ELF 文件,也称为目标文件

主要分为三种类型

  1. 可重定位文件

    用于与其它目标文件进行连接以构建可执行文件或动态链接库

    可重定位文件引用其他目标文件中的符号时,只给出一个名字而没有地址,需要在链接时进行重定位,所以称为可重定位文件

    //main.c
    #include <stdio.h>
    int main(int argc, char const *argv[])
    {
    	fuc();
    	return 0;
    }
    
    //func.c
    void fuc()
    {
    	puts("A1_D4i");
    }
    
    gcc -c main.c -o main.o
    gcc -c fuc.c -o fuc.o
       
    file main.o
    main.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
    file fuc.o
    fuc.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
    
  2. 共享目标文件

    即动态连接库文件

    使用情况有两种

    • 在连接过程中与其它动态链接库或可重定位文件一起构建新的目标文件
    • 在可执行文件被加载的过程中,被动态链接到新的进程中,成为运行代码的一部分

    file libc-2.27.so
    libc-2.27.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/l, BuildID[sha1]=b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0, for GNU/Linux 3.2.0, stripped
    

    共享目标文件是可执行的

    ./libc-2.27.so
    GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
    Copyright (C) 2018 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.
    There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
    PARTICULAR PURPOSE.
    Compiled by GNU CC version 7.3.0.
    libc ABIs: UNIQUE IFUNC
    For bug reporting instructions, please see:
    <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>
    
  3. 可执行文件

    经过链接的,可以执行的程序文件

    gcc main.o fuc.o test
    file test
    test: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=6ec6ce6732da07955bd3a4aceabaa43e7f3f620b, not stripped
    ./test
    A1_D4i
    
//hello.c
#include <stdio.h>
int	main(int argc, char const *argv[])
{
	puts("hello world");
	return 0;
}
gcc hello.c -o hello1
file hello1
hello1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=fd4ebdec6f6339fcdd99a594cd6b62180fcc375c, not stripped
生成了ELF 64-bit LSB shared object而不是ELF 64-bit LSB executable

gcc hello.c -o hello2 -no-pie
file hello2
hello2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=5677faa90a57d5665abefef0a3a2137ee0c97b82, not stripped
关闭pie,生成了ELF 64-bit LSB executable

Position-Independent-Executable是Binutils,glibc和gcc的一个功能,能用来创建介于共享库和通常可执行代码之间的代码–能像共享库一样可重分配地址的程序,这种程序必须连接到Scrt1.o。标准的可执行程序需要固定的地址,并且只有被装载到这个地址时,程序才能正确执行。PIE能使程序像共享库一样在主存任何位置装载,这需要将程序编译成位置无关,并链接为ELF共享对象。

ELF Header

#define EI_NIDENT 16
typedef struct
{
    unsigned char e_ident[EI_NIDENT];
    Elf_Half    e_type;
    Elf_Half    e_machine;
    Elf_Word    e_version;
    Elf_Addr    e_entry;
    Elf_Off     e_phoff;
    Elf_Off     e_shoff;
    Elf_Word    e_flags;
    Elf_Half    e_ehsize;
    Elf_Half    e_phentsize;
    Elf_Half    e_phnum;
    Elf_Half    e_shentsize;
    Elf_Half    e_shnum;
    Elf_Half    e_shstrndx;
} Elf_Ehdr;

readelf -h查看ELF Header

readelf -h hello2
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:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400400
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6376 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28
  • e_ident[EI_NIDENT]

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

    16字节

    e_ident[0] - e_ident[3]为Magic number

    '\x7fELF'
    

    e_ident[4]是class,指明文件类型

    Class:                             ELF64
    

    1

    e_ident[5]是data,指明编码格式

    Data:                              2's complement, little endian
    

    2

    e_ident[6]是version,指明elf文件头的版本

    Version:                           1 (current)
    

    e_ident[7]-e_ident[14]为pad,通常为0

  • e_type

    ELF文件类型

    Type:                              EXEC (Executable file)
    

    3

  • e_machine

    指定适用的处理器体系结构

     Machine:                           Advanced Micro Devices X86-64
    

    4

  • e_version

    指明目标文件的版本

    Version:                           0x1
    

    5

  • e_entry

    指明程序入口的虚拟地址,即当文件被加载到进程空间里后,入口程序在进程地址空间里的地址

    Entry point address:               0x400400
    
  • e_phof

    指明程序头表开始处在文件中的偏移量。如果没有程序头表,该值应设为0

    Start of program headers:          64 (bytes into file)
    
  • e_shoff

    指明节头表开始处在文件中的偏移量。如果没有节头表,该值应设为0

    Start of section headers:          6376 (bytes into file)
    
  • e_flags

    含有处理器特定的标志位。标志的名字符合”EF_machine_flag”的格式

    对于 Intel 架构的处理器来说,它没有定义任何标志位,所以 e_flags 应该为0

    Flags:                             0x0
    
  • e_ehsize

    指明 ELF 文件头的大小,以字节为单位

    Size of this header:               64 (bytes)
    
  • e_phentsize

    指明在程序头表中每一个表项的大小,以字节为单位

    Size of program headers:           56 (bytes)
    
  • e_phnum

    指明程序头表中总共有多少个表项

    如果一个目标文件中没有程序头表,该值应设为0

    Number of program headers:         9
    
  • e_shentsize 指明在节头表中每一个表项的大小,以字节为单位

    Size of section headers:           64 (bytes)
    
  • e_shnum 指明节头表中总共有多少个表项

    如果一个目标文件中没有节头表,该值应设为0

    Number of section headers:         29
    
  • e_shstrndx 指明节头表中与节名字表相对应的表项的索引

    如果文件没有节名字表,此值应设置为SHN_UNDEF

    Section header string table index: 28
    

节头表

所有节都登记在一张称为节头表的数组里

节头表的每一个表项是一个 Elf_Shdr结构,通过每一个表项可以定位到对应的节

typedef struct {
	Elf_Word sh_name;
	Elf_Word sh_type;
	Elf_Word sh_flags;
	Elf_Addr sh_addr;
	Elf_Off sh_offset;
	Elf_Word sh_size;
	Elf_Word sh_link;
	Elf_Word sh_info;
	Elf_Word sh_addralign;
	Elf_Word sh_entsize;
} Elf_Shdr;
Number of section headers:         29
Size of section headers:           64 (bytes)

readelf --section-headers hello2
There are 29 section headers, starting at offset 0x18e8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000060  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400318  00000318
       000000000000003d  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400356  00000356
       0000000000000008  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400360  00000360
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400380  00000380
       0000000000000030  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004003b0  000003b0
       0000000000000018  0000000000000018  AI       5    22     8
  [11] .init             PROGBITS         00000000004003c8  000003c8
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000004003e0  000003e0
       0000000000000020  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400400  00000400
       0000000000000182  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         0000000000400584  00000584
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000400590  00000590
       0000000000000010  0000000000000000   A       0     0     4
  [16] .eh_frame_hdr     PROGBITS         00000000004005a0  000005a0
       000000000000003c  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         00000000004005e0  000005e0
       0000000000000100  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .dynamic          DYNAMIC          0000000000600e20  00000e20
       00000000000001d0  0000000000000010  WA       6     0     8
  [21] .got              PROGBITS         0000000000600ff0  00000ff0
       0000000000000010  0000000000000008  WA       0     0     8
  [22] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000020  0000000000000008  WA       0     0     8
  [23] .data             PROGBITS         0000000000601020  00001020
       0000000000000010  0000000000000000  WA       0     0     8
  [24] .bss              NOBITS           0000000000601030  00001030
       0000000000000008  0000000000000000  WA       0     0     1
  [25] .comment          PROGBITS         0000000000000000  00001030
       000000000000002b  0000000000000001  MS       0     0     1
  [26] .symtab           SYMTAB           0000000000000000  00001060
       00000000000005b8  0000000000000018          27    43     8
  [27] .strtab           STRTAB           0000000000000000  00001618
       00000000000001c9  0000000000000000           0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  000017e1
       0000000000000103  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)
  • sh_name

    本节的名字

    名字的字符串并不储存在这里,只是一个索引号,指向字符串表节中的某个位置

  • sh_type

    指明本节的类型

    所有的节类型如下

    6

    • SHT_NULL

      表明本节头是一个无效的(非活动的)节头,它也没有对应的节

      本节头中的其它成员的值也都是没有意义的

    • SHT_PROGBITS

      表明本节所含有的信息是由程序定义的

      本节内容的格式和含义都由程序来决定

    • SHT_SYMTAB/SHT_DYNSYM

      这两类节都含有符号表

      目前,目标文件中最多只能各包含一个

    • SHT_STRTAB

      表明本节是字符串表节

    • SHT_HASH

      表明本节包含一张哈希表

      目前,一个目标文件中最多只能有一张哈希表

    • SHT_DYNAMIC

      表明本节包含的是动态连接信息

      目前,一个目标文件中最多只能有一个 SHT_DYNAMIC 节

    • SHT_NOTE

      表明本节包含的信息用于以某种方式来标记本文件

    • SHT_NOBITS

      表明本节的内容是空的,节并不占用实际的空间

    • SHT_REL

      表明本节是一个重定位节

    • SHT_SHLIB

      此值是一个保留值,暂未指定语义

    • SHT_DYNSYM

      表明本节是符号表,与 SHT_SYMTAB 同义

    • SHT_LOPROC

      为特殊处理器保留的节类型索引值的下边界

    • SHT_HIPROC

      为特殊处理器保留的节类型索引值的上边界

      SHT_LOPROC ~ SHT_HIPROC区间是为特殊处理器节类型的保留值

    • SHT_LOUSER

      为应用程序保留节类型索引值的下边界

    • 为应用程序保留节类型索引值的下边界

      SHT_LOUSER ~ SHT_HIUSER 区间的节类型可由应用程序自行定义,是一段保留值

  • sh_flags

    指明本节的属性

    当某种属性被设置时,相应标志位设为1,反之设为0

    未定义标志位全部设为0

    7

    • SHF_WRITE

      如果此标志被设置,表示本节所包含的内容在进程运行过程中是可写的

    • SHF_ALLOC

      如果此标志被设置,表示本节内容在进程运行过程中要占用内存单元

    • SHF_EXECINSTR

      如果此标志被设置,表示此节内容是指令代码

    • SHF_MASKPROC

      所有被此值所覆盖的位都是保留做特殊处理器扩展用的

  • sh_addr

    如果本节的内容需要映射到进程空间中去,指明映射的起始地址

    如果不需要映射,此值为0

  • sh_offset

    指明本节所在的位置

    该值是节的第一个字节相对于文件开头的偏移量,单位是字节

  • sh_size

    指明节的大小,单位是字节

    节类型为SHT_NOBITS时此值仍然可能为非零,但没有实际的意义

  • sh_link

    索引值,指向节头表中本节所对应的位置

  • sh_info

    此节的附加信息

特殊节

.为前缀的节名字是为系统保留的

  • .bss

    包含目标文件中未初始化的全局变量

    一般情况下,可执行程序在开始运行的时候,系统会把这一段内容清零

    在运行期间的 bss 段是由系统初始化而成的,在目标文件中.bss 节并不包含任何内容,其长度为 0,所以它的节类型为 SHT_NOBITS

  • .comment

    包含版本控制信息

  • .data/.data1

    存放程序中被初始化过的全局变量

  • .debug

    含有调试信息

  • .dynamic

    包含动态连接信息

  • .dynstr

    含有用于动态连接的字符串

  • .dynsym

    节含有动态连接符号表

  • fini

    此节包含进程终止时要执行的程序指令。当程序正常退出时,系统会执行这一节中的代码

  • .got

    包含全局偏移表

  • .hash

    包含符号哈希表

  • .init

    包含进程初始化时要执行的程序指令。当程序开始运行时,系统会在进入主函数之前执行这一节中的代码

  • .interp

    节含有 ELF 程序解析器的路径名

  • .line

    包含些调试符号的行号,为程序指令码与源文件的行号建立起联系

  • .note

    注释节

  • .plt

    包含进程链接表

  • .relname/.relaname

    包含重定位信息

  • .rodata/.rodata1

包含程序中的只读数据

  • .shstrtab

    含有所有其它节的名字

  • .strtab

    存放字符串,主要是符号表项的名字

  • .symtab

    存放符号表

  • .text

    包含程序指令代码


Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).