linux 获取so基址,ld.so分析5 _dl_start-程序员宅基地

技术标签: linux 获取so基址  

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) |

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30325819/article/details/116972247

智能推荐

云上攻防-云原生篇&amp;Kubernetes&amp;K8s安全&amp;API&amp;Kubelet未授权访问&amp;容器执行(1)-程序员宅基地

文章浏览阅读974次,点赞14次,收藏10次。Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,不要看完;在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察。

[android] 将Java程序移植到android上_安卓 java层 移植-程序员宅基地

文章浏览阅读8.4k次。将Java程序移植到android上_安卓 java层 移植

对象存储入门 [ minIo ozone ]_对象存储minio和apache ozone-程序员宅基地

文章浏览阅读3k次。一. 前言OSS(Object Storage Service)俗称对象存储,主要提供图片、文档、音频、视频等二进制文件的海量存储功能。目前除了公有云提供对象存储服务外,一般私有云比较关心一些开源的分布式对象存储解决方案。公有云一般都提供对象存储服务,如阿里云的OSS,华为云的OBS,腾讯云的COS。一些开源的对象存储都会遵循Amazon s3协议。Amazon s3协议定义了操作对象存储的Resestfull风格的API。通过在pom中引用aws-java-sdk-s3可以实现对存储的操作。 _对象存储minio和apache ozone

Macbook安装Latex工具详细教程_mac安装latex-程序员宅基地

文章浏览阅读3.3k次,点赞6次,收藏23次。Index of /CTAN/systems/mac/mactex/ | 清华大学开源软件镜像站,致力于为国内和校内用户提供高质量的开源软件镜像、Linux 镜像源服务,帮助用户更方便地获取开源软件。Index of /CTAN/systems/mac/mactex/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror。1.安装Mactex用清华镜像安装更快,其中选择下图所示版本。2.安装Sublime Text。,安装后重启Sublime。_mac安装latex

华为云云耀云服务器L实例评测|搭建WordPress网站_云耀服务器可以创建网站吗-程序员宅基地

文章浏览阅读2w次,点赞56次,收藏49次。如今,云计算技术已经让云端建设变得更加容易,不再是高级技术人员的专利。通过本文所述的步骤,您可以轻松地在云服务器上搭建自己的网站或应用程序,而且成本非常低廉。从设置服务器密码到安装WordPress,每一步都是为了让您更好地利用云计算的便利性。所以,不要犹豫,开始您的云计算之旅吧!无论是个人博客、在线商店还是其他项目,云计算都可以为您提供可扩展、安全和高效的解决方案。让云端成为您的创意空间,让数字世界更加精彩。_云耀服务器可以创建网站吗

shell正则_shell 遍历路径正则-程序员宅基地

文章浏览阅读300次。http://blog.chinaunix.net/uid-25120309-id-3349691.html_shell 遍历路径正则

随便推点

IDEA用Maven打包Spark(去掉不需要的依赖) POM文件_idea如何打没有依赖的包-程序员宅基地

文章浏览阅读2.3k次。1. 根据项目需要添加依赖;服务器上已经有大数据组件的依赖都无需打包依赖,所以可以在依赖加上<scope>provided</scope>2. 如果使用IDEA,在本地运行项目时要在运行配置勾选【Include dependencies with “Provided” scope】,否则会编译出错以下是POM.xml文件<?xml version="1.0" ..._idea如何打没有依赖的包

linux下实现录音和播放_linux c语言 应用 一边录音 一边播放-程序员宅基地

文章浏览阅读3.4k次。linux下编写本地录音和播放demo测试代码:1. 通过读.wav格式的音频流,将音频流写入DMA缓存区,实现语音播放;2. 通过读DMA缓存区的音频流,实现录音。首先要配置硬件参数,包括设置采样位数、通道数、采样率等,然后向DMA缓存区写或者读,实现播放和录音,具体见下文:/************************************************************************* > File Name: local_player.c_linux c语言 应用 一边录音 一边播放

PHP中几种常见的超时处理全面总结_除了 file_get_contents 还有哪些能卡死进程-程序员宅基地

文章浏览阅读943次。在PHP开发中工作里非常多使用到超时处理到超时的场合,我说几个场景: 1. 异步获取数据如果某个后端数据源获取不成功则跳过,不影响整个页面展现 2. 为了保证Web服务器不会因为当个页面处理性能差而导致无法访问其他页面,则会对某些页面操作设置 3. 对于某些上传或者不确定处理时间的场合,则需要对整个流程中所有超时设置为无限,否则任何一个环节设置不当,都会导致莫名执行中断 4_除了 file_get_contents 还有哪些能卡死进程

【数学建模】元胞自动机-程序员宅基地

文章浏览阅读3.1k次,点赞55次,收藏63次。【数学建模】元胞自动机_元胞自动机

python 基因序列提取_Python + 生物信息 05 : 提取 CDS 等其他特征序列-程序员宅基地

文章浏览阅读1.4k次。1 介绍在基因结构分析或其他生物功能分析中会时常用到 CDS 序列,以及其他诸如 mRNA 序列,misc RNA序列等具有生物意义的序列片段。而NCBI 的基因库中已经包含有这些的信息,但是只有一部分是整理可下载的。而剩下的一部分可以通过 genbank给出的位点信息来提取,个人能力有限,这里只做抛转之用。下面以提取 CDS 为例,记录提取序列过程,其他特征序列类似。2 结构目录3 Python..._提取dna中cds序列

php 获取 mysql多行数据 存在数组中_mysql 将多行数据添加到数组-程序员宅基地

文章浏览阅读753次。个人习惯使用面向对象的方式来操作mysql。首先定义一个二维数组,用来存储结果。结果集取得一行就是一个数组,因为有多行数据,所以是二维。$data=array();这条代码$result->fetch_assoc()是: 从结果集中取得一行作为关联数组:那么就有:while($row = $result->fetch_assoc()){ array_push($data,$row);}不断从结果集读取数据,并将每行数据加到二维数组data中,直到读取完毕_mysql 将多行数据添加到数组

推荐文章

热门文章

相关标签