美文网首页
可执行程序工作原理

可执行程序工作原理

作者: CurryCoder | 来源:发表于2021-10-07 15:44 被阅读0次

    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:以反汇编的格式显示

    相关文章

      网友评论

          本文标题:可执行程序工作原理

          本文链接:https://www.haomeiwen.com/subject/jnkznltx.html