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;

}

}

阅读(3077) | 评论(0) | 转发(0) |

linux 获取so基址,ld.so分析5 _dl_start相关推荐

  1. linux ld so 源码分析,ld.so分析2

    ld.so分析2 内核是如何执行程序的,本分析基于内核版本2.4.0 1.用户空间接口 man execve显示如下的函数原型 execve - execute program SYNOPSIS #i ...

  2. python将Linux下使用top命令获取的进程信息进行分析做可视化展示

    python将Linux下使用top命令获取的进程信息进行分析做可视化展示 版本 版本 作者 日期 备注 v1.0 ZY 2020.11.10 初版完成 文章目录 python将Linux下使用top ...

  3. 获取linux内核基址,LInux内核如何获取段基址和段长度

    引题: 0.11内核版本代码在do_exit函数中需要获取段的基址和长度 free_page_tables (get_base (current->ldt[1]), get_limit (0x0 ...

  4. ARM linux的启动部分源代码简略分析

    ARM linux的启动部分源代码简略分析 以友善之臂的mini2440开发板为平台,以较新的内核linux-2.6.32.7版本为例,仅作说明之用. 当内核映像被加载到RAM之后,Bootloade ...

  5. ARM linux的启动部分源代码简略分析【转】

    转自:http://www.cnblogs.com/armlinux/archive/2011/11/07/2396784.html ARM linux的启动部分源代码简略分析 以友善之臂的mini2 ...

  6. python上网行为分析_python实战练手项目---获取谷歌浏览器的历史记录,分析一个人的上网行为...

    python实战练手项目---获取谷歌浏览器的历史记录,分析一个人的上网行为 谷歌浏览器的历史浏览记录存储在名为History sqlite文件中,在mac环境下,该文件的地址是 /Users/zha ...

  7. Linux保护文件实现,Linux完整性保护机制模块实现分析(1)

    原标题:Linux完整性保护机制模块实现分析(1) 2 详细分析2.1 模块功能描述 文件系统完整性模块包含四种机制:监控磁盘机制.同步机制.检查修复文件系统机制.监视文件系统机制. 1.监控磁盘机制 ...

  8. linux源码acl,Linux自主访问控制机制模块详细分析之posix_acl.c核心代码注释与acl.c文件介绍...

    原标题:Linux自主访问控制机制模块详细分析之posix_acl.c核心代码注释与acl.c文件介绍 2.4.4.6 核心代码注释 1 posix_acl_permission() int(stru ...

  9. Linux内核IP Queue机制的分析(一)

    将会通过包括本文在内的三篇文章,对IP Queue机制从用户态的应用到内核态的模块程序设计进行分析.三篇文章的题目分别是: Linux内核IP Queue机制的分析(一)--用户态接收数据包 Linu ...

最新文章

  1. python 画希尔伯特曲线
  2. exit()函数详解与Exit() 和 Return() 的区别
  3. 关于 数据文件自增长 的一点理解
  4. linux 同步IO: sync msync、fsync、fdatasync与 fflush
  5. (50)Xilinx Multiplier IP核配置(十一)(第10天)
  6. 一些常用的WebServices
  7. 比较三个数的大小,让其按大小顺序排列
  8. WSO2 ESB 5.0.0 配置 MySQL 数据源
  9. 金蝶KIS专业版“登录时出现问题,请重新输入”终极解决全过程
  10. 常微分方程简要复习_笔记_第2章:一阶微分方程的初等解法
  11. 版权微talk | 两部门发文,拟出台相关方案,全面加强知识产权保护
  12. 360 os3.0 android7.1,360OS 3.0系统
  13. linux进程signal,Linux Signal 示例
  14. 韩咏梅:幸福只需要七分饱(转自新加坡联合早报)
  15. 智己汽车,兼顾豪华舒适和操控性能
  16. JS两个日期之间计算时间差
  17. 了解阿克曼转向原理的作用
  18. ADB 使用详细教程——Awesome Adb
  19. 201_DMA-BUF简单介绍
  20. 微信 - 电脑(PC)版微信关闭自动下载文件

热门文章

  1. ofo 被爆仍在自动续费;苹果加紧培养新一代接班人;谷歌推出 Android 11 Go | 极客头条...
  2. 基于代码、社区,两步成为开源赢家!
  3. 第四代移动机器人:灵动科技V-AMR全球首发
  4. 哟,2020 年了,用 Vue 做一个自己的小程序吧!| 原力计划
  5. 万人马拉松,人脸识别系统如何又快又准完成校验?
  6. “夸夸机器人” App 来了:变身百万粉丝大 V,48 万人给你的帖子点赞
  7. 写给新手看的 Spring Boot 入门学习指南
  8. 物联网时代,安全该如何加固?
  9. 万维网之父:Facebook、Google 等硅谷巨头必须被拆分!
  10. 中兴侵权案败诉需赔 2.89 亿元;HTC 裁员 1500 人;Android P Beta 3 发布 | CSDN 极客头条...