glibc中动态链接器(ld.so)的bootstrap代码解析

本文深入探讨了glibc中的动态链接器ld.so如何在Linux环境中完成自身加载和重定位过程。ld.so作为一个共享对象,其GOT表解析和初始化涉及到一个特殊的bootstrap机制,该机制解决了启动过程中的先有鸡还是先有蛋的问题。
部署运行你感兴趣的模型镜像

我们都知道动态链接是由一个叫做动态链接器的工具来来完成,在linux中即为ld.so(ld.linux.so),

但是它本身也作为一个共享对象存在,那么它本身的重定位,GOT表解析又是谁完成的呢?

解开这个先有鸡还是先有蛋的谜团的是使glibc中的一段代码,也就是本文要分析的代码,

它使用了一种要做bootstrap的思想,中文我也想不好太好的词来翻译,只可意会不可言喻呀!

static ElfW(Addr)//Elf32_Addr或者Elf64_Addr
 __attribute_used__ internal_function
_dl_start (void *arg)
{
#ifdef DONT_USE_BOOTSTRAP_MAP
# define bootstrap_map GL(dl_rtld_map)
//展开之后为#define bootstap_map rtld_local._dl_trld_map
#else
  struct dl_start_final_info info;
# define bootstrap_map info.l
#endif

  /* This #define produces dynamic linking inline functions for
     bootstrap relocation instead of general-purpose relocation.
     Since ld.so must not have any undefined symbols the result
     is trivial: always the map of ld.so itself.  */
#define RTLD_BOOTSTRAP
#define RESOLVE_MAP(sym, version, flags) (&bootstrap_map)
#include "dynamic-link.h"
/*里面有几个动态链接需要用的宏和函数,会用到前面这两个宏
如头文件dynamic-link.h中下面这段代码,会根据上面宏的定义与否功能不同
#ifndef RESOLVE_MAP
static
#else
auto
#endif
inline void __attribute__ ((unused, always_inline))
elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
{
  ElfW(Dyn) *dyn = l->l_ld;
  ElfW(Dyn) **info;

#ifndef RTLD_BOOTSTRAP
  if (dyn == NULL)
    return;
#endif
*/
  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.  Don't use
     `memset' since it might not be built in or inlined and we cannot
     make function calls at this point.  Use '__builtin_memset' if we
     know it is available.  We do not have to clear the memory if we
     do not have to use the temporary bootstrap_map.  Global variables
     are initialized to zero by default.  */
#ifndef DONT_USE_BOOTSTRAP_MAP
# ifdef HAVE_BUILTIN_MEMSET
/*初始化bootstrap_map的l_info字段,不能使用memset是因为这段代码绝对不能依赖任何其他模块
而memset属于其他模块,需要切断这个依赖,如果HAVE_BUILTIN_MEMSET有效,则使用__builtin_memset
如果没有,则手动循环清零*/
  __builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info));
# else
  for (size_t cnt = 0;
       cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]);
       ++cnt)
    bootstrap_map.l_info[cnt] = 0;
# endif
# if USE___THREAD
  bootstrap_map.l_tls_modid = 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(),
  elf_machine_load_address()跟平台相关,表示 the run-time load address of the shared object.*/

  /* Read our own dynamic section and fill in the info array.  */
  bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic ();
  /*_rtld_local._dl_rtld_map.l_ld =(void *) _rtld_local._dl_rtld_map.l_addr elf_machine_dynamic(),
  elf_machine_dynamic()跟平台相关,表示 the link-time address of _DYNAMIC.*/
  
  
  elf_get_dynamic_info (&bootstrap_map, NULL);
  /* Read the dynamic section at DYN and fill in INFO with indices DT_*.  */
#if NO_TLS_OFFSET != 0
  bootstrap_map.l_tls_offset = NO_TLS_OFFSET;
#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.  */
#if USE___THREAD
  dtv_t initdtv[3];
  ElfW(Ehdr) *ehdr
# ifdef DONT_USE_BOOTSTRAP_MAP
    = (ElfW(Ehdr) *) &_begin;
# else
#  error This will not work with prelink.
    = (ElfW(Ehdr) *) bootstrap_map.l_addr;//上面获取的地址
# endif
  ElfW(Phdr) *phdr = (ElfW(Phdr) *) ((void *) ehdr + ehdr->e_phoff);
  /* e_phoff 为 Program header table file offset */
  size_t cnt = ehdr->e_phnum;	
  /* e_phnum 为  Program header table entry count */
  while (cnt-- > 0)
    if (phdr[cnt].p_type == PT_TLS)/* PT_TLS is usually the last phdr.  */
      {
	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;
	if (phdr[cnt].p_align == 0)
	  bootstrap_map.l_tls_firstbyte_offset = 0;
	else
	  bootstrap_map.l_tls_firstbyte_offset = (phdr[cnt].p_vaddr
						  & (phdr[cnt].p_align - 1));
	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
/* 这个宏为平台相关,具体解释为The TCB can have any size and the memory following the address the
   thread pointer points to is unspecified.  Allocate the TCB there.  */
	tlsblock = alloca (roundup (bootstrap_map.l_tls_blocksize,
				    TLS_INIT_TCB_ALIGN)
			   + TLS_INIT_TCB_SIZE
			   + max_align);
			   /*#define alloca(size) __builtin_alloca (size)*/
# elif TLS_DTV_AT_TP/* The TP points to the start of the thread blocks.  */
	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
	/*__builtin_mempcpy*/
	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);

	const char *lossage = TLS_INIT_TP ((char *) tlsblock
					   + bootstrap_map.l_tls_offset, 0);
# elif TLS_DTV_AT_TP
	INSTALL_DTV (tlsblock, initdtv);
	const char *lossage = TLS_INIT_TP (tlsblock, 0);
# else
#  error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
# endif
	if (__builtin_expect (lossage != NULL, 0))
	  _dl_fatal_printf ("cannot set up thread-local storage: %s\n",
			    lossage);

	/* So far this is module number one.  */
	bootstrap_map.l_tls_modid = 1;

	/* There can only be one PT_TLS entry.  */
	break;
      }
#endif	/* USE___THREAD */

#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
	 data access using the global offset table.  */

      ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0);
    }
  bootstrap_map.l_relocated = 1;/*bootstrap完成*/

  /* 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
     function, that way the compiler cannot put accesses to the GOT
     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_ns)[LM_ID_BASE]._ns_loaded, entry);
  }
}



您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值