编译与链接——探究ELF文件结构

2021-05-07  本文已影响0人  侠之大者_7d3f

前言

在之前的学习中了解到GCC编译Cpp文件生成的目标文件(.o) 中的各个段(section)的含义, 重点实践了代码段数据段 这些常见的段。对于一个完整的ELF文件,除了各个之外,还包含了一些其他附属信息等待挖掘。


测试环境


ELF文件的结构组成

ELF文件的查看分析工具: readelf 命令

-a --all               Equivalent to: -h -l -S -s -r -d -V -A -I
  -h --file-header       Display the ELF file header
  -l --program-headers   Display the program headers
     --segments          An alias for --program-headers
  -S --section-headers   Display the sections' header
     --sections          An alias for --section-headers
  -g --section-groups    Display the section groups
  -t --section-details   Display the section details
  -e --headers           Equivalent to: -h -l -S
  -s --syms              Display the symbol table
     --symbols           An alias for --syms
  --dyn-syms             Display the dynamic symbol table
  -n --notes             Display the core notes (if present)
  -r --relocs            Display the relocations (if present)
  -u --unwind            Display the unwind info (if present)
  -d --dynamic           Display the dynamic section (if present)
  -V --version-info      Display the version sections (if present)
  -A --arch-specific     Display architecture specific information (if any)
  -c --archive-index     Display the symbol/file index in an archive

继续采用之前的程序: SimpleSection.cpp

/**
 * @file SimpleSectipn.cpp
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2021-04-27
 * 
 * @copyright Copyright (c) 2021
 * 
 */

int printf(const char* format, ... );

int global_init_var = 84;
int global_uninit_var;

void func1(int i){
    printf("%d\n", i);
}

// 使用GCC的扩展功能,制定函数到 ABC段
__attribute__((section("ABC"))) void func2(){
    printf("Hello,world!\n");
}

__attribute__((section("DEF"))) int g_index = 12345;

int main(){
    static int static_var = 85;
    static int static_var2;

    int a = 1;
    int b;

    func1(static_var + static_var2 + a + b);

    return a;
}

ELF 头文件(ELF Header)

readelf -h xxx.o 读取elf文件的头部信息,主要包含内容:

readelf -h SimpleSectipn.o             
ELF 头:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  Version:                           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:          1488 (bytes into file)
  标志:             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         17
  Section header string table index: 16

段表 (Section Header Table)

段表用于记录各个段的信息,比如段的字节大小,段名称,读写权限等。段表并不是段本身,段本身是指.text, .data, .rodata 等。
readelf -S xxx.o 读取段表信息, 下面读取的结果段的编号从0~16, 总共17个段, 常见的段: .text, .data, .rodata, .bss, .comment, 以及自定义的2个段 ABC, DEF。 标号为0的段大小为NULL, 实际上有效的段为1~16

There are 17 section headers, starting at offset 0x5d0:

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       000000000000005f  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000458
       0000000000000078  0000000000000018   I      14     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000a0
       0000000000000008  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  000000a8
       0000000000000008  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  000000a8
       0000000000000012  0000000000000000   A       0     0     1
  [ 6] ABC               PROGBITS         0000000000000000  000000ba
       000000000000001c  0000000000000000  AX       0     0     1
  [ 7] .relaABC          RELA             0000000000000000  000004d0
       0000000000000030  0000000000000018   I      14     6     8
  [ 8] DEF               PROGBITS         0000000000000000  000000d8
       0000000000000004  0000000000000000  WA       0     0     4
  [ 9] .comment          PROGBITS         0000000000000000  000000dc
       000000000000002b  0000000000000001  MS       0     0     1
  [10] .note.GNU-stack   PROGBITS         0000000000000000  00000107
       0000000000000000  0000000000000000           0     0     1
  [11] .note.gnu.propert NOTE             0000000000000000  00000108
       0000000000000020  0000000000000000   A       0     0     8
  [12] .eh_frame         PROGBITS         0000000000000000  00000128
       0000000000000078  0000000000000000   A       0     0     8
  [13] .rela.eh_frame    RELA             0000000000000000  00000500
       0000000000000048  0000000000000018   I      14    12     8
  [14] .symtab           SYMTAB           0000000000000000  000001a0
       0000000000000210  0000000000000018          15    14     8
  [15] .strtab           STRTAB           0000000000000000  000003b0
       00000000000000a7  0000000000000000           0     0     1
  [16] .shstrtab         STRTAB           0000000000000000  00000548
       0000000000000081  0000000000000000           0     0     1

重定位表 (Relocation Table)

readelf 输出的结果中, [1] 段的类型为RELA重定位

[1]  .rela.text        RELA        

符号表 (Symbol Table)

符号表 对于链接时候比较重要, 经常会遇到链接时候的一些错误undefined reference 错误, 和符号表有很大的关系。
查看目标文件的符号表nm 命令:
C++代码中的变量名称:g_index, global_init_var, 等 以及函数名称: main, func1, func2, 程序中调用了标准库的函数printf, printf显然不在SimpleSection.cpp中定义, 因此printf为外部符号

nm SimpleSectipn.o  
0000000000000000 D g_index
0000000000000000 D global_init_var
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 B global_uninit_var
0000000000000028 T main
0000000000000000 T _Z5func1i
0000000000000000 T _Z5func2v
                 U _Z6printfPKcz
0000000000000004 d _ZZ4mainE10static_var
0000000000000004 b _ZZ4mainE11static_var2

也可以采用 readelf -s xxx.o 查看符号表的信息


readelf -s SimpleSectipn.o 

Symbol table '.symtab' contains 22 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS SimpleSectipn.cpp
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 _ZZ4mainE10static_var
     9: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 _ZZ4mainE11static_var2
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT   10 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT   11 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT   12 
    13: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
    14: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    15: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 global_uninit_var
    16: 0000000000000000    40 FUNC    GLOBAL DEFAULT    1 _Z5func1i
    17: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z6printfPKcz
    19: 0000000000000000    28 FUNC    GLOBAL DEFAULT    6 _Z5func2v
    20: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    8 g_index
    21: 0000000000000028    55 FUNC    GLOBAL DEFAULT    1 main
上一篇下一篇

猜你喜欢

热点阅读