可执行程序工作原理

1 ELF目标文件格式

  • 目标文件:编译器编译后生成的文件,目标指的是目标平台(例如:x86/x64平台、arm64平台等),这决定了编译器使用的机器指令集。
  • 目标文件与目标平台是二进制兼容的(即目标文件已经是适应某一种CPU架构体系上的二进制指令),例如一个64位的x64平台上编译出来的目标文件是无法链接成arm64平台上的可执行文件。
  • 最古老的目标文件格式是a.out,后来陆续发展成为COFF格式、PE格式、ELF格式等;
  • ELF(Executable and Linkable Format):可执行的和可链接的格式,是一个目标文件格式的标准。它用于存储Linux程序。

2 ELF文件的3种类型(可执行文件格式中3种不同类型的目标文件)

  • 可重定位文件:一般是中间文件,还需要继续处理。由编译器和汇编器创建,一个源文件会生成一个可重定位文件。文件中保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件、静态库文件或共享目标文件。
  • 可执行文件:一般由多个可重定位文件结合而成,是完成了所有重定位和符号解析工作的文件,文件中保存着一个用来执行的程序。
  • 共享目标文件:即共享库,是可以被可执行文件或其他库文件使用的目标文件。

3 ELF文件的作用

  • ELF文件参与程序的链接和程序的执行,可以从如下两个方面来看待ELF文件的作用:
    • 如果用于编译和链接(可重定位目标文件),则编译器和链接器会把ELF文件看成节的集合,所有节由节头表描述,段头表可选;
    • 如果用于加载执行(可执行文件),则加载器会把ELF文件看成段头表描述的段的集合,一个段可能包含多个节,节头表可选;

4 从源文件hello.c------>可执行文件hello的详细过程

  • 预处理:gcc -E hello.c -o hello.i,主要是删除所有的注释、删除所有的#define,展开所有的宏定义、处理所有的条件预编译命令、处理#include预编译命令、添加行号及文件名标识
  • 编译:gcc -S hello.i -o hello.s,主要是检查代码的规范性、是否有语法错误,检查无误后,gcc把代码翻译成汇编代码,不会进一步翻译成机器代码
  • 汇编:gcc -c hello.s -o hello.o------目标文件(hello.o是可重定位目标文件),汇编后生成的xxx.o文件已经是ELF格式的文件了,目标文件至少含有3个section,分别是.text(用于存放程序执行代码的一块内存区域)、.data(用于存放程序中已经初始化的全局变量的一块内存区域)、.bss(用于存放程序中未初始化的全局变量的一块内存区域)
    • 查看目标文件的节头表信息(section table header):readelf -S hello.o
      • Type列是PROGBITS表示该节存储的是代码;
      • 由于当前汇编生成的是可重定位目标文件还不是可执行文件,所以没有设置其对应的虚拟地址。在下一步链接完成后,该部分会变成将在代码段在内存中的虚拟地址,故当前Address列全部是0;
      • 代码段中Flags列的含义,A表示需要加载到内存中,X表示对应内存需要可执行;
[cdl@h3c temp]$ readelf -S hello.o
There are 13 section headers, starting at offset 0x138:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000015  0000000000000000  AX       0     0     4
  [ 2] .rela.text        RELA             0000000000000000  00000598
       0000000000000030  0000000000000018          11     1     8
  [ 3] .data             PROGBITS         0000000000000000  00000058
       0000000000000000  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  00000058
       0000000000000000  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  00000058
       000000000000000c  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  00000064
       000000000000002e  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  00000092
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  00000098
       0000000000000038  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  000005c8
       0000000000000018  0000000000000018          11     8     8
  [10] .shstrtab         STRTAB           0000000000000000  000000d0
       0000000000000061  0000000000000000           0     0     1
  [11] .symtab           SYMTAB           0000000000000000  00000478
       0000000000000108  0000000000000018          12     9     8
  [12] .strtab           STRTAB           0000000000000000  00000580
       0000000000000013  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
  • 链接:gcc hello.o -o hello-------可执行文件hello,链接环节是将各种代码和数据部分收集起来并组合成一个单一文件的过程,这个文件可以被加载到内存中并执行。换句话说,链接就是把多个文件拼接到一起,本质上是section的拼接。
  • 对比链接前目标文件的节头表信息,可以发现可执行文件的节头表中节多了,多出来的节是从外部库中添加过来的,编译器进行了整合,并安排了地址布局。同时,代码段.text的Address列不再是全为0,已经有值了。
[cdl@h3c temp]$ readelf -S hello
There are 39 section headers, starting at offset 0x11c8:

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         0000000000400200  00000200
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             000000000040021c  0000021c
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             000000000040023c  0000023c
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400260  00000260
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           0000000000400280  00000280
       0000000000000060  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           00000000004002e0  000002e0
       000000000000003d  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000040031e  0000031e
       0000000000000008  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400328  00000328
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400348  00000348
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400360  00000360
       0000000000000030  0000000000000018   A       5    12     8
  [11] .init             PROGBITS         0000000000400390  00000390
       0000000000000018  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000004003a8  000003a8
       0000000000000030  0000000000000010  AX       0     0     4
  [13] .text             PROGBITS         00000000004003e0  000003e0
       00000000000001d8  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         00000000004005b8  000005b8
       000000000000000e  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         00000000004005c8  000005c8
       000000000000001c  0000000000000000   A       0     0     8
  [16] .eh_frame_hdr     PROGBITS         00000000004005e4  000005e4
       0000000000000024  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400608  00000608
       000000000000007c  0000000000000000   A       0     0     8
  [18] .ctors            PROGBITS         0000000000600688  00000688
       0000000000000010  0000000000000000  WA       0     0     8
  [19] .dtors            PROGBITS         0000000000600698  00000698
       0000000000000010  0000000000000000  WA       0     0     8
  [20] .jcr              PROGBITS         00000000006006a8  000006a8
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          00000000006006b0  000006b0
       0000000000000190  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000600840  00000840
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000600848  00000848
       0000000000000028  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         0000000000600870  00000870
       0000000000000004  0000000000000000  WA       0     0     4
  [25] .bss              NOBITS           0000000000600878  00000874
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .comment          PROGBITS         0000000000000000  00000874
       000000000000002d  0000000000000001  MS       0     0     1
  [27] .debug_aranges    PROGBITS         0000000000000000  000008b0
       0000000000000060  0000000000000000           0     0     16
  [28] .debug_pubnames   PROGBITS         0000000000000000  00000910
       000000000000005f  0000000000000000           0     0     1
  [29] .debug_info       PROGBITS         0000000000000000  0000096f
       000000000000023b  0000000000000000           0     0     1
  [30] .debug_abbrev     PROGBITS         0000000000000000  00000baa
       000000000000012a  0000000000000000           0     0     1
  [31] .debug_line       PROGBITS         0000000000000000  00000cd4
       0000000000000120  0000000000000000           0     0     1
  [32] .debug_str        PROGBITS         0000000000000000  00000df4
       0000000000000100  0000000000000001  MS       0     0     1
  [33] .debug_loc        PROGBITS         0000000000000000  00000ef4
       00000000000000d9  0000000000000000           0     0     1
  [34] .debug_pubtypes   PROGBITS         0000000000000000  00000fcd
       000000000000002f  0000000000000000           0     0     1
  [35] .debug_ranges     PROGBITS         0000000000000000  00000ffc
       0000000000000050  0000000000000000           0     0     1
  [36] .shstrtab         STRTAB           0000000000000000  0000104c
       0000000000000177  0000000000000000           0     0     1
  [37] .symtab           SYMTAB           0000000000000000  00001b88
       0000000000000738  0000000000000018          38    59     8
  [38] .strtab           STRTAB           0000000000000000  000022c0
       000000000000020e  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
  • 查看段头表(program headers table):readelf -l hello.o,链接后得到的可执行文件多了段头表。可执行文件的执行,本质上是操作系统按照段头表的排列,将可执行文件按照排列好的布局加载到内存中,再跳转到其中的代码段进行执行。
[cdl@h3c temp]$ readelf -l hello

Elf file type is EXEC (Executable file)
Entry point 0x4003e0
There are 8 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001c0 0x00000000000001c0  R E    8
  INTERP         0x0000000000000200 0x0000000000400200 0x0000000000400200
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000684 0x0000000000000684  R E    200000
  LOAD           0x0000000000000688 0x0000000000600688 0x0000000000600688
                 0x00000000000001ec 0x0000000000000200  RW     200000
  DYNAMIC        0x00000000000006b0 0x00000000006006b0 0x00000000006006b0
                 0x0000000000000190 0x0000000000000190  RW     8
  NOTE           0x000000000000021c 0x000000000040021c 0x000000000040021c
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x00000000000005e4 0x00000000004005e4 0x00000000004005e4
                 0x0000000000000024 0x0000000000000024  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     

5 链接与库

  • 链接过程详细上来说分为两个步骤:符号解析和重定位;根据链接的时间点不同,可以分成静态链接和动态链接;
  • 如下以hello.c文件为例说明符号解析和重定位过程
    • 符号解析:由于hello.c文件中只有两个符号main和printf,由于main的实现就在hello.c文件中,而printf的实现显然不在hello.c中,因此,hello.c编译为hello.o后,main这个符号就是有定义的,printf这个符号就是无定义的。注意:这里讨论的符号有无定义是指函数对应的机器指令地址是否在当前文件中。编译器需要到其他共享库中找到printf这个符号的定义(机器指令片段),找到后把该片机器指令与hello.o拼接到一起,生成可执行文件hello。这样,hello中符号printf就有了明确的地址。
    • 重定位:在拼接所有的目标文件同时,编译器会确定各个函数加载到内存中的运行地址,然后反过来去修改所有调用该函数的机器指令,使得该指令能跳转到正确的内存地址,这个过程即重定位。
  • 符号:符号包含全局变量和全局函数,链接器上下文中的3种不同符号如下:
    • 由模块定义并能被其他模块引用的全局符号。全局链接器符号对应非静态的C函数和被定义为不带C static属性的全局变量;
    • 由其他模块定义并被模块引用的全局符号。这些符号称为外部符号,对应定义在其他模块中的C函数和变量;
    • 只被模块定义和引用的本地符号。
  • 符号表:一种供编译器用于保存有关源程序构造的各种信息的数据结构。这些信息在编译器分析阶段被逐步收集并保存在符号表中,它们在综合阶段用于生成目标代码。符号表的作用是查找未知函数在其他库文件中的代码段的具体位置。以hello为例,其调用的printf是外部库提供的函数。在链接前,编译器需要把类似printf这类的符号都记录下来,存储在符号表中。
  • 链接前,查看符号表:readelf -s hello.o或objdump -t hello.o
[cdl@h3c temp]$ readelf -s hello.o

Symbol table '.symtab' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
     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    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000    21 FUNC    GLOBAL DEFAULT    1 main
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts
  • 链接后,查看符号表:readelf -s hello或objdump -t hello
[cdl@h3c temp]$ readelf -s hello

Symbol table '.dynsym' contains 4 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 77 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 
     2: 000000000040021c     0 SECTION LOCAL  DEFAULT    2 
     3: 000000000040023c     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000400260     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000400280     0 SECTION LOCAL  DEFAULT    5 
     6: 00000000004002e0     0 SECTION LOCAL  DEFAULT    6 
     7: 000000000040031e     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000400328     0 SECTION LOCAL  DEFAULT    8 
     9: 0000000000400348     0 SECTION LOCAL  DEFAULT    9 
    10: 0000000000400360     0 SECTION LOCAL  DEFAULT   10 
    11: 0000000000400390     0 SECTION LOCAL  DEFAULT   11 
    12: 00000000004003a8     0 SECTION LOCAL  DEFAULT   12 
    13: 00000000004003e0     0 SECTION LOCAL  DEFAULT   13 
    14: 00000000004005b8     0 SECTION LOCAL  DEFAULT   14 
    15: 00000000004005c8     0 SECTION LOCAL  DEFAULT   15 
    16: 00000000004005e4     0 SECTION LOCAL  DEFAULT   16 
    17: 0000000000400608     0 SECTION LOCAL  DEFAULT   17 
    18: 0000000000600688     0 SECTION LOCAL  DEFAULT   18 
    19: 0000000000600698     0 SECTION LOCAL  DEFAULT   19 
    20: 00000000006006a8     0 SECTION LOCAL  DEFAULT   20 
    21: 00000000006006b0     0 SECTION LOCAL  DEFAULT   21 
    22: 0000000000600840     0 SECTION LOCAL  DEFAULT   22 
    23: 0000000000600848     0 SECTION LOCAL  DEFAULT   23 
    24: 0000000000600870     0 SECTION LOCAL  DEFAULT   24 
    25: 0000000000600878     0 SECTION LOCAL  DEFAULT   25 
    26: 0000000000000000     0 SECTION LOCAL  DEFAULT   26 
    27: 0000000000000000     0 SECTION LOCAL  DEFAULT   27 
    28: 0000000000000000     0 SECTION LOCAL  DEFAULT   28 
    29: 0000000000000000     0 SECTION LOCAL  DEFAULT   29 
    30: 0000000000000000     0 SECTION LOCAL  DEFAULT   30 
    31: 0000000000000000     0 SECTION LOCAL  DEFAULT   31 
    32: 0000000000000000     0 SECTION LOCAL  DEFAULT   32 
    33: 0000000000000000     0 SECTION LOCAL  DEFAULT   33 
    34: 0000000000000000     0 SECTION LOCAL  DEFAULT   34 
    35: 0000000000000000     0 SECTION LOCAL  DEFAULT   35 
    36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS init.c
    37: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS initfini.c
    38: 000000000040040c     0 FUNC    LOCAL  DEFAULT   13 call_gmon_start
    39: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    40: 0000000000600688     0 OBJECT  LOCAL  DEFAULT   18 __CTOR_LIST__
    41: 0000000000600698     0 OBJECT  LOCAL  DEFAULT   19 __DTOR_LIST__
    42: 00000000006006a8     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
    43: 0000000000400430     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    44: 0000000000600878     1 OBJECT  LOCAL  DEFAULT   25 completed.6364
    45: 0000000000600880     8 OBJECT  LOCAL  DEFAULT   25 dtor_idx.6366
    46: 00000000004004a0     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    47: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    48: 0000000000600690     0 OBJECT  LOCAL  DEFAULT   18 __CTOR_END__
    49: 0000000000400680     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    50: 00000000006006a8     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
    51: 0000000000400580     0 FUNC    LOCAL  DEFAULT   13 __do_global_ctors_aux
    52: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS initfini.c
    53: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
    54: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS elf-init.c
    55: 0000000000600848     0 OBJECT  LOCAL  DEFAULT   23 _GLOBAL_OFFSET_TABLE_
    56: 0000000000600684     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    57: 0000000000600684     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
    58: 00000000006006b0     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    59: 0000000000600870     0 NOTYPE  WEAK   DEFAULT   24 data_start
    60: 00000000004004e0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    61: 00000000004003e0     0 FUNC    GLOBAL DEFAULT   13 _start
    62: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    63: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    64: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@@GLIBC_2.2.5
    65: 00000000004005b8     0 FUNC    GLOBAL DEFAULT   14 _fini
    66: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    67: 00000000004005c8     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    68: 0000000000600870     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
    69: 00000000004005d0     0 OBJECT  GLOBAL HIDDEN    15 __dso_handle
    70: 00000000006006a0     0 OBJECT  GLOBAL HIDDEN    19 __DTOR_END__
    71: 00000000004004f0   137 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    72: 0000000000600874     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    73: 0000000000600888     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    74: 0000000000600874     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    75: 00000000004004c4    21 FUNC    GLOBAL DEFAULT   13 main
    76: 0000000000400390     0 FUNC    GLOBAL DEFAULT   11 _init
  • 对比上述链接前后main函数的符号表,我们可以关注Value和Ndx列。Value在链接前是0,在链接后是4004c4。对于符号main来说,Value就是内存地址。在链接前可执行文件各部分没有分配内存地址,所以Value的值是0。Ndx是符号main对应的节区编号,链接前是1,链接后是13,这是因为链接后加入了外部库的节区。由于main函数本身就在hello.o文件中,所以类型是FUNC,大小21也是已知的。puts(printf)是调用外部的函数,也就是外部符号。

  • 重定位:把程序的逻辑地址空间转化成内存中实际的物理地址空间,即在装入时对目标程序中指令和数据的修改过程。它实现了多道程序在内存中同时运行的基础。重定位具体分为两步:

    • 重定位节和符号定义
    • 重定位节中的符号引用
  • 可重定位表中的每一条记录对应一个需要重定位的符号。汇编器将为可重定位文件中每个包含需要重定位符号的段都建立一个重定位表,可重定位表的查看方法:readelf -r hello.o

[cdl@h3c temp]$ readelf -r hello.o

Relocation section '.rela.text' at offset 0x598 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000005  00050000000a R_X86_64_32       0000000000000000 .rodata + 0
00000000000a  000a00000002 R_X86_64_PC32     0000000000000000 puts - 4

Relocation section '.rela.eh_frame' at offset 0x5c8 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
  • 上面显示了hello.o的可重定位表信息,仅关注puts。其描述的是代码段的第0xa字节处有一个地址,需要被替换成符号puts将来的内存地址。通过反汇编hello.o,找到"9: e8 00 00 00 00"机器代码,即代码的第9字节,e8就是call指令,链接后"00 00 00 00"(第10~第13字节)就会被替换成puts链接后的地址。
[cdl@h3c temp]$ objdump -d hello.o

hello.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   bf 00 00 00 00          mov    $0x0,%edi
   9:   e8 00 00 00 00          callq  e <main+0xe>
   e:   b8 00 00 00 00          mov    $0x0,%eax
  13:   c9                      leaveq 
  14:   c3                      retq

6 ELF常用命令

  • man elf:查看其详细的格式定义
  • readelf:用于显示一个或多个elf格式的目标文件
    • -a:等价于-h -l -S -s -r -d -V -A -I
    • -h:显示elf文件开始的文件头信息
    • -S:显示节头信息
    • -l:显示段头信息
    • -s:显示符号表信息
    • -r:显示可重定位段的信息
  • objdump:显示二进制文件信息,用于查看目标文件或者可执行的目标文件构成的gcc工具
    • -f:显示目标文件中每个文件的整个头部摘要信息
    • -h:显示目标文件各个节的头部摘要信息
    • -r:显示文件的重定位入口
    • -d:以反汇编的格式显示
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容