ld.so分析5 _dl_start

来源:互联网 发布:慕思城的淘宝视频 编辑:程序博客网 时间:2024/05/19 17:10
ld.so分析5 _dl_start 2010-05-06 08:53:24

分类: LINUX

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;
  }
}