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 entries:
Tag Type Name/Value
0x0000000e (SONAME) Library soname: [ld-linux.so.2]
0x00000004 (HASH) 0x94
0x00000005 (STRTAB) 0x48c
0x00000006 (SYMTAB) 0x1dc
0x0000000a (STRSZ) 719 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000003 (PLTGOT) 0x120e8
0x00000002 (PLTRELSZ) 72 (bytes)
0x00000014 (PLTREL) REL
0x00000017 (JMPREL) 0x8c8
0x00000011 (REL) 0x858
0x00000012 (RELSZ) 112 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffc (VERDEF) 0x7b4
0x6ffffffd (VERDEFNUM) 5
0x6ffffff0 (VERSYM) 0x75c
0x6ffffffa (RELCOUNT) 5
0x00000000 (NULL) 0x0
[zws@mail elf]$ readelf -x 11 ld.so
Hex dump of section '.dynamic':
0x00012000 0e000000 95020000 04000000 94000000 ................
0x00012010 05000000 8c040000 06000000 dc010000 ................
0x00012020 0a000000 cf020000 0b000000 10000000 ................
0x00012030 03000000 e8200100 02000000 48000000 ..... ......H...
0x00012040 14000000 11000000 17000000 c8080000 ................
0x00012050 11000000 58080000 12000000 70000000 ....X.......p...
0x00012060 13000000 08000000 fcffff6f b4070000 ...........o....
0x00012070 fdffff6f 05000000 f0ffff6f 5c070000 ...o.......o\...
0x00012080 faffff6f 05000000 00000000 00000000 ...o............
0x00012090 00000000 00000000 00000000 00000000 ................
0x000120a0 00000000 00000000 00000000 00000000 ................
*/
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
/* Don't adjust .dynamic unnecessarily. */
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)
{
/* Flags are used. Translate to the old form where available.
Since these l_info entries are only tested for NULL pointers it
is ok if they point to the DT_FLAGS entry. */
l->l_flags = info[DT_FLAGS]->d_un.d_val;
//#ifdef RTLD_BOOTSTRAP
/* These three flags must not be set for ld.so. */
// 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
/* The dynamic linker should have none of these set. */
// assert (info[DT_RUNPATH] == NULL);
// assert (info[DT_RPATH] == NULL);
//#else
if (info[DT_RUNPATH] != NULL)
/* If both RUNPATH and RPATH are given, the latter is ignored. */
info[DT_RPATH] = NULL;
//#endif
}
6._dl_start执行自我重定位
/*
#if USE_TLS
# if !defined HAVE___THREAD && !defined DONT_USE_BOOTSTRAP_MAP
/* Signal that we have not found TLS data so far. * /
bootstrap_map.l_tls_modid = 0;
# endif
/* Get the dynamic linker's own program header. First we need the ELF
file header. The `_begin' symbol created by the linker script points
to it. When we have something like GOTOFF relocs, we can use a plain
reference to find the runtime address. Without that, we have to rely
on the `l_addr' value, which is not the value we want when prelinked. * /
#ifdef DONT_USE_BOOTSTRAP_MAP
ehdr = (ElfW(Ehdr) *) &_begin;
#else
ehdr = (ElfW(Ehdr) *) bootstrap_map.l_addr;
#endif
phdr = (ElfW(Phdr) *) ((ElfW(Addr)) ehdr + ehdr->e_phoff);
for (cnt = 0; cnt < ehdr->e_phnum; ++cnt)
if (phdr[cnt].p_type == PT_TLS)
{
void *tlsblock;
size_t max_align = MAX (TLS_INIT_TCB_ALIGN, phdr[cnt].p_align);
char *p;
bootstrap_map.l_tls_blocksize = phdr[cnt].p_memsz;
bootstrap_map.l_tls_align = phdr[cnt].p_align;
assert (bootstrap_map.l_tls_blocksize != 0);
bootstrap_map.l_tls_initimage_size = phdr[cnt].p_filesz;
bootstrap_map.l_tls_initimage = (void *) (bootstrap_map.l_addr
+ phdr[cnt].p_vaddr);
/* We can now allocate the initial TLS block. This can happen
on the stack. We'll get the final memory later when we
know all about the various objects loaded at startup
time. * /
# if TLS_TCB_AT_TP
tlsblock = alloca (roundup (bootstrap_map.l_tls_blocksize,
TLS_INIT_TCB_ALIGN)
+ TLS_INIT_TCB_SIZE
+ max_align);
# elif TLS_DTV_AT_TP
tlsblock = alloca (roundup (TLS_INIT_TCB_SIZE,
bootstrap_map.l_tls_align)
+ bootstrap_map.l_tls_blocksize
+ max_align);
# else
/* In case a model with a different layout for the TCB and DTV
is defined add another #elif here and in the following #ifs. * /
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
# endif
/* Align the TLS block. * /
tlsblock = (void *) (((uintptr_t) tlsblock + max_align - 1)
& ~(max_align - 1));
/* Initialize the dtv. [0] is the length, [1] the generation
counter. * /
initdtv[0].counter = 1;
initdtv[1].counter = 0;
/* Initialize the TLS block. * /
# if TLS_TCB_AT_TP
initdtv[2].pointer = tlsblock;
# elif TLS_DTV_AT_TP
bootstrap_map.l_tls_offset = roundup (TLS_INIT_TCB_SIZE,
bootstrap_map.l_tls_align);
initdtv[2].pointer = (char *) tlsblock + bootstrap_map.l_tls_offset;
# else
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
# endif
p = __mempcpy (initdtv[2].pointer, bootstrap_map.l_tls_initimage,
bootstrap_map.l_tls_initimage_size);
# ifdef HAVE_BUILTIN_MEMSET
__builtin_memset (p, '\0', (bootstrap_map.l_tls_blocksize
- bootstrap_map.l_tls_initimage_size));
# else
{
size_t remaining = (bootstrap_map.l_tls_blocksize
- bootstrap_map.l_tls_initimage_size);
while (remaining-- > 0)
*p++ = '\0';
}
#endif
/* Install the pointer to the dtv. * /
/* Initialize the thread pointer. * /
# if TLS_TCB_AT_TP
bootstrap_map.l_tls_offset
= roundup (bootstrap_map.l_tls_blocksize, TLS_INIT_TCB_ALIGN);
INSTALL_DTV ((char *) tlsblock + bootstrap_map.l_tls_offset,
initdtv);
if (TLS_INIT_TP ((char *) tlsblock + bootstrap_map.l_tls_offset, 0)
!= 0)
_dl_fatal_printf ("cannot setup thread-local storage\n");
# elif TLS_DTV_AT_TP
INSTALL_DTV (tlsblock, initdtv);
if (TLS_INIT_TP (tlsblock, 0) != 0)
_dl_fatal_printf ("cannot setup thread-local storage\n");
# else
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
# endif
/* So far this is module number one. * /
bootstrap_map.l_tls_modid = 1;
/* The TP got initialized. * /
bootstrap_map.l_tls_tp_initialized = 1;
/* There can only be one PT_TLS entry. * /
break;
}
#endif /* use TLS * /
*/
//#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)])
{
/* Relocate ourselves so we can do normal function calls and 自我重定位,以便能够使用GOT调用函数和访问数据
data access using the global offset table. */
ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0);
}
7._dl_start->ELF_DYNAMIC_RELOCATE (dynamic-link.h)
/* This can't just be an inline function because GCC is too dumb
to inline functions containing inlines themselves. */
# 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)
/* Set up the loaded object described by L so its unrelocated PLT
entries will jump to the on-demand fixup code in dl-runtime.c. */
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
{
/* The GOT entries for functions in the PLT have not yet been filled
in. Their initial contents will arrange when called to push an
offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1],
and then jump to _GLOBAL_OFFSET_TABLE[2]. */
got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]);//取PLTGOT地址
/* If a library is prelinked but we have to relocate anyway,
we have to be able to undo the prelinking of .got.plt.
The prelinker saved us here address of .plt + 0x16. */
/*
[zws@mail elf]$ readelf -x 21 a.out
Hex dump of section '.got.plt':
0x080494e8 1c940408 00000000 00000000 5e820408 ............^...
0x080494f8 6e820408 n...
第一个存放.dynamic节的地址
第二个存放link_map地址
第三个存放_dl_runtime_resolve地址
*/
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; /* Identify this shared object.存放本模块的link_map */
/* The got[2] entry contains the address of a function which gets
called to get the address of a so far unresolved function and
jump to it. The profiling extension of the dynamic linker allows
to intercept the calls to collect information. In this case we
don't store the address in the GOT so that all future calls also
end in this function. */
if (__builtin_expect (profile, 0))
{
got[2] = (Elf32_Addr) &_dl_runtime_profile;
if (_dl_name_match_p (GL(dl_profile), l))
/* This is the object we are looking for. Say that we really
want profiling and the timers are started. */
GL(dl_profile_map) = l;
}
else
/* This function will get called to fix up the GOT entry indicated by
the offset on the stack, and then jump to the resolved address. */
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]) /* DT_REL,是否有.rel.dyn节,0x00000011 (REL) 0x858*/ \
{ \
ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]);/*节地址,节长*/ \
ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val;/* 0x00000012 (RELSZ) 112 (bytes)*/ \
} \
if ((map)->l_info[DT_PLTREL]/*是否有.rel.plt
节, 0x00000014 (PLTREL) REL*/ \
&& (!test_rel/*test_rel==0*/ || (map)->l_info[DT_PLTREL]->d_un.d_val == DT_##RELOC/*值是否为DT_REL*/)) \
{ \
ElfW(Addr) start = D_PTR ((map), l_info[DT_JMPREL]); /*.rel.plt节地址, 0x00000017 (JMPREL) 0x8c8*/ \
\
if (! ELF_DURING_STARTUP /*该宏定位为1*/ \
&& ((do_lazy)/*do_lazy==0*/ \
/* This test does not only detect whether the relocation \
sections are in the right order, it also checks whether \
there is a DT_REL/DT_RELA section. */ \
|| ranges[0].start + ranges[0].size != start))/*.rel.dyn节和.rel.plt节不连续*/ \
{ \
ranges[1].start = start; \
ranges[1].size = (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \
ranges[1].lazy = (do_lazy); \
} \
else \
{ \
/* Combine processing the sections.显然应该走这里 */ \
assert (ranges[0].start + ranges[0].size == start); /*地址连续*/ \
ranges[0].size += (map)->l_info[DT_PLTRELSZ]->d_un.d_val;/*合并大小, 0x00000002 (PLTRELSZ) 72 (bytes)*/ \
} \
} \
\
if (ELF_DURING_STARTUP) /*1*/ \
elf_dynamic_do_##reloc ((map), ranges[0].start, ranges[0].size, 0); /*调用elf_dynamic_do_rel */ \
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的重定位信息
[zws@mail ~/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
[zws@mail ~/glibc-2.3/build/elf]$
10._dl_start->ELF_DYNAMIC_RELOCATE ->_ELF_DYNAMIC_DO_RELOC->elf_dynamic_do_rel(elf/dynamic-link.h)
执行实质的重定位操作
/* Perform the relocations in MAP on the running program image as specified
by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT
relocations; they should be set up to call _dl_runtime_resolve, rather
than fully resolved now. */
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;
/*
#if (!defined DO_RELA || !defined ELF_MACHINE_PLT_REL) && !defined RTLD_BOOTSTRAP
/* We never bind lazily during ld.so bootstrap. Unfortunately gcc is
not clever enough to see through all the function calls to realize
that. * /
if (lazy)
{
/* Doing lazy PLT relocations; they need very little info. * /
for (; r < end; ++r)
elf_machine_lazy_rel (map, l_addr, r);
}
else
#endif
*/
{
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)));
/*
#ifndef RTLD_BOOTSTRAP
/* This is defined in rtld.c, but nowhere in the static libc.a; make
the reference weak so static programs can still link. This
declaration cannot be done when compiling rtld.c (i.e. #ifdef
RTLD_BOOTSTRAP) because rtld.c contains the common defn for
_dl_rtld_map, which is incompatible with a weak decl in the same
file. * /
# ifndef SHARED
weak_extern (GL(dl_rtld_map));
# endif
if (map != &GL(dl_rtld_map)) /* Already done in rtld itself. * /
# if !defined DO_RELA || defined ELF_MACHINE_REL_RELATIVE
/* Rela platforms get the offset from r_addend and this must
be copied in the relocation address. Therefore we can skip
the relative relocations only if this is for rel
relocations or rela relocations if they are computed as
memory_loc += l_addr... * /
if (l_addr != 0)
# else
/* ...or we know the object has been prelinked. * /
if (l_addr != 0 || ! map->l_info[VALIDX(DT_GNU_PRELINKED)])
# endif
#endif
*/
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
/* The dynamic linker always uses versioning. */
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));
/*等价于
Elf32_Half ndx = version[((r->r_info) >> 8)] & 0x7fff;
elf_machine_rel (map, r, &symtab[((r->r_info) >> 8)],
&map->l_versions[ndx],
(void *) (l_addr + r->r_offset));
*/
}
}
/*
#ifndef RTLD_BOOTSTRAP
else
for (; r < end; ++r)
elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL,
(void *) (l_addr + r->r_offset));
#endif
*/
}
}
ld.so的版本符号表是
[zws@mail ~/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)
/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
MAP is the object containing the reloc. */
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);
/*
#if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
if (__builtin_expect (r_type == R_386_RELATIVE, 0))
{
# if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
/* This is defined in rtld.c, but nowhere in the static libc.a;
make the reference weak so static programs can still link.
This declaration cannot be done when compiling rtld.c
(i.e. #ifdef RTLD_BOOTSTRAP) because rtld.c contains the
common defn for _dl_rtld_map, which is incompatible with a
weak decl in the same file. * /
# ifndef SHARED
weak_extern (_dl_rtld_map);
# endif
if (map != &GL(dl_rtld_map)) /* Already done in rtld itself. * /
# endif
*reloc_addr += map->l_addr;
}
# ifndef RTLD_BOOTSTRAP
else if (__builtin_expect (r_type == R_386_NONE, 0))
return;
# endif
else
#endif
*/
{
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,完成动态链接
/* Please note that we don't allow profiling of this object and
therefore need not test whether we have to allocate the array
for the relocation results (as done in dl-reloc.c). */
/* Now life is sane; we can call functions and access global data.
Set up to use the operating system facilities, and find out from
the operating system's program loader where to find the program
header table in core. Put the rest of _dl_start into a separate
将_dl_start中剩下的工作放在独立的函数中,这样编译器就不会将需要
function, that way the compiler cannot put accesses to the GOT
访问GOT的操作放在ELF_DYNAMIC_RELOCATE之前
before ELF_DYNAMIC_RELOCATE. */
{
//#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;
}
}