程序员的自我修养:链接、装载与库阅读1:https://developer.aliyun.com/article/1597070
五、重定位表
5.1 重定位表结构
/* Relocation table entry without addend (in section of type SHT_REL). */ typedef struct { Elf32_Addr r_offset; /* Address */ Elf32_Word r_info; /* Relocation type and symbol index */ } Elf32_Rel; /* I have seen two different definitions of the Elf64_Rel and Elf64_Rela structures, so we'll leave them out until Novell (or whoever) gets their act together. */ /* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ typedef struct { Elf64_Addr r_offset; /* Address,8bytes */ Elf64_Xword r_info; /* Relocation type and symbol index,8bytes */ } Elf64_Rel; /* Relocation table entry with addend (in section of type SHT_RELA). */ typedef struct { Elf32_Addr r_offset; /* Address */ Elf32_Word r_info; /* Relocation type and symbol index */ Elf32_Sword r_addend; /* Addend */ } Elf32_Rela; typedef struct { Elf64_Addr r_offset; /* Address,8bytes */ Elf64_Xword r_info; /* Relocation type and symbol index,8bytes */ Elf64_Sxword r_addend; /* Addend,8bytes */ } Elf64_Rela;
5.2 命令行段输出
readelf -r a.o 重定位节 '.rela.text' at offset 0x378 contains 5 entries: 偏移量 信息 类型 符号值 符号名称 + 加数 000000000017 000500000002 R_X86_64_PC32 0000000000000000 .rodata - 4 000000000021 001000000004 R_X86_64_PLT32 0000000000000000 printf - 4 00000000003d 000300000002 R_X86_64_PC32 0000000000000000 .data + 0 000000000043 000400000002 R_X86_64_PC32 0000000000000000 .bss - 4 000000000056 000e00000004 R_X86_64_PLT32 0000000000000000 func1 - 4 重定位节 '.rela.eh_frame' at offset 0x3f0 contains 2 entries: 偏移量 信息 类型 符号值 符号名称 + 加数 000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0 000000000040 000200000002 R_X86_64_PC32 0000000000000000 .text + 28
5.2 命令行段输出
readelf -r a.o 重定位节 '.rela.text' at offset 0x378 contains 5 entries: 偏移量 信息 类型 符号值 符号名称 + 加数 000000000017 000500000002 R_X86_64_PC32 0000000000000000 .rodata - 4 000000000021 001000000004 R_X86_64_PLT32 0000000000000000 printf - 4 00000000003d 000300000002 R_X86_64_PC32 0000000000000000 .data + 0 000000000043 000400000002 R_X86_64_PC32 0000000000000000 .bss - 4 000000000056 000e00000004 R_X86_64_PLT32 0000000000000000 func1 - 4 重定位节 '.rela.eh_frame' at offset 0x3f0 contains 2 entries: 偏移量 信息 类型 符号值 符号名称 + 加数 000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0 000000000040 000200000002 R_X86_64_PC32 0000000000000000 .text + 28
上面列举了32和64位系统的重定位结构,目前我们分析的是64位系统,而64位下有两个结构,一个是Elf64_Rel,另一个是Elf64_Rela。经过实际考察此处应当选择Elf64_Rela结构,每个域大小为24字节,共5个域,总大小为120字节。具体数据可参考下图。
.rela.text段(重定位表),其在文件中起始位置为:0x498+64*2=0x518(段开始位置为0x498,每个section header大小为64)
5.3 二进制段分析
0x378开始的0x78字节如下:
从上面分析可知,重定位主要描述了哪些字段需要重定位。
六、动态链接
6.1 源码
// Lib.c #include <stdio.h> void foobar(int i) { printf("Printing from Lib.so %d\n", i); sleep(-1); } // Lib.h #ifndef LIB_H #define LIB_H void foobar(int i); #endif // Program1.c #include "Lib.h" int main() { foobar(1); return 0; } // Program2.c #include "Lib.h" #include <stdio.h> //int ab = 10; void ab() { int c = 55; int b = 66; int m = c + b; printf("The m 中国 is %d\n", m); } int main() { ab(); foobar(2); return 0; } //e4 b8 ad e5 9b bd
执行:
# 生成 Lib.so 共享库 gcc -fPIC -shared -o Lib.so Lib.c # 生成 Program1 gcc -o Program1 Program1.c ./Lib.so # 生成 Program2 gcc -o Program2 Program2.c ./Lib.so
6.2 .interp 段
.interp段(是interpreter(解释器)的缩写),它保存了可执行文件所需要的动态链接器的路径,linux下一般为 /lib64/ld-linux-x86-64.so.2 ,使用命令:readelf -l Program1 | grep interpreter 可查看。
6.3 .dynamic 段
.dynamic段保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的位置等。
6.3.1 表结构
/* Dynamic section entry. */ typedef struct { Elf64_Sxword d_tag; /* Dynamic entry type,8bytes */ union { Elf64_Xword d_val; /* Integer value,8bytes */ Elf64_Addr d_ptr; /* Address value,8bytes */ } d_un; } Elf64_Dyn; /* Legal values for d_tag (dynamic entry type). */ #define DT_NULL 0 /* Marks end of dynamic section */ #define DT_NEEDED 1 /* Name of needed library */ #define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ #define DT_PLTGOT 3 /* Processor defined value */ #define DT_HASH 4 /* Address of symbol hash table */ #define DT_STRTAB 5 /* Address of string table */ #define DT_SYMTAB 6 /* Address of symbol table */ #define DT_RELA 7 /* Address of Rela relocs */ #define DT_RELASZ 8 /* Total size of Rela relocs */ #define DT_RELAENT 9 /* Size of one Rela reloc */ #define DT_STRSZ 10 /* Size of string table */ #define DT_SYMENT 11 /* Size of one symbol table entry */ #define DT_INIT 12 /* Address of init function */ #define DT_FINI 13 /* Address of termination function */ #define DT_SONAME 14 /* Name of shared object */ #define DT_RPATH 15 /* Library search path (deprecated) */ #define DT_SYMBOLIC 16 /* Start symbol search here */ #define DT_REL 17 /* Address of Rel relocs */ #define DT_RELSZ 18 /* Total size of Rel relocs */ #define DT_RELENT 19 /* Size of one Rel reloc */ #define DT_PLTREL 20 /* Type of reloc in PLT */ #define DT_DEBUG 21 /* For debugging; unspecified */ #define DT_TEXTREL 22 /* Reloc might modify .text */ #define DT_JMPREL 23 /* Address of PLT relocs */ #define DT_BIND_NOW 24 /* Process relocations of object */ #define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ #define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ #define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ #define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ #define DT_RUNPATH 29 /* Library search path */ #define DT_FLAGS 30 /* Flags for the object being loaded */ #define DT_ENCODING 32 /* Start of encoded range */ #define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ #define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ #define DT_SYMTAB_SHNDX 34 /* Address of SYMTAB_SHNDX section */ #define DT_NUM 35 /* Number used */ #define DT_LOOS 0x6000000d /* Start of OS-specific */ #define DT_HIOS 0x6ffff000 /* End of OS-specific */ #define DT_LOPROC 0x70000000 /* Start of processor-specific */ #define DT_HIPROC 0x7fffffff /* End of processor-specific */ #define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */
6.3.2 命令输出
readelf -d Lib.so Dynamic section at offset 0x2e20 contains 24 entries: 标记 类型 名称/值 0x0000000000000001 (NEEDED) 共享库:[libc.so.6] 0x000000000000000c (INIT) 0x1000 0x000000000000000d (FINI) 0x1170 0x0000000000000019 (INIT_ARRAY) 0x3e10 0x000000000000001b (INIT_ARRAYSZ) 8 (bytes) 0x000000000000001a (FINI_ARRAY) 0x3e18 0x000000000000001c (FINI_ARRAYSZ) 8 (bytes) 0x000000006ffffef5 (GNU_HASH) 0x2f0 0x0000000000000005 (STRTAB) 0x3d8 0x0000000000000006 (SYMTAB) 0x318 0x000000000000000a (STRSZ) 127 (bytes) 0x000000000000000b (SYMENT) 24 (bytes) 0x0000000000000003 (PLTGOT) 0x4000 0x0000000000000002 (PLTRELSZ) 48 (bytes) 0x0000000000000014 (PLTREL) RELA 0x0000000000000017 (JMPREL) 0x530 0x0000000000000007 (RELA) 0x488 0x0000000000000008 (RELASZ) 168 (bytes) 0x0000000000000009 (RELAENT) 24 (bytes) 0x000000006ffffffe (VERNEED) 0x468 0x000000006fffffff (VERNEEDNUM) 1 0x000000006ffffff0 (VERSYM) 0x458 0x000000006ffffff9 (RELACOUNT) 3 0x0000000000000000 (NULL) 0x0
6.3.3 二进制分析
(1).dynstr(dynamic symbol table),动态符号字符串表,对标(.strtab(字符串表))。
Lib.so的段其实地址为:0x37F8,.dynstr段序号为【5】,每个段大小为64字节,故.dynstr在文件中偏移地址为:0x37F8 + 64 * 5 = 0x3938 处。查看可得知其在文件中的偏移地址为:0x3D8,共0x7F字节。
(2)Lib.so的段其实地址为:0x37F8,.dynamic 段序号为【21】,每个段大小为64字节,故.dynamic在文件中偏移地址为:0x37F8 + 64 * 21 = 0x3D38 处。
0x2e20开始的0x1c0字节如下:
从上面可知,其共有0x1C0个字节,每个entry大小为16字节,故理论上应当有28个entries,但为什么 readelf 只输出24个entries呢?分析其最后5个entries,发现其值都为0,而0表示动态段结束,故只显示了24个entries。
6.3 .dynsym段
.dynsym(dynamic symbol table),只保存动态链接相关的符号,对标(.symtab(符号表))。
Lib.so的段其实地址为:0x37F8,.dynsym段序号为【4】,每个段大小为64字节,故.dynsym在文件中偏移地址为:0x37F8 + 64 * 4 = 0x38F8 处。
查看可得知其在文件中的偏移地址为:0x318,共0xC0字节,每个entry为0x18字节。
6.3.1 表结构
其表结构和.symtab(符号表)一样
/* Symbol table entry. */ typedef struct { Elf64_Word st_name; /* Symbol name (string tbl index),4bytes */ unsigned char st_info; /* Symbol type and binding,1bytes */ unsigned char st_other; /* Symbol visibility,1bytes */ Elf64_Section st_shndx; /* Section index,2bytes */ Elf64_Addr st_value; /* Symbol value,8bytes */ Elf64_Xword st_size; /* Symbol size,8bytes */ } Elf64_Sym;
6.3.2 命令行输出
readelf -s Lib.so Symbol table '.dynsym' contains 8 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2) 3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ 4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.2.5 (2) 6: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2) 7: 0000000000001139 55 FUNC GLOBAL DEFAULT 14 foobar Symbol table '.symtab' contains 53 entries: Num: Value Size Type Bind Vis Ndx Name ... ... ... ...
6.3.3 二进制分析
6.4 .rela.dyn段
.rela.dyn,动态链接重定位表,对标(.rela.text),实际上是对数据引用的修正,它所修正的位置位于“.got” 以及数据段。
Lib.so的段其实地址为:0x37F8,.rela.dyn段序号为【8】,每个段大小为64字节,故.rela.dyn在文件中偏移地址为:0x37F8 + 64 * 8 = 0x39F8 处。
具体分析参加 重定位表 一节
6.5 .rela.plt段
.rela.plt,动态链接重定位表,对标(.rela.data) ,实际上是对函数引用的修正,它所修正的位置位于“.got.plt”。
Lib.so的段其实地址为:0x37F8,.rela.plt 段序号为【9】,每个段大小为64字节,故.rela.plt 在文件中偏移地址为:0x37F8 + 64 * 9 = 0x3A38 处。
具体分析参加 重定位表 一节
七、Segment
7.1 结构
/* Program segment header. */ typedef struct { Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */ Elf32_Addr p_paddr; /* Segment physical address */ Elf32_Word p_filesz; /* Segment size in file */ Elf32_Word p_memsz; /* Segment size in memory */ Elf32_Word p_flags; /* Segment flags */ Elf32_Word p_align; /* Segment alignment */ } Elf32_Phdr; typedef struct { Elf64_Word p_type; /* Segment type,4bytes */ Elf64_Word p_flags; /* Segment flags,4bytes */ Elf64_Off p_offset; /* Segment file offset,8bytes */ Elf64_Addr p_vaddr; /* Segment virtual address,8bytes */ Elf64_Addr p_paddr; /* Segment physical address,8bytes */ Elf64_Xword p_filesz; /* Segment size in file,8bytes */ Elf64_Xword p_memsz; /* Segment size in memory,8bytes */ Elf64_Xword p_align; /* Segment alignment,8bytes */ } Elf64_Phdr; // 共56bytes
7.2 命令输出
readelf -l Lib.so Elf 文件类型为 DYN (共享目标文件) Entry point 0x1080 There are 11 program headers, starting at offset 64 程序头: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000560 0x0000000000000560 R 0x1000 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x000000000000017d 0x000000000000017d R E 0x1000 LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000 0x00000000000000dc 0x00000000000000dc R 0x1000 LOAD 0x0000000000002e10 0x0000000000003e10 0x0000000000003e10 0x0000000000000220 0x0000000000000228 RW 0x1000 DYNAMIC 0x0000000000002e20 0x0000000000003e20 0x0000000000003e20 0x00000000000001c0 0x00000000000001c0 RW 0x8 NOTE 0x00000000000002a8 0x00000000000002a8 0x00000000000002a8 0x0000000000000020 0x0000000000000020 R 0x8 NOTE 0x00000000000002c8 0x00000000000002c8 0x00000000000002c8 0x0000000000000024 0x0000000000000024 R 0x4 GNU_PROPERTY 0x00000000000002a8 0x00000000000002a8 0x00000000000002a8 0x0000000000000020 0x0000000000000020 R 0x8 GNU_EH_FRAME 0x000000000000201c 0x000000000000201c 0x000000000000201c 0x000000000000002c 0x000000000000002c R 0x4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 0x10 GNU_RELRO 0x0000000000002e10 0x0000000000003e10 0x0000000000003e10 0x00000000000001f0 0x00000000000001f0 R 0x1 Section to Segment mapping: 段节... 00 .note.gnu.property .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 01 .init .plt .plt.got .plt.sec .text .fini 02 .rodata .eh_frame_hdr .eh_frame 03 .init_array .fini_array .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.gnu.property 06 .note.gnu.build-id 07 .note.gnu.property 08 .eh_frame_hdr 09 10 .init_array .fini_array .dynamic .got
7.3 二进制分析