0
点赞
收藏
分享

微信扫一扫

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响

米小格儿 2023-01-05 阅读 98


一、在线编码查看

​​查看UTF-8,UTF-16编码​​

​​汉字字符集编码查询​​

二、ELF头

ELF是一种用于​​二进制文件​​​、​​可执行文件​​​、​​目标代码​​、共享库和核心转储格式文件,它由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。实际上,一个文件中不一定包含全部内容,而且它们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。接下来我们将通过代码来具体进行分析。

1、源码

我们将以如下一段代码来分析elf的格式

#include <stdio.h>

int global_init_var = 84;
int global_uninit_var;

void func1(int i) {
printf("%d\n", i);
}

int main(void) {
static int static_var = 85;
static int static_var2;
int a = 1;
int b;

func1(static_var + static_var2 + a + b);

return 0;
}

编译:

# 首先生成a.o文件
gcc -c a.c

# 生成可执行文件
gcc -o a a.c

# 执行
./a
86

2、分析

2.1 ELF头结构

我们知道ELF文件最开始部分为ELF的头,其64位的结构如下:

/* Type for a 16-bit quantity.  */
typedef uint16_t Elf64_Half;

/* Types for signed and unsigned 32-bit quantities. */
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;

/* Types for signed and unsigned 64-bit quantities. */
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;

/* Type of addresses. */
typedef uint64_t Elf64_Addr;

/* Type of file offsets. */
typedef uint64_t Elf64_Off;

/* Type for section indices, which are 16-bit quantities. */
typedef uint16_t Elf64_Section;

/* Type for version symbol information. */
typedef Elf64_Half Elf64_Versym;

#define EI_NIDENT (16)

typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info, 16bytes */
Elf64_Half e_type; /* Object file type,2bytes */
Elf64_Half e_machine; /* Architecture, 2bytes */
Elf64_Word e_version; /* Object file version, 4bytes */
Elf64_Addr e_entry; /* Entry point virtual address,8bytes */
Elf64_Off e_phoff; /* Program header table file offset,8bytes */
Elf64_Off e_shoff; /* Section header table file offset,8bytes */
Elf64_Word e_flags; /* Processor-specific flags,4bytes */
Elf64_Half e_ehsize; /* ELF header size in bytes,2bytes */
Elf64_Half e_phentsize; /* Program header table entry size,2bytes */
Elf64_Half e_phnum; /* Program header table entry count,2bytes */
Elf64_Half e_shentsize; /* Section header table entry size,2bytes */
Elf64_Half e_shnum; /* Section header table entry count,2bytes */
Elf64_Half e_shstrndx; /* Section header string table index,2bytes */
} Elf64_Ehdr;

// 共64字节

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU

2.2 readelf命令输出

首先我们通过readelf把ELF的头文件读取出来

readelf -h a.o

ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
Version: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: REL (可重定位文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x0
程序头起点: 0 (bytes into file)
Start of section headers: 1176 (bytes into file)
标志: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 14
Section header string table index: 13

从以上信息可知,其含有14个section header,从文件1176字节处开始,每个section header大小为64字节。

2.3 二进制ELF头分析

我们根据上述结构对二进制文件进行解析,可得到如下图所示:

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU_02

从图上得知,其确实能和readelf命令输出的头信息及段信息对应上。

三、ELF段

3.1 段结构

typedef struct
{
Elf64_Word sh_name; /* Section name (string tbl index),4bytes */
Elf64_Word sh_type; /* Section type,4bytes */
Elf64_Xword sh_flags; /* Section flags,8bytes */
Elf64_Addr sh_addr; /* Section virtual addr at execution,8bytes */
Elf64_Off sh_offset; /* Section file offset,8byts */
Elf64_Xword sh_size; /* Section size in bytes,8bytes */
Elf64_Word sh_link; /* Link to another section,4bytes */
Elf64_Word sh_info; /* Additional section information,4bytes */
Elf64_Xword sh_addralign; /* Section alignment,8bytes */
Elf64_Xword sh_entsize; /* Entry size if section holds table,8bytes */
} Elf64_Shdr;

// 共 8+8+8+8+8+8+8+8=64字节

/* Legal values for sh_type (section type).  */

#define SHT_NULL 0 /* Section header table entry unused */
#define SHT_PROGBITS 1 /* Program data */
#define SHT_SYMTAB 2 /* Symbol table */
#define SHT_STRTAB 3 /* String table */
#define SHT_RELA 4 /* Relocation entries with addends */
#define SHT_HASH 5 /* Symbol hash table */
#define SHT_DYNAMIC 6 /* Dynamic linking information */
#define SHT_NOTE 7 /* Notes */
#define SHT_NOBITS 8 /* Program space with no data (bss) */
#define SHT_REL 9 /* Relocation entries, no addends */
#define SHT_SHLIB 10 /* Reserved */
#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
#define SHT_INIT_ARRAY 14 /* Array of constructors */
#define SHT_FINI_ARRAY 15 /* Array of destructors */
#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
#define SHT_GROUP 17 /* Section group */
#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
#define SHT_NUM 19 /* Number of defined types. */
#define SHT_LOOS 0x60000000 /* Start OS-specific. */
#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */
#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
#define SHT_SUNW_move 0x6ffffffa
#define SHT_SUNW_COMDAT 0x6ffffffb
#define SHT_SUNW_syminfo 0x6ffffffc
#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
#define SHT_HIOS 0x6fffffff /* End OS-specific type */
#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
#define SHT_LOUSER 0x80000000 /* Start of application-specific */
#define SHT_HIUSER 0x8fffffff /* End of application-specific */

/* Legal values for sh_flags (section flags). */

#define SHF_WRITE (1 << 0) /* Writable */
#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
#define SHF_EXECINSTR (1 << 2) /* Executable */
#define SHF_MERGE (1 << 4) /* Might be merged */
#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
required */
#define SHF_GROUP (1 << 9) /* Section is member of a group. */
#define SHF_TLS (1 << 10) /* Section hold thread-local data. */
#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */
#define SHF_MASKOS 0x0ff00000 /* OS-specific. */
#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
#define SHF_ORDERED (1 << 30) /* Special ordering requirement
(Solaris). */
#define SHF_EXCLUDE (1U << 31) /* Section is excluded unless
referenced or allocated (Solaris).*/

3.2 命令行段输出

从上面信息可知,该文件共有14个段(e_shnum),从文件0x498(e_shoff,十进制1176)处开始,每个section header大小为64(e_shentsize)字节。接下来我们分析下段信息:

首先我们使用readelf把其段描述读取出来:

readelf -S a.o
There are 14 section headers, starting at offset 0x498:

节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
0000000000000061 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 00000378
0000000000000078 0000000000000018 I 11 1 8
[ 3] .data PROGBITS 0000000000000000 000000a4
0000000000000008 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 000000ac
0000000000000004 0000000000000000 WA 0 0 4
[ 5] .rodata PROGBITS 0000000000000000 000000ac
0000000000000004 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 000000b0
000000000000002b 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 000000db
0000000000000000 0000000000000000 0 0 1
[ 8] .note.gnu.propert NOTE 0000000000000000 000000e0
0000000000000020 0000000000000000 A 0 0 8
[ 9] .eh_frame PROGBITS 0000000000000000 00000100
0000000000000058 0000000000000000 A 0 0 8
[10] .rela.eh_frame RELA 0000000000000000 000003f0
0000000000000030 0000000000000018 I 11 9 8
[11] .symtab SYMTAB 0000000000000000 00000158
00000000000001b0 0000000000000018 12 12 8
[12] .strtab STRTAB 0000000000000000 00000308
0000000000000070 0000000000000000 0 0 1
[13] .shstrtab STRTAB 0000000000000000 00000420
0000000000000074 0000000000000000 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),
l (large), p (processor specific)

.strtab(string table):字符串表。

.shstrtab(section header string table):段表字符串表。

.text:代码段

其输出了14个段信息,并显示了段的详细信息。我们看下文件0x498处是否能和以上信息对应上:

3.3 二进制段分析

上面我们有讲到,该文件共有14个段(e_shnum),从文件0x498(e_shoff,十进制1176)处开始,每个section header大小为64(e_shentsize)字节,到0x818处截止。

从Elf64_Shdr结构可知,sh_name指向.shstrtab中的序号,所以我们先分析.shstrtab段,其在文件中起始位置为:0x498+64*13=0x7d8。

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU_03

我们再看下.text字段,其在文件中起始位置为:0x498+64*1=0x4d8(段开始位置为0x498,每个section header大小为64)。

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU_04

我们再看下.strtab段(字符串段),其在文件中起始位置为:0x498+64*12=0x798(段开始位置为0x498,每个section header大小为64)。

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU_05

0x308开始的0x70字节如下:

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_#define_06

 

四、符号表

4.1 符号表结构

/* 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;

/* The syminfo section if available contains additional information about
every dynamic symbol. */

typedef struct
{
Elf64_Half si_boundto; /* Direct bindings, symbol bound to */
Elf64_Half si_flags; /* Per symbol flags */
} Elf64_Syminfo;

/* Possible values for si_boundto. */
#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */
#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */
#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */

/* Possible bitmasks for si_flags. */
#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */
#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */
#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */
#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy
loaded */
/* Syminfo version values. */
#define SYMINFO_NONE 0
#define SYMINFO_CURRENT 1
#define SYMINFO_NUM 2

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU_07

 

4.2 命令行段输出

readelf -s a.o

Symbol table '.symtab' contains 18 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.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: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 static_var.2321
7: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 static_var2.2322
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 9
11: 0000000000000000 0 SECTION LOCAL DEFAULT 6
12: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_init_var
13: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM global_uninit_var
14: 0000000000000000 40 FUNC GLOBAL DEFAULT 1 func1
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
17: 0000000000000028 57 FUNC GLOBAL DEFAULT 1 main

 

4.3 二进制段分析

.symtab段(符号表),其在文件中起始位置为:0x498+64*11=0x758(段开始位置为0x498,每个section header大小为64)

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_Word_08

从上图可知,其共有0x1b0字节,每个域大小为0x18,所以有18个符号,与命令行输出的18个符号对应上了。

0x158开始的0x1b0字节如下:

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_Word_09

五、重定位表

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;

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_Word_10

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)

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_#define_11

5.3 二进制段分析

0x378开始的0x78字节如下:

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_Word_12

从上面分析可知,重定位主要描述了哪些字段需要重定位。

六、动态链接

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 */

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_Word_13

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字节。

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_#define_14

(2)Lib.so的段其实地址为:0x37F8,.dynamic 段序号为【21】,每个段大小为64字节,故.dynamic在文件中偏移地址为:0x37F8 + 64 * 21 = 0x3D38 处。

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_#define_15

0x2e20开始的0x1c0字节如下:

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_#define_16

从上面可知,其共有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 处。

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_Word_17

查看可得知其在文件中的偏移地址为: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 二进制分析

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU_18

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

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU_19

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 二进制分析

程序员的自我修养:链接、装载与库阅读字符编码方式对ELF显示的影响_GNU_20

 

八、参考

​​字符编码方式对ELF显示的影响​​

举报

相关推荐

0 条评论