ELF File in linux

ELF File in linux

Executable and Linking Format

ELF 格式文件在编译和运行过程有四种文件类型

  • relocatable object file 可重定位的对象文件, 就是只编译不链接时一个.C文件生成的.o文件.
  • 可执行文件
  • 动态链接库文件.so
  • coredump 文件

relocatable object file 工作在linking view 下, 各个section 只在链接时是需要的. section header table 描述所有section的信息,在linking view下section table 是必须存在的.

可执行文件和 动态链接库工作在executable view 下, 在执行视角下基本单位是segment

ELF 如何管理 section & segment

ELF header 结构中有e_shoff,e_shentsize, e_shnum

  • e_shoff : ELF section header table 的相对于文件开头的offset
  • e_shentsize: section header table 每个entry 的size
  • e_shnum: section header table entry 的数量
    对于progream header 相应的有e_phoff, e_phentsize, e_phnum

sections

section header table 是section header 组成的array, section header 管理每一个section 的信息.

section 是link view 下基本单元.

查看object file的section header

bash 复制代码
readelf -S main.o
There are 14 section headers, starting at offset 0x410:

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       000000000000009d  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  000002c0
       0000000000000090  0000000000000018   I      11     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000dd
       0000000000000000  0000000000000000  WA       0     0     1
  [ 4] .bss              NOBITS           0000000000000000  000000dd
       0000000000000000  0000000000000000  WA       0     0     1
  [ 5] .rodata           PROGBITS         0000000000000000  000000dd
       0000000000000020  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  000000fd
       000000000000002f  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  0000012c
       0000000000000000  0000000000000000           0     0     1
  [ 8] .note.gnu.pr[...] NOTE             0000000000000000  00000130
       0000000000000030  0000000000000000   A       0     0     8
  [ 9] .eh_frame         PROGBITS         0000000000000000  00000160
       0000000000000078  0000000000000000   A       0     0     8
  [10] .rela.eh_frame    RELA             0000000000000000  00000350
       0000000000000048  0000000000000018   I      11     9     8
  [11] .symtab           SYMTAB           0000000000000000  000001d8
       00000000000000c0  0000000000000018          12     4     8
  [12] .strtab           STRTAB           0000000000000000  00000298
       0000000000000026  0000000000000000           0     0     1
  [13] .shstrtab         STRTAB           0000000000000000  00000398
       0000000000000074  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),
  D (mbind), l (large), p (processor specific)
  
symbol table section

object file 中symbol table section 用来记录符号的定义信息,包括符号名和地址,实现定位和重定位这个symbol. linker 使用符号表和重定位表完成链接操作. 首先object file 中调用操作执行时,它的目标就是一个符号,此时,会有一个符号项记录这个符号. symbol table 和reclocation entry 相互配合,一个类似一个拼图突出的部分(符号定义位置), 一个类似一个拼图凹进去的位置(符号的引用位置)

以这个简单程序为例:

c 复制代码
#include <stdio.h>
int test_sum(int a, int b);
int test_sub(int a, int b)
{
   return a - b;
}
int main(int argc, char *argv[])
{
  int a = 1;
  int b = 2;
  int c = 0;

  c = test_sum(a,b);
  printf("test sum is %d\n", c);
  c = test_sub(10, 5);
  printf("test sub is %d\n", c);
  return 0;
}
int test_sum(int a, int b)
{
  return a + b;
}
bash 复制代码
readelf -s main.o

Symbol table '.symtab' contains 8 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 .text
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 .rodata
     4: 0000000000000000    18 FUNC    GLOBAL DEFAULT    1 test_sub
     5: 0000000000000012   119 FUNC    GLOBAL DEFAULT    1 main
     6: 0000000000000089    20 FUNC    GLOBAL DEFAULT    1 test_sum
     7: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

object file 有八个符号, 4,5,6是在file中定义的函数符号,它们的地址按照先后顺序排列. Ndx 表示符号关联的section 的index, 符号所在的section 是.text. 符号7 并没有在file中定义,他表示对这个符号的引用操作, Ndx 是UND.

Linker在链接的过程中,它会合并多个section, section的位置会变,并且生成可执行文件时会用virtual address 替换对应的st_value,链接的过程中,找到符号并修改符号的地址就是重定位.

编译后```bash

readelf -r main.o

重定位节 '.rela.text' at offset 0x2c0 contains 6 entries:

偏移量 信息 类型 符号值 符号名称 + 加数

000000000041 000600000004 R_X86_64_PLT32 0000000000000089 test_sum - 4

00000000004e 00030000000a R_X86_64_32 0000000000000000 .rodata + 0

000000000058 000700000004 R_X86_64_PLT32 0000000000000000 printf - 4

000000000067 000400000004 R_X86_64_PLT32 0000000000000000 test_sub - 4

000000000074 00030000000a R_X86_64_32 0000000000000000 .rodata + 10

00000000007e 000700000004 R_X86_64_PLT32 0000000000000000 printf - 4

的可执行文件中的符号表 复制代码
```bash
Symbol table '.symtab' contains 36 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crt1.o
     2: 00000000004012f0    32 OBJECT  LOCAL  DEFAULT   18 __abi_tag
     3: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtbegin.o
     4: 00000000004003c0     0 FUNC    LOCAL  DEFAULT    4 deregister_tm_clones
     5: 00000000004003f0     0 FUNC    LOCAL  DEFAULT    4 register_tm_clones
     6: 0000000000400430     0 FUNC    LOCAL  DEFAULT    4 __do_global_dtors_aux
     7: 000000000040300c     1 OBJECT  LOCAL  DEFAULT   25 completed.0
     8: 0000000000402e00     0 OBJECT  LOCAL  DEFAULT   20 __do_global_dtor[...]
     9: 0000000000400460     0 FUNC    LOCAL  DEFAULT    4 frame_dummy
    10: 0000000000402df8     0 OBJECT  LOCAL  DEFAULT   19 __frame_dummy_in[...]
    11: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    12: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtend.o
    13: 00000000004012a8     0 OBJECT  LOCAL  DEFAULT   16 __FRAME_END__
    14: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
    15: 0000000000402e08     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    16: 00000000004011a0     0 NOTYPE  LOCAL  DEFAULT   15 __GNU_EH_FRAME_HDR
    17: 0000000000402fe8     0 OBJECT  LOCAL  DEFAULT   23 _GLOBAL_OFFSET_TABLE_
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_mai[...]
    19: 0000000000403008     0 NOTYPE  WEAK   DEFAULT   24 data_start
    20: 000000000040300c     0 NOTYPE  GLOBAL DEFAULT   24 _edata
    21: 0000000000400504     0 FUNC    GLOBAL HIDDEN     5 _fini
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5
    23: 0000000000403008     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
    24: 0000000000400466    18 FUNC    GLOBAL DEFAULT    4 test_sub
    25: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    26: 0000000000401178     0 OBJECT  GLOBAL HIDDEN    14 __dso_handle
    27: 0000000000401170     4 OBJECT  GLOBAL DEFAULT   14 _IO_stdin_used
    28: 0000000000403010     0 NOTYPE  GLOBAL DEFAULT   25 _end
    29: 00000000004003b0     5 FUNC    GLOBAL HIDDEN     4 _dl_relocate_sta[...]
    30: 0000000000400380    38 FUNC    GLOBAL DEFAULT    4 _start
    31: 000000000040300c     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
    32: 0000000000400478   119 FUNC    GLOBAL DEFAULT    4 main
    33: 0000000000403010     0 OBJECT  GLOBAL HIDDEN    24 __TMC_END__
    34: 00000000004004ef    20 FUNC    GLOBAL DEFAULT    4 test_sum
    35: 000000000040033c     0 FUNC    GLOBAL HIDDEN     2 _init

可以看到链接后的可执行文件三个符号对应的Ndx变成了4,三个符号的value 被修改为virtual address,但是三个符号的相对位置没有变化。

Relocation

重定位是连接符号引用和符号定义的过程。比如在一个object file 中定义了一个函数,它对应一个符号,在下面的code中有对这个函数的调用语句,有对这个符号的引用。当object file 被link 时,这段代码的位置发生了改变,引用这个符号的所有绝对跳转指令的符号地址部分都需要根据更新后代码的位置进行修改。Relocation entry 实际上记录的就是对一个符号的调用的位置. 当这个符号的地址准备好后,要更新的地址就是Relocation entry 记录的地方.

r_offset在object file 中表示相对符号所在section的offset,Relocation 时如何找到对应的section? r_info的低位byte表示relocation entry的type, 高位其他表示symbol table 的index,通过index 就能找到对应的symbol,symbol中又有所在的section 信息。

在可执行文件和动态链接库中r_offset 是virtual address.

bash 复制代码
readelf -r main.o

重定位节 '.rela.text' at offset 0x2c0 contains 6 entries:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000041  000600000004 R_X86_64_PLT32    0000000000000089 test_sum - 4
00000000004e  00030000000a R_X86_64_32       0000000000000000 .rodata + 0
000000000058  000700000004 R_X86_64_PLT32    0000000000000000 printf - 4
000000000067  000400000004 R_X86_64_PLT32    0000000000000000 test_sub - 4
000000000074  00030000000a R_X86_64_32       0000000000000000 .rodata + 10
00000000007e  000700000004 R_X86_64_PLT32    0000000000000000 printf - 4

Program loading and dynamic linking

可执行对象文件是process的静态表示。程序的加载和连接在运行视角下基于segment的过程与在链接视角下基于section的过程有很大不同。

  • 程序的加载: 将elf format的文家加载到内存,使得进程能够运行
  • 动态链接:在系统加载程序后,通过加载程序依赖的连接库文件来解决程序代码中的符号引用,这些链接库是进程的一部分。

Program Header

program header table 只有在可执行文件和动态链接库文件中存在. program header 描述segment,多个program header结构组成table,在ELF header中有指向它的地址. 当然在可执行文件和动态链接库文件中必然有section header table,在形成可执行文件时构成相应segment的seciton是排列在一起的.

查看可执行文件的segment

bash 复制代码
readelf -l test_main

Elf 文件类型为 EXEC (可执行文件)
Entry point 0x400380
There are 13 program headers, starting at offset 64

程序头:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000002d8 0x00000000000002d8  R      0x8
  INTERP         0x0000000000001000 0x0000000000401000 0x0000000000401000
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000511 0x0000000000000511  R E    0x1000
  LOAD           0x0000000000001000 0x0000000000401000 0x0000000000401000
                 0x0000000000000310 0x0000000000000310  R      0x1000
  LOAD           0x0000000000001df8 0x0000000000402df8 0x0000000000402df8
                 0x0000000000000214 0x0000000000000218  RW     0x1000
  DYNAMIC        0x0000000000001e08 0x0000000000402e08 0x0000000000402e08
                 0x00000000000001d0 0x00000000000001d0  RW     0x8
  NOTE           0x0000000000000318 0x0000000000400318 0x0000000000400318
                 0x0000000000000024 0x0000000000000024  R      0x4
  NOTE           0x00000000000012b0 0x00000000004012b0 0x00000000004012b0
                 0x0000000000000040 0x0000000000000040  R      0x8
  NOTE           0x00000000000012f0 0x00000000004012f0 0x00000000004012f0
                 0x0000000000000020 0x0000000000000020  R      0x4
  GNU_PROPERTY   0x00000000000012b0 0x00000000004012b0 0x00000000004012b0
                 0x0000000000000040 0x0000000000000040  R      0x8
  GNU_EH_FRAME   0x00000000000011a0 0x00000000004011a0 0x00000000004011a0
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000001df8 0x0000000000402df8 0x0000000000402df8
                 0x0000000000000208 0x0000000000000208  R      0x1

 Section to Segment mapping:
  段节...
   00     
   01     .interp 
   02     .note.gnu.build-id .init .plt .text .fini 
   03     .interp .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .rodata .eh_frame_hdr .eh_frame .note.gnu.property .note.ABI-tag 
   04     .init_array .fini_array .dynamic .got .got.plt .data .bss 
   05     .dynamic 
   06     .note.gnu.build-id 
   07     .note.gnu.property 
   08     .note.ABI-tag 
   09     .note.gnu.property 
   10     .eh_frame_hdr 
   11     
   12     .init_array .fini_array .dynamic .got 

查看那可执行文件的secion

bash 复制代码
readelf -S test_main 
There are 32 section headers, starting at offset 0x2928:

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.gnu.bu[...] NOTE             0000000000400318  00000318
       0000000000000024  0000000000000000   A       0     0     4
  [ 2] .init             PROGBITS         000000000040033c  0000033c
       000000000000001b  0000000000000000  AX       0     0     4
  [ 3] .plt              PROGBITS         0000000000400360  00000360
       0000000000000020  0000000000000010  AX       0     0     16
  [ 4] .text             PROGBITS         0000000000400380  00000380
       0000000000000183  0000000000000000  AX       0     0     16
  [ 5] .fini             PROGBITS         0000000000400504  00000504
       000000000000000d  0000000000000000  AX       0     0     4
  [ 6] .interp           PROGBITS         0000000000401000  00001000
       000000000000001c  0000000000000000   A       0     0     1
  [ 7] .gnu.hash         GNU_HASH         0000000000401020  00001020
       000000000000001c  0000000000000000   A       8     0     8
  [ 8] .dynsym           DYNSYM           0000000000401040  00001040
       0000000000000060  0000000000000018   A       9     1     8
  [ 9] .dynstr           STRTAB           00000000004010a0  000010a0
       000000000000004a  0000000000000000   A       0     0     1
  [10] .gnu.version      VERSYM           00000000004010ea  000010ea
       0000000000000008  0000000000000002   A       8     0     2
  [11] .gnu.version_r    VERNEED          00000000004010f8  000010f8
       0000000000000030  0000000000000000   A       9     1     8
  [12] .rela.dyn         RELA             0000000000401128  00001128
       0000000000000030  0000000000000018   A       8     0     8
  [13] .rela.plt         RELA             0000000000401158  00001158
       0000000000000018  0000000000000018  AI       8    23     8
  [14] .rodata           PROGBITS         0000000000401170  00001170
       0000000000000030  0000000000000000   A       0     0     8
  [15] .eh_frame_hdr     PROGBITS         00000000004011a0  000011a0
       000000000000003c  0000000000000000   A       0     0     4
  [16] .eh_frame         PROGBITS         00000000004011e0  000011e0
       00000000000000cc  0000000000000000   A       0     0     8
  [17] .note.gnu.pr[...] NOTE             00000000004012b0  000012b0
       0000000000000040  0000000000000000   A       0     0     8
  [18] .note.ABI-tag     NOTE             00000000004012f0  000012f0
       0000000000000020  0000000000000000   A       0     0     4
  [19] .init_array       INIT_ARRAY       0000000000402df8  00001df8
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000402e00  00001e00
       0000000000000008  0000000000000008  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000402e08  00001e08
       00000000000001d0  0000000000000010  WA       9     0     8
  [22] .got              PROGBITS         0000000000402fd8  00001fd8
       0000000000000010  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000402fe8  00001fe8
       0000000000000020  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         0000000000403008  00002008
       0000000000000004  0000000000000000  WA       0     0     1
  [25] .bss              NOBITS           000000000040300c  0000200c
       0000000000000004  0000000000000000  WA       0     0     1
  [26] .comment          PROGBITS         0000000000000000  0000200c
       000000000000002e  0000000000000001  MS       0     0     1
  [27] .annobin.notes    PROGBITS         0000000000000000  0000203a
       000000000000014f  0000000000000001  MS       0     0     1
  [28] .gnu.build.a[...] NOTE             0000000000405010  0000218c
       0000000000000144  0000000000000000           0     0     4
  [29] .symtab           SYMTAB           0000000000000000  000022d0
       0000000000000360  0000000000000018          30    18     8
  [30] .strtab           STRTAB           0000000000000000  00002630
       00000000000001bd  0000000000000000           0     0     1
  [31] .shstrtab         STRTAB           0000000000000000  000027ed
       000000000000013b  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),
  D (mbind), l (large), p (processor specific)

保存有动态链接信息的section有.dynsym, .dynstr , .interp, .hash, .dynamic, .rel,

.rela, .got and.plt

Text segment

Data segment

Dynamic linking

有一个PT_INTERP 类型的segment 保存了program interpreter 的路径,在加载一个使用动态链接的可执行文件时,program interpreter 就是dynamic linker, 在加载可执行文件时经历这几个过程:

  • 加载可执行文件的segment到进程地址空间
  • 加载共享库到进程地址空间
  • 关闭可执行文件的文件描述符
  • 将执行控制权交给进程
Dynamic section

动态链接的程序有一个Dynamic segment, segment只有一个section 叫做Dynamic section, 它由一组dynamic structure 组成,每个structure 有不同的类型记录动态链接信息提供给dynamic linker 使用.

bash 复制代码
 readelf -d test_main 

Dynamic section at offset 0x1e08 contains 24 entries:
  标记        类型                         名称/值
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
 0x000000000000000c (INIT)               0x40033c
 0x000000000000000d (FINI)               0x400504
 0x0000000000000019 (INIT_ARRAY)         0x402df8
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x402e00
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x401020
 0x0000000000000005 (STRTAB)             0x4010a0
 0x0000000000000006 (SYMTAB)             0x401040
 0x000000000000000a (STRSZ)              74 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x402fe8
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x401158
 0x0000000000000007 (RELA)               0x401128
 0x0000000000000008 (RELASZ)             48 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x4010f8
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x4010ea
 0x0000000000000000 (NULL)               0x0
  • DT_NEEDED 依赖的共享库名称
  • DT_RELA 记录动态链接重定位表 可以看到0x401128 就是section rela.dyn的地址
  • DT_JMPREL 记录PLT 的重定位表, 0x401158 是rela.plt的地址. 上边这些重定位表都排列在一起,组成一个大表
  • DT_SONAME 记录动态链接库的soname, 这个soname也是依赖它的可执行程序NEEDED中要记录的名字
  • DT_SYMTAB 记录动态链接符号表 0x401040 就是section .dynsym 的地址
  • DT_PLTGOT 记录plt 的got 地址,0x402fe8就是section .got.plt 的地址
相关推荐
bksczm1 小时前
popen解析
linux·运维·服务器
深念Y1 小时前
技术探索记录 在 Android 手机上运行 One API
android·linux·服务器·智能手机·go·交叉编译·服务
星核0penstarry2 小时前
DeepSeek-V4-Flash 正式公测:大模型行业进入「极速平价普惠时代」
java·开发语言·人工智能
qetfw2 小时前
Debian 配置 AIDE 文件完整性检测:基线初始化、变更检查与数据库更新
linux·网络·数据库·debian
小生不才yz2 小时前
Linux设备与存储精读 · L02-02 | inode 与目录项:名字、元数据、数据分别在哪
linux
xieliyu.2 小时前
Java 线程池入门详解:线程池基础定义 + ThreadPoolExecutor 全参构造方法逐参数解析
java·开发语言·idea·javaee
AOwhisky4 小时前
Linux(CentOS)系统管理入门笔记(第二十一期)——防火墙管理(Firewalld)——zone、服务端口、富规则与端口转发
linux·运维·笔记·安全·centos·防火墙
老白干4 小时前
jjwt 0.9.1 在 JDK 11+ 上的两个“坑”与完整解决方案
java·python·log4j
lsh曙光10 小时前
延时at指令和定时cron指令
linux·服务器·网络