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的符号表也能例证
[zws@mail ~/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使用_rtld_local@GOTOFF而不是_rtld_local@GOT,
从而_rtld_local不需要重定位,这个一定很重要
2._dl_start中的动态链接内联函数
/* This #define produces dynamic linking inline functions for
bootstrap relocation instead of general-purpose relocation. */
#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
/* Partly clean the `bootstrap_map' structure up. 部分清空bootstrap_map结构. Don't use
`memset' since it might not be built in or inlined and we cannot
不使用memset是因为它不是内建的或内联函数,我们现在还不能调用.
make function calls at this point. Use '__builtin_memset' if we
如果有效的话,使用__builtin_memset
know it is available. We do not have to clear the memory if we
如果不必使用临时bootstrap_map则不需要清0
do not have to use the temporary bootstrap_map. Global variables
全局变量缺省初始化为0
are initialized to zero by default. */
/*
#ifndef DONT_USE_BOOTSTRAP_MAP
# ifdef HAVE_BUILTIN_MEMSET
__builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info));
# else
for (cnt = 0;
cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]);
++cnt)
bootstrap_map.l_info[cnt] = 0;
# endif
#endif
*/
/* Figure out the run-time load address of the dynamic linker itself. */
bootstrap_map.l_addr = elf_machine_load_address ();// 加载地址 _rtld_local._dl_rtld_map.l_addr = elf_machine_load_address ();
/* Read our own dynamic section and fill in the info array. */
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)
/* Return the link-time address of _DYNAMIC. Conveniently, this is the
first element of the GOT, a special entry that is never relocated. */
static inline Elf32_Addr //__attribute__ ((unused, const))
elf_machine_dynamic (void)
{
/* This produces a GOTOFF reloc that resolves to zero at link time, so in
fact just loads from the GOT register directly. By doing it without
an asm we can let the compiler choose any register. */
extern const Elf32_Addr _GLOBAL_OFFSET_TABLE_[] attribute_hidden;
return _GLOBAL_OFFSET_TABLE_[0];
}
/* Return the run-time load address of the shared object. */
static inline Elf32_Addr //__attribute__ ((unused))
elf_machine_load_address (void)
{
/* Compute the difference between the runtime address of _DYNAMIC as seen
by a GOTOFF reference, and the link-time address found in the special
unrelocated first GOT entry. */
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_TABLE_@GOTOFF(%ebx), %edx//取GOT[0],即ld.so的dynamic节被ld静态链接时安排的地址
leal _DYNAMIC@GOTOFF(%ebx), %eax//取dynamic节运行时加载到内存中的地址
subl %edx, %eax//dynamic的地址-got[0],即得镜像加载基址
movl %eax, 456+_rtld_local@GOTOFF(%ebx)//该地址存入l_addr
C代码和汇编代码对照着看,就能明白一二。
5.elf_get_dynamic_info (dynamic-link.h)
/* Read the dynamic section at DYN and fill in INFO with indices DT_*. */
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
/*
[zws@mail elf]$ readelf -d ld.so
Dynamic section at offset 0x12000 contains 18 entrie