1.5.3 Linux ELF

    然后分别执行下列命令生成三个文件:

    1. gcc -m32 elfDemo.c -o elfDemo.out
    2. gcc -m32 -static elfDemo.c -o elfDemo_static.out

    使用 ldd 命令打印所依赖的共享库:

    1. $ ldd elfDemo.out
    2. linux-gate.so.1 (0xf77b1000)
    3. libc.so.6 => /usr/lib32/libc.so.6 (0xf7597000)
    4. /lib/ld-linux.so.2 => /usr/lib/ld-linux.so.2 (0xf77b3000)
    5. $ ldd elfDemo_static.out
    6. not a dynamic executable

    elfDemo_static.out 采用了静态链接的方式。

    使用 file 命令查看相应的文件格式:

    1. $ file elfDemo.o
    2. elfDemo.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
    3. $ file elfDemo.out
    4. elfDemo.out: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=50036015393a99344897cbf34099256c3793e172, not stripped
    5. $ file elfDemo_static.out
    6. elfDemo_static.out: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=276c839c20b4c187e4b486cf96d82a90c40f4dae, not stripped
    7. $ file -L /usr/lib32/libc.so.6
    8. /usr/lib32/libc.so.6: ELF 32-bit LSB shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib32/ld-linux.so.2, BuildID[sha1]=ee88d1b2aa81f104ab5645d407e190b244203a52, for GNU/Linux 3.2.0, not stripped

    于是我们得到了 Linux 可执行文件格式 ELF (Executable Linkable Format)文件的三种类型:

    • 可重定位文件(Relocatable file)
      • 包含了代码和数据,可以和其他目标文件链接生成一个可执行文件或共享目标文件。
      • elfDemo.o
    • 可执行文件(Executable File)
      • 包含了可以直接执行的文件。
      • elfDemo_static.out
    • 共享目标文件(Shared Object File)
      • 包含了用于链接的代码和数据,分两种情况。一种是链接器将其与其他的可重定位文件和共享目标文件链接起来,生产新的目标文件。另一种是动态链接器将多个共享目标文件与可执行文件结合,作为进程映像的一部分。
      • elfDemo.out
      • libc-2.25.so

    此时他们的结构如图:

    可以看到,在这个简化的 ELF 文件中,开头是一个“文件头”,之后分别是代码段、数据段和.bss段。程序源代码编译后,执行语句变成机器指令,保存在.text段;已初始化的全局变量和局部静态变量都保存在.data段;未初始化的全局变量和局部静态变量则放在.bss段。

    把程序指令和程序数据分开存放有许多好处,从安全的角度讲,当程序被加载后,数据和指令分别被映射到两个虚拟区域。由于数据区域对于进程来说是可读写的,而指令区域对于进程来说是只读的,所以这两个虚存区域的权限可以被分别设置成可读写和只读,可以防止程序的指令被改写和利用。

    接下来,我们更深入地探索目标文件,使用 objdump 来查看目标文件的内部结构:

    1. $ objdump -h elfDemo.o
    2. elfDemo.o: file format elf32-i386
    3. Sections:
    4. Idx Name Size VMA LMA File off Algn
    5. 0 .group 00000008 00000000 00000000 00000034 2**2
    6. CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
    7. 1 .text 00000078 00000000 00000000 0000003c 2**0
    8. CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
    9. 2 .data 00000008 00000000 00000000 000000b4 2**2
    10. CONTENTS, ALLOC, LOAD, DATA
    11. 3 .bss 00000004 00000000 00000000 000000bc 2**2
    12. ALLOC
    13. 4 .rodata 00000004 00000000 00000000 000000bc 2**0
    14. CONTENTS, ALLOC, LOAD, READONLY, DATA
    15. 5 .text.__x86.get_pc_thunk.ax 00000004 00000000 00000000 000000c0 2**0
    16. CONTENTS, ALLOC, LOAD, READONLY, CODE
    17. 6 .comment 00000012 00000000 00000000 000000c4 2**0
    18. CONTENTS, READONLY
    19. 7 .note.GNU-stack 00000000 00000000 00000000 000000d6 2**0
    20. CONTENTS, READONLY
    21. 8 .eh_frame 0000007c 00000000 00000000 000000d8 2**2
    22. CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

    代码段

    1. $ objdump -x -s -d elfDemo.o
    2. ......
    3. Sections:
    4. Idx Name Size VMA LMA File off Algn
    5. ......
    6. 1 .text 00000078 00000000 00000000 0000003c 2**0
    7. CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
    8. ......
    9. Contents of section .text:
    10. 0000 5589e553 83ec04e8 fcffffff 05010000 U..S............
    11. 0010 0083ec08 ff75088d 90000000 005289c3 .....u.......R..
    12. 0020 e8fcffff ff83c410 908b5dfc c9c38d4c ..........]....L
    13. 0030 240483e4 f0ff71fc 5589e551 83ec14e8 $.....q.U..Q....
    14. 0040 fcffffff 05010000 00c745f4 1e000000 ..........E.....
    15. 0050 8b880000 00008b55 f401ca8b 80040000 .......U........
    16. 0060 0001d083 ec0c50e8 fcffffff 83c41090 ......P.........
    17. 0070 8b4dfcc9 8d61fcc3 .M...a..
    18. ......
    19. Disassembly of section .text:
    20. 00000000 <func>:
    21. 0: 55 push %ebp
    22. 1: 89 e5 mov %esp,%ebp
    23. 3: 53 push %ebx
    24. 4: 83 ec 04 sub $0x4,%esp
    25. 7: e8 fc ff ff ff call 8 <func+0x8>
    26. 8: R_386_PC32 __x86.get_pc_thunk.ax
    27. c: 05 01 00 00 00 add $0x1,%eax
    28. d: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
    29. 11: 83 ec 08 sub $0x8,%esp
    30. 14: ff 75 08 pushl 0x8(%ebp)
    31. 17: 8d 90 00 00 00 00 lea 0x0(%eax),%edx
    32. 19: R_386_GOTOFF .rodata
    33. 1d: 52 push %edx
    34. 1e: 89 c3 mov %eax,%ebx
    35. 20: e8 fc ff ff ff call 21 <func+0x21>
    36. 21: R_386_PLT32 printf
    37. 25: 83 c4 10 add $0x10,%esp
    38. 28: 90 nop
    39. 29: 8b 5d fc mov -0x4(%ebp),%ebx
    40. 2c: c9 leave
    41. 2d: c3 ret
    42. 0000002e <main>:
    43. 2e: 8d 4c 24 04 lea 0x4(%esp),%ecx
    44. 32: 83 e4 f0 and $0xfffffff0,%esp
    45. 35: ff 71 fc pushl -0x4(%ecx)
    46. 38: 55 push %ebp
    47. 3b: 51 push %ecx
    48. 3c: 83 ec 14 sub $0x14,%esp
    49. 3f: e8 fc ff ff ff call 40 <main+0x12>
    50. 40: R_386_PC32 __x86.get_pc_thunk.ax
    51. 44: 05 01 00 00 00 add $0x1,%eax
    52. 45: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
    53. 49: c7 45 f4 1e 00 00 00 movl $0x1e,-0xc(%ebp)
    54. 50: 8b 88 00 00 00 00 mov 0x0(%eax),%ecx
    55. 52: R_386_GOTOFF global_init_var
    56. 56: 8b 55 f4 mov -0xc(%ebp),%edx
    57. 59: 01 ca add %ecx,%edx
    58. 5b: 8b 80 04 00 00 00 mov 0x4(%eax),%eax
    59. 5d: R_386_GOTOFF .data
    60. 61: 01 d0 add %edx,%eax
    61. 63: 83 ec 0c sub $0xc,%esp
    62. 66: 50 push %eax
    63. 67: e8 fc ff ff ff call 68 <main+0x3a>
    64. 68: R_386_PC32 func
    65. 6c: 83 c4 10 add $0x10,%esp
    66. 6f: 90 nop
    67. 70: 8b 4d fc mov -0x4(%ebp),%ecx
    68. 73: c9 leave
    69. 74: 8d 61 fc lea -0x4(%ecx),%esp
    70. 77: c3 ret

    Contents of section .text 是 的数据的十六进制形式,总共 0x78 个字节,最左边一列是偏移量,中间 4 列是内容,最右边一列是 ASCII 码形式。下面的 Disassembly of section .text 是反汇编结果。

    数据段和只读数据段

    .data 段保存已经初始化了的全局变量和局部静态变量。elfDemo.c 中共有两个这样的变量,global_init_varlocal_static_init_var,每个变量 4 个字节,一共 8 个字节。由于小端序的原因,0a000000 表示 global_init_var 值(10)的十六进制 0x0a14000000 表示 local_static_init_var 值(20)的十六进制 0x14

    .rodata 段保存只读数据,包括只读变量和字符串常量。elfDemo.c 中调用 printf 的时候,用到了一个字符串变量 %d\n,它是一种只读数据,保存在 .rodata 段中,可以从输出结果看到字符串常量的 ASCII 形式,以 \0 结尾。

    1. Sections:
    2. Idx Name Size VMA LMA File off Algn
    3. 3 .bss 00000004 00000000 00000000 000000bc 2**2
    4. ALLOC

    .bss 段保存未初始化的全局变量和局部静态变量。

    对象文件参与程序链接(构建程序)和程序执行(运行程序)。ELF 结构几相关信息在 /usr/include/elf.h 文件中。

    img

    • ELF 文件头(ELF Header) 在目标文件格式的最前面,包含了描述整个文件的基本属性。
    • 程序头表(Program Header Table) 是可选的,它告诉系统怎样创建一个进程映像。可执行文件必须有程序头表,而重定位文件不需要。
    • 段(Section) 包含了链接视图中大量的目标文件信息。
    • 段表(Section Header Table) 包含了描述文件中所有段的信息。

    32位数据类型

    文件头

    ELF 文件头必然存在于 ELF 文件的开头,表明这是一个 ELF 文件。定义如下:

    1. typedef struct
    2. {
    3. unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
    4. Elf32_Half e_type; /* Object file type */
    5. Elf32_Half e_machine; /* Architecture */
    6. Elf32_Word e_version; /* Object file version */
    7. Elf32_Addr e_entry; /* Entry point virtual address */
    8. Elf32_Off e_phoff; /* Program header table file offset */
    9. Elf32_Off e_shoff; /* Section header table file offset */
    10. Elf32_Word e_flags; /* Processor-specific flags */
    11. Elf32_Half e_ehsize; /* ELF header size in bytes */
    12. Elf32_Half e_phentsize; /* Program header table entry size */
    13. Elf32_Half e_phnum; /* Program header table entry count */
    14. Elf32_Half e_shentsize; /* Section header table entry size */
    15. Elf32_Half e_shnum; /* Section header table entry count */
    16. Elf32_Half e_shstrndx; /* Section header string table index */
    17. } Elf32_Ehdr;
    18. typedef struct
    19. {
    20. unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
    21. Elf64_Half e_type; /* Object file type */
    22. Elf64_Half e_machine; /* Architecture */
    23. Elf64_Word e_version; /* Object file version */
    24. Elf64_Addr e_entry; /* Entry point virtual address */
    25. Elf64_Off e_phoff; /* Program header table file offset */
    26. Elf64_Off e_shoff; /* Section header table file offset */
    27. Elf64_Word e_flags; /* Processor-specific flags */
    28. Elf64_Half e_ehsize; /* ELF header size in bytes */
    29. Elf64_Half e_phentsize; /* Program header table entry size */
    30. Elf64_Half e_phnum; /* Program header table entry count */
    31. Elf64_Half e_shentsize; /* Section header table entry size */
    32. Elf64_Half e_shnum; /* Section header table entry count */
    33. Elf64_Half e_shstrndx; /* Section header string table index */
    34. } Elf64_Ehdr;

    e_ident 保存着 ELF 的幻数和其他信息,最前面四个字节是幻数,用字符串表示为 \177ELF,其后的字节如果是 32 位则是 ELFCLASS32 (1),如果是 64 位则是 ELFCLASS64 (2),再其后的字节表示端序,小端序为 ELFDATA2LSB (1),大端序为 ELFDATA2LSB (2)。最后一个字节则表示 ELF 的版本。

    现在我们使用 readelf 命令来查看 elfDome.out 的文件头:

    1. $ readelf -h elfDemo.out
    2. ELF Header:
    3. Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
    4. Class: ELF32
    5. Data: 2's complement, little endian
    6. Version: 1 (current)
    7. OS/ABI: UNIX - System V
    8. ABI Version: 0
    9. Type: DYN (Shared object file)
    10. Machine: Intel 80386
    11. Version: 0x1
    12. Entry point address: 0x3e0
    13. Start of program headers: 52 (bytes into file)
    14. Start of section headers: 6288 (bytes into file)
    15. Flags: 0x0
    16. Size of this header: 52 (bytes)
    17. Size of program headers: 32 (bytes)
    18. Number of program headers: 9
    19. Size of section headers: 40 (bytes)
    20. Number of section headers: 30
    21. Section header string table index: 29

    程序头表是由 ELF 头的 e_phoff 指定的偏移量和 e_phentsizee_phnum 共同确定大小的表格组成。e_phentsize 表示表格中程序头的大小,e_phnum 表示表格中程序头的数量。

    1. typedef struct
    2. {
    3. Elf32_Word p_type; /* Segment type */
    4. Elf32_Off p_offset; /* Segment file offset */
    5. Elf32_Addr p_vaddr; /* Segment virtual address */
    6. Elf32_Addr p_paddr; /* Segment physical address */
    7. Elf32_Word p_filesz; /* Segment size in file */
    8. Elf32_Word p_memsz; /* Segment size in memory */
    9. Elf32_Word p_flags; /* Segment flags */
    10. Elf32_Word p_align; /* Segment alignment */
    11. } Elf32_Phdr;
    12. typedef struct
    13. {
    14. Elf64_Word p_type; /* Segment type */
    15. Elf64_Word p_flags; /* Segment flags */
    16. Elf64_Off p_offset; /* Segment file offset */
    17. Elf64_Addr p_vaddr; /* Segment virtual address */
    18. Elf64_Addr p_paddr; /* Segment physical address */
    19. Elf64_Xword p_filesz; /* Segment size in file */
    20. Elf64_Xword p_memsz; /* Segment size in memory */
    21. Elf64_Xword p_align; /* Segment alignment */
    22. } Elf64_Phdr;

    使用 readelf 来查看程序头:

    1. $ readelf -l elfDemo.out
    2. Elf file type is DYN (Shared object file)
    3. Entry point 0x3e0
    4. There are 9 program headers, starting at offset 52
    5. Program Headers:
    6. Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
    7. PHDR 0x000034 0x00000034 0x00000034 0x00120 0x00120 R E 0x4
    8. INTERP 0x000154 0x00000154 0x00000154 0x00013 0x00013 R 0x1
    9. [Requesting program interpreter: /lib/ld-linux.so.2]
    10. LOAD 0x000000 0x00000000 0x00000000 0x00780 0x00780 R E 0x1000
    11. LOAD 0x000ef4 0x00001ef4 0x00001ef4 0x00130 0x0013c RW 0x1000
    12. NOTE 0x000168 0x00000168 0x00000168 0x00044 0x00044 R 0x4
    13. GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
    14. GNU_RELRO 0x000ef4 0x00001ef4 0x00001ef4 0x0010c 0x0010c R 0x1
    15. Section to Segment mapping:
    16. Segment Sections...
    17. 00
    18. 01 .interp
    19. 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
    20. 03 .init_array .fini_array .dynamic .got .got.plt .data .bss
    21. 04 .dynamic
    22. 05 .note.ABI-tag .note.gnu.build-id
    23. 06 .eh_frame_hdr
    24. 07
    25. 08 .init_array .fini_array .dynamic .got

    段表(Section Header Table)是一个以 Elf32_Shdr 结构体为元素的数组,每个结构体对应一个段,它描述了各个段的信息。ELF 文件头的 e_shoff 成员给出了段表在 ELF 中的偏移,e_shnum 成员给出了段描述符的数量,e_shentsize 给出了每个段描述符的大小。

    使用 readelf 命令查看目标文件中完整的段:

    1. $ readelf -S elfDemo.o
    2. There are 15 section headers, starting at offset 0x41c:
    3. Section Headers:
    4. [Nr] Name Type Addr Off Size ES Flg Lk Inf Al
    5. [ 0] NULL 00000000 000000 000000 00 0 0 0
    6. [ 1] .group GROUP 00000000 000034 000008 04 12 16 4
    7. [ 2] .text PROGBITS 00000000 00003c 000078 00 AX 0 0 1
    8. [ 3] .rel.text REL 00000000 000338 000048 08 I 12 2 4
    9. [ 4] .data PROGBITS 00000000 0000b4 000008 00 WA 0 0 4
    10. [ 5] .bss NOBITS 00000000 0000bc 000004 00 WA 0 0 4
    11. [ 6] .rodata PROGBITS 00000000 0000bc 000004 00 A 0 0 1
    12. [ 7] .text.__x86.get_p PROGBITS 00000000 0000c0 000004 00 AXG 0 0 1
    13. [ 8] .comment PROGBITS 00000000 0000c4 000012 01 MS 0 0 1
    14. [ 9] .note.GNU-stack PROGBITS 00000000 0000d6 000000 00 0 0 1
    15. [10] .eh_frame PROGBITS 00000000 0000d8 00007c 00 A 0 0 4
    16. [11] .rel.eh_frame REL 00000000 000380 000018 08 I 12 10 4
    17. [12] .symtab SYMTAB 00000000 000154 000140 10 13 13 4
    18. [13] .strtab STRTAB 00000000 000294 0000a2 00 0 0 1
    19. [14] .shstrtab STRTAB 00000000 000398 000082 00 0 0 1
    20. Key to Flags:
    21. W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
    22. L (link order), O (extra OS processing required), G (group), T (TLS),
    23. C (compressed), x (unknown), o (OS specific), E (exclude),
    24. p (processor specific)

    注意,ELF 段表的第一个元素是被保留的,类型为 NULL。

    字符串表

    字符串表以段的形式存在,包含了以 null 结尾的字符序列。对象文件使用这些字符串来表示符号和段名称,引用字符串时只需给出在表中的偏移即可。字符串表的第一个字符和最后一个字符为空字符,以确保所有字符串的开始和终止。通常段名为 .strtab 的字符串表是 字符串表(Strings Table),段名为 .shstrtab 的是段表字符串表(Section Header String Table)。

    可以使用 readelf 读取这两个表:

    1. $ readelf -x .strtab elfDemo.o
    2. Hex dump of section '.strtab':
    3. 0x00000000 00656c66 44656d6f 2e63006c 6f63616c .elfDemo.c.local
    4. 0x00000010 5f737461 7469635f 696e6974 5f766172 _static_init_var
    5. 0x00000020 2e323139 35006c6f 63616c5f 73746174 .2195.local_stat
    6. 0x00000030 69635f75 6e696e69 745f7661 722e3231 ic_uninit_var.21
    7. 0x00000040 39360067 6c6f6261 6c5f696e 69745f76 96.global_init_v
    8. 0x00000050 61720067 6c6f6261 6c5f756e 696e6974 ar.global_uninit
    9. 0x00000060 5f766172 0066756e 63005f5f 7838362e _var.func.__x86.
    10. 0x00000070 6765745f 70635f74 68756e6b 2e617800 get_pc_thunk.ax.
    11. 0x00000080 5f474c4f 42414c5f 4f464653 45545f54 _GLOBAL_OFFSET_T
    12. 0x00000090 41424c45 5f007072 696e7466 006d6169 ABLE_.printf.mai
    13. 0x000000a0 6e00
    14. $ readelf -x .shstrtab elfDemo.o
    15. Hex dump of section '.shstrtab':
    16. 0x00000000 002e7379 6d746162 002e7374 72746162 ..symtab..strtab
    17. 0x00000010 002e7368 73747274 6162002e 72656c2e ..shstrtab..rel.
    18. 0x00000020 74657874 002e6461 7461002e 62737300 text..data..bss.
    19. 0x00000030 2e726f64 61746100 2e746578 742e5f5f .rodata..text.__
    20. 0x00000040 7838362e 6765745f 70635f74 68756e6b x86.get_pc_thunk
    21. 0x00000050 2e617800 2e636f6d 6d656e74 002e6e6f .ax..comment..no
    22. 0x00000060 74652e47 4e552d73 7461636b 002e7265 te.GNU-stack..re
    23. 0x00000070 6c2e6568 5f667261 6d65002e 67726f75 l.eh_frame..grou
    24. 0x00000080 7000

    目标文件的符号表保存了定位和重定位程序的符号定义和引用所需的信息。符号表索引是这个数组的下标。索引0指向表中的第一个条目,作为未定义的符号索引。

    1. typedef struct
    2. {
    3. Elf32_Word st_name; /* Symbol name (string tbl index) */
    4. Elf32_Addr st_value; /* Symbol value */
    5. Elf32_Word st_size; /* Symbol size */
    6. unsigned char st_info; /* Symbol type and binding */
    7. unsigned char st_other; /* Symbol visibility */
    8. Elf32_Section st_shndx; /* Section index */
    9. } Elf32_Sym;
    10. typedef struct
    11. {
    12. Elf64_Word st_name; /* Symbol name (string tbl index) */
    13. unsigned char st_info; /* Symbol type and binding */
    14. unsigned char st_other; /* Symbol visibility */
    15. Elf64_Section st_shndx; /* Section index */
    16. Elf64_Addr st_value; /* Symbol value */
    17. Elf64_Xword st_size; /* Symbol size */
    18. } Elf64_Sym;

    查看符号表:

    1. $ readelf -s elfDemo.o
    2. Symbol table '.symtab' contains 20 entries:
    3. Num: Value Size Type Bind Vis Ndx Name
    4. 0: 00000000 0 NOTYPE LOCAL DEFAULT UND
    5. 1: 00000000 0 FILE LOCAL DEFAULT ABS elfDemo.c
    6. 2: 00000000 0 SECTION LOCAL DEFAULT 2
    7. 3: 00000000 0 SECTION LOCAL DEFAULT 4
    8. 4: 00000000 0 SECTION LOCAL DEFAULT 5
    9. 5: 00000000 0 SECTION LOCAL DEFAULT 6
    10. 6: 00000004 4 OBJECT LOCAL DEFAULT 4 local_static_init_var.219
    11. 7: 00000000 4 OBJECT LOCAL DEFAULT 5 local_static_uninit_var.2
    12. 8: 00000000 0 SECTION LOCAL DEFAULT 7
    13. 9: 00000000 0 SECTION LOCAL DEFAULT 9
    14. 10: 00000000 0 SECTION LOCAL DEFAULT 10
    15. 11: 00000000 0 SECTION LOCAL DEFAULT 8
    16. 12: 00000000 0 SECTION LOCAL DEFAULT 1
    17. 13: 00000000 4 OBJECT GLOBAL DEFAULT 4 global_init_var
    18. 14: 00000004 4 OBJECT GLOBAL DEFAULT COM global_uninit_var
    19. 15: 00000000 46 FUNC GLOBAL DEFAULT 2 func
    20. 16: 00000000 0 FUNC GLOBAL HIDDEN 7 __x86.get_pc_thunk.ax
    21. 17: 00000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
    22. 18: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf
    23. 19: 0000002e 74 FUNC GLOBAL DEFAULT 2 main

    重定位

    重定位是连接符号定义与符号引用的过程。可重定位文件必须具有描述如何修改段内容的信息,从而运行可执行文件和共享对象文件保存进程程序映像的正确信息。

    1. typedef struct
    2. {
    3. Elf32_Addr r_offset; /* Address */
    4. Elf32_Word r_info; /* Relocation type and symbol index */
    5. } Elf32_Rel;
    6. typedef struct
    7. {
    8. Elf64_Addr r_offset; /* Address */
    9. Elf64_Xword r_info; /* Relocation type and symbol index */
    10. Elf64_Sxword r_addend; /* Addend */

    查看重定位表:

    • $ man elf