从linker的角度看ELF
🛠️

从linker的角度看ELF

Tags
Published
Sub-item
Parent item
Author
AI summary
 

示例so文件

下面统一用这个简单的so作为说明示例

常用工具

工欲善其事必先利其器,先说说用到的工具
llvm-readelf / IDA / 010editer
 
关于 llvm-readelf 展开细说
  • --file-header Display file header -h Alias for --file-header
    • 查看文件头
      > llvm-readelf -h "libemulator_check.so" ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: AArch64 Version: 0x1 Entry point address: 0xAA0 Start of program headers: 64 (bytes into file) Start of section headers: 8672 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 8 Size of section headers: 64 (bytes) Number of section headers: 24 Section header string table index: 23
 
  • --program-headers Display program headers --segments Alias for --program-headers -l Alias for --program-headers
    • 查看程序头表
      > llvm-readelf -l "libemulator_check.so" Elf file type is DYN (Shared object file) Entry point 0xaa0 There are 8 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0012f8 0x0012f8 R E 0x1000 LOAD 0x001d68 0x0000000000002d68 0x0000000000002d68 0x0002b8 0x000368 RW 0x1000 DYNAMIC 0x001d80 0x0000000000002d80 0x0000000000002d80 0x0001f0 0x0001f0 RW 0x8 NOTE 0x000200 0x0000000000000200 0x0000000000000200 0x000024 0x000024 R 0x4 NOTE 0x001260 0x0000000000001260 0x0000000000001260 0x000098 0x000098 R 0x4 GNU_EH_FRAME 0x001118 0x0000000000001118 0x0000000000001118 0x000044 0x000044 R 0x4 GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 GNU_RELRO 0x001d68 0x0000000000002d68 0x0000000000002d68 0x000298 0x000298 R 0x1 Section to Segment mapping: Segment Sections... 00 .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .plt .text .rodata .eh_frame_hdr .eh_frame .note.android.ident 01 .fini_array .data.rel.ro .dynamic .got .data .bss 02 .dynamic 03 .note.gnu.build-id 04 .note.android.ident 05 .eh_frame_hdr 06 07 .fini_array .data.rel.ro .dynamic .got None .comment .shstrtab
      结构体
      // ndk\24.0.8215888\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\linux\elf.h // p_type:段的类型,表示段的用途,如 PT_LOAD、PT_DYNAMIC、PT_INTERP 等等 typedef struct elf32_phdr { Elf32_Word p_type; // 段类型 Elf32_Off p_offset; // 段在文件中的偏移 Elf32_Addr p_vaddr; // 段在内存中的虚拟地址 Elf32_Addr p_paddr; // 段在内存中的物理地址(未使用) Elf32_Word p_filesz; // 段在文件中的大小(以字节为单位) Elf32_Word p_memsz; // 段在内存中的大小(以字节为单位) Elf32_Word p_flags; // 段标志 Elf32_Word p_align; // 段的对齐要求 } Elf32_Phdr; typedef struct elf64_phdr { Elf64_Word p_type; // 段类型 Elf64_Word p_flags; // 段标志 Elf64_Off p_offset; // 段在文件中的偏移 Elf64_Addr p_vaddr; // 段在内存中的虚拟地址 Elf64_Addr p_paddr; // 段在内存中的物理地址(未使用) Elf64_Xword p_filesz; // 段在文件中的大小(以字节为单位) Elf64_Xword p_memsz; // 段在内存中的大小(以字节为单位) Elf64_Xword p_align; // 段的对齐要求 } Elf64_Phdr;
 
  • --section-headers Display section headers -S Alias for --section-headers
    • 查看 section headers
      > llvm-readelf -S "libemulator_check.so" There are 24 section headers, starting at offset 0x21e0: Section Headers: [Nr] Name Type Address Off Size ES Flg Lk Inf Al [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 [ 1] .note.gnu.build-id NOTE 0000000000000200 000200 000024 00 A 0 0 4 [ 2] .hash HASH 0000000000000228 000228 0000c4 04 A 4 0 8 [ 3] .gnu.hash GNU_HASH 00000000000002f0 0002f0 0000a8 00 A 4 0 8 [ 4] .dynsym DYNSYM 0000000000000398 000398 0002d0 18 A 5 3 8 [ 5] .dynstr STRTAB 0000000000000668 000668 00013a 00 A 0 0 1 [ 6] .gnu.version VERSYM 00000000000007a2 0007a2 00003c 02 A 4 0 2 [ 7] .gnu.version_r VERNEED 00000000000007e0 0007e0 000020 00 A 5 1 8 [ 8] .rela.dyn RELA 0000000000000800 000800 0000f0 18 A 4 0 8 [ 9] .rela.plt RELA 00000000000008f0 0008f0 0000f0 18 AI 4 19 8 [10] .plt PROGBITS 00000000000009e0 0009e0 0000c0 10 AX 0 0 16 [11] .text PROGBITS 0000000000000aa0 000aa0 000514 00 AX 0 0 4 [12] .rodata PROGBITS 0000000000000fb4 000fb4 000163 00 A 0 0 4 [13] .eh_frame_hdr PROGBITS 0000000000001118 001118 000044 00 A 0 0 4 [14] .eh_frame PROGBITS 0000000000001160 001160 000100 00 A 0 0 8 [15] .note.android.ident NOTE 0000000000001260 001260 000098 00 A 0 0 4 [16] .fini_array FINI_ARRAY 0000000000002d68 001d68 000010 08 WA 0 0 8 [17] .data.rel.ro PROGBITS 0000000000002d78 001d78 000008 00 WA 0 0 8 [18] .dynamic DYNAMIC 0000000000002d80 001d80 0001f0 10 WA 5 0 8 [19] .got PROGBITS 0000000000002f70 001f70 000090 08 WA 0 0 8 [20] .data PROGBITS 0000000000003000 002000 000020 00 WA 0 0 8 [21] .bss NOBITS 0000000000003020 002020 0000b0 00 WA 0 0 8 [22] .comment PROGBITS 0000000000000000 002020 0000dc 01 MS 0 0 1 [23] .shstrtab STRTAB 0000000000000000 0020fc 0000dd 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), R (retain), p (processor specific)
      结构体
      // ndk\24.0.8215888\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\linux\elf.h // sh_type:段的类型,如 SHT_PROGBITS、SHT_SYMTAB、SHT_STRTAB // PROGBITS:程序段,主要包括代码和数据 // SYMTAB:符号表 // STRTAB:字符串表 // REL:重定位表,包含重定位信息,在链接的时候会用到 // HASH:符号表的哈希 // DYNAMIC:动态链接信息 // NOTE:提示性信息 // NOBITS:表示该段在文件中没有内容,比如.bss段 // DNYSYM:动态链接的符号表 // sh_flags:段的标志,如 SHF_WRITE(可写)、SHF_ALLOC(占用内存)和 SHF_EXECINSTR(可执行代码) typedef struct { uint32_t sh_name; // 段名称(字符串表索引) uint32_t sh_type; // 段类型 uint32_t sh_flags; // 段标志 uint32_t sh_addr; // 段的虚拟地址(在执行期间) uint32_t sh_offset; // 段在文件中的偏移 uint32_t sh_size; // 段大小(以字节为单位) uint32_t sh_link; // 链接到另一个段 uint32_t sh_info; // 段的附加信息 uint32_t sh_addralign; // 段的对齐要求 uint32_t sh_entsize; // 条目大小(如果段保存一个表) } Elf32_Shdr; typedef struct { uint32_t sh_name; // 段名称(字符串表索引) uint32_t sh_type; // 段类型 uint64_t sh_flags; // 段标志 uint64_t sh_addr; // 段的虚拟地址(在执行期间) uint64_t sh_offset; // 段在文件中的偏移 uint64_t sh_size; // 段大小(以字节为单位) uint32_t sh_link; // 链接到另一个段 uint32_t sh_info; // 段的附加信息 uint64_t sh_addralign; // 段的对齐要求 uint64_t sh_entsize; // 条目大小(如果段保存一个表) } Elf64_Shdr;
 
  • --headers Equivalent to setting: --file-header, --program-headers, --section-headers -e Alias for --headers ( )
    • 查看基础头信息一般情况直接使用这个就可以了 ( 包含以上三个 )
      > llvm-readelf -e "libemulator_check.so" ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: AArch64 Version: 0x1 Entry point address: 0xAA0 Start of program headers: 64 (bytes into file) Start of section headers: 8672 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 8 Size of section headers: 64 (bytes) Number of section headers: 24 Section header string table index: 23 There are 24 section headers, starting at offset 0x21e0: Section Headers: [Nr] Name Type Address Off Size ES Flg Lk Inf Al [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 [ 1] .note.gnu.build-id NOTE 0000000000000200 000200 000024 00 A 0 0 4 [ 2] .hash HASH 0000000000000228 000228 0000c4 04 A 4 0 8 [ 3] .gnu.hash GNU_HASH 00000000000002f0 0002f0 0000a8 00 A 4 0 8 [ 4] .dynsym DYNSYM 0000000000000398 000398 0002d0 18 A 5 3 8 [ 5] .dynstr STRTAB 0000000000000668 000668 00013a 00 A 0 0 1 [ 6] .gnu.version VERSYM 00000000000007a2 0007a2 00003c 02 A 4 0 2 [ 7] .gnu.version_r VERNEED 00000000000007e0 0007e0 000020 00 A 5 1 8 [ 8] .rela.dyn RELA 0000000000000800 000800 0000f0 18 A 4 0 8 [ 9] .rela.plt RELA 00000000000008f0 0008f0 0000f0 18 AI 4 19 8 [10] .plt PROGBITS 00000000000009e0 0009e0 0000c0 10 AX 0 0 16 [11] .text PROGBITS 0000000000000aa0 000aa0 000514 00 AX 0 0 4 [12] .rodata PROGBITS 0000000000000fb4 000fb4 000163 00 A 0 0 4 [13] .eh_frame_hdr PROGBITS 0000000000001118 001118 000044 00 A 0 0 4 [14] .eh_frame PROGBITS 0000000000001160 001160 000100 00 A 0 0 8 [15] .note.android.ident NOTE 0000000000001260 001260 000098 00 A 0 0 4 [16] .fini_array FINI_ARRAY 0000000000002d68 001d68 000010 08 WA 0 0 8 [17] .data.rel.ro PROGBITS 0000000000002d78 001d78 000008 00 WA 0 0 8 [18] .dynamic DYNAMIC 0000000000002d80 001d80 0001f0 10 WA 5 0 8 [19] .got PROGBITS 0000000000002f70 001f70 000090 08 WA 0 0 8 [20] .data PROGBITS 0000000000003000 002000 000020 00 WA 0 0 8 [21] .bss NOBITS 0000000000003020 002020 0000b0 00 WA 0 0 8 [22] .comment PROGBITS 0000000000000000 002020 0000dc 01 MS 0 0 1 [23] .shstrtab STRTAB 0000000000000000 0020fc 0000dd 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), R (retain), p (processor specific) Elf file type is DYN (Shared object file) Entry point 0xaa0 There are 8 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0012f8 0x0012f8 R E 0x1000 LOAD 0x001d68 0x0000000000002d68 0x0000000000002d68 0x0002b8 0x000368 RW 0x1000 DYNAMIC 0x001d80 0x0000000000002d80 0x0000000000002d80 0x0001f0 0x0001f0 RW 0x8 NOTE 0x000200 0x0000000000000200 0x0000000000000200 0x000024 0x000024 R 0x4 NOTE 0x001260 0x0000000000001260 0x0000000000001260 0x000098 0x000098 R 0x4 GNU_EH_FRAME 0x001118 0x0000000000001118 0x0000000000001118 0x000044 0x000044 R 0x4 GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 GNU_RELRO 0x001d68 0x0000000000002d68 0x0000000000002d68 0x000298 0x000298 R 0x1 Section to Segment mapping: Segment Sections... 00 .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .plt .text .rodata .eh_frame_hdr .eh_frame .note.android.ident 01 .fini_array .data.rel.ro .dynamic .got .data .bss 02 .dynamic 03 .note.gnu.build-id 04 .note.android.ident 05 .eh_frame_hdr 06 07 .fini_array .data.rel.ro .dynamic .got None .comment .shstrtab
 

 
  • --dynamic-table Display the dynamic section table --dynamic Alias for --dynamic-table -d Alias for --dynamic-table
    • 查看动态表(dynamic table)← 给链接器用的
      > llvm-readelf -d "libemulator_check.so" Dynamic section at offset 0x1d80 contains 27 entries: Tag Type Name/Value 0x0000000000000001 (NEEDED) Shared library: [liblog.so] 0x0000000000000001 (NEEDED) Shared library: [libm.so] 0x0000000000000001 (NEEDED) Shared library: [libdl.so] 0x0000000000000001 (NEEDED) Shared library: [libc.so] 0x000000000000000e (SONAME) Library soname: ["libemulator_check.so"] 0x000000000000001a (FINI_ARRAY) 0x2d68 0x000000000000001c (FINI_ARRAYSZ) 16 (bytes) 0x0000000000000004 (HASH) 0x228 0x000000006ffffef5 (GNU_HASH) 0x2f0 0x0000000000000005 (STRTAB) 0x668 0x0000000000000006 (SYMTAB) 0x398 0x000000000000000a (STRSZ) 314 (bytes) 0x000000000000000b (SYMENT) 24 (bytes) 0x0000000000000003 (PLTGOT) 0x2f70 0x0000000000000002 (PLTRELSZ) 240 (bytes) 0x0000000000000014 (PLTREL) RELA 0x0000000000000017 (JMPREL) 0x8f0 0x0000000000000007 (RELA) 0x800 0x0000000000000008 (RELASZ) 240 (bytes) 0x0000000000000009 (RELAENT) 24 (bytes) 0x000000000000001e (FLAGS) BIND_NOW 0x000000006ffffffb (FLAGS_1) NOW 0x000000006ffffffe (VERNEED) 0x7e0 0x000000006fffffff (VERNEEDNUM) 1 0x000000006ffffff0 (VERSYM) 0x7a2 0x000000006ffffff9 (RELACOUNT) 5 0x0000000000000000 (NULL) 0x0
      结构体
      typedef struct { Elf64_Sxword d_tag; // 条目类型 union { Elf64_Xword d_val; // 无符号整数值 Elf64_Addr d_ptr; // 指针值 } d_un; } Elf64_Dyn; d_tag 有一下值 DT_NULL (0):表示动态数组的结束 DT_NEEDED (1):指示一个依赖的动态链接库名称字符串表索引 DT_PLTRELSZ (2):PLT 的重定位表项数量 DT_PLTGOT (3):PLT 的全局偏移地址 DT_HASH (4): 符号哈希表地址 DT_STRTAB (5):字符串表地址 DT_SYMTAB (6):符号表地址 DT_RELA (7):RELA 格式的重定位表地址 DT_RELASZ (8):RELA 格式的重定位表大小 DT_RELAENT (9):单个 RELA 重定位表项的大小 DT_STRSZ (10):字符串表大小 DT_SYMENT (11):单个符号表项的大小 DT_INIT (12):库初始化函数的地址 DT_FINI (13):库终止函数的地址 DT_SONAME (14):共享对象名称 DT_RPATH (15):运行时库搜索路径 DT_SYMBOLIC (16):不要解析所有符号依赖项 DT_REL (17):REL 格式的重定位表地址 DT_RELSZ (18):REL 格式的重定位表大小 DT_RELENT (19):单个 REL 重定位表项的大小 DT_PLTREL (20):PLT 的重定位表格式 DT_DEBUG (21):调试信息地址 DT_TEXTREL (22):可执行段中有重定位项 DT_JMPREL (23):PLT 的重定位表地址 DT_BIND_NOW (24):立即绑定动态符号 DT_INIT_ARRAY (25):初始化函数数组地址 DT_FINI_ARRAY (26):终止函数数组地址 DT_INIT_ARRAYSZ (27):初始化函数数组大小 DT_FINI_ARRAYSZ (28):终止函数数组大小 DT_RUNPATH (29):库运行时搜索路径 DT_FLAGS (30):动态标志 DT_ENCODING (32):编码 DT_PREINIT_ARRAY (32):预初始化函数数组地址 DT_PREINIT_ARRAYSZ (33):预初始化函数数组大小 DT_GNU_HASH (0x6ffffef5):GNU 扩展的符号哈希表地址
 
  • --symbols Display the symbol table. Also display the dynamic symbol table when using GNU output style for ELF --dyn-syms Display the dynamic symbol table (这个在于查看 .dynsym section) -syms Alias for --symbols -s Alias for --symbols
    • 查看符号表
      > llvm-readelf -s "libemulator_check.so" Symbol table '.dynsym' contains 30 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000aa0 0 SECTION LOCAL DEFAULT 11 .text 2: 0000000000002d78 0 SECTION LOCAL DEFAULT 17 .data.rel.ro 3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize@LIBC 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@LIBC 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __android_log_print 6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sigaction@LIBC 7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND mmap@LIBC 8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getpagesize@LIBC 9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@LIBC 10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND munmap@LIBC 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fopen@LIBC 12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@LIBC 13: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__ 14: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS __bss_start__ 15: 0000000000003000 4 OBJECT GLOBAL DEFAULT 20 a 16: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS __bss_start 17: 0000000000000adc 44 FUNC GLOBAL DEFAULT 11 my_sigaction 18: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS __end__ 19: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS _end 20: 0000000000000fc8 4 OBJECT GLOBAL DEFAULT 12 handledSignalsNum 21: 0000000000000e74 192 FUNC GLOBAL DEFAULT 11 JNI_OnLoad 22: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS _edata 23: 0000000000000fb4 20 OBJECT GLOBAL DEFAULT 12 handledSignals 24: 0000000000003028 8 OBJECT GLOBAL DEFAULT 21 asmcheck 25: 0000000000000c24 592 FUNC GLOBAL DEFAULT 11 detect 26: 0000000000003030 160 OBJECT GLOBAL DEFAULT 21 old_handlers 27: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS __bss_end__ 28: 0000000000000b08 176 FUNC GLOBAL DEFAULT 11 load 29: 0000000000000bb8 108 FUNC GLOBAL DEFAULT 11 getArch
      导出符号
      什么是导出符号
      bool Symbol::is_exported() const { // 初始状态是符号的段索引不为未定义 bool is_exported = shndx() != SECTION_INDEX::UNDEF; // 导出符号必须有一个地址 // 如果符号的值为 0,则符号的大小必须大于 0 is_exported = is_exported && (value() != 0 || (value() == 0 && size() > 0)); // 导出符号必须绑定到 GLOBAL 或 WEAK is_exported = is_exported && (binding() == BINDING::GLOBAL || binding() == BINDING::WEAK); // 导出符号必须是以下类型之一 is_exported = is_exported && (type() == TYPE::FUNC || type() == TYPE::GNU_IFUNC || type() == TYPE::OBJECT); return is_exported; }
      符号的大小
      int global_array[10]; // 对象类型符号,大小为 sizeof(int) * 10,即 40 字节 struct Point { int x, y; }; struct Point global_point; // 对象类型符号,大小为 sizeof(int) * 2,即 8 字节 如果是函数就是符号大小就是函数的长度
      结构体
      下面是IDA中看到的符号表
      notion image
      它对应的结构体如下
      // toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\linux\elf.h // https://cs.android.com/android-llvm/toolchain/llvm-project/+/master:llvm/include/llvm/BinaryFormat/ELF.h;l=1002?q=Elf64_Sym typedef struct { 2 Elf64_Word st_name; // 符号名称的字符串表偏移 3 unsigned char st_info; // 符号类型和绑定信息 4 unsigned char st_other; // 保留,为将来使用 5 Elf64_Half st_shndx; // 符号所在的节(Section)索引 6 Elf64_Addr st_value; // 符号的值(如地址) 7 Elf64_Xword st_size; // 符号的大小(以字节为单位) 8} Elf64_Sym; // Elf64_Sym = 4 + 1 + 1 + 2 + 8 + 8 = 24 (3*8) typedef struct elf32_sym { Elf32_Word st_name; Elf32_Addr st_value; Elf32_Word st_size; unsigned char st_info; unsigned char st_other; Elf32_Half st_shndx; } Elf32_Sym; // st_info 解释如下(包含一下两个信息) #define ELF64_ST_BIND(info) ((info) >> 4) // 获取绑定信息 #define ELF64_ST_TYPE(info) ((info) & 0xf) // 获取符号类型信息 // 1.绑定信息(Binding Information) // 绑定信息表示符号的绑定属性,指示符号如何与其定义关联。在 st_info 字节的低 4 位中定义了绑定信息,常见的绑定属性包括: // STB_LOCAL:局部符号,只在当前对象文件或共享库中可见,对于其他模块不可见。 // STB_GLOBAL:全局符号,对于所有模块都可见。例如,公共函数和全局变量通常是全局符号。 // STB_WEAK:弱符号,具有全局可见性,但其定义可以被同名的强符号覆盖。 // 2.符号类型信息(Symbol Type Information) // 符号类型信息定义了符号的具体类型,即符号所代表的实体类型。在 st_info 字节的高 4 位中定义了符号类型信息,常见的符号类型包括: // STT_NOTYPE:未定义的符号类型,通常用于符号表中的占位符或特殊标记。 // STT_OBJECT:对象符号,代表一个数据对象,如变量或数组。 // STT_FUNC:函数符号,代表一个函数或代码段的入口点。 // STT_SECTION:节符号,代表一个节(section)的位置或大小。 // STT_FILE:文件符号,用于标识源文件名称。
 
  • --relocs Display the relocation entries in the file --dyn-relocations Display the dynamic relocation entries in the file --relocations Alias for --relocs -r Alias for --relocs ( 重定位表 )( IDA视图 )
    • 查看重定位表
      > llvm-readelf -r "libemulator_check.so" Relocation section '.rela.dyn' at offset 0x800 contains 10 entries: Offset Info Type Symbol's Value Symbol's Name + Addend 0000000000002d68 0000000000000403 R_AARCH64_RELATIVE ab0 0000000000002d70 0000000000000403 R_AARCH64_RELATIVE aa0 0000000000002d78 0000000000000403 R_AARCH64_RELATIVE 2d78 0000000000003008 0000000000000403 R_AARCH64_RELATIVE 1035 0000000000003010 0000000000000403 R_AARCH64_RELATIVE 103d 0000000000002fe0 0000001800000401 R_AARCH64_GLOB_DAT 0000000000003028 asmcheck + 0 0000000000002fe8 0000000f00000401 R_AARCH64_GLOB_DAT 0000000000003000 a + 0 0000000000002ff0 0000001100000401 R_AARCH64_GLOB_DAT 0000000000000adc my_sigaction + 0 0000000000002ff8 0000001a00000401 R_AARCH64_GLOB_DAT 0000000000003030 old_handlers + 0 0000000000003018 0000001900000101 R_AARCH64_ABS64 0000000000000c24 detect + 0 Relocation section '.rela.plt' at offset 0x8f0 contains 10 entries: Offset Info Type Symbol's Value Symbol's Name + Addend 0000000000002f88 0000000300000402 R_AARCH64_JUMP_SLOT 0000000000000000 __cxa_finalize@LIBC + 0 0000000000002f90 0000000400000402 R_AARCH64_JUMP_SLOT 0000000000000000 __stack_chk_fail@LIBC + 0 0000000000002f98 0000000500000402 R_AARCH64_JUMP_SLOT 0000000000000000 __android_log_print + 0 0000000000002fa0 0000000600000402 R_AARCH64_JUMP_SLOT 0000000000000000 sigaction@LIBC + 0 0000000000002fa8 0000000700000402 R_AARCH64_JUMP_SLOT 0000000000000000 mmap@LIBC + 0 0000000000002fb0 0000000800000402 R_AARCH64_JUMP_SLOT 0000000000000000 getpagesize@LIBC + 0 0000000000002fb8 0000000900000402 R_AARCH64_JUMP_SLOT 0000000000000000 exit@LIBC + 0 0000000000002fc0 0000000a00000402 R_AARCH64_JUMP_SLOT 0000000000000000 munmap@LIBC + 0 0000000000002fc8 0000000b00000402 R_AARCH64_JUMP_SLOT 0000000000000000 fopen@LIBC + 0 0000000000002fd0 0000000c00000402 R_AARCH64_JUMP_SLOT 0000000000000000 __cxa_atexit@LIBC + 0
      💡
      这里额外说一下 重定位表其实分为两个部分
      • 基址重定位(Base Relocation)— 0x403: 用于调整绝对地址,主要用于动态库加载 ( rela )
      • 符号重定位(Symbol Relocation)— 0x402: 用于解析符号表中的符号地址 ( jumprela,写got表 , plt过程调用)
      • 地址重定位(Address Relocation)— 0x401: 用于相对地址的调整,通常用于相对地址跳转或加载
      IDA视图如下
      notion image
 
  • -unwind Display unwind information -u Alias for --unwind
    • 异常处理(unwind)信息 ( 用于栈回溯 [ libunwind.so ] )
      > llvm-readelf --unwind "libemulator_check.so" // 异常处理的表头信息,包含指向详细的 .eh_frame 段的入口 EHFrameHeader { Address: 0x1118 // 在内存中的地址 Offset: 0x1118 // 文件中的偏移值 Size: 0x44 // 该段的大小 Corresponding Section: .eh_frame_hdr // 所对应的节名称 Header { // 表头信息,包括各类编码及指针 version: 1 // 版本号 eh_frame_ptr_enc: 0x1b // 指向 .eh_frame 段的指针的编码方式 fde_count_enc: 0x3 // FDE (帧描述条目) 的计数的编码方式 table_enc: 0x3b // 表格的编码方式 eh_frame_ptr: 0x1160 // .eh_frame 段的开始地址 fde_count: 7 // FDE 结构的数量 entry 0 { initial_location: 0xadc // FDE 入口初始地址 ( 函数入口地址 ) address: 0x1178 // 对应的地址 } entry 1 { initial_location: 0xb08 // FDE 入口初始地址 address: 0x1198 // 对应的地址 } entry 2 { initial_location: 0xbb8 // FDE 入口初始地址 address: 0x11b8 // 对应的地址 } entry 3 { initial_location: 0xc24 // FDE 入口初始地址 address: 0x11d8 // 对应的地址 } entry 4 { initial_location: 0xe74 // FDE 入口初始地址 address: 0x11f8 // 对应的地址 } entry 5 { initial_location: 0xf34 // FDE 入口初始地址 address: 0x1230 // 对应的地址 } entry 6 { initial_location: 0xf38 // FDE 入口初始地址 address: 0x1248 // 对应的地址 } } } // .eh_frame 段的详细信息,其中包含 CIE 和 FDE 结构 .eh_frame section at offset 0x1160 address 0x1160: // 表示该段在文件中的偏移和地址 [0x1160] CIE length=20 // CIE 段起始位置及长度 version: 1 // 版本号 augmentation: zR // 额外信息描述 code_alignment_factor: 1 // 指令地址对齐因子 data_alignment_factor: -4 // 数据地址对齐因子 return_address_register: 30 // 用于存储返回地址的寄存器编号 Program: // 异常处理指令 DW_CFA_def_cfa: reg31 +0 // 定义 CFA (Canonical Frame Address) 为寄存器 31 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 [0x1178] FDE length=28 cie=[0x1160] // FDE 段起始位置及长度,引用 CIE 的偏移 initial_location: 0xadc // 指令块的初始地址 address_range: 0x2c (end : 0xb08) // 指令块的地址范围 Program: // 堆栈展开指令 DW_CFA_advance_loc: 8 // 前进指令位置 8 DW_CFA_def_cfa: reg29 +16 // 定义 CFA 为寄存器 29 + 16 DW_CFA_offset: reg30 -8 // 寄存器 30 偏移 -8 存储 DW_CFA_offset: reg29 -16 // 寄存器 29 偏移 -16 存储 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 DW_CFA_nop: // 无操作,占位符 ......
 

 
➡️
.hash (使用的是 SysV 哈希函数) hash 是最早使用的符号哈希表格式。GNU 的 ld 链接器默认生成 .hash 段,所有兼容的 ELF 解析工具都能处理它。 结构: 基于简单的哈希桶,查找时间复杂度较高(最坏情况下 O(n))
➡️
.gnu.hash (使用的是 GNU 哈希函数) .gnu.hash 是 GNU 动态链接器引入的一种改进的哈希表格式。它可以显著提高大型程序的符号查找性能。 结构: 使用了一种更复杂的哈希机制,通过位图和索引表进一步加快查找速度,查找时间复杂度接近 O(1)
 
  • --gnu-hash-table Display .gnu.hash section
    • GNU 哈希表
      > llvm-readelf --gnu-hash-table "libemulator_check.so" GnuHashTable { Num Buckets: 17 First Hashed Symbol Index: 13 Num Mask Words: 2 Shift Count: 7 Bloom Filter: [0x88207010495020E1, 0x40090C41008488] Buckets: [13, 0, 14, 15, 17, 0, 20, 22, 23, 25, 26, 0, 0, 27, 28, 29, 0] Values: [0xE834AF01, 0x943C5477, 0x2B606, 0x1C5871D9, 0xD619DB6A, 0x7997EF58, 0x7C92E3BB, 0x149E2C16, 0x8E8223E3, 0xECD54543, 0x37231686, 0x9A8AC6A5, 0xF887E55F, 0xD108D715, 0x1B57DABF, 0x7C9A2D85, 0xF05FC7C3] }
 
  • --hash-table Display .hash section
    • SysV 哈希表
      > llvm-readelf --hash-table "libemulator_check.so" HashTable { Num Buckets: 17 Num Chains: 30 Buckets: [0, 22, 29, 25, 0, 27, 28, 20, 5, 0, 11, 16, 4, 19, 12, 7, 0] Chains: [0, 0, 0, 23, 15, 24, 18, 0, 13, 0, 26, 14, 6, 0, 0, 0, 21, 0, 0, 9, 0, 17, 3, 0, 0, 0, 0, 10, 0, 8] }
 
  • --hash-symbols Display the dynamic symbols derived from the hash section
    • 哈希函数分布对应的符号
      > llvm-readelf --hash-symbols "libemulator_check.so" Symbol table of .hash for image: Num Buc: Value Size Type Bind Vis Ndx Name 22 1: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS _edata 3 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize@LIBC 23 1: 0000000000000fb4 20 OBJECT GLOBAL DEFAULT 12 handledSignals 29 2: 0000000000000bb8 108 FUNC GLOBAL DEFAULT 11 getArch 8 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getpagesize@LIBC 13 2: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__ 25 3: 0000000000000c24 592 FUNC GLOBAL DEFAULT 11 detect 27 5: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS __bss_end__ 10 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND munmap@LIBC 26 5: 0000000000003030 160 OBJECT GLOBAL DEFAULT 21 old_handlers 28 6: 0000000000000b08 176 FUNC GLOBAL DEFAULT 11 load 20 7: 0000000000000fc8 4 OBJECT GLOBAL DEFAULT 12 handledSignalsNum 5 8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __android_log_print 24 8: 0000000000003028 8 OBJECT GLOBAL DEFAULT 21 asmcheck 11 10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fopen@LIBC 14 10: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS __bss_start__ 16 11: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS __bss_start 21 11: 0000000000000e74 192 FUNC GLOBAL DEFAULT 11 JNI_OnLoad 17 11: 0000000000000adc 44 FUNC GLOBAL DEFAULT 11 my_sigaction 4 12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@LIBC 15 12: 0000000000003000 4 OBJECT GLOBAL DEFAULT 20 a 19 13: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS _end 9 13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@LIBC 12 14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@LIBC 6 14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sigaction@LIBC 18 14: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS __end__ 7 15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND mmap@LIBC Symbol table of .gnu.hash for image: Num Buc: Value Size Type Bind Vis Ndx Name 13 0: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__ 14 2: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS __bss_start__ 15 3: 0000000000003000 4 OBJECT GLOBAL DEFAULT 20 a 16 3: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS __bss_start 17 4: 0000000000000adc 44 FUNC GLOBAL DEFAULT 11 my_sigaction 18 4: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS __end__ 19 4: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS _end 20 6: 0000000000000fc8 4 OBJECT GLOBAL DEFAULT 12 handledSignalsNum 21 6: 0000000000000e74 192 FUNC GLOBAL DEFAULT 11 JNI_OnLoad 22 7: 0000000000003020 0 NOTYPE GLOBAL DEFAULT ABS _edata 23 8: 0000000000000fb4 20 OBJECT GLOBAL DEFAULT 12 handledSignals 24 8: 0000000000003028 8 OBJECT GLOBAL DEFAULT 21 asmcheck 25 9: 0000000000000c24 592 FUNC GLOBAL DEFAULT 11 detect 26 10: 0000000000003030 160 OBJECT GLOBAL DEFAULT 21 old_handlers 27 13: 00000000000030d0 0 NOTYPE GLOBAL DEFAULT ABS __bss_end__ 28 14: 0000000000000b08 176 FUNC GLOBAL DEFAULT 11 load 29 15: 0000000000000bb8 108 FUNC GLOBAL DEFAULT 11 getArch
       

       
在xdl和linker源码中都有使用到
 

 
  • -x <name or index> Alias for --hex-dump
    • 展示对应段的hex值
      > llvm-readelf -x .dynstr "libemulator_check.so" Hex dump of section '.dynstr': 0x00000668 005f5f63 78615f66 696e616c 697a6500 .__cxa_finalize. 0x00000678 5f5f6378 615f6174 65786974 004a4e49 __cxa_atexit.JNI 0x00000688 5f4f6e4c 6f616400 5f5f616e 64726f69 _OnLoad.__androi 0x00000698 645f6c6f 675f7072 696e7400 5f5f7374 d_log_print.__st 0x000006a8 61636b5f 63686b5f 6661696c 0061736d ack_chk_fail.asm 0x000006b8 63686563 6b006465 74656374 00666f70 check.detect.fop 0x000006c8 656e0067 65744172 63680067 65747061 en.getArch.getpa 0x000006d8 67657369 7a650068 616e646c 65645369 gesize.handledSi 0x000006e8 676e616c 73006861 6e646c65 64536967 gnals.handledSig 0x000006f8 6e616c73 4e756d00 6c6f6164 006d6d61 nalsNum.load.mma 0x00000708 70006d75 6e6d6170 006d795f 73696761 p.munmap.my_siga 0x00000718 6374696f 6e006f6c 645f6861 6e646c65 ction.old_handle 0x00000728 7273006c 69626c6f 672e736f 006c6962 rs.liblog.so.lib 0x00000738 6d2e736f 006c6962 646c2e73 6f006c69 m.so.libdl.so.li 0x00000748 62632e73 6f005f65 64617461 005f5f62 bc.so._edata.__b 0x00000758 73735f73 74617274 005f5f62 73735f73 ss_start.__bss_s 0x00000768 74617274 5f5f005f 5f627373 5f656e64 tart__.__bss_end 0x00000778 5f5f005f 5f656e64 5f5f005f 656e6400 __.__end__._end. 0x00000788 6c696265 6d756c61 746f725f 63686563 libemulator_chec 0x00000798 6b2e736f 004c4942 4300 k.so.LIBC.
 

 
 
。。。