ld.so分析5 _dl_start 2010-05-06 08:53:24
分类: LINUX
ld.so分析5 _dl_start
对于不关心的地方,我们都//或注释掉
1._dl_start中的变量声明
static Elf32_Addr //我们假设是i386 32位平台,ElfW(Addr)被宏扩展为Elf32_Addr
//ElfW(Addr)
//__attribute_used__ internal_function
//__attribute__ ((__used__)) __attribute__ ((regparm (3), stdcall))
_dl_start (void *arg)//arg参数值argc地址
{
//#ifdef DONT_USE_BOOTSTRAP_MAP
# define bootstrap_map GL(dl_rtld_map)
//#else
// struct dl_start_final_info info;
//# define bootstrap_map info.l
//#endif
//#if USE_TLS || (!DONT_USE_BOOTSTRAP_MAP && !HAVE_BUILTIN_MEMSET)
// size_t cnt;
//#endif
//#ifdef USE_TLS
// ElfW(Ehdr) *ehdr;
// ElfW(Phdr) *phdr;
// dtv_t initdtv[3];
//#endif
宏GL定义如下
# define GL(name) _rtld_local._##name
展开
#define bootstrap_map _rtld_local._dl_rtld_map
_rtld_local是什么呢?
查看rtld.c的预处理文件可发现如下定义
struct rtld_global _rtld_global =
{
# 1 "../sysdeps/unix/sysv/linux/i386/dl-procinfo.c" 1
# 47 "../sysdeps/unix/sysv/linux/i386/dl-procinfo.c"
._dl_x86_cap_flags
= {
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
"cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
"pat", "pse36", "pn", "clflush", "20", "dts", "acpi", "mmx",
"fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "amd3d"
}
,
._dl_x86_platforms
= {
"i386", "i486", "i586", "i686"
}
,
# 92 "rtld.c" 2
._dl_debug_fd = 2,
._dl_dynamic_weak = 1,
._dl_lazy = 1,
._dl_fpu_control = 0x037f,
._dl_correct_cache_id = 3,
._dl_hwcap_mask = HWCAP_IMPORTANT,
._dl_load_lock = {{0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, { 0, 0 }}}
};
extern struct rtld_global _rtld_local __attribute__ ((visibility ("hidden")));
extern __typeof (_rtld_global) _rtld_local __attribute__ ((alias ("_rtld_global")));;
结构rtld_global的内容就不贴出来了,大家自己查吧
这里指出,_rtld_local是_rtld_global的别名.查看ld.so的符号表也能例证
[[email protected] ~/glibc-2.3/build/elf]$readelf -s ld.so|grep _rtld
332: 00012140 980 OBJECT LOCAL HIDDEN 14 _rtld_local
462: 00012140 980 OBJECT GLOBAL DEFAULT 14 _rtld_global
_rtld_local._dl_rtld_map的类型是struct link_map.这个类型非常重要,是动态链接的核心数据结构
注意这里的HIDDEN属性,这个属性保证访问_rtld_local使用[email protected]而不是[email protected],
从而_rtld_local不需要重定位,这个一定很重要
2._dl_start中的动态链接内联函数
#define RTLD_BOOTSTRAP
#define RESOLVE_MAP(sym, version, flags) \
((*(sym))->st_shndx == SHN_UNDEF ? 0 : &bootstrap_map)
#define RESOLVE(sym, version, flags) \
((*(sym))->st_shndx == SHN_UNDEF ? 0 : bootstrap_map.l_addr)
#include "dynamic-link.h"
这里先定义了三个宏,然后包含dynamic-link.h头文件,里面定义了几个动态链接需要用到的宏或函数。
这些宏或函数用到了前面定义的三个宏,因此,根据这三个宏定义的不同,动态链接宏或函数的功能会有所不同,
前面的注释也说明了这一点。至于有这些动态链接宏或函数的功能,后面涉及到的时候再分析。
3.获取ld.so的加载基址
if (HP_TIMING_INLINE && HP_TIMING_AVAIL)
//#ifdef DONT_USE_BOOTSTRAP_MAP
HP_TIMING_NOW (start_time);//获得开始时间
//#else
// HP_TIMING_NOW (info.start_time);
//#endif
bootstrap_map.l_addr = elf_machine_load_address ();// 加载地址 _rtld_local._dl_rtld_map.l_addr = elf_machine_load_address ();
bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic ();//动态节地址
elf_get_dynamic_info (&bootstrap_map);//取动态信息
4.elf_machine_dynamic和elf_machine_load_address (sysdeps/i386/dl-machine.h)
static inline Elf32_Addr //__attribute__ ((unused, const))
elf_machine_dynamic (void)
{
extern const Elf32_Addr _GLOBAL_OFFSET_TABLE_[] attribute_hidden;
return _GLOBAL_OFFSET_TABLE_[0];
}
static inline Elf32_Addr //__attribute__ ((unused))
elf_machine_load_address (void)
{
extern Elf32_Dyn bygotoff[] asm ("_DYNAMIC");// attribute_hidden;
return (Elf32_Addr) &bygotoff - elf_machine_dynamic ();
}
有点晦涩难懂,看看汇编代码
bootstrap_map.l_addr = elf_machine_load_address ();
生成的汇编代码如下
movl _GLOBAL_OFFSET_[email protected](%ebx), %edx//取GOT[0],即ld.so的dynamic节被ld静态链接时安排的地址
leal [email protected](%ebx), %eax//取dynamic节运行时加载到内存中的地址
subl %edx, %eax//dynamic的地址-got[0],即得镜像加载基址
movl %eax, [email protected](%ebx)//该地址存入l_addr
C代码和汇编代码对照着看,就能明白一二。
5.elf_get_dynamic_info (dynamic-link.h)
static inline void //__attribute__ ((unused, always_inline))
elf_get_dynamic_info (struct link_map *l)
{
ElfW(Dyn) *dyn = l->l_ld;
ElfW(Dyn) **info;
//#ifndef RTLD_BOOTSTRAP
if (dyn == NULL)
return;
//#endif
info = l->l_info;//取保存dynamic信息的数据结构
while (dyn->d_tag != DT_NULL)//遍历
{
if (dyn->d_tag < DT_NUM)//长度34,索引范围 [0,33]
info[dyn->d_tag] = dyn;
else if (dyn->d_tag >= DT_LOPROC &&
dyn->d_tag < DT_LOPROC + DT_THISPROCNUM)//0,(0x70000000,0x70000000)
info[dyn->d_tag - DT_LOPROC + DT_NUM] = dyn;
else if ((Elf32_Word) DT_VERSIONTAGIDX (dyn->d_tag) < DT_VERSIONTAGNUM)// 16,[0x6ffffff0,0x6fffffff]->[49,34]
info[VERSYMIDX (dyn->d_tag)] = dyn;
else if ((Elf32_Word) DT_EXTRATAGIDX (dyn->d_tag) < DT_EXTRANUM)// 3,[0x7fffffffd,0x7fffffff]
info[DT_EXTRATAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
+ DT_VERSIONTAGNUM] = dyn;
else if ((Elf32_Word) DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM)// 12,[0x6ffffdf4,0x6ffffdff]
info[DT_VALTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
+ DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn;
else if ((Elf32_Word) DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM)// 10 ,[0x6ffffef6,0x6ffffeff]
info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
+ DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
++dyn;
}
//#ifndef DL_RO_DYN_SECTION
if (l->l_addr != 0)//加载地址
{
//调整地址
ElfW(Addr) l_addr = l->l_addr;
if (info[DT_HASH] != NULL)
info[DT_HASH]->d_un.d_ptr += l_addr;
if (info[DT_PLTGOT] != NULL)
info[DT_PLTGOT]->d_un.d_ptr += l_addr;
if (info[DT_STRTAB] != NULL)
info[DT_STRTAB]->d_un.d_ptr += l_addr;
if (info[DT_SYMTAB] != NULL)
info[DT_SYMTAB]->d_un.d_ptr += l_addr;
//# if ! ELF_MACHINE_NO_RELA
if (info[DT_RELA] != NULL)
info[DT_RELA]->d_un.d_ptr += l_addr;
//# endif
//# if ! ELF_MACHINE_NO_REL
if (info[DT_REL] != NULL)
info[DT_REL]->d_un.d_ptr += l_addr;
//# endif
if (info[DT_JMPREL] != NULL)
info[DT_JMPREL]->d_un.d_ptr += l_addr;
if (info[VERSYMIDX (DT_VERSYM)] != NULL)
info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr += l_addr;
}
//#endif
if (info[DT_PLTREL] != NULL)
{
//#if ELF_MACHINE_NO_RELA
// assert (info[DT_PLTREL]->d_un.d_val == DT_REL);
//#elif ELF_MACHINE_NO_REL
// assert (info[DT_PLTREL]->d_un.d_val == DT_RELA);
//#else
assert (info[DT_PLTREL]->d_un.d_val == DT_REL
|| info[DT_PLTREL]->d_un.d_val == DT_RELA);
//#endif
}
//#if ! ELF_MACHINE_NO_RELA
if (info[DT_RELA] != NULL)
assert (info[DT_RELAENT]->d_un.d_val == sizeof (ElfW(Rela)));
//# endif
//# if ! ELF_MACHINE_NO_REL
if (info[DT_REL] != NULL)
assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel)));
//#endif
if (info[DT_FLAGS] != NULL)
{
l->l_flags = info[DT_FLAGS]->d_un.d_val;
//#ifdef RTLD_BOOTSTRAP
// assert ((l->l_flags & (DF_SYMBOLIC | DF_TEXTREL | DF_BIND_NOW)) == 0);
//#else
if (l->l_flags & DF_SYMBOLIC)
info[DT_SYMBOLIC] = info[DT_FLAGS];
if (l->l_flags & DF_TEXTREL)
info[DT_TEXTREL] = info[DT_FLAGS];
if (l->l_flags & DF_BIND_NOW)
info[DT_BIND_NOW] = info[DT_FLAGS];
//#endif
}
if (info[VERSYMIDX (DT_FLAGS_1)] != NULL)
l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val;
//#ifdef RTLD_BOOTSTRAP
// assert (info[DT_RUNPATH] == NULL);
// assert (info[DT_RPATH] == NULL);
//#else
if (info[DT_RUNPATH] != NULL)
info[DT_RPATH] = NULL;
//#endif
}
6._dl_start执行自我重定位
//#ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
// ELF_MACHINE_BEFORE_RTLD_RELOC (bootstrap_map.l_info);
//#endif
if (bootstrap_map.l_addr || ! bootstrap_map.l_info[VALIDX(DT_GNU_PRELINKED)])
{
ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0);
}
7._dl_start->ELF_DYNAMIC_RELOCATE (dynamic-link.h)
# define ELF_DYNAMIC_RELOCATE(map, lazy, consider_profile) \
do { \
int edr_lazy = elf_machine_runtime_setup ((map), (lazy), \
(consider_profile)); \
ELF_DYNAMIC_DO_REL ((map), edr_lazy); \
ELF_DYNAMIC_DO_RELA ((map), edr_lazy); \
} while (0)
8._dl_start->ELF_DYNAMIC_RELOCATE ->elf_machine_runtime_setup(sysdeps/i386/dl-machine.h)
static inline int //__attribute__ ((unused))
elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
{
Elf32_Addr *got;
extern void _dl_runtime_resolve (Elf32_Word);// attribute_hidden;
extern void _dl_runtime_profile (Elf32_Word);// attribute_hidden;
if (l->l_info[DT_JMPREL] && lazy)//有JMPREL且lazy
{
got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]);//取PLTGOT地址
if (got[1])
{
l->l_mach.plt = got[1] + l->l_addr;
l->l_mach.gotplt = (Elf32_Addr) &got[3];
}
got[1] = (Elf32_Addr) l;
if (__builtin_expect (profile, 0))
{
got[2] = (Elf32_Addr) &_dl_runtime_profile;
if (_dl_name_match_p (GL(dl_profile), l))
GL(dl_profile_map) = l;
}
else
got[2] = (Elf32_Addr) &_dl_runtime_resolve;//存放解析函数
}
return lazy;
}
前面传给lazy参数值为0,因此直接返回0,接下来的两个宏定义如下,注意lazy==0
#define ELF_DYNAMIC_DO_REL(map,lazy) _ELF_DYNAMIC_DO_RELOC (REL, rel, map, lazy, _ELF_CHECK_REL)
#define ELF_DYNAMIC_DO_RELA(map,lazy)
9._dl_start->ELF_DYNAMIC_RELOCATE ->_ELF_DYNAMIC_DO_RELOC(elf/dynamic-link.h)
处理.rel.dyn和.rel.plt重定位节
# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, do_lazy, test_rel) \
do { \
struct { ElfW(Addr) start, size; int lazy; } ranges[2]; \
ranges[0].lazy = 0; \
ranges[0].size = ranges[1].size = 0; \
ranges[0].start = 0; \
\
if ((map)->l_info[DT_##RELOC]) \
{ \
ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]); \
ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val; \
} \
if ((map)->l_info[DT_PLTREL] \
&& (!test_rel || (map)->l_info[DT_PLTREL]->d_un.d_val == DT_##RELOC)) \
{ \
ElfW(Addr) start = D_PTR ((map), l_info[DT_JMPREL]); \
\
if (! ELF_DURING_STARTUP \
&& ((do_lazy) \
\
|| ranges[0].start + ranges[0].size != start)) \
{ \
ranges[1].start = start; \
ranges[1].size = (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \
ranges[1].lazy = (do_lazy); \
} \
else \
{ \
\
assert (ranges[0].start + ranges[0].size == start); \
ranges[0].size += (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \
} \
} \
\
if (ELF_DURING_STARTUP) \
elf_dynamic_do_##reloc ((map), ranges[0].start, ranges[0].size, 0); \
else \
{ \
int ranges_index; \
for (ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc ((map), \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].lazy); \
} \
} while (0)
看看ld.so的重定位信息
[[email protected] ~/glibc-2.3/build/elf]$readelf -r ld.so
Relocation section '.rel.dyn' at offset 0x858 contains 14 entries:
Offset Info Type Sym.Value Sym. Name
000120c0 00000008 R_386_RELATIVE
000120c8 00000008 R_386_RELATIVE
000120d8 00000008 R_386_RELATIVE
000120dc 00000008 R_386_RELATIVE
000120e0 00000008 R_386_RELATIVE
000120b0 00000106 R_386_GLOB_DAT 000126d0 __libc_internal_tsd_se
000120b4 00000206 R_386_GLOB_DAT 00012140 _rtld_global
000120b8 00000606 R_386_GLOB_DAT 00000000 __pthread_mutex_lock
000120bc 00000706 R_386_GLOB_DAT 000126d4 __libc_stack_end
000120c4 00000a06 R_386_GLOB_DAT 00000000 __pthread_mutex_init
000120cc 00001106 R_386_GLOB_DAT 000126e4 __libc_internal_tsd_ge
000120d0 00001306 R_386_GLOB_DAT 00000000 __pthread_mutex_unlock
000120d4 00001806 R_386_GLOB_DAT 00000000 __pthread_mutex_destro
000120e4 00002606 R_386_GLOB_DAT 000126f8 _r_debug
Relocation section '.rel.plt' at offset 0x8c8 contains 9 entries:
Offset Info Type Sym.Value Sym. Name
000120f4 00000607 R_386_JUMP_SLOT 00000000 __pthread_mutex_lock
000120f8 00000907 R_386_JUMP_SLOT 0000bdc4 __libc_memalign
000120fc 00000a07 R_386_JUMP_SLOT 00000000 __pthread_mutex_init
00012100 00000b07 R_386_JUMP_SLOT 0000bea0 malloc
00012104 00001207 R_386_JUMP_SLOT 0000bec2 calloc
00012108 00001307 R_386_JUMP_SLOT 00000000 __pthread_mutex_unlock
0001210c 00001807 R_386_JUMP_SLOT 00000000 __pthread_mutex_destro
00012110 00001b07 R_386_JUMP_SLOT 0000bf25 realloc
00012114 00002907 R_386_JUMP_SLOT 0000beff free
[[email protected] ~/glibc-2.3/build/elf]$
10._dl_start->ELF_DYNAMIC_RELOCATE ->_ELF_DYNAMIC_DO_RELOC->elf_dynamic_do_rel(elf/dynamic-link.h)
执行实质的重定位操作
static inline void
elf_dynamic_do_rel (struct link_map *map,
ElfW(Addr) reladdr, ElfW(Addr) relsize,
int lazy)
{
const ElfW(Rel) *r = (const void *) reladdr;
const ElfW(Rel) *end = (const void *) (reladdr + relsize);
ElfW(Addr) l_addr = map->l_addr;
{
const ElfW(Sym) *const symtab =
(const void *) D_PTR (map, l_info[DT_SYMTAB]);//取符号表
ElfW(Word) nrelative = (map->l_info[RELCOUNT_IDX] == NULL
? 0 : map->l_info[RELCOUNT_IDX]->d_un.d_val);//R_386_RELATIVE重定位项个数 0x6ffffffa (RELCOUNT) 5
const ElfW(Rel) *relative = r;// 0x00000011 (REL) 0x858
r = r + MIN (nrelative, relsize / sizeof (ElfW(Rel)));
for (; relative < r; ++relative)
DO_ELF_MACHINE_REL_RELATIVE (map, l_addr, relative);//先处理前面的相对重定位
11._dl_start->ELF_DYNAMIC_RELOCATE ->_ELF_DYNAMIC_DO_RELOC->elf_dynamic_do_rel->DO_ELF_MACHINE_REL_RELATIVE (elf/do-rel.h)
重定位R_386_RELATIVE重定位项
# define DO_ELF_MACHINE_REL_RELATIVE(map, l_addr, relative) \
elf_machine_rel_relative (l_addr, relative, \
(void *) (l_addr + relative->r_offset))
11._dl_start->ELF_DYNAMIC_RELOCATE ->_ELF_DYNAMIC_DO_RELOC->elf_dynamic_do_rel->DO_ELF_MACHINE_REL_RELATIVE-> elf_machine_rel_relative (sysdeps/i386/dl-machine.h)
static inline void
elf_machine_rel_relative (Elf32_Addr l_addr, const Elf32_Rel *reloc,
Elf32_Addr *const reloc_addr)
{
assert (ELF32_R_TYPE (reloc->r_info) == R_386_RELATIVE);//肯定是R_386_RELATIVE重定位类型
*reloc_addr += l_addr;//原地址加上模块加载地址
}
12._dl_start->ELF_DYNAMIC_RELOCATE ->_ELF_DYNAMIC_DO_RELOC->elf_dynamic_do_rel(elf/dynamic-link.h)
//#ifdef RTLD_BOOTSTRAP
assert (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL);//动态链接器总是使用版本信息
//#else
// if (map->l_info[VERSYMIDX (DT_VERSYM)])
//#endif
{
const ElfW(Half) *const version =
(const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);//0x6ffffff0 (VERSYM) 0x75c
for (; r < end; ++r)
{
ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)],
&map->l_versions[ndx],
(void *) (l_addr + r->r_offset));
}
}
}
}
ld.so的版本符号表是
[[email protected] ~/glibc-2.3/build/elf]$objdump -sj .gnu.version ld.so
ld.so: file format elf32-i386
Contents of section .gnu.version:
075c 00000500 05000500 05000500 00000500 ................
076c 05000200 00000200 05000300 05000500 ................
077c 05000500 02000000 05000500 05000500 ................
078c 00000200 05000200 05000500 05000500 ................
079c 05000500 05000300 05000500 02000500 ................
07ac 04000200 0500 ......
typedef uint16_t Elf32_Half;
map->l_versions其实为空,不过elf_machine_rel 中没有用到
11._dl_start->ELF_DYNAMIC_RELOCATE ->_ELF_DYNAMIC_DO_RELOC->elf_dynamic_do_rel->DO_ELF_MACHINE_REL_RELATIVE-> elf_machine_relmap->l_versions其实为空,不过elf_machine_rel (sysdeps/i386/dl-machine.h)
static inline void
elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
const Elf32_Sym *sym, const struct r_found_version *version,
Elf32_Addr *const reloc_addr)
{
const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
{
const Elf32_Sym *const refsym = sym;
//#if defined USE_TLS && !defined RTLD_BOOTSTRAP
// struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
// Elf32_Addr value = sym == NULL ? 0 : sym_map->l_addr + sym->st_value;
//#else
Elf32_Addr value = RESOLVE (&sym, version, r_type);//等价于Elf32_Addr value = ((*(&sym))->st_shndx == 0 ? 0 : _rtld_local._dl_rtld_map.l_addr);
//# ifndef RTLD_BOOTSTRAP
// if (sym != NULL)
//# endif
value += sym->st_value;//加上sym->st_value中的值
//#endif
switch (r_type)
{
case R_386_GLOB_DAT: //ld.so中只有这两个
case R_386_JMP_SLOT:
*reloc_addr = value;
break;
一路返回到_dl_start中,就完成重定位了。
大家想一想如何保证到现在还没有用到重定位的数据?
通过全部使用inline函数或宏,且只使用_rtld_local(vis为hidden)和局部变量来保证.
12.返回_dl_start,完成动态链接
{
//#ifdef DONT_USE_BOOTSTRAP_MAP
ElfW(Addr) entry = _dl_start_final (arg);//完成动态链接,返回可执行文件入口
//#else
// ElfW(Addr) entry = _dl_start_final (arg, &info);
//#endif
//#ifndef ELF_MACHINE_START_ADDRESS
# define ELF_MACHINE_START_ADDRESS(map, start) (start)
//#endif
return ELF_MACHINE_START_ADDRESS (GL(dl_loaded), entry);//等价于return entry;
}
}