ld.so 分析之6 _dl_start_final
1.setup hash
/* This is the second half of _dl_start (below). It can be inlined safely
这是_dl_start的第二部分.
under DONT_USE_BOOTSTRAP_MAP, where it is careful not to make any GOT
在DONT_USE_BOOTSTRAP_MAP下它能被安全内联,在DONT_USE_BOOTSTRAP_MAP下不允许引用GOT
references. When the tools don't permit us to avoid using a GOT entry
for _dl_rtld_global (no attribute_hidden support), we must make sure
this function is not inlined (see below).
当编译器允许我们使用GOT访问_dl_rtld_global,我们必须让f该函数不内联
*/
//#ifdef DONT_USE_BOOTSTRAP_MAP
static inline Elf32_Addr//
//ElfW(Addr) __attribute__ ((always_inline)) 总是内联
_dl_start_final (void *arg)
//#else
//static ElfW(Addr) __attribute__ ((noinline))
//_dl_start_final (void *arg, struct dl_start_final_info *info)
//#endif
{
ElfW(Addr) start_addr;
if (HP_TIMING_AVAIL)// 1
{
/* If it hasn't happen yet record the startup time. */
// if (! HP_TIMING_INLINE)// 1
// HP_TIMING_NOW (start_time);
//#if !defined DONT_USE_BOOTSTRAP_MAP && !defined HP_TIMING_NONAVAIL
// else
// start_time = info->start_time;
//#endif
/* Initialize the timing functions. */
HP_TIMING_DIFF_INIT ();
}
/* Transfer data about ourselves to the permanent link_map structure. */
/*
#ifndef DONT_USE_BOOTSTRAP_MAP
GL(dl_rtld_map).l_addr = info->l.l_addr;
GL(dl_rtld_map).l_ld = info->l.l_ld;
memcpy (GL(dl_rtld_map).l_info, info->l.l_info,
sizeof GL(dl_rtld_map).l_info);
GL(dl_rtld_map).l_mach = info->l.l_mach;
#endif
*/
_dl_setup_hash (&GL(dl_rtld_map));// _dl_setup_hash (&_rtld_local._dl_rtld_map);
GL(dl_rtld_map).l_opencount = 1;
2._dl_start_final->_dl_setup_hash
/* Cache the location of MAP's hash table. */
void
//internal_function
_dl_setup_hash (struct link_map *map)
{
Elf_Symndx *hash;//typedef uint32_t Elf_Symndx;
Elf_Symndx nchain;
if (!map->l_info[DT_HASH])// 例如ld.so的0x00000004 (HASH) 0x94
return;
hash = (void *) D_PTR (map, l_info[DT_HASH]);//map->l_info[DT_HASH]->d_un.d_ptr,取hash表地址
map->l_nbuckets = *hash++;
nchain = *hash++;
map->l_buckets = hash;
hash += map->l_nbuckets;
map->l_chain = hash;
}
ld.so hash表的内容是
[zws@mail ~/glibc-2.3/build/elf]$ objdump -sj .hash ld.so
ld.so: file format elf32-i386
Contents of section .hash:
0094 25000000 2b000000 0d000000 21000000 %...+.......!...
00a4 28000000 00000000 06000000 22000000 (..........."...
00b4 00000000 00000000 00000000 08000000 ................
00c4 1e000000 00000000 1a000000 23000000 ............#...
00d4 26000000 0e000000 1d000000 17000000 &...............
00e4 25000000 24000000 00000000 13000000 %...$...........
00f4 00000000 0b000000 18000000 14000000 ................
0104 27000000 1b000000 00000000 15000000 '...............
0114 00000000 29000000 1c000000 00000000 ....)...........
0124 0c000000 2a000000 19000000 00000000 ....*...........
0134 00000000 00000000 00000000 00000000 ................
0144 00000000 00000000 00000000 00000000 ................
0154 00000000 00000000 00000000 00000000 ................
0164 05000000 02000000 03000000 07000000 ................
0174 00000000 10000000 00000000 00000000 ................
0184 00000000 00000000 00000000 00000000 ................
0194 00000000 00000000 00000000 11000000 ................
01a4 01000000 0a000000 00000000 0f000000 ................
01b4 00000000 09000000 20000000 04000000 ........ .......
01c4 00000000 16000000 12000000 00000000 ................
01d4 00000000 1f000000 ........
hash表的作用是加快链接速度。当在动态链接库中查找是否有需要被外部链接的函数时,
如果直接线性搜索库的动态符号表且表比较大,速度很慢。采用散列的方法查找就比较好。
这里l_nbuckets值为0x25=37,nchain 值为0x2b=43,l_buckets存放散列表入口,l_chain用于将散列值相同的符号连接成单链表。
nchain其实就是动态符号数。该链表中第一个符号索引值A存在l_buckets中,下一个符号的索引值B存放在索引值A在l_chain中的偏移处等等。
问题1.hash表大小是如何计算的?
由binutils 1.18 的bfd/elflink.c文件中compute_bucket_count 计算
/* Array used to determine the number of hash table buckets to use
based on the number of symbols there are. If there are fewer than
定义一个数组用于根据符号数来计算hash表大小
3 symbols we use 1 bucket, fewer than 17 symbols we use 3 buckets,
少于3个符号使用一个桶,少于17个符号使用3个桶
fewer than 37 we use 17 buckets, and so forth. We never use more
少于37个符号使用17个桶,等等
than 32771 buckets.
我们从不使用超过32771个桶的hash
*/
static const size_t elf_buckets[] =
{
1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209,
16411, 32771, 0
};
/* Compute bucket count for hashing table. We do not use a static set
of possible tables sizes anymore. Instead we determine for all
possible reasonable sizes of the table the outcome (i.e., the
number of collisions etc) and choose the best solution. The
weighting functions are not too simple to allow the table to grow
without bounds. Instead one of the weighting factors is the size.
Therefore the result is always a good payoff between few collisions
(= short chain lengths) and table size. */
static size_t
compute_bucket_count (struct bfd_link_info *info, unsigned long int *hashcodes,
unsigned long int nsyms, int gnu_hash)
{
size_t dynsymcount = elf_hash_table (info)->dynsymcount;
size_t best_size = 0;
unsigned long int i;
bfd_size_type amt;
/* We have a problem here. The following code to optimize the table
size requires an integer type with more the 32 bits. If
BFD_HOST_U_64_BIT is set we know about such a type. */
#ifdef BFD_HOST_U_64_BIT
。。。忽略64位系统
#endif /* defined (BFD_HOST_U_64_BIT) * /
{
/* This is the fallback solution if no 64bit type is available or if we
are not supposed to spend much time on optimizations. We select the
bucket count using a fixed set of numbers. */
for (i = 0; elf_buckets[i] != 0; i++)//循环查找elf_buckets数组
{
best_size = elf_buckets[i];//取桶大小
if (nsyms < elf_buckets[i + 1])//如果符号数小于适用的符号数
break;//找到
}
if (gnu_hash && best_size < 2)
best_size = 2;
}
return best_size;
}
由于本例中符号数是43,根据计算得桶大小是37,和前面的桶值相等。
问题2:符号的hash值如何计算?
同样在elflink.c中,由bfd_elf_hash计算
/* Standard ELF hash function. Do not change this function; you will
cause invalid hash tables to be generated. */
unsigned long
bfd_elf_hash (const char *namearg)
{
const unsigned char *name = (const unsigned char *) namearg;
unsigned long h = 0;
unsigned long g;
int ch;
while ((ch = *name++) != '\0')
{
h = (h << 4) + ch;
if ((g = (h & 0xf0000000)) != 0)
{
h ^= g >> 24;
/* The ELF ABI says `h &= ~g', but this is equivalent in
this case and on some machines one insn instead of two. */
h ^= g;
}
}
return h & 0xffffffff;
}
一个符号的hash值%桶大小即得其在hash表中的索引.
问题3.动态符号表的内容是什么?
动态符号表中存放的是全局符号,包括本地和外地。
本地提供给其他模块使用,本地符号的Ndx是数,指出该符号所在的节。外地符号的Ndx是UND,需要动态链接。
例如
readelf -s ld.so
Symbol table '.dynsym' contains 43 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 000126d0 4 OBJECT GLOBAL DEFAULT 15 __libc_internal_tsd_set@@GLIBC_PRIVATE
2: 00012140 980 OBJECT GLOBAL DEFAULT 14 _rtld_global@@GLIBC_PRIVATE
3: 00009f6b 44 FUNC GLOBAL DEFAULT 9 _dl_debug_printf@@GLIBC_PRIVATE
4: 0000a372 1066 FUNC GLOBAL DEFAULT 9 _dl_check_map_versions@@GLIBC_PRIVATE
5: 00006808 757 FUNC GLOBAL DEFAULT 9 _dl_lookup_versioned_symb@@GLIBC_PRIVATE
6: 00000000 0 NOTYPE WEAK DEFAULT UND __pthread_mutex_lock
7: 000126d4 4 OBJECT GLOBAL DEFAULT 15 __libc_stack_end@@GLIBC_PRIVATE
8: 000096e0 307 FUNC GLOBAL DEFAULT 9 _dl_init@@GLIBC_PRIVATE
9: 0000bdc4 220 FUNC WEAK DEFAULT 9 __libc_memalign@@GLIBC_2.0
10: 00000000 0 NOTYPE WEAK DEFAULT UND __pthread_mutex_init
11: 0000bea0 34 FUNC WEAK DEFAULT 9 malloc@@GLIBC_2.0
12: 00006189 605 FUNC GLOBAL DEFAULT 9 _dl_lookup_symbol_skip@@GLIBC_PRIVATE
13: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.1
14: 000063e6 1058 FUNC GLOBAL DEFAULT 9 _dl_lookup_versioned_symb@@GLIBC_PRIVATE
15: 00012514 4 OBJECT GLOBAL DEFAULT 14 __libc_enable_secure@@GLIBC_PRIVATE
16: 00005ec4 709 FUNC GLOBAL DEFAULT 9 _dl_lookup_symbol@@GLIBC_PRIVATE
17: 000126e4 4 OBJECT GLOBAL DEFAULT 15 __libc_internal_tsd_get@@GLIBC_PRIVATE
18: 0000bec2 61 FUNC WEAK DEFAULT 9 calloc@@GLIBC_2.0
19: 00000000 0 NOTYPE WEAK DEFAULT UND __pthread_mutex_unlock
20: 000126ec 4 OBJECT GLOBAL DEFAULT 15 __libc_internal_tsd_addre@@GLIBC_PRIVATE
21: 00009b86 5 FUNC GLOBAL DEFAULT 9 _dl_debug_state@@GLIBC_PRIVATE
22: 00012120 4 OBJECT GLOBAL DEFAULT 14 _dl_argv@@GLIBC_PRIVATE
23: 000030db 272 FUNC GLOBAL DEFAULT 9 _dl_dst_substitute@@GLIBC_PRIVATE
24: 00000000 0 NOTYPE WEAK DEFAULT UND __pthread_mutex_destroy
25: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.0
26: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_PRIVATE
27: 0000bf25 139 FUNC WEAK DEFAULT 9 realloc@@GLIBC_2.0
28: 0000b43c 373 FUNC GLOBAL DEFAULT 9 _dl_get_origin@@GLIBC_PRIVATE
29: 0000a868 1981 FUNC GLOBAL DEFAULT 9 _dl_start_profile@@GLIBC_PRIVATE
30: 00007ca0 1072 FUNC GLOBAL DEFAULT 9 _dl_relocate_object@@GLIBC_PRIVATE
31: 00003056 133 FUNC GLOBAL DEFAULT 9 _dl_dst_count@@GLIBC_PRIVATE
32: 00012124 4 OBJECT GLOBAL DEFAULT 14 _dl_starting_up@@GLIBC_PRIVATE
33: 00005b89 75 FUNC GLOBAL DEFAULT 9 _dl_unload_cache@@GLIBC_PRIVATE
34: 0000f460 14 OBJECT GLOBAL DEFAULT 10 _dl_out_of_memory@@GLIBC_PRIVATE
35: 0000b025 562 FUNC GLOBAL DEFAULT 9 _dl_mcount@@GLIBC_2.1
36: 00004b84 1917 FUNC GLOBAL DEFAULT 9 _dl_map_object@@GLIBC_PRIVATE
37: 000091cc 433 FUNC GLOBAL DEFAULT 9 _dl_signal_error@@GLIBC_PRIVATE
38: 000126f8 20 OBJECT GLOBAL DEFAULT 15 _r_debug@@GLIBC_2.0
39: 0000940d 318 FUNC GLOBAL DEFAULT 9 _dl_catch_error@@GLIBC_PRIVATE
40: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.3
41: 0000beff 38 FUNC WEAK DEFAULT 9 free@@GLIBC_2.0
42: 00008255 3291 FUNC GLOBAL DEFAULT 9 _dl_map_object_deps@@GLIBC_PRIVATE
符号表的第一个符号总是被保留的.因此实际可用的符号数是42
上面是通过节表来显示动态符号表的,我们也可以通过动态节来显示动态符号表
[zws@mail ~/glibc-2.3/build/elf]$readelf -Ds ld.so
Symbol table for image:
Num Buc: Value Size Type Bind Vis Ndx Name
13 0: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.1
5 0: 00006808 757 FUNC GLOBAL DEFAULT 9 _dl_lookup_versioned_symbol_skip
33 1: 00005b89 75 FUNC GLOBAL DEFAULT 9 _dl_unload_cache
40 2: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.3
6 4: 00000000 0 NOTYPE WEAK DEFAULT UND __pthread_mutex_lock
34 5: 0000f460 14 OBJECT GLOBAL DEFAULT 10 _dl_out_of_memory
9 5: 0000bdc4 220 FUNC WEAK DEFAULT 9 __libc_memalign
8 9: 000096e0 307 FUNC GLOBAL DEFAULT 9 _dl_init
30 10: 00007ca0 1072 FUNC GLOBAL DEFAULT 9 _dl_relocate_object
10 10: 00000000 0 NOTYPE WEAK DEFAULT UND __pthread_mutex_init
26 12: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_PRIVATE
35 13: 0000b025 562 FUNC GLOBAL DEFAULT 9 _dl_mcount
32 13: 00012124 4 OBJECT GLOBAL DEFAULT 14 _dl_starting_up
15 13: 00012514 4 OBJECT GLOBAL DEFAULT 14 __libc_enable_secure
3 13: 00009f6b 44 FUNC GLOBAL DEFAULT 9 _dl_debug_printf
38 14: 000126f8 20 OBJECT GLOBAL DEFAULT 15 _r_debug
22 14: 00012120 4 OBJECT GLOBAL DEFAULT 14 _dl_argv
14 15: 000063e6 1058 FUNC GLOBAL DEFAULT 9 _dl_lookup_versioned_symbol
2 15: 00012140 980 OBJECT GLOBAL DEFAULT 14 _rtld_global
29 16: 0000a868 1981 FUNC GLOBAL DEFAULT 9 _dl_start_profile
1 16: 000126d0 4 OBJECT GLOBAL DEFAULT 15 __libc_internal_tsd_set
23 17: 000030db 272 FUNC GLOBAL DEFAULT 9 _dl_dst_substitute
37 18: 000091cc 433 FUNC GLOBAL DEFAULT 9 _dl_signal_error
36 19: 00004b84 1917 FUNC GLOBAL DEFAULT 9 _dl_map_object
4 19: 0000a372 1066 FUNC GLOBAL DEFAULT 9 _dl_check_map_versions
19 21: 00000000 0 NOTYPE WEAK DEFAULT UND __pthread_mutex_unlock
11 23: 0000bea0 34 FUNC WEAK DEFAULT 9 malloc
24 24: 00000000 0 NOTYPE WEAK DEFAULT UND __pthread_mutex_destroy
20 25: 000126ec 4 OBJECT GLOBAL DEFAULT 15 __libc_internal_tsd_address
39 26: 0000940d 318 FUNC GLOBAL DEFAULT 9 _dl_catch_error
18 26: 0000bec2 61 FUNC WEAK DEFAULT 9 calloc
16 26: 00005ec4 709 FUNC GLOBAL DEFAULT 9 _dl_lookup_symbol
7 26: 000126d4 4 OBJECT GLOBAL DEFAULT 15 __libc_stack_end
27 27: 0000bf25 139 FUNC WEAK DEFAULT 9 realloc
21 29: 00009b86 5 FUNC GLOBAL DEFAULT 9 _dl_debug_state
41 31: 0000beff 38 FUNC WEAK DEFAULT 9 free
28 32: 0000b43c 373 FUNC GLOBAL DEFAULT 9 _dl_get_origin
17 32: 000126e4 4 OBJECT GLOBAL DEFAULT 15 __libc_internal_tsd_get
12 34: 00006189 605 FUNC GLOBAL DEFAULT 9 _dl_lookup_symbol_skip
42 35: 00008255 3291 FUNC GLOBAL DEFAULT 9 _dl_map_object_deps
31 35: 00003056 133 FUNC GLOBAL DEFAULT 9 _dl_dst_count
25 36: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.0
这里Num列显示该符号在符号表中的索引,Buc列显示该符号在Hash表中的索引,索引值相同的符号按照选后顺序显示.
可见索引为0的保留符号是不进入hash表的
可用如下命令显示各种长度桶的直方图,用于分析散列效果.
[zws@mail ~/glibc-2.3/build/elf]$readelf -I ld.so
Histogram for bucket list length (total of 37 buckets):
Length Number % of total Coverage
0 10 ( 27.0%) //长度为0的桶占总桶的27%
1 16 ( 43.2%) 38.1% //长度为1的桶占总桶的43.2%,其总符号数占总符号的38.1%
2 9 ( 24.3%) 81.0% //....
3 0 ( 0.0%) 81.0%
4 2 ( 5.4%) 100.0%
length为桶长,number为相同桶长的个数,% of total为相同桶长的个数占总桶的百分比,coverage为相同桶长的桶中总符号数
占总符号数的百分比。
问题4:.dynsym节和.symtab节的联系和区别?
[zws@mail ~/glibc-2.3/build/elf]$readelf -S ld.so
There are 30 section headers, starting at offset 0x96078:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .hash HASH 00000094 000094 000148 04 A 2 0 4
[ 2] .dynsym DYNSYM 000001dc 0001dc 0002b0 10 A 3 1 4
[ 3] .dynstr STRTAB 0000048c 00048c 0002cf 00 A 0 0 1
[ 4] .gnu.version VERSYM 0000075c 00075c 000056 02 A 2 0 2
[ 5] .gnu.version_d VERDEF 000007b4 0007b4 0000a4 00 A 3 5 4
[ 6] .rel.dyn REL 00000858 000858 000070 08 A 2 0 4
[ 7] .rel.plt REL 000008c8 0008c8 000048 08 A 2 8 4
[ 8] .plt PROGBITS 00000910 000910 0000a0 04 AX 0 0 4
[ 9] .text PROGBITS 000009b0 0009b0 00e6ce 00 AX 0 0 16
[10] .rodata PROGBITS 0000f080 00f080 002e60 00 A 0 0 32
[11] .dynamic DYNAMIC 00012000 012000 0000b0 08 WA 3 0 4
[12] .got PROGBITS 000120b0 0120b0 000038 04 WA 0 0 4
[13] .got.plt PROGBITS 000120e8 0120e8 000030 04 WA 0 0 4
[14] .data PROGBITS 00012120 012120 000408 00 WA 0 0 32
[15] .bss NOBITS 00012540 012528 0001cc 00 WA 0 0 32
[16] .stab PROGBITS 00000000 012528 0004f8 0c 17 0 4
[17] .stabstr STRTAB 00000000 012a20 000276 00 0 0 1
[18] .comment PROGBITS 00000000 012c96 0009f6 00 0 0 1
[19] .debug_aranges PROGBITS 00000000 01368c 0005e0 00 0 0 1
[20] .debug_pubnames PROGBITS 00000000 013c6c 000bd9 00 0 0 1
[21] .debug_info PROGBITS 00000000 014845 06722d 00 0 0 1
[22] .debug_abbrev PROGBITS 00000000 07ba72 006978 00 0 0 1
[23] .debug_line PROGBITS 00000000 0823ea 009e0e 00 0 0 1
[24] .debug_frame PROGBITS 00000000 08c1f8 001934 00 0 0 4
[25] .debug_str PROGBITS 00000000 08db2c 0083e3 01 MS 0 0 1
[26] .gnu.warning.llse PROGBITS 00000000 095f20 00003f 00 0 0 32
[27] .shstrtab STRTAB 00000000 095f5f 000118 00 0 0 1
[28] .symtab SYMTAB 00000000 096528 001f70 10 29 461 4
[29] .strtab STRTAB 00000000 098498 00174a 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
strip ld.so后发现
[zws@mail ~/glibc-2.3/build/elf]$readelf -S ldx.so
There are 19 section headers, starting at offset 0x12ffc:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .hash HASH 00000094 000094 000148 04 A 2 0 4
[ 2] .dynsym DYNSYM 000001dc 0001dc 0002b0 10 A 3 1 4
[ 3] .dynstr STRTAB 0000048c 00048c 0002cf 00 A 0 0 1
[ 4] .gnu.version VERSYM 0000075c 00075c 000056 02 A 2 0 2
[ 5] .gnu.version_d VERDEF 000007b4 0007b4 0000a4 00 A 3 5 4
[ 6] .rel.dyn REL 00000858 000858 000070 08 A 2 0 4
[ 7] .rel.plt REL 000008c8 0008c8 000048 08 A 2 8 4
[ 8] .plt PROGBITS 00000910 000910 0000a0 04 AX 0 0 4
[ 9] .text PROGBITS 000009b0 0009b0 00e6ce 00 AX 0 0 16
[10] .rodata PROGBITS 0000f080 00f080 002e60 00 A 0 0 32
[11] .dynamic DYNAMIC 00012000 012000 0000b0 08 WA 3 0 4
[12] .got PROGBITS 000120b0 0120b0 000038 04 WA 0 0 4
[13] .got.plt PROGBITS 000120e8 0120e8 000030 04 WA 0 0 4
[14] .data PROGBITS 00012120 012120 000408 00 WA 0 0 32
[15] .bss NOBITS 00012540 012528 0001cc 00 WA 0 0 32
[16] .comment PROGBITS 00000000 012528 0009f6 00 0 0 1
[17] .gnu.warning.llse PROGBITS 00000000 012f20 00003f 00 0 0 32
[18] .shstrtab STRTAB 00000000 012f5f 00009c 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
少了11个节,分别是
[16] .stab PROGBITS 00000000 012528 0004f8 0c 17 0 4
[17] .stabstr STRTAB 00000000 012a20 000276 00 0 0 1
[19] .debug_aranges PROGBITS 00000000 01368c 0005e0 00 0 0 1
[20] .debug_pubnames PROGBITS 00000000 013c6c 000bd9 00 0 0 1
[21] .debug_info PROGBITS 00000000 014845 06722d 00 0 0 1
[22] .debug_abbrev PROGBITS 00000000 07ba72 006978 00 0 0 1
[23] .debug_line PROGBITS 00000000 0823ea 009e0e 00 0 0 1
[24] .debug_frame PROGBITS 00000000 08c1f8 001934 00 0 0 4
[25] .debug_str PROGBITS 00000000 08db2c 0083e3 01 MS 0 0 1
[28] .symtab SYMTAB 00000000 096528 001f70 10 29 461 4
[29] .strtab STRTAB 00000000 098498 00174a 00 0 0 1
前面9个都是调试信息节。后面一个是.symtab,一个是.symtab的字符表节,他们的符号表的Lk值是29,指向.strtab节.
说明这些信息都不是执行程序必须的。执行程序用的符号表是.dynsym节,以及为其服务的.hash和.dynstr.
.dynsym和.symtab分别占用文件空间,两者磁盘空间不相干。在内容上,.dynsym是.symtab的子集。.dynsym用于执行时动态链接,
.symtab由objdump等分析程序使用.这里也体现了EFL的执行和存储两种视图。
两者侧重点不同,但是相通。链接程序将所有执行时需要的节安排在一起,并安排在最前面。这样程序加载的时候,基址就在文件头处。
在hash节前面的ELF文件头,然后是程序头。
执行时不需要的节放在后面,准确说是.bss后。.bss不占用文件空间,但是占用内存空间。
因此前面的
[16] .comment PROGBITS 00000000 012528 0009f6 00 0 0 1
[17] .gnu.warning.llse PROGBITS 00000000 012f20 00003f 00 0 0 32
[18] .shstrtab STRTAB 00000000 012f5f 00009c 00 0 0 1
也可以删掉
还有节表本身,被安排在最后,也可以删掉,不会影响程序的执行。
我们可以作如下实验,编写一个简单的hello world程序,编译执行。
使用readelf -S hello,找到.bss节,假设文件偏移是x.
使用python脚本将其截断。
f=open("hello","r+")
f.seek(x)
f.truncate()
f.close
再执行一下hello,看是否还是可执行.
3.设置map_start和map_end
GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin;// 0
GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;// bss 最后
/* Copy the TLS related data if necessary. */
/*#if USE_TLS && !defined DONT_USE_BOOTSTRAP_MAP
//# ifdef HAVE___THREAD
// assert (info->l.l_tls_modid != 0);
//# else
if (info->l.l_tls_modid != 0)
//# endif
{
GL(dl_rtld_map).l_tls_blocksize = info->l.l_tls_blocksize;
GL(dl_rtld_map).l_tls_align = info->l.l_tls_align;
GL(dl_rtld_map).l_tls_initimage_size = info->l.l_tls_initimage_size;
GL(dl_rtld_map).l_tls_initimage = info->l.l_tls_initimage;
GL(dl_rtld_map).l_tls_offset = info->l.l_tls_offset;
GL(dl_rtld_map).l_tls_modid = 1;
GL(dl_rtld_map).l_tls_tp_initialized
= info->l.l_tls_tp_initialized;
}
#endif
*/
//#if HP_TIMING_AVAIL
HP_TIMING_NOW (GL(dl_cpuclock_offset));
//#endif
查看ld.so的符号表
405: 00000000 0 NOTYPE LOCAL DEFAULT ABS _begin
417: 0001270c 0 NOTYPE LOCAL DEFAULT ABS _end
对比节表可发现
_end正好执行bss结尾(是地址而不是文件偏移)
4._dl_sysdep_start
/* Call the OS-dependent function to set up life so we can do things like
调用操作系统相关函数,建立操作环境,这样就能执行文件访问等操作
file access. It will call `dl_main' (below) to do all the real work
of the dynamic linker, and then unwind our frame and run the user
这将会调用dl_main完成所有的动态链接工作
entry point on the same stack we entered on.
最后退出并执行用户入口
*/
start_addr = _dl_sysdep_start (arg, &dl_main);//传递dl_main函数,返回用户入口地址
//#ifndef HP_TIMING_NONAVAIL
if (HP_TIMING_AVAIL)// 1
{
hp_timing_t end_time;
/* Get the current time. */
HP_TIMING_NOW (end_time);//记录end_time
/* Compute the difference. */
HP_TIMING_DIFF (rtld_total_time, start_time, end_time);//计算耗时
}
//#endif
if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_STATISTICS, 0))
print_statistics ();//如果需要,输出统计信息
return start_addr;
}
输出的统计信息如下
[zws@mail ~/glibc-2.3/build/elf]$ LD_DEBUG=statistics ls /proc/slabinfo
30251:
30251: runtime linker statistics:
30251: total startup time in dynamic loader: 1141112 clock cycles
30251: time needed for relocation: 498188 clock cycles (43.6%)
30251: number of relocations: 103
30251: number of relocations from cache: 5
30251: time needed to load objects: 377760 clock cycles (33.1%)
/proc/slabinfo
30251:
30251: runtime linker statistics:
30251: final number of relocations: 156
30251: final number of relocations from cache: 5
[zws@mail ~/glibc-2.3/build/elf]$
阅读(2649) | 评论(0) | 转发(0) |