典型的可重定向文件结构
- .text : 代码段,包含函数的指令
- .data : 数据段,保存已经初始化的全局静态变量和局部静态变量
- .rodata : 只读数据,只读变量(const 修饰的变量)和字符串常量
- .bss : 未初始化的全局变量和局部静态变量
以下面这段代码为例:
/*
* SimpleSection.c
* gcc -c SimpleSection.c
*/
int printf(const char *format, ...);
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;
}
gcc -c SimpleSection.c -o SimpleSection.o
ELF Header
typedef struct elf64_hdr {
unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 0100 3e00 0100 0000 :.ELF..............>.....
00000018: 0000 0000 0000 0000 0000 0000 0000 0000 0804 0000 0000 0000 :........................
00000030: 0000 0000 4000 0000 0000 4000 0e00 0d00
| 字段 | 占位(字节) | 示意 | HEX |
|---|---|---|---|
| e_ident | 16 | Magic… | 7f45 4c46 0201 0100 0000 0000 0000 000 |
| e_type | 2 | ELF 文件类型 - 1 : REL -2 : EXEC -3 : DYN | 0001 |
| e_machine | 2 | CPU 平台属性 | 003e |
| e_version | 4 | ELF 版本号 | 0000 0001 |
| e_entry | 8 | 入口地址 | 0000 0000 0000 0000 |
| e_phoff | 8 | start of program header | 0000 0000 0000 0000 |
| e_shoff | 8 | start of section header | 0000 0000 0000 0408 |
| e_flags | 4 | 标志位 | 0000 0000 |
| e_ehsize | 2 | size of header | 0040(64) |
| e_phentsize | 2 | size of program header | 0000 |
| e_phnum | 2 | number of program header | 0000 |
| e_shentsize | 2 | size of section header | 0040 |
| e_shnum | 2 | number of section header | 000e(14) |
| e_shstrndx | 2 | section header string table index (段表字符串表) | 000d(13) |
使用 readelf 查看
readelf -h SimpleSection.o
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: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 1032 (bytes into file)
Flags: 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
通过上面的信息,可以得出该 .o 文件的结构大致如下:
Section header table(SHT)
Section Header
typedef struct elf64_shdr {
Elf64_Word sh_name; /* Section name, index in string tbl */
Elf64_Word sh_type; /* Type of section */
Elf64_Xword sh_flags; /* Miscellaneous section attributes */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Size of section in bytes */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
以 .text 为例(其它的 section 同理):
2000 0000 0100 0000 0600 0000 0000 0000
0000 0000 0000 0000 4000 0000 0000 0000
6400 0000 0000 0000 0000 0000 0000 0000
0100 0000 0000 0000 0000 0000 0000 0000
| 字段 | 占位(字节) | 示意 | HEX |
|---|---|---|---|
| sh_name | 4 | section name,它位于 “.shstrtab” 的字符串表,sh_name 是段名字符串在 “.shstrtab” 中的偏移 | 0000 0020 |
| sh_type | 4 | 类型 - 0:SHT_NULL - 1:SHT_PROGBITS - 2:SHT_SYMTAB 符号表 - 3:SHT_STRTAB 字符串表 - 4:SHT_RELA 重定位表 - 5:SHT_HASH - 6:SHT_DYNAMIC 动态链接信息 - 7:SHT_NOTE - 8:SHT_NOBITS - 9:SHT_REL 重定位信息 - 10:SHT_SHLIB - 11:SHT_DNYSYM 动态链接符号表 | 0000 0001 |
| sh_flags | 8 | 标志位 | 0000 0000 0000 0006 |
| sh_addr | 8 | 段虚拟地址。如果该段可以被加载,则表示该段被加载后在进程地址空间的虚拟地址。 | 0000 0000 0000 0000 |
| sh_offset | 8 | 该段在文件中的偏移 | 0000 0000 0000 0040(64) |
| sh_size | 8 | 段的长度 | 0000 0000 0000 0064(100) |
| sh_link | 4 | 0000 0000 | |
| sh_info | 4 | 0000 0000 | |
| sh_addralign | 8 | 段地址对齐 | 0000 0000 0000 0001 |
| sh_entsize | 8 | section entry size:项的长度 | 0000 0000 0000 0000 |
readelf -SW SimpleSection.o
There are 14 section headers, starting at offset 0x408:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000000000 000040 000064 00 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 0002e8 000078 18 I 11 1 8
[ 3] .data PROGBITS 0000000000000000 0000a4 000008 00 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 0000ac 000008 00 WA 0 0 4
[ 5] .rodata PROGBITS 0000000000000000 0000ac 000004 00 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 0000b0 000027 01 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 0000d7 000000 00 0 0 1
[ 8] .note.gnu.property NOTE 0000000000000000 0000d8 000020 00 A 0 0 8
[ 9] .eh_frame PROGBITS 0000000000000000 0000f8 000058 00 A 0 0 8
[10] .rela.eh_frame RELA 0000000000000000 000360 000030 18 I 11 9 8
[11] .symtab SYMTAB 0000000000000000 000150 000138 18 12 8 8
[12] .strtab STRTAB 0000000000000000 000288 000060 00 0 0 1
[13] .shstrtab STRTAB 0000000000000000 000390 000074 00 0 0 1
Symtab 符号表
这个表里记录目标文件中所有用到的所有符号,每个符号有一个对应的符号值(Symbol Value),对于变量和函数来说,符号值就是它们的地址。
typedef struct elf64_sym {
Elf64_Word st_name; /* Symbol name, index in string tbl */
unsigned char st_info; /* Type and binding attributes */
unsigned char st_other; /* No defined meaning, 0 */
Elf64_Half st_shndx; /* Associated section index */
Elf64_Addr st_value; /* Value of the symbol */
Elf64_Xword st_size; /* Associated symbol size */
} Elf64_Sym;
以 func1 为例*:*
.symtab 在 SHT[11] 位置,它在文件的偏移量为 0x150
func1 **在 .symtab[10] 位置
4e00 0000 1200 0100 0000 0000 0000 0000 2b00 0000 0000 0000
| 字段 | 占位(字节) | 示意 | HEX |
|---|---|---|---|
| st_name | 4 | 符号名。在符号表中的偏移 | 004e 0000(func1) |
| st_info | 1 | 符号类型和绑定信息 | 12 |
| st_other | 1 | - | 00 |
| st_shndx | 2 | 符号所在段 | 0001 |
| st_value | 8 | 符号相对应的值 | 0000 0000 0000 0000 |
| st_size | 8 | 符号大小 | 0000 0000 0000 00b2 |
st_name = strtab + 4e = 0x288 + 0x4e = 0x2D6
= { 6675 6e63 3100 }
转换成 = func1
符号值(st_value)
- 在目标文件中,该符号定义不是“COMMON”块,则 st_value 表示该符号在段中的偏移。即符号所对应的函数或者变量位于由 st_shndx 指定的段,偏移 st_value 的位置。
- 在目标文件中,该符号是“COMMON”块,则 st_value 表示该符号的对齐属性。
- 在可执行文件中,st_value 表示符号的虚拟地址。
readelf -s SimpleSection.o
Symbol table '.symtab' contains 13 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS SimpleSection.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 .data
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 .bss
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata
6: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 static_var.1
7: 0000000000000004 4 OBJECT LOCAL DEFAULT 4 static_var2.0
8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_init_var
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 4 global_uninit_var
10: 0000000000000000 43 FUNC GLOBAL DEFAULT 1 func1
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
12: 000000000000002b 57 FUNC GLOBAL DEFAULT 1 main