图片引用《程序员自我修养》一书中
下图是整个ELF的结构:
起始是ELF的头文件:
具体头文件内容见图2
图1. ELF文件的结构
图2. ELF头文件内容<这个结构定义在Elf32_Ehdr或者是64的版本中>
在Elf头文件的一些属性解释:
e_entery :入口地址,如果是可重定向文件的话该值为0
e_shoff (start of section header):这个属性很重要,是段表的偏移
e_shstrndx (section header string table index):段表字符串表的段在段表中的偏移位置
段表结构:
图3 每个段的信息
图4 段的属性
图5 段描述符
属性说明:
sh_name: 段名称,段名实际上是在字符串表中的 (.shstrtab)
sh_type:段属性 见书(p77)
sh_flag:段标志位,表示该段在进程虚拟空间中的属性,有SHF_WRITE(可写),SHF_ALLOC(在进程空间需要分配空间,有些指示或者控制的段不会有这个属性,但是代码段,数据段,.bss等都会有这个属性)
SHF_EXECINSTR(可执行,一般指代码段)
sh_link,sh_info:段的链接信息具体见下图
图6 链接信息
重定位表:
如果某个段的sh_type属性是SHT_REL,说明这个是一个重定位表,比如.rel.text 说明是针对.text段的重定位,.text段中至少有一个地方需要重定位,
如果.data中也有需要重定位的, 就会有.rel.data ,同时如果sh_type属性是SHT_REL的时候,sh_link表示符号表的下标,sh_info表示对那个段作用
如果某个段的sh_type属性是SHT_REL,说明这个是一个重定位表,比如.rel.text 说明是针对.text段的重定位,.text段中至少有一个地方需要重定位,
如果.data中也有需要重定位的, 就会有.rel.data ,同时如果sh_type属性是SHT_REL的时候,sh_link表示符号表的下标,sh_info表示对那个段作用
字符串表和段表字符串表:
字符串表方的是普通的字符串, 所有符号表中的符号都是放在这里的,函数名变量名等等,在 符号表结构中有一个是st_name其实用到的是在字符串表中的下标
段表字符串表放的是段名字,最常见的是sh_name每个段的名称,它本身也是一个段,所以e_shstrndx说明的是段表字符串表这个段在段表中的下标
符号表:
字符串表方的是普通的字符串, 所有符号表中的符号都是放在这里的,函数名变量名等等,在 符号表结构中有一个是st_name其实用到的是在字符串表中的下标
段表字符串表放的是段名字,最常见的是sh_name每个段的名称,它本身也是一个段,所以e_shstrndx说明的是段表字符串表这个段在段表中的下标
符号表:
每个函数和变量都有自己特有的名字,在连接中这些名字就是符号
每个符号对应一个符号值,如果是函数和变量的话,符号值就是它们的地址
除了函数和标量外其他的符号:
1.定义在本目标文件中的全局符号,可以被其他目标文件应用,比如程序中的func,main和global变量
2.在本目标中引用的全局符号,但是这个符号没有定义在本目标文件中,比如print符号
3.段名也是一种符号(.text, .data不过这些符号是编译器产生的)
4.局部变量:比如static_var, static_var2这些符号对于链接的时候是没有用的
5.行号信息
这里最重要的时候第一和第二,因为链接过程只关心全局符号之间的协调。
每个符号对应一个符号值,如果是函数和变量的话,符号值就是它们的地址
图7 符号表的属性
属性介绍:
st_name:符号在字符串中的具体下标,所以函数名和全局标量,静态变量名的字符串都是在字符串表中
st_value:
1.在目标文件中,如果不是COMMON块,也即是sh_shndx符号所在段不为SHN_COMMON),st_value表示该符号在段中的偏移,最常见的是初始化好的全局变量符号和函数
2.在目标文件中,如果是COMMON块,表示对齐属性
3.如果是可执行文件,表示符号的虚拟地址,对于动态连接器很有用
1.在目标文件中,如果不是COMMON块,也即是sh_shndx符号所在段不为SHN_COMMON),st_value表示该符号在段中的偏移,最常见的是初始化好的全局变量符号和函数
2.在目标文件中,如果是COMMON块,表示对齐属性
3.如果是可执行文件,表示符号的虚拟地址,对于动态连接器很有用
st_size:符号的大小,如果是int为4个字节,如果是double是8个字节,注意在变量名称修饰中没有把变量的类型属性放进去
其他属性见图8
图8 符号表中字段属性
除了函数和标量外其他的符号:
1.定义在本目标文件中的全局符号,可以被其他目标文件应用,比如程序中的func,main和global变量
2.在本目标中引用的全局符号,但是这个符号没有定义在本目标文件中,比如print符号
3.段名也是一种符号(.text, .data不过这些符号是编译器产生的)
4.局部变量:比如static_var, static_var2这些符号对于链接的时候是没有用的
5.行号信息
这里最重要的时候第一和第二,因为链接过程只关心全局符号之间的协调。
下面是自己打印出来的符号表:
readelf -s simplesection.c
Symbol table '.symtab' contains 69 entries:
Num: Value Size Type Bind Vis Ndx Name
26: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
27: 08049f14 0 OBJECT LOCAL DEFAULT 17 __CTOR_LIST__
28: 08049f1c 0 OBJECT LOCAL DEFAULT 18 __DTOR_LIST__
29: 08049f24 0 OBJECT LOCAL DEFAULT 19 __JCR_LIST__
30: 08048340 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
31: 0804a01c 1 OBJECT LOCAL DEFAULT 24 completed.7065
32: 0804a020 4 OBJECT LOCAL DEFAULT 24 dtor_idx.7067
33: 080483a0 0 FUNC LOCAL DEFAULT 13 frame_dummy
34: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
35: 08049f18 0 OBJECT LOCAL DEFAULT 17 __CTOR_END__
36: 080484ec 0 OBJECT LOCAL DEFAULT 16 __FRAME_END__
37: 08049f24 0 OBJECT LOCAL DEFAULT 19 __JCR_END__
38: 08048490 0 FUNC LOCAL DEFAULT 13 __do_global_ctors_aux
39: 00000000 0 FILE LOCAL DEFAULT ABS simplesection.c
40: 0804a018 4 OBJECT LOCAL DEFAULT 23 static_var.1254 //注意这个名字已经被修饰过了
41: 0804a024 4 OBJECT LOCAL DEFAULT 24 static_var2.1255
42: 08049ff4 0 OBJECT LOCAL DEFAULT 22 _GLOBAL_OFFSET_TABLE_
43: 08049f14 0 NOTYPE LOCAL DEFAULT 17 __init_array_end
44: 08049f14 0 NOTYPE LOCAL DEFAULT 17 __init_array_start
45: 08049f28 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC
46: 0804a00c 0 NOTYPE WEAK DEFAULT 23 data_start
47: 0804a014 4 OBJECT GLOBAL DEFAULT 23 global_init_var //因为这个应用的是本地目标文件中的全局函数,已经初始化23表示该符号所在段在段表中的偏移,应该在.data段中,而.data段的下标是23
48: 08048420 5 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
49: 08048310 0 FUNC GLOBAL DEFAULT 13 _start
50: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
51: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
52: 080484d8 4 OBJECT GLOBAL DEFAULT 15 _fp_hw
53: 080484bc 0 FUNC GLOBAL DEFAULT 14 _fini
54: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
55: 0804a028 4 OBJECT GLOBAL DEFAULT 24 global_uninti_var //因为这个应用的是本地目标文件中的全局函数,未初始化,24表示该符号所在段在段表中的偏移,应该在.bss段中,而.bss段的下标是24
56: 080484dc 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used 57: 0804a00c 0 NOTYPE GLOBAL DEFAULT 23 __data_start
58: 080483c4 27 FUNC GLOBAL DEFAULT 13 func
59: 0804a010 0 OBJECT GLOBAL HIDDEN 23 __dso_handle
60: 08049f20 0 OBJECT GLOBAL HIDDEN 18 __DTOR_END__
61: 08048430 90 FUNC GLOBAL DEFAULT 13 __libc_csu_init
62: 00000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.0 //为定义的print函数
63: 0804a01c 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
64: 0804a02c 0 NOTYPE GLOBAL DEFAULT ABS _end
65: 0804a01c 0 NOTYPE GLOBAL DEFAULT ABS _edata //数据段的结束
66: 0804848a 0 FUNC GLOBAL HIDDEN 13 __i686.get_pc_thunk.bx
67: 080483df 54 FUNC GLOBAL DEFAULT 13 main
68: 08048294 0 FUNC GLOBAL DEFAULT 11 _init
一些特殊的符号:
_start :程序的开始地址,不是入口函数,是最开始的地方
__bss_start:bss段开始的地方
data_start:数据段开始的地方
_edata :数据段结束的地方
这些符号可以再程序中直接使用 ,比如:
#include "stdio.h"
extern char _start[];
extern char data_start[];
int main()
{
printf("start %X\n",_start);
printf("data_start %X\n",data_start);
return 0;
}