linux 获取so基址,ld.so分析5 _dl_start

ld.so分析5 _dl_start

对于不关心的地方,我们都//或/**/注释掉

1._dl_start中的变量声明

static Elf32_Addr //我们假设是i386 32位平台,ElfW(Addr)被宏扩展为Elf32_Addr

//ElfW(Addr)

//__attribute_used__ internal_function

//__attribute__ ((__used__)) __attribute__ ((regparm (3), stdcall))

_dl_start (void *arg)//arg参数值argc地址

{

//#ifdef DONT_USE_BOOTSTRAP_MAP

# define bootstrap_map GL(dl_rtld_map)

//#else

//  struct dl_start_final_info info;

//# define bootstrap_map info.l

//#endif

//#if USE_TLS || (!DONT_USE_BOOTSTRAP_MAP && !HAVE_BUILTIN_MEMSET)

//  size_t cnt;

//#endif

//#ifdef USE_TLS

//  ElfW(Ehdr) *ehdr;

//  ElfW(Phdr) *phdr;

//  dtv_t initdtv[3];

//#endif

宏GL定义如下

#  define GL(name) _rtld_local._##name

展开

#define bootstrap_map _rtld_local._dl_rtld_map

_rtld_local是什么呢?

查看rtld.c的预处理文件可发现如下定义

struct rtld_global _rtld_global =

{

# 1 "../sysdeps/unix/sysv/linux/i386/dl-procinfo.c" 1

# 47 "../sysdeps/unix/sysv/linux/i386/dl-procinfo.c"

._dl_x86_cap_flags

= {

"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",

"cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",

"pat", "pse36", "pn", "clflush", "20", "dts", "acpi", "mmx",

"fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "amd3d"

}

,

._dl_x86_platforms

= {

"i386", "i486", "i586", "i686"

}

,

# 92 "rtld.c" 2

._dl_debug_fd = 2,

._dl_dynamic_weak = 1,

._dl_lazy = 1,

._dl_fpu_control = 0x037f,

._dl_correct_cache_id = 3,

._dl_hwcap_mask = HWCAP_IMPORTANT,

._dl_load_lock = { {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, { 0, 0 }}}

};

extern struct rtld_global _rtld_local __attribute__ ((visibility ("hidden")));

extern __typeof (_rtld_global) _rtld_local __attribute__ ((alias ("_rtld_global")));;

结构rtld_global的内容就不贴出来了,大家自己查吧

这里指出,_rtld_local是_rtld_global的别名.查看ld.so的符号表也能例证

[zws@mail ~/glibc-2.3/build/elf]$readelf -s ld.so|grep _rtld

332: 00012140   980 OBJECT  LOCAL  HIDDEN   14 _rtld_local

462: 00012140   980 OBJECT  GLOBAL DEFAULT   14 _rtld_global

_rtld_local._dl_rtld_map的类型是struct link_map.这个类型非常重要,是动态链接的核心数据结构

注意这里的HIDDEN属性,这个属性保证访问_rtld_local使用_rtld_local@GOTOFF而不是_rtld_local@GOT,

从而_rtld_local不需要重定位,这个一定很重要

2._dl_start中的动态链接内联函数

/* This #define produces dynamic linking inline functions for

bootstrap relocation instead of general-purpose relocation.  */

#define RTLD_BOOTSTRAP

#define RESOLVE_MAP(sym, version, flags) \

((*(sym))->st_shndx == SHN_UNDEF ? 0 : &bootstrap_map)

#define RESOLVE(sym, version, flags) \

((*(sym))->st_shndx == SHN_UNDEF ? 0 : bootstrap_map.l_addr)

#include "dynamic-link.h"

这里先定义了三个宏,然后包含dynamic-link.h头文件,里面定义了几个动态链接需要用到的宏或函数。

这些宏或函数用到了前面定义的三个宏,因此,根据这三个宏定义的不同,动态链接宏或函数的功能会有所不同,

前面的注释也说明了这一点。至于有这些动态链接宏或函数的功能,后面涉及到的时候再分析。

3.获取ld.so的加载基址

if (HP_TIMING_INLINE && HP_TIMING_AVAIL)

//#ifdef DONT_USE_BOOTSTRAP_MAP

HP_TIMING_NOW (start_time);//获得开始时间

//#else

//    HP_TIMING_NOW (info.start_time);

//#endif

/* Partly clean the `bootstrap_map' structure up. 部分清空bootstrap_map结构. Don't use

`memset' since it might not be built in or inlined and we cannot

不使用memset是因为它不是内建的或内联函数,我们现在还不能调用.

make function calls at this point.  Use '__builtin_memset' if we

如果有效的话,使用__builtin_memset

know it is available.  We do not have to clear the memory if we

如果不必使用临时bootstrap_map则不需要清0

do not have to use the temporary bootstrap_map.  Global variables

全局变量缺省初始化为0

are initialized to zero by default.  */

/*

#ifndef DONT_USE_BOOTSTRAP_MAP

# ifdef HAVE_BUILTIN_MEMSET

__builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info));

# else

for (cnt = 0;

cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]);

++cnt)

bootstrap_map.l_info[cnt] = 0;

# endif

#endif

*/

/* Figure out the run-time load address of the dynamic linker itself.  */

bootstrap_map.l_addr = elf_machine_load_address ();//  加载地址 _rtld_local._dl_rtld_map.l_addr = elf_machine_load_address ();

/* Read our own dynamic section and fill in the info array.  */

bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic ();//动态节地址

elf_get_dynamic_info (&bootstrap_map);//取动态信息

4.elf_machine_dynamic和elf_machine_load_address (sysdeps/i386/dl-machine.h)

/* Return the link-time address of _DYNAMIC.  Conveniently, this is the

first element of the GOT, a special entry that is never relocated.  */

static inline Elf32_Addr //__attribute__ ((unused, const))

elf_machine_dynamic (void)

{

/* This produces a GOTOFF reloc that resolves to zero at link time, so in

fact just loads from the GOT register directly.  By doing it without

an asm we can let the compiler choose any register.  */

extern const Elf32_Addr _GLOBAL_OFFSET_TABLE_[] attribute_hidden;

return _GLOBAL_OFFSET_TABLE_[0];

}

/* Return the run-time load address of the shared object.  */

static inline Elf32_Addr //__attribute__ ((unused))

elf_machine_load_address (void)

{

/* Compute the difference between the runtime address of _DYNAMIC as seen

by a GOTOFF reference, and the link-time address found in the special

unrelocated first GOT entry.  */

extern Elf32_Dyn bygotoff[] asm ("_DYNAMIC");// attribute_hidden;

return (Elf32_Addr) &bygotoff - elf_machine_dynamic ();

}

有点晦涩难懂,看看汇编代码

bootstrap_map.l_addr = elf_machine_load_address ();

生成的汇编代码如下

movl    _GLOBAL_OFFSET_TABLE_@GOTOFF(%ebx), %edx//取GOT[0],即ld.so的dynamic节被ld静态链接时安排的地址

leal    _DYNAMIC@GOTOFF(%ebx), %eax//取dynamic节运行时加载到内存中的地址

subl    %edx, %eax//dynamic的地址-got[0],即得镜像加载基址

movl    %eax, 456+_rtld_local@GOTOFF(%ebx)//该地址存入l_addr

C代码和汇编代码对照着看,就能明白一二。

5.elf_get_dynamic_info (dynamic-link.h)

/* Read the dynamic section at DYN and fill in INFO with indices DT_*.  */

static inline void //__attribute__ ((unused, always_inline))

elf_get_dynamic_info (struct link_map *l)

{

ElfW(Dyn) *dyn = l->l_ld;

ElfW(Dyn) **info;

//#ifndef RTLD_BOOTSTRAP

if (dyn == NULL)

return;

//#endif

/*

[zws@mail elf]$ readelf -d ld.so

Dynamic section at offset 0x12000 contains 18 entrie

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值