linux进程分析之旅04---程序加载分析之_RTLD

 

一.RTLD的作用

         当程序需要动态链接 的应用被操作系统加载时,系统必须要重定位,然后加载它所需要的所有动态库文件。 这项工作是由glibc中RTLD(Run time dynamic linker)来负责完成的。而ld.so是动态链接程序RTLD生成的动态链接库.那么就出现一个问题:RTLD也就是ld.so是如何加载,加载完成之后它又是如何加载其他的so函数的呢?通过下文分析RTLD的逻辑就可以明白。

使用strace查看程序所需的动态链接的执行流程

1.ldd命令查看所需动态库:

2.strace命令,查看so库加载过程 open->fstat->mmap->close:

二.内核如何进入到用户态 _start

     内核执行load_elf_binary到start_thread()这个宏操作会将pc和sp改成新的地址,就使得CPU在返回用户空间时就进入新的程序入口,即函数 _start(可以在gdb中使用 b _start 设置断点分析)。如果存在解释器映像,那么这就是解释器映像的程序入口,否则就是目标映像的程序入口。

那么什么情况下有解释器映像存在,什么情况下没有呢? 

如果目标映像与各种库的链接是静态链接,因而无需依靠共享库、即动态链接库,那就不需要解释器映像;否则就一定要有解释器映像存在。

      对于一个目标程序, gcc在编译时,除非显示的使用static标签,否则所有程序的链接都是动态链接的,也就是说需要解释器。由此可见,我们的程序在被内核加载到内存,内核跳到用户空间后并不是执行我们程序的,而是先把控制权交到用户空间的解释器,由解释器加载运行用户程序所需要的动态库(比如glibc等等),然后控制权才会转移到用户程序。

PS:有兴趣的小伙伴可以使用strace命令执行static和非static程序异同,就可以发现static程序没有对glibc及其他动态库进行加载映射(open->fstat->mmap->close),也就明白了strace在执行main函数之前的一些函数是libc中的。

 

三、GLibc源码中的RTLD函数执行流程

RTLD_START()					(sysdeps/i386/dl-machine.h)
  _dl_start()					(elf/rtld.c)
	elf_machine_load_addr()
	elf_get_dynamic_info()					
	ELF_DYNAMIC_RELOCATE()			(elf/dynamic-link.h)
	  elf_machine_runtime_setup()		(sysdeps/i386/dl-machine.h)
	    _ELF_DYNAMIC_DO_RELOC() 		(sysdeps/i386/dl-machine.h)
		elf_dynamic_do_rel()		(elf/do-rel.h)
	            elf_machine{,_lazy_}rel() 	(sysdeps/i386/dl-machine.h)
  _dl_start_final()				(elf/rtld.c)
	_dl_sysdep_start()			(sysdeps/generic/dl-sysdeps.h)
	  _dl_main()				(elf/rtld.c)
	     process_envvars()			(elf/rtld.c)
	     elf_get_dynamic_info()	
	     _dl_setup_hash()			(elf.dl-lookup.c)
	     _dl_new_object()			(elf/dl-object.c)
	     _dl_map_object()			(elf/dl-load.c)
	     _dl_map_object_from_fd()		(elf/dl-load.c)	
	        add_name_to_object()		(elf/dl-load.c)	
	        _dl_new_object()		(elf/dl-object.c)
	        map_segment()			
	        ELF_{PREFERED,FIXED}_ADDRESS()	
	        mprotect()			
	        munmap()
	       _dl_setup_hash()			(elf/dl-lookup.c)
	     _dl_map_object_deps()		(elf/dl-deps.c)
		preload()		
		   _dl_lookup_symbol()		(elf/dl-lookup.c)
		      do_lookup()
		_dl_relocate_object()		(loop in elf/dl-reloc.c)
  _start()					(main binary)

 

1.回到第一个问题,ld.so是如何加载进去的?

那就是 ld.so自身加载

在elf/rtld.c 的_dl_start函数中

 if (bootstrap_map.l_addr || ! bootstrap_map.l_info[VALIDX(DT_GNU_PRELINKED)])
    {
      ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0, 0);
    }
# define ELF_DYNAMIC_RELOCATE(map, lazy, consider_profile, skip_ifunc) \
  do {                                        \
    int edr_lazy = elf_machine_runtime_setup ((map), (lazy),              \
                          (consider_profile));        \
    ELF_DYNAMIC_DO_REL ((map), edr_lazy, skip_ifunc);                 \
    ELF_DYNAMIC_DO_RELA ((map), edr_lazy, skip_ifunc);                \
  } while (0)

  ELF_DYNAMIC_RELOCATE宏定义用来对ld.so自身进行重定位,主要是修改.rela.dyn和.rela.plt中的各个地址值。ld.so需要自己给自己重定位。然后将l_relocated设置为1表示重定位完成。后面就可以访问全局变量和函数了。

2.分析elf/dl-sysdep.c中的 _dl_start->_dl_start_final->_dl_sysdep_start函数


1.通过DL_FIND_ARG_COMPONENTS宏获取栈中的数据,根据kernel 源码 sys_execve函数可知这里依次保存了参数数量、用户变量、环境变量和其他变量(该变量在linux源码的create_elf_table子函数中设置),
DL_FIND_ARG_COMPONENTS宏将这些变量分别存储在用户空间的_dl_argc、_dl_argv、_environ和_dl_auxv中。 
2.接着初始化user_entry为ENTRY_POINT。然后遍历create_elf_table函数中设置的变量,依次设置到各个变量中,其中最重要的就是获取用户程序的入口设置到user_entry中。
3.通过DL_SYSDEP_INIT宏用于设置堆的起始地址,再通过DL_PLATFORM_INIT检查相应变量。接着调整堆的起始地址和页面对齐。 
最后调用dl_main准备为用户程序的执行搭建环境,传入的参数phdr为用户程序的程序头,phnum为程序头的个数,user_entry为程序的起始点。


3.分析elf/rtld.c  中 dl_main函数

static void
dl_main (const ElfW(Phdr) *phdr,
	 ElfW(Word) phnum,
	 ElfW(Addr) *user_entry,
	 ElfW(auxv_t) *auxv)
{
  const ElfW(Phdr) *ph;
  enum mode mode;
  struct link_map *main_map;
  size_t file_size;
  char *file;
  bool has_interp = false;
  unsigned int i;
  bool prelinked = false;
  bool rtld_is_main = false;
#ifndef HP_TIMING_NONAVAIL
  hp_timing_t start;
  hp_timing_t stop;
  hp_timing_t diff;
#endif
  void *tcbp = NULL;

  GL(dl_init_static_tls) = &_dl_nothread_init_static_tls;

#if defined SHARED && defined _LIBC_REENTRANT \
    && defined __rtld_lock_default_lock_recursive
  GL(dl_rtld_lock_recursive) = rtld_lock_default_lock_recursive;
  GL(dl_rtld_unlock_recursive) = rtld_lock_default_unlock_recursive;
#endif

  /* The explicit initialization here is cheaper than processing the reloc
     in the _rtld_local definition's initializer.  */
  GL(dl_make_stack_executable_hook) = &_dl_make_stack_executable;

  /* Process the environment variable which control the behaviour.  */
  process_envvars (&mode);

#ifndef HAVE_INLINED_SYSCALLS
  /* Set up a flag which tells we are just starting.  */
  _dl_starting_up = 1;
#endif

  if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
    {
      /* Ho ho.  We are not the program interpreter!  We are the program
	 itself!  This means someone ran ld.so as a command.  Well, that
	 might be convenient to do sometimes.  We support it by
	 interpreting the args like this:

	 ld.so PROGRAM ARGS...

	 The first argument is the name of a file containing an ELF
	 executable we will load and run with the following arguments.
	 To simplify life here, PROGRAM is searched for using the
	 normal rules for shared objects, rather than $PATH or anything
	 like that.  We just load it and use its entry point; we don't
	 pay attention to its PT_INTERP command (we are the interpreter
	 ourselves).  This is an easy way to test a new ld.so before
	 installing it.  */
      rtld_is_main = true;

      /* Note the place where the dynamic linker actually came from.  */
      GL(dl_rtld_map).l_name = rtld_progname;

      while (_dl_argc > 1)
	if (! strcmp (_dl_argv[1], "--list"))
	  {
	    mode = list;
	    GLRO(dl_lazy) = -1;	/* This means do no dependency analysis.  */

	    ++_dl_skip_args;
	    --_dl_argc;
	    ++_dl_argv;
	  }
	else if (! strcmp (_dl_argv[1], "--verify"))
	  {
	    mode = verify;

	    ++_dl_skip_args;
	    --_dl_argc;
	    ++_dl_argv;
	  }
	else if (! strcmp (_dl_argv[1], "--inhibit-cache"))
	  {
	    GLRO(dl_inhibit_cache) = 1;
	    ++_dl_skip_args;
	    --_dl_argc;
	    ++_dl_argv;
	  }
	else if (! strcmp (_dl_argv[1], "--library-path")
		 && _dl_argc > 2)
	  {
	    library_path = _dl_argv[2];

	    _dl_skip_args += 2;
	    _dl_argc -= 2;
	    _dl_argv += 2;
	  }
	else if (! strcmp (_dl_argv[1], "--inhibit-rpath")
		 && _dl_argc > 2)
	  {
	    GLRO(dl_inhibit_rpath) = _dl_argv[2];

	    _dl_skip_args += 2;
	    _dl_argc -= 2;
	    _dl_argv += 2;
	  }
	else if (! strcmp (_dl_argv[1], "--audit") && _dl_argc > 2)
	  {
	    process_dl_audit (_dl_argv[2]);

	    _dl_skip_args += 2;
	    _dl_argc -= 2;
	    _dl_argv += 2;
	  }
	else
	  break;

      /* If we have no further argument the program was called incorrectly.
	 Grant the user some education.  */
      if (_dl_argc < 2)
	_dl_fatal_printf ("\
Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
You have invoked `ld.so', the helper program for shared library executables.\n\
This program usually lives in the file `/lib/ld.so', and special directives\n\
in executable files using ELF shared libraries tell the system's program\n\
loader to load the helper program from this file.  This helper program loads\n\
the shared libraries needed by the program executable, prepares the program\n\
to run, and runs it.  You may invoke this helper program directly from the\n\
command line to load and run an ELF executable file; this is like executing\n\
that file itself, but always uses this helper program from the file you\n\
specified, instead of the helper program file specified in the executable\n\
file you run.  This is mostly of use for maintainers to test new versions\n\
of this helper program; chances are you did not intend to run this program.\n\
\n\
  --list                list all dependencies and how they are resolved\n\
  --verify              verify that given object really is a dynamically linked\n\
			object we can handle\n\
  --inhibit-cache       Do not use " LD_SO_CACHE "\n\
  --library-path PATH   use given PATH instead of content of the environment\n\
			variable LD_LIBRARY_PATH\n\
  --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names\n\
			in LIST\n\
  --audit LIST          use objects named in LIST as auditors\n");

      ++_dl_skip_args;
      --_dl_argc;
      ++_dl_argv;

      /* The initialization of _dl_stack_flags done below assumes the
	 executable's PT_GNU_STACK may have been honored by the kernel, and
	 so a PT_GNU_STACK with PF_X set means the stack started out with
	 execute permission.  However, this is not really true if the
	 dynamic linker is the executable the kernel loaded.  For this
	 case, we must reinitialize _dl_stack_flags to match the dynamic
	 linker itself.  If the dynamic linker was built with a
	 PT_GNU_STACK, then the kernel may have loaded us with a
	 nonexecutable stack that we will have to make executable when we
	 load the program below unless it has a PT_GNU_STACK indicating
	 nonexecutable stack is ok.  */

      for (ph = phdr; ph < &phdr[phnum]; ++ph)
	if (ph->p_type == PT_GNU_STACK)
	  {
	    GL(dl_stack_flags) = ph->p_flags;
	    break;
	  }

      if (__builtin_expect (mode, normal) == verify)
	{
	  const char *objname;
	  const char *err_str = NULL;
	  struct map_args args;
	  bool malloced;

	  args.str = rtld_progname;
	  args.loader = NULL;
	  args.mode = __RTLD_OPENEXEC;
	  (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit,
				  &args);
	  if (__glibc_unlikely (err_str != NULL))
	    /* We don't free the returned string, the programs stops
	       anyway.  */
	    _exit (EXIT_FAILURE);
	}
      else
	{
	  HP_TIMING_NOW (start);
	  _dl_map_object (NULL, rtld_progname, lt_executable, 0,
			  __RTLD_OPENEXEC, LM_ID_BASE);
	  HP_TIMING_NOW (stop);

	  HP_TIMING_DIFF (load_time, start, stop);
	}

      /* Now the map for the main executable is available.  */
      main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;

      if (__builtin_expect (mode, normal) == normal
	  && GL(dl_rtld_map).l_info[DT_SONAME] != NULL
	  && main_map->l_info[DT_SONAME] != NULL
	  && strcmp ((const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB])
		     + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val,
		     (const char *) D_PTR (main_map, l_info[DT_STRTAB])
		     + main_map->l_info[DT_SONAME]->d_un.d_val) == 0)
	_dl_fatal_printf ("loader cannot load itself\n");

      phdr = main_map->l_phdr;
      phnum = main_map->l_phnum;
      /* We overwrite here a pointer to a malloc()ed string.  But since
	 the malloc() implementation used at this point is the dummy
	 implementations which has no real free() function it does not
	 makes sense to free the old string first.  */
      main_map->l_name = (char *) "";
      *user_entry = main_map->l_entry;

#ifdef HAVE_AUX_VECTOR
      /* Adjust the on-stack auxiliary vector so that it looks like the
	 binary was executed directly.  */
      for (ElfW(auxv_t) *av = auxv; av->a_type != AT_NULL; av++)
	switch (av->a_type)
	  {
	  case AT_PHDR:
	    av->a_un.a_val = (uintptr_t) phdr;
	    break;
	  case AT_PHNUM:
	    av->a_un.a_val = phnum;
	    break;
	  case AT_ENTRY:
	    av->a_un.a_val = *user_entry;
	    break;
	  case AT_EXECFN:
	    av->a_un.a_val = (uintptr_t) _dl_argv[0];
	    break;
	  }
#endif
    }
  else
    {
      /* Create a link_map for the executable itself.
	 This will be what dlopen on "" returns.  */
      main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
				 __RTLD_OPENEXEC, LM_ID_BASE);
      assert (main_map != NULL);
      main_map->l_phdr = phdr;
      main_map->l_phnum = phnum;
      main_map->l_entry = *user_entry;

      /* Even though the link map is not yet fully initialized we can add
	 it to the map list since there are no possible users running yet.  */
      _dl_add_to_namespace_list (main_map, LM_ID_BASE);
      assert (main_map == GL(dl_ns)[LM_ID_BASE]._ns_loaded);

      /* At this point we are in a bit of trouble.  We would have to
	 fill in the values for l_dev and l_ino.  But in general we
	 do not know where the file is.  We also do not handle AT_EXECFD
	 even if it would be passed up.

	 We leave the values here defined to 0.  This is normally no
	 problem as the program code itself is normally no shared
	 object and therefore cannot be loaded dynamically.  Nothing
	 prevent the use of dynamic binaries and in these situations
	 we might get problems.  We might not be able to find out
	 whether the object is already loaded.  But since there is no
	 easy way out and because the dynamic binary must also not
	 have an SONAME we ignore this program for now.  If it becomes
	 a problem we can force people using SONAMEs.  */

      /* We delay initializing the path structure until we got the dynamic
	 information for the program.  */
    }

  main_map->l_map_end = 0;
  main_map->l_text_end = 0;
  /* Perhaps the executable has no PT_LOAD header entries at all.  */
  main_map->l_map_start = ~0;
  /* And it was opened directly.  */
  ++main_map->l_direct_opencount;

  /* Scan the program header table for the dynamic section.  */
  for (ph = phdr; ph < &phdr[phnum]; ++ph)
    switch (ph->p_type)
      {
      case PT_PHDR:
	/* Find out the load address.  */
	main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr;
	break;
      case PT_DYNAMIC:
	/* This tells us where to find the dynamic section,
	   which tells us everything we need to do.  */
	main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr;
	break;
      case PT_INTERP:
	/* This "interpreter segment" was used by the program loader to
	   find the program interpreter, which is this program itself, the
	   dynamic linker.  We note what name finds us, so that a future
	   dlopen call or DT_NEEDED entry, for something that wants to link
	   against the dynamic linker as a shared library, will know that
	   the shared object is already loaded.  */
	_dl_rtld_libname.name = ((const char *) main_map->l_addr
				 + ph->p_vaddr);
	/* _dl_rtld_libname.next = NULL;	Already zero.  */
	GL(dl_rtld_map).l_libname = &_dl_rtld_libname;

	/* Ordinarilly, we would get additional names for the loader from
	   our DT_SONAME.  This can't happen if we were actually linked as
	   a static executable (detect this case when we have no DYNAMIC).
	   If so, assume the filename component of the interpreter path to
	   be our SONAME, and add it to our name list.  */
	if (GL(dl_rtld_map).l_ld == NULL)
	  {
	    const char *p = NULL;
	    const char *cp = _dl_rtld_libname.name;

	    /* Find the filename part of the path.  */
	    while (*cp != '\0')
	      if (*cp++ == '/')
		p = cp;

	    if (p != NULL)
	      {
		_dl_rtld_libname2.name = p;
		/* _dl_rtld_libname2.next = NULL;  Already zero.  */
		_dl_rtld_libname.next = &_dl_rtld_libname2;
	      }
	  }

	has_interp = true;
	break;
      case PT_LOAD:
	{
	  ElfW(Addr) mapstart;
	  ElfW(Addr) allocend;

	  /* Remember where the main program starts in memory.  */
	  mapstart = (main_map->l_addr
		      + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1)));
	  if (main_map->l_map_start > mapstart)
	    main_map->l_map_start = mapstart;

	  /* Also where it ends.  */
	  allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz;
	  if (main_map->l_map_end < allocend)
	    main_map->l_map_end = allocend;
	  if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end)
	    main_map->l_text_end = allocend;
	}
	break;

      case PT_TLS:
	if (ph->p_memsz > 0)
	  {
	    /* Note that in the case the dynamic linker we duplicate work
	       here since we read the PT_TLS entry already in
	       _dl_start_final.  But the result is repeatable so do not
	       check for this special but unimportant case.  */
	    main_map->l_tls_blocksize = ph->p_memsz;
	    main_map->l_tls_align = ph->p_align;
	    if (ph->p_align == 0)
	      main_map->l_tls_firstbyte_offset = 0;
	    else
	      main_map->l_tls_firstbyte_offset = (ph->p_vaddr
						  & (ph->p_align - 1));
	    main_map->l_tls_initimage_size = ph->p_filesz;
	    main_map->l_tls_initimage = (void *) ph->p_vaddr;

	    /* This image gets the ID one.  */
	    GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1;
	  }
	break;

      case PT_GNU_STACK:
	GL(dl_stack_flags) = ph->p_flags;
	break;

      case PT_GNU_RELRO:
	main_map->l_relro_addr = ph->p_vaddr;
	main_map->l_relro_size = ph->p_memsz;
	break;

      case PT_NOTE:
	if (_rtld_process_pt_note (main_map, ph))
	  _dl_error_printf ("\
ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
	break;
      }

  /* Adjust the address of the TLS initialization image in case
     the executable is actually an ET_DYN object.  */
  if (main_map->l_tls_initimage != NULL)
    main_map->l_tls_initimage
      = (char *) main_map->l_tls_initimage + main_map->l_addr;
  if (! main_map->l_map_end)
    main_map->l_map_end = ~0;
  if (! main_map->l_text_end)
    main_map->l_text_end = ~0;
  if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name)
    {
      /* We were invoked directly, so the program might not have a
	 PT_INTERP.  */
      _dl_rtld_libname.name = GL(dl_rtld_map).l_name;
      /* _dl_rtld_libname.next = NULL;	Already zero.  */
      GL(dl_rtld_map).l_libname =  &_dl_rtld_libname;
    }
  else
    assert (GL(dl_rtld_map).l_libname); /* How else did we get here?  */

  /* If the current libname is different from the SONAME, add the
     latter as well.  */
  if (GL(dl_rtld_map).l_info[DT_SONAME] != NULL
      && strcmp (GL(dl_rtld_map).l_libname->name,
		 (const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB])
		 + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val) != 0)
    {
      static struct libname_list newname;
      newname.name = ((char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB])
		      + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_ptr);
      newname.next = NULL;
      newname.dont_free = 1;

      assert (GL(dl_rtld_map).l_libname->next == NULL);
      GL(dl_rtld_map).l_libname->next = &newname;
    }
  /* The ld.so must be relocated since otherwise loading audit modules
     will fail since they reuse the very same ld.so.  */
  assert (GL(dl_rtld_map).l_relocated);

  if (! rtld_is_main)
    {
      /* Extract the contents of the dynamic section for easy access.  */
      elf_get_dynamic_info (main_map, NULL);
      /* Set up our cache of pointers into the hash table.  */
      _dl_setup_hash (main_map);
    }

  if (__builtin_expect (mode, normal) == verify)
    {
      /* We were called just to verify that this is a dynamic
	 executable using us as the program interpreter.  Exit with an
	 error if we were not able to load the binary or no interpreter
	 is specified (i.e., this is no dynamically linked binary.  */
      if (main_map->l_ld == NULL)
	_exit (1);

      /* We allow here some platform specific code.  */
#ifdef DISTINGUISH_LIB_VERSIONS
      DISTINGUISH_LIB_VERSIONS;
#endif
      _exit (has_interp ? 0 : 2);
    }

  struct link_map **first_preload = &GL(dl_rtld_map).l_next;
  /* Set up the data structures for the system-supplied DSO early,
     so they can influence _dl_init_paths.  */
  setup_vdso (main_map, &first_preload);

#ifdef DL_SYSDEP_OSCHECK
  DL_SYSDEP_OSCHECK (_dl_fatal_printf);
#endif

  /* Initialize the data structures for the search paths for shared
     objects.  */
  _dl_init_paths (library_path);

  /* Initialize _r_debug.  */
  struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr,
					    LM_ID_BASE);
  r->r_state = RT_CONSISTENT;

  /* Put the link_map for ourselves on the chain so it can be found by
     name.  Note that at this point the global chain of link maps contains
     exactly one element, which is pointed to by dl_loaded.  */
  if (! GL(dl_rtld_map).l_name)
    /* If not invoked directly, the dynamic linker shared object file was
       found by the PT_INTERP name.  */
    GL(dl_rtld_map).l_name = (char *) GL(dl_rtld_map).l_libname->name;
  GL(dl_rtld_map).l_type = lt_library;
  main_map->l_next = &GL(dl_rtld_map);
  GL(dl_rtld_map).l_prev = main_map;
  ++GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
  ++GL(dl_load_adds);

  /* If LD_USE_LOAD_BIAS env variable has not been seen, default
     to not using bias for non-prelinked PIEs and libraries
     and using it for executables or prelinked PIEs or libraries.  */
  if (GLRO(dl_use_load_bias) == (ElfW(Addr)) -2)
    GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0;

  /* Set up the program header information for the dynamic linker
     itself.  It is needed in the dl_iterate_phdr callbacks.  */
  const ElfW(Ehdr) *rtld_ehdr;

  /* Starting from binutils-2.23, the linker will define the magic symbol
     __ehdr_start to point to our own ELF header if it is visible in a
     segment that also includes the phdrs.  If that's not available, we use
     the old method that assumes the beginning of the file is part of the
     lowest-addressed PT_LOAD segment.  */
#ifdef HAVE_EHDR_START
  extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden")));
  rtld_ehdr = &__ehdr_start;
#else
  rtld_ehdr = (void *) GL(dl_rtld_map).l_map_start;
#endif
  assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
  assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));

  const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;

  GL(dl_rtld_map).l_phdr = rtld_phdr;
  GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;


  /* PT_GNU_RELRO is usually the last phdr.  */
  size_t cnt = rtld_ehdr->e_phnum;
  while (cnt-- > 0)
    if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
      {
	GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr;
	GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz;
	break;
      }

  /* Add the dynamic linker to the TLS list if it also uses TLS.  */
  if (GL(dl_rtld_map).l_tls_blocksize != 0)
    /* Assign a module ID.  Do this before loading any audit modules.  */
    GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();

  /* If we have auditing DSOs to load, do it now.  */
  bool need_security_init = true;
  if (__glibc_unlikely (audit_list != NULL)
      || __glibc_unlikely (audit_list_string != NULL))
    {
      struct audit_ifaces *last_audit = NULL;
      struct audit_list_iter al_iter;
      audit_list_iter_init (&al_iter);

      /* Since we start using the auditing DSOs right away we need to
	 initialize the data structures now.  */
      tcbp = init_tls ();

      /* Initialize security features.  We need to do it this early
	 since otherwise the constructors of the audit libraries will
	 use different values (especially the pointer guard) and will
	 fail later on.  */
      security_init ();
      need_security_init = false;

      while (true)
	{
	  const char *name = audit_list_iter_next (&al_iter);
	  if (name == NULL)
	    break;

	  int tls_idx = GL(dl_tls_max_dtv_idx);

	  /* Now it is time to determine the layout of the static TLS
	     block and allocate it for the initial thread.  Note that we
	     always allocate the static block, we never defer it even if
	     no DF_STATIC_TLS bit is set.  The reason is that we know
	     glibc will use the static model.  */
	  struct dlmopen_args dlmargs;
	  dlmargs.fname = name;
	  dlmargs.map = NULL;

	  const char *objname;
	  const char *err_str = NULL;
	  bool malloced;
	  (void) _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit,
				  &dlmargs);
	  if (__glibc_unlikely (err_str != NULL))
	    {
	    not_loaded:
	      _dl_error_printf ("\
ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
				name, err_str);
	      if (malloced)
		free ((char *) err_str);
	    }
	  else
	    {
	      struct lookup_args largs;
	      largs.name = "la_version";
	      largs.map = dlmargs.map;

	      /* Check whether the interface version matches.  */
	      (void) _dl_catch_error (&objname, &err_str, &malloced,
				      lookup_doit, &largs);

	      unsigned int (*laversion) (unsigned int);
	      unsigned int lav;
	      if  (err_str == NULL
		   && (laversion = largs.result) != NULL
		   && (lav = laversion (LAV_CURRENT)) > 0
		   && lav <= LAV_CURRENT)
		{
		  /* Allocate structure for the callback function pointers.
		     This call can never fail.  */
		  union
		  {
		    struct audit_ifaces ifaces;
#define naudit_ifaces 8
		    void (*fptr[naudit_ifaces]) (void);
		  } *newp = malloc (sizeof (*newp));

		  /* Names of the auditing interfaces.  All in one
		     long string.  */
		  static const char audit_iface_names[] =
		    "la_activity\0"
		    "la_objsearch\0"
		    "la_objopen\0"
		    "la_preinit\0"
#if __ELF_NATIVE_CLASS == 32
		    "la_symbind32\0"
#elif __ELF_NATIVE_CLASS == 64
		    "la_symbind64\0"
#else
# error "__ELF_NATIVE_CLASS must be defined"
#endif
#define STRING(s) __STRING (s)
		    "la_" STRING (ARCH_LA_PLTENTER) "\0"
		    "la_" STRING (ARCH_LA_PLTEXIT) "\0"
		    "la_objclose\0";
		  unsigned int cnt = 0;
		  const char *cp = audit_iface_names;
		  do
		    {
		      largs.name = cp;
		      (void) _dl_catch_error (&objname, &err_str, &malloced,
					      lookup_doit, &largs);

		      /* Store the pointer.  */
		      if (err_str == NULL && largs.result != NULL)
			{
			  newp->fptr[cnt] = largs.result;

			  /* The dynamic linker link map is statically
			     allocated, initialize the data now.   */
			  GL(dl_rtld_map).l_audit[cnt].cookie
			    = (intptr_t) &GL(dl_rtld_map);
			}
		      else
			newp->fptr[cnt] = NULL;
		      ++cnt;

		      cp = (char *) rawmemchr (cp, '\0') + 1;
		    }
		  while (*cp != '\0');
		  assert (cnt == naudit_ifaces);

		  /* Now append the new auditing interface to the list.  */
		  newp->ifaces.next = NULL;
		  if (last_audit == NULL)
		    last_audit = GLRO(dl_audit) = &newp->ifaces;
		  else
		    last_audit = last_audit->next = &newp->ifaces;
		  ++GLRO(dl_naudit);

		  /* Mark the DSO as being used for auditing.  */
		  dlmargs.map->l_auditing = 1;
		}
	      else
		{
		  /* We cannot use the DSO, it does not have the
		     appropriate interfaces or it expects something
		     more recent.  */
#ifndef NDEBUG
		  Lmid_t ns = dlmargs.map->l_ns;
#endif
		  _dl_close (dlmargs.map);

		  /* Make sure the namespace has been cleared entirely.  */
		  assert (GL(dl_ns)[ns]._ns_loaded == NULL);
		  assert (GL(dl_ns)[ns]._ns_nloaded == 0);

		  GL(dl_tls_max_dtv_idx) = tls_idx;
		  goto not_loaded;
		}
	    }
	}

      /* If we have any auditing modules, announce that we already
	 have two objects loaded.  */
      if (__glibc_unlikely (GLRO(dl_naudit) > 0))
	{
	  struct link_map *ls[2] = { main_map, &GL(dl_rtld_map) };

	  for (unsigned int outer = 0; outer < 2; ++outer)
	    {
	      struct audit_ifaces *afct = GLRO(dl_audit);
	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
		{
		  if (afct->objopen != NULL)
		    {
		      ls[outer]->l_audit[cnt].bindflags
			= afct->objopen (ls[outer], LM_ID_BASE,
					 &ls[outer]->l_audit[cnt].cookie);

		      ls[outer]->l_audit_any_plt
			|= ls[outer]->l_audit[cnt].bindflags != 0;
		    }

		  afct = afct->next;
		}
	    }
	}
    }

  /* Keep track of the currently loaded modules to count how many
     non-audit modules which use TLS are loaded.  */
  size_t count_modids = _dl_count_modids ();

  /* Set up debugging before the debugger is notified for the first time.  */
#ifdef ELF_MACHINE_DEBUG_SETUP
  /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way.  */
  ELF_MACHINE_DEBUG_SETUP (main_map, r);
  ELF_MACHINE_DEBUG_SETUP (&GL(dl_rtld_map), r);
#else
  if (main_map->l_info[DT_DEBUG] != NULL)
    /* There is a DT_DEBUG entry in the dynamic section.  Fill it in
       with the run-time address of the r_debug structure  */
    main_map->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;

  /* Fill in the pointer in the dynamic linker's own dynamic section, in
     case you run gdb on the dynamic linker directly.  */
  if (GL(dl_rtld_map).l_info[DT_DEBUG] != NULL)
    GL(dl_rtld_map).l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
#endif

  /* We start adding objects.  */
  r->r_state = RT_ADD;
  _dl_debug_state ();
  LIBC_PROBE (init_start, 2, LM_ID_BASE, r);

  /* Auditing checkpoint: we are ready to signal that the initial map
     is being constructed.  */
  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
    {
      struct audit_ifaces *afct = GLRO(dl_audit);
      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
	{
	  if (afct->activity != NULL)
	    afct->activity (&main_map->l_audit[cnt].cookie, LA_ACT_ADD);

	  afct = afct->next;
	}
    }

  /* We have two ways to specify objects to preload: via environment
     variable and via the file /etc/ld.so.preload.  The latter can also
     be used when security is enabled.  */
  assert (*first_preload == NULL);
  struct link_map **preloads = NULL;
  unsigned int npreloads = 0;

  if (__glibc_unlikely (preloadlist != NULL))
    {
      HP_TIMING_NOW (start);
      npreloads += handle_ld_preload (preloadlist, main_map);
      HP_TIMING_NOW (stop);
      HP_TIMING_DIFF (diff, start, stop);
      HP_TIMING_ACCUM_NT (load_time, diff);
    }

  /* There usually is no ld.so.preload file, it should only be used
     for emergencies and testing.  So the open call etc should usually
     fail.  Using access() on a non-existing file is faster than using
     open().  So we do this first.  If it succeeds we do almost twice
     the work but this does not matter, since it is not for production
     use.  */
  static const char preload_file[] = "/etc/ld.so.preload";
  if (__glibc_unlikely (__access (preload_file, R_OK) == 0))
    {
      /* Read the contents of the file.  */
      file = _dl_sysdep_read_whole_file (preload_file, &file_size,
					 PROT_READ | PROT_WRITE);
      if (__glibc_unlikely (file != MAP_FAILED))
	{
	  /* Parse the file.  It contains names of libraries to be loaded,
	     separated by white spaces or `:'.  It may also contain
	     comments introduced by `#'.  */
	  char *problem;
	  char *runp;
	  size_t rest;

	  /* Eliminate comments.  */
	  runp = file;
	  rest = file_size;
	  while (rest > 0)
	    {
	      char *comment = memchr (runp, '#', rest);
	      if (comment == NULL)
		break;

	      rest -= comment - runp;
	      do
		*comment = ' ';
	      while (--rest > 0 && *++comment != '\n');
	    }

	  /* We have one problematic case: if we have a name at the end of
	     the file without a trailing terminating characters, we cannot
	     place the \0.  Handle the case separately.  */
	  if (file[file_size - 1] != ' ' && file[file_size - 1] != '\t'
	      && file[file_size - 1] != '\n' && file[file_size - 1] != ':')
	    {
	      problem = &file[file_size];
	      while (problem > file && problem[-1] != ' '
		     && problem[-1] != '\t'
		     && problem[-1] != '\n' && problem[-1] != ':')
		--problem;

	      if (problem > file)
		problem[-1] = '\0';
	    }
	  else
	    {
	      problem = NULL;
	      file[file_size - 1] = '\0';
	    }

	  HP_TIMING_NOW (start);

	  if (file != problem)
	    {
	      char *p;
	      runp = file;
	      while ((p = strsep (&runp, ": \t\n")) != NULL)
		if (p[0] != '\0')
		  npreloads += do_preload (p, main_map, preload_file);
	    }

	  if (problem != NULL)
	    {
	      char *p = strndupa (problem, file_size - (problem - file));

	      npreloads += do_preload (p, main_map, preload_file);
	    }

	  HP_TIMING_NOW (stop);
	  HP_TIMING_DIFF (diff, start, stop);
	  HP_TIMING_ACCUM_NT (load_time, diff);

	  /* We don't need the file anymore.  */
	  __munmap (file, file_size);
	}
    }

  if (__glibc_unlikely (*first_preload != NULL))
    {
      /* Set up PRELOADS with a vector of the preloaded libraries.  */
      struct link_map *l = *first_preload;
      preloads = __alloca (npreloads * sizeof preloads[0]);
      i = 0;
      do
	{
	  preloads[i++] = l;
	  l = l->l_next;
	} while (l);
      assert (i == npreloads);
    }

  /* Load all the libraries specified by DT_NEEDED entries.  If LD_PRELOAD
     specified some libraries to load, these are inserted before the actual
     dependencies in the executable's searchlist for symbol resolution.  */
  HP_TIMING_NOW (start);
  _dl_map_object_deps (main_map, preloads, npreloads, mode == trace, 0);
  HP_TIMING_NOW (stop);
  HP_TIMING_DIFF (diff, start, stop);
  HP_TIMING_ACCUM_NT (load_time, diff);

  /* Mark all objects as being in the global scope.  */
  for (i = main_map->l_searchlist.r_nlist; i > 0; )
    main_map->l_searchlist.r_list[--i]->l_global = 1;

  /* Remove _dl_rtld_map from the chain.  */
  GL(dl_rtld_map).l_prev->l_next = GL(dl_rtld_map).l_next;
  if (GL(dl_rtld_map).l_next != NULL)
    GL(dl_rtld_map).l_next->l_prev = GL(dl_rtld_map).l_prev;

  for (i = 1; i < main_map->l_searchlist.r_nlist; ++i)
    if (main_map->l_searchlist.r_list[i] == &GL(dl_rtld_map))
      break;

  bool rtld_multiple_ref = false;
  if (__glibc_likely (i < main_map->l_searchlist.r_nlist))
    {
      /* Some DT_NEEDED entry referred to the interpreter object itself, so
	 put it back in the list of visible objects.  We insert it into the
	 chain in symbol search order because gdb uses the chain's order as
	 its symbol search order.  */
      rtld_multiple_ref = true;

      GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1];
      if (__builtin_expect (mode, normal) == normal)
	{
	  GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist
				    ? main_map->l_searchlist.r_list[i + 1]
				    : NULL);
#ifdef NEED_DL_SYSINFO_DSO
	  if (GLRO(dl_sysinfo_map) != NULL
	      && GL(dl_rtld_map).l_prev->l_next == GLRO(dl_sysinfo_map)
	      && GL(dl_rtld_map).l_next != GLRO(dl_sysinfo_map))
	    GL(dl_rtld_map).l_prev = GLRO(dl_sysinfo_map);
#endif
	}
      else
	/* In trace mode there might be an invisible object (which we
	   could not find) after the previous one in the search list.
	   In this case it doesn't matter much where we put the
	   interpreter object, so we just initialize the list pointer so
	   that the assertion below holds.  */
	GL(dl_rtld_map).l_next = GL(dl_rtld_map).l_prev->l_next;

      assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next);
      GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map);
      if (GL(dl_rtld_map).l_next != NULL)
	{
	  assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev);
	  GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map);
	}
    }

  /* Now let us see whether all libraries are available in the
     versions we need.  */
  {
    struct version_check_args args;
    args.doexit = mode == normal;
    args.dotrace = mode == trace;
    _dl_receive_error (print_missing_version, version_check_doit, &args);
  }

  /* We do not initialize any of the TLS functionality unless any of the
     initial modules uses TLS.  This makes dynamic loading of modules with
     TLS impossible, but to support it requires either eagerly doing setup
     now or lazily doing it later.  Doing it now makes us incompatible with
     an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever
     used.  Trying to do it lazily is too hairy to try when there could be
     multiple threads (from a non-TLS-using libpthread).  */
  bool was_tls_init_tp_called = tls_init_tp_called;
  if (tcbp == NULL)
    tcbp = init_tls ();

  if (__glibc_likely (need_security_init))
    /* Initialize security features.  But only if we have not done it
       earlier.  */
    security_init ();

  if (__builtin_expect (mode, normal) != normal)
    {
      /* We were run just to list the shared libraries.  It is
	 important that we do this before real relocation, because the
	 functions we call below for output may no longer work properly
	 after relocation.  */
      struct link_map *l;

      if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
	{
	  struct r_scope_elem *scope = &main_map->l_searchlist;

	  for (i = 0; i < scope->r_nlist; i++)
	    {
	      l = scope->r_list [i];
	      if (l->l_faked)
		{
		  _dl_printf ("\t%s => not found\n", l->l_libname->name);
		  continue;
		}
	      if (_dl_name_match_p (GLRO(dl_trace_prelink), l))
		GLRO(dl_trace_prelink_map) = l;
	      _dl_printf ("\t%s => %s (0x%0*Zx, 0x%0*Zx)",
			  DSO_FILENAME (l->l_libname->name),
			  DSO_FILENAME (l->l_name),
			  (int) sizeof l->l_map_start * 2,
			  (size_t) l->l_map_start,
			  (int) sizeof l->l_addr * 2,
			  (size_t) l->l_addr);

	      if (l->l_tls_modid)
		_dl_printf (" TLS(0x%Zx, 0x%0*Zx)\n", l->l_tls_modid,
			    (int) sizeof l->l_tls_offset * 2,
			    (size_t) l->l_tls_offset);
	      else
		_dl_printf ("\n");
	    }
	}
      else if (GLRO(dl_debug_mask) & DL_DEBUG_UNUSED)
	{
	  /* Look through the dependencies of the main executable
	     and determine which of them is not actually
	     required.  */
	  struct link_map *l = main_map;

	  /* Relocate the main executable.  */
	  struct relocate_args args = { .l = l,
					.reloc_mode = ((GLRO(dl_lazy)
						       ? RTLD_LAZY : 0)
						       | __RTLD_NOIFUNC) };
	  _dl_receive_error (print_unresolved, relocate_doit, &args);

	  /* This loop depends on the dependencies of the executable to
	     correspond in number and order to the DT_NEEDED entries.  */
	  ElfW(Dyn) *dyn = main_map->l_ld;
	  bool first = true;
	  while (dyn->d_tag != DT_NULL)
	    {
	      if (dyn->d_tag == DT_NEEDED)
		{
		  l = l->l_next;
#ifdef NEED_DL_SYSINFO_DSO
		  /* Skip the VDSO since it's not part of the list
		     of objects we brought in via DT_NEEDED entries.  */
		  if (l == GLRO(dl_sysinfo_map))
		    l = l->l_next;
#endif
		  if (!l->l_used)
		    {
		      if (first)
			{
			  _dl_printf ("Unused direct dependencies:\n");
			  first = false;
			}

		      _dl_printf ("\t%s\n", l->l_name);
		    }
		}

	      ++dyn;
	    }

	  _exit (first != true);
	}
      else if (! main_map->l_info[DT_NEEDED])
	_dl_printf ("\tstatically linked\n");
      else
	{
	  for (l = main_map->l_next; l; l = l->l_next)
	    if (l->l_faked)
	      /* The library was not found.  */
	      _dl_printf ("\t%s => not found\n", l->l_libname->name);
	    else if (strcmp (l->l_libname->name, l->l_name) == 0)
	      _dl_printf ("\t%s (0x%0*Zx)\n", l->l_libname->name,
			  (int) sizeof l->l_map_start * 2,
			  (size_t) l->l_map_start);
	    else
	      _dl_printf ("\t%s => %s (0x%0*Zx)\n", l->l_libname->name,
			  l->l_name, (int) sizeof l->l_map_start * 2,
			  (size_t) l->l_map_start);
	}

      if (__builtin_expect (mode, trace) != trace)
	for (i = 1; i < (unsigned int) _dl_argc; ++i)
	  {
	    const ElfW(Sym) *ref = NULL;
	    ElfW(Addr) loadbase;
	    lookup_t result;

	    result = _dl_lookup_symbol_x (_dl_argv[i], main_map,
					  &ref, main_map->l_scope,
					  NULL, ELF_RTYPE_CLASS_PLT,
					  DL_LOOKUP_ADD_DEPENDENCY, NULL);

	    loadbase = LOOKUP_VALUE_ADDRESS (result, false);

	    _dl_printf ("%s found at 0x%0*Zd in object at 0x%0*Zd\n",
			_dl_argv[i],
			(int) sizeof ref->st_value * 2,
			(size_t) ref->st_value,
			(int) sizeof loadbase * 2, (size_t) loadbase);
	  }
      else
	{
	  /* If LD_WARN is set, warn about undefined symbols.  */
	  if (GLRO(dl_lazy) >= 0 && GLRO(dl_verbose))
	    {
	      /* We have to do symbol dependency testing.  */
	      struct relocate_args args;
	      unsigned int i;

	      args.reloc_mode = ((GLRO(dl_lazy) ? RTLD_LAZY : 0)
				 | __RTLD_NOIFUNC);

	      i = main_map->l_searchlist.r_nlist;
	      while (i-- > 0)
		{
		  struct link_map *l = main_map->l_initfini[i];
		  if (l != &GL(dl_rtld_map) && ! l->l_faked)
		    {
		      args.l = l;
		      _dl_receive_error (print_unresolved, relocate_doit,
					 &args);
		    }
		}

	      if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
		  && rtld_multiple_ref)
		{
		  /* Mark the link map as not yet relocated again.  */
		  GL(dl_rtld_map).l_relocated = 0;
		  _dl_relocate_object (&GL(dl_rtld_map),
				       main_map->l_scope, __RTLD_NOIFUNC, 0);
		}
	    }
#define VERNEEDTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERNEED))
	  if (version_info)
	    {
	      /* Print more information.  This means here, print information
		 about the versions needed.  */
	      int first = 1;
	      struct link_map *map;

	      for (map = main_map; map != NULL; map = map->l_next)
		{
		  const char *strtab;
		  ElfW(Dyn) *dyn = map->l_info[VERNEEDTAG];
		  ElfW(Verneed) *ent;

		  if (dyn == NULL)
		    continue;

		  strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
		  ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);

		  if (first)
		    {
		      _dl_printf ("\n\tVersion information:\n");
		      first = 0;
		    }

		  _dl_printf ("\t%s:\n", DSO_FILENAME (map->l_name));

		  while (1)
		    {
		      ElfW(Vernaux) *aux;
		      struct link_map *needed;

		      needed = find_needed (strtab + ent->vn_file);
		      aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);

		      while (1)
			{
			  const char *fname = NULL;

			  if (needed != NULL
			      && match_version (strtab + aux->vna_name,
						needed))
			    fname = needed->l_name;

			  _dl_printf ("\t\t%s (%s) %s=> %s\n",
				      strtab + ent->vn_file,
				      strtab + aux->vna_name,
				      aux->vna_flags & VER_FLG_WEAK
				      ? "[WEAK] " : "",
				      fname ?: "not found");

			  if (aux->vna_next == 0)
			    /* No more symbols.  */
			    break;

			  /* Next symbol.  */
			  aux = (ElfW(Vernaux) *) ((char *) aux
						   + aux->vna_next);
			}

		      if (ent->vn_next == 0)
			/* No more dependencies.  */
			break;

		      /* Next dependency.  */
		      ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
		    }
		}
	    }
	}

      _exit (0);
    }

  if (main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)]
      && ! __builtin_expect (GLRO(dl_profile) != NULL, 0)
      && ! __builtin_expect (GLRO(dl_dynamic_weak), 0))
    {
      ElfW(Lib) *liblist, *liblistend;
      struct link_map **r_list, **r_listend, *l;
      const char *strtab = (const void *) D_PTR (main_map, l_info[DT_STRTAB]);

      assert (main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)] != NULL);
      liblist = (ElfW(Lib) *)
		main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)]->d_un.d_ptr;
      liblistend = (ElfW(Lib) *)
		   ((char *) liblist +
		    main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)]->d_un.d_val);
      r_list = main_map->l_searchlist.r_list;
      r_listend = r_list + main_map->l_searchlist.r_nlist;

      for (; r_list < r_listend && liblist < liblistend; r_list++)
	{
	  l = *r_list;

	  if (l == main_map)
	    continue;

	  /* If the library is not mapped where it should, fail.  */
	  if (l->l_addr)
	    break;

	  /* Next, check if checksum matches.  */
	  if (l->l_info [VALIDX(DT_CHECKSUM)] == NULL
	      || l->l_info [VALIDX(DT_CHECKSUM)]->d_un.d_val
		 != liblist->l_checksum)
	    break;

	  if (l->l_info [VALIDX(DT_GNU_PRELINKED)] == NULL
	      || l->l_info [VALIDX(DT_GNU_PRELINKED)]->d_un.d_val
		 != liblist->l_time_stamp)
	    break;

	  if (! _dl_name_match_p (strtab + liblist->l_name, l))
	    break;

	  ++liblist;
	}


      if (r_list == r_listend && liblist == liblistend)
	prelinked = true;

      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
	_dl_debug_printf ("\nprelink checking: %s\n",
			  prelinked ? "ok" : "failed");
    }


  /* Now set up the variable which helps the assembler startup code.  */
  GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist = &main_map->l_searchlist;

  /* Save the information about the original global scope list since
     we need it in the memory handling later.  */
  GLRO(dl_initial_searchlist) = *GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist;

  /* Remember the last search directory added at startup, now that
     malloc will no longer be the one from dl-minimal.c.  As a side
     effect, this marks ld.so as initialized, so that the rtld_active
     function returns true from now on.  */
  GLRO(dl_init_all_dirs) = GL(dl_all_dirs);

  /* Print scope information.  */
  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
    {
      _dl_debug_printf ("\nInitial object scopes\n");

      for (struct link_map *l = main_map; l != NULL; l = l->l_next)
	_dl_show_scope (l, 0);
    }

  _rtld_main_check (main_map, _dl_argv[0]);

  if (prelinked)
    {
      if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL)
	{
	  ElfW(Rela) *conflict, *conflictend;
#ifndef HP_TIMING_NONAVAIL
	  hp_timing_t start;
	  hp_timing_t stop;
#endif

	  HP_TIMING_NOW (start);
	  assert (main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)] != NULL);
	  conflict = (ElfW(Rela) *)
	    main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)]->d_un.d_ptr;
	  conflictend = (ElfW(Rela) *)
	    ((char *) conflict
	     + main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)]->d_un.d_val);
	  _dl_resolve_conflicts (main_map, conflict, conflictend);
	  HP_TIMING_NOW (stop);
	  HP_TIMING_DIFF (relocate_time, start, stop);
	}


      /* Mark all the objects so we know they have been already relocated.  */
      for (struct link_map *l = main_map; l != NULL; l = l->l_next)
	{
	  l->l_relocated = 1;
	  if (l->l_relro_size)
	    _dl_protect_relro (l);

	  /* Add object to slot information data if necessasy.  */
	  if (l->l_tls_blocksize != 0 && tls_init_tp_called)
	    _dl_add_to_slotinfo (l);
	}
    }
  else
    {
      /* Now we have all the objects loaded.  Relocate them all except for
	 the dynamic linker itself.  We do this in reverse order so that copy
	 relocs of earlier objects overwrite the data written by later
	 objects.  We do not re-relocate the dynamic linker itself in this
	 loop because that could result in the GOT entries for functions we
	 call being changed, and that would break us.  It is safe to relocate
	 the dynamic linker out of order because it has no copy relocs (we
	 know that because it is self-contained).  */

      int consider_profiling = GLRO(dl_profile) != NULL;
#ifndef HP_TIMING_NONAVAIL
      hp_timing_t start;
      hp_timing_t stop;
#endif

      /* If we are profiling we also must do lazy reloaction.  */
      GLRO(dl_lazy) |= consider_profiling;

      HP_TIMING_NOW (start);
      unsigned i = main_map->l_searchlist.r_nlist;
      while (i-- > 0)
	{
	  struct link_map *l = main_map->l_initfini[i];

	  /* While we are at it, help the memory handling a bit.  We have to
	     mark some data structures as allocated with the fake malloc()
	     implementation in ld.so.  */
	  struct libname_list *lnp = l->l_libname->next;

	  while (__builtin_expect (lnp != NULL, 0))
	    {
	      lnp->dont_free = 1;
	      lnp = lnp->next;
	    }
	  /* Also allocated with the fake malloc().  */
	  l->l_free_initfini = 0;

	  if (l != &GL(dl_rtld_map))
	    _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0,
				 consider_profiling);

	  /* Add object to slot information data if necessasy.  */
	  if (l->l_tls_blocksize != 0 && tls_init_tp_called)
	    _dl_add_to_slotinfo (l);
	}
      HP_TIMING_NOW (stop);

      HP_TIMING_DIFF (relocate_time, start, stop);

      /* Now enable profiling if needed.  Like the previous call,
	 this has to go here because the calls it makes should use the
	 rtld versions of the functions (particularly calloc()), but it
	 needs to have _dl_profile_map set up by the relocator.  */
      if (__glibc_unlikely (GL(dl_profile_map) != NULL))
	/* We must prepare the profiling.  */
	_dl_start_profile ();
    }

  if ((!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0)
      || count_modids != _dl_count_modids ())
    ++GL(dl_tls_generation);

  /* Now that we have completed relocation, the initializer data
     for the TLS blocks has its final values and we can copy them
     into the main thread's TLS area, which we allocated above.
     Note: thread-local variables must only be accessed after completing
     the next step.  */
  _dl_allocate_tls_init (tcbp);

  /* And finally install it for the main thread.  */
  if (! tls_init_tp_called)
    {
      const char *lossage = TLS_INIT_TP (tcbp);
      if (__glibc_unlikely (lossage != NULL))
	_dl_fatal_printf ("cannot set up thread-local storage: %s\n",
			  lossage);
    }

  /* Make sure no new search directories have been added.  */
  assert (GLRO(dl_init_all_dirs) == GL(dl_all_dirs));

  if (! prelinked && rtld_multiple_ref)
    {
      /* There was an explicit ref to the dynamic linker as a shared lib.
	 Re-relocate ourselves with user-controlled symbol definitions.

	 We must do this after TLS initialization in case after this
	 re-relocation, we might call a user-supplied function
	 (e.g. calloc from _dl_relocate_object) that uses TLS data.  */

#ifndef HP_TIMING_NONAVAIL
      hp_timing_t start;
      hp_timing_t stop;
      hp_timing_t add;
#endif

      HP_TIMING_NOW (start);
      /* Mark the link map as not yet relocated again.  */
      GL(dl_rtld_map).l_relocated = 0;
      _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0);
      HP_TIMING_NOW (stop);
      HP_TIMING_DIFF (add, start, stop);
      HP_TIMING_ACCUM_NT (relocate_time, add);
    }

  /* Do any necessary cleanups for the startup OS interface code.
     We do these now so that no calls are made after rtld re-relocation
     which might be resolved to different functions than we expect.
     We cannot do this before relocating the other objects because
     _dl_relocate_object might need to call `mprotect' for DT_TEXTREL.  */
  _dl_sysdep_start_cleanup ();

#ifdef SHARED
  /* Auditing checkpoint: we have added all objects.  */
  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
    {
      struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
      /* Do not call the functions for any auditing object.  */
      if (head->l_auditing == 0)
	{
	  struct audit_ifaces *afct = GLRO(dl_audit);
	  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
	    {
	      if (afct->activity != NULL)
		afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT);

	      afct = afct->next;
	    }
	}
    }
#endif

  /* Notify the debugger all new objects are now ready to go.  We must re-get
     the address since by now the variable might be in another object.  */
  r = _dl_debug_initialize (0, LM_ID_BASE);
  r->r_state = RT_CONSISTENT;
  _dl_debug_state ();
  LIBC_PROBE (init_complete, 2, LM_ID_BASE, r);

#if defined USE_LDCONFIG && !defined MAP_COPY
  /* We must munmap() the cache file.  */
  _dl_unload_cache ();
#endif

  /* Once we return, _dl_sysdep_start will invoke
     the DT_INIT functions and then *USER_ENTRY.  */
}


1.遍历用户程序的Segment头。类型为PT_PHDR的Segment标识了第一个Segment头的装载地址p_vaddr,将实际的装载地址phdr减去该值就是整个elf文件的装载地址,存储在l_addr中。 
2.用上面确定的装载地址加上.dynamic节的装载地址p_vaddr就得到该节实际的装载地址,将其存储在l_ld中。 
再往下找到类型为PT_INTERP的Segment头,其装载地址就是解释器自身路径的起始地址,将该路径保存在_dl_rtld_libname中,将标准的路径保存在_dl_rtld_libname2中,两个变量的类型都是libname_list,用来形成字符串链表。 
3.计算代码段、数据段、bss段(这些段的类型都为PT_LOAD)的最低起始地址,保存在main_map的l_map_start中,最高结束地址保存在l_map_end中。 
4.如果ld.so以解释器身份运行,这里通过elf_get_dynamic_info获取用户程序.dynamic段的信息,然后通过_dl_setup_hash函数获取.hash节的信息并初始化
5.通过_dl_init_paths函数设置库的搜索路径,传入的参数library_path是在process_envvars函数中从堆栈中取出的LD_LIBRARY_PATH的值。
6.old_nloaded之前保存了原来共享库的全局列表。然后通过_dl_catch_error函数装载共享库。 
_dl_catch_error函数封装了异常处理机制,前三个参数用于捕获异常信息,真正执行的函数为map_doit,参数为args。 
当有新的共享库加载到内存中时,会创建link_map结构插入到全局的_dl_ns数组的_ns_nloaded变量中
7._dl_map_object函数首先通过for循环检查将要加载的共享库是否已经加载的内存中,即存在于全局链表_dl_ns中。 
8.解析玩elf文件的各个Segment后,接下来通过elf_get_dynamic_info函数获得加载的共享库.dynamic段的信息,将其存入link_map结构的l_info中。 

总之:RTLD的基本完成以下流程
1.INTERPRETOR 解释器自身重定位
2.栈信,环境变量.dynamic segment 信息收集
3. PHT 解析。
4.共享库加载,解析,重定位。
5.动态符号解析
6.GOT 修复
 

参考:

https://blog.csdn.net/conansonic/article/details/54236335

https://blog.csdn.net/yejing_utopia/article/details/40986463

https://johntortugo.wordpress.com/2012/08/27/understanding-linux-elf-rtld-internals/

https://blog.csdn.net/gatieme/article/details/51628257

https://blog.csdn.net/conansonic/article/details/54896433

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值