本章分析ld.so的主体dl_main,这个函数非常复杂,只论篇幅就占了这个c文件的一半以上。
static void
dl_main(constElfW(Phdr) *phdr,
ElfW(Word) phnum,
ElfW(Addr) *user_entry)
{
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;
#ifdef _LIBC_REENTRANT
/* Explicit initialization since the relocwouldjust be more work. */
GL(dl_error_catch_tsd)=&_dl_initial_error_catch_tsd;
#endif
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 cheaperthanprocessing the reloc
in the _rtld_localdefinition'sinitializer. */
GL(dl_make_stack_executable_hook)=&_dl_make_stack_executable;
/* Process the environment variable whichcontrolthe behaviour. */
process_envvars (&mode);//处理环境变量
==========================process_envvars start============================
/* Process allenvironments variables thedynamic linker must recognize.
Since all of them start with `LD_' we are abit smarter whilefinding
all the entries. */
extern char **_environ attribute_hidden;
static void
process_envvars (enum mode *modep)
{
char **runp = _environ;//指向全局变量,上面标红的
char *envline;
enum mode mode = normal;
char *debug_output = NULL;
/* This is the default place for profiling datafile. */
//根据__libc_enable_secure判断dl_profile_output路劲是/var/tmp还是/var/profile
GLRO(dl_profile_output) =&"/var/tmp\0/var/profile"[INTUSE(__libc_enable_secure)? 9 : 0];
//寻找以LD_开头的环境变量
while ((envline = _dl_next_ld_env_entry(&runp)) != NULL)
{
size_t len = 0;
while (envline[len] != '\0'&& envline[len] !='=')
++len;
if (envline[len] != '=')
/* This is a "LD_" variable at theendof the string without
a '=' character. Ignore itsince otherwise we willaccess
invalid memory below. */
continue;
//分别处理各种链接选项环境变量
switch (len)
{
case 4:
/* Warning level, verbose or not. */
if (memcmp (envline, "WARN", 4) == 0)
GLRO(dl_verbose) = envline[5] != '\0';
break;
case 5:
/* Debugging of the dynamic linker? */
if (memcmp (envline, "DEBUG", 5) == 0)
{
process_dl_debug (&envline[6]);
break;
}
if (memcmp (envline, "AUDIT", 5) == 0)
process_dl_audit (&envline[6]);
break;
case 7:
/* Print information about versions. */
if (memcmp (envline, "VERBOSE", 7) == 0)
{
version_info = envline[8] != '\0';
break;
}
/* List of objects to be preloaded. */
if (memcmp (envline, "PRELOAD", 7) == 0)
{
preloadlist = &envline[8];
break;
}
/* Which shared object shall be profiled. */
if (memcmp (envline, "PROFILE", 7) == 0&& envline[8]!= '\0')
GLRO(dl_profile) = &envline[8];
break;
case 8:
/* Do we bind early? */
if (memcmp (envline, "BIND_NOW", 8) == 0)
{
GLRO(dl_lazy) = envline[9] == '\0';
break;
}
if (memcmp (envline, "BIND_NOT", 8) == 0)
GLRO(dl_bind_not) = envline[9] != '\0';
break;
case 9:
/* Test whether we want to see the content of theauxiliary
array passed up from the kernel. */
if (!INTUSE(__libc_enable_secure)
&& memcmp (envline, "SHOW_AUXV",9) == 0)
_dl_show_auxv ();
break;
case 10:
/* Mask for the important hardware capabilities. */
if (memcmp (envline, "HWCAP_MASK", 10) ==0)
GLRO(dl_hwcap_mask) = __strtoul_internal(&envline[11], NULL,
0, 0);
break;
case 11:
/* Path where the binary is found. */
if (!INTUSE(__libc_enable_secure)
&& memcmp (envline,"ORIGIN_PATH", 11) ==0)
GLRO(dl_origin_path) = &envline[12];
break;
case 12:
/* The library search path. */
if (memcmp (envline, "LIBRARY_PATH", 12)== 0)
{
library_path = &envline[13];
break;
}
/* Where to place the profiling data file. */
if (memcmp (envline, "DEBUG_OUTPUT", 12)== 0)
{
debug_output = &envline[13];
break;
}
if (!INTUSE(__libc_enable_secure)
&& memcmp (envline,"DYNAMIC_WEAK", 12)== 0)
GLRO(dl_dynamic_weak) = 1;
break;
case 13:
/* We might have some extra environment variablewith length 13
to handle. */
#ifdef EXTRA_LD_ENVVARS_13
EXTRA_LD_ENVVARS_13
#endif
if (!INTUSE(__libc_enable_secure)
&& memcmp (envline,"USE_LOAD_BIAS", 13)== 0)
{
GLRO(dl_use_load_bias) = envline[14]== '1' ? -1 : 0;
break;
}
if (memcmp (envline, "POINTER_GUARD", 13)== 0)
GLRO(dl_pointer_guard) = envline[14] != '0';
break;
case 14:
/* Where to place the profiling data file. */
if (!INTUSE(__libc_enable_secure)
&& memcmp (envline,"PROFILE_OUTPUT", 14)== 0
&& envline[15] != '\0')
GLRO(dl_profile_output) = &envline[15];
break;
case 16:
/* The mode of the dynamic linker can be set. */
if (memcmp (envline, "TRACE_PRELINKING",16) == 0)
{
mode = trace;
GLRO(dl_verbose) = 1;
GLRO(dl_debug_mask) |=DL_DEBUG_PRELINK;
GLRO(dl_trace_prelink) =&envline[17];
}
break;
case 20:
/* The mode of the dynamic linker can be set. */
if (memcmp (envline,"TRACE_LOADED_OBJECTS", 20) == 0)
mode = trace;
break;
/* We might have some extra environment variable tohandle. This
is tricky due to the pre-processing of thelength of the name
in the switch statement here. Thecode here assumes thatadded
environment variables have a differentlength. */
#ifdef EXTRA_LD_ENVVARS
EXTRA_LD_ENVVARS
#endif
}
}
/* The caller wants this information. */
*modep = mode;
/* Extra security for SUID binaries. Removeall dangerousenvironment
variables. */
//处理SUID特殊权限的额外安全选项
if (__builtin_expect(INTUSE(__libc_enable_secure), 0))
{
static const char unsecure_envvars[]=
#ifdef EXTRA_UNSECURE_ENVVARS
EXTRA_UNSECURE_ENVVARS
//宏定义于sysdeps/unix/sysv/linux/i386中,
#defineEXTRA_UNSECURE_ENVVARS "LD_AOUT_LIBRARY_PATH\0" "LD_AOUT_PRELOAD\0"
#endif
UNSECURE_ENVVARS;//sysdeps/generic/unisecvars.h
=====================================================
#defineUNSECURE_ENVVARS\
"GCONV_PATH\0" \
"GETCONF_DIR\0" \
"HOSTALIASES\0" \
"LD_AUDIT\0" \
"LD_DEBUG\0" \
"LD_DEBUG_OUTPUT\0" \
"LD_DYNAMIC_WEAK\0" \
"LD_LIBRARY_PATH\0" \
"LD_ORIGIN_PATH\0" \
"LD_PRELOAD\0" \
"LD_PROFILE\0" \
"LD_SHOW_AUXV\0" \
"LD_USE_LOAD_BIAS\0" \
"LOCALDOMAIN\0" \
"LOCPATH\0" \
"MALLOC_TRACE\0" \
"NIS_PATH\0" \
"NLSPATH\0" \
"RESOLV_HOST_CONF\0" \
"RES_OPTIONS\0" \
"TMPDIR\0" \
"TZDIR\0"
=====================================================
const char *nextp;
nextp = unsecure_envvars;
do
{
unsetenv (nextp);//注销危险环境变量
/* We could use rawmemchr but this need not befast. */
nextp = (char *) (strchr) (nextp, '\0') + 1;
}
while (*nextp != '\0');
if (__access("/etc/suid-debug", F_OK) != 0)//如果此文件不存在,注销MALLOC_CHECK_
{
unsetenv ("MALLOC_CHECK_");
GLRO(dl_debug_mask) = 0;
}
if (mode != normal)
_exit (5);
}
/* If we have to run the dynamic linker indebugging mode and the
LD_DEBUG_OUTPUT environment variableis given, we write thedebug
messages to this file. */
链接器运行于调试状态下,并且环境变量LD_DEBUG_OUTPUT存在,保存debuglog
else if (any_debug && debug_output !=NULL)
{
#ifdef O_NOFOLLOW
const int flags = O_WRONLY |O_APPEND | O_CREAT |O_NOFOLLOW;
#else
const int flags = O_WRONLY |O_APPEND | O_CREAT;
#endif
size_t name_len = strlen(debug_output);
char buf[name_len + 12];
char *startp;
buf[name_len + 11] = '\0';
startp = _itoa (__getpid (),&buf[name_len + 11], 10,0);
*--startp = '.';
startp = memcpy (startp - name_len,debug_output,name_len);
GLRO(dl_debug_fd) = __open (startp,flags, DEFFILEMODE);
if (GLRO(dl_debug_fd) == -1)
/* We use standard output if opening the file failed. */
GLRO(dl_debug_fd) = STDOUT_FILENO;
}
}
==========================process_envvars end============================
#ifndef HAVE_INLINED_SYSCALLS
/* Set up a flag which tells we are juststarting. */
INTUSE(_dl_starting_up) = 1;
#endif
//ld有两种用途,或者说使用方式
//1,ld.so用作一个应用程序手动调用,下面用于处理指定的连接选项,如--list,--verify,--inhibit-rpath等
if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
{
/* Ho ho. We are nottheprogram interpreter! We are the program
itself! This means someone ran ld.so as acommand. Well, that
might be convenient to do sometimes. We supportitby
interpreting the args like this:
ld.so PROGRAM ARGS...
The first argument is the name of a file containing anELF
executable we will load and run with thefollowingarguments.
To simplify life here, PROGRAM issearchedfor using the
normal rules for shared objects, rather than$PATHor anything
like that. We just load it and use its entrypoint;we don't
pay attention to its PT_INTERP command (we aretheinterpreter
ourselves). This is an easy way to test anewld.so before
installing it. */
rtld_is_main = true; //设置ld.so作为程序使用
/* Note the place where thedynamiclinker actually came from. */
GL(dl_rtld_map).l_name = rtld_progname;
//_dl_rtld_map.l_name = rtld_progname;#definertld_progname (INTUSE(_dl_argv)[0]) ----sysdeps/generic/ldsodefs.h
while (_dl_argc > 1)//迭代处理选项参数
if (! strcmp (INTUSE(_dl_argv)[1], "--list"))//处理库依赖
{
mode = list;
GLRO(dl_lazy) = -1;/* This means donodependency analysis. */
++_dl_skip_args;
--_dl_argc;
++INTUSE(_dl_argv);
}
else if (! strcmp (INTUSE(_dl_argv)[1], "--verify"))//确保程序是动态链接且ld.so能处理
{
mode = verify;
++_dl_skip_args;
--_dl_argc;
++INTUSE(_dl_argv);
}
else if (! strcmp (INTUSE(_dl_argv)[1], "--library-path")//用其后面的路劲代替LD_LIBRARY_PATH
&& _dl_argc > 2)
{
library_path = INTUSE(_dl_argv)[2];
_dl_skip_args += 2;
_dl_argc -= 2;
INTUSE(_dl_argv) += 2;
}
else if (! strcmp (INTUSE(_dl_argv)[1], "--inhibit-rpath")//同--ignore-rpath(glibc2曾支持),忽略其参数中的对象的RPATH和RUNPATH信息,如果是suid或sgid,忽略。
&& _dl_argc > 2)
{
GLRO(dl_inhibit_rpath) =INTUSE(_dl_argv)[2];
_dl_skip_args += 2;
_dl_argc -= 2;
INTUSE(_dl_argv) += 2;
}
else if (! strcmp (INTUSE(_dl_argv)[1], "--audit")&&_dl_argc > 2)//用audit选项之后的列表作为auditors
{
process_dl_audit (INTUSE(_dl_argv)[2]);
_dl_skip_args += 2;
_dl_argc -= 2;
INTUSE(_dl_argv) += 2;
}
else
break;
/* If we have no further argument theprogram wascalled 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 sharedlibraryexecutables.\n\
This program usually lives in the file `/lib/ld.so',andspecial directives\n\
in executable files using ELF shared libraries tellthesystem's program\n\
loader to load the helper program from thisfile. This helper program loads\n\
the shared libraries needed by the programexecutable,prepares the program\n\
to run, and runs it. You may invoke thishelperprogram directly from the\n\
command line to load and run an ELF executable file;thisis like executing\n\
that file itself, but always uses this helper programfromthe file you\n\
specified, instead of the helper program file specifiedinthe executable\n\
file you run. This is mostly of use formaintainersto test new versions\n\
of this helper program; chances are you did not intendtorun 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\
--library-path PATH use given PATHinsteadof content of the environment\n\
variable LD_LIBRARY_PATH\n\
--inhibit-rpath LIST ignore RUNPATH andRPATHinformation in object names\n\
in LIST\n");
++_dl_skip_args;
--_dl_argc;
++INTUSE(_dl_argv);
/* The initialization of_dl_stack_flags done belowassumes the
executable's PT_GNU_STACK may have been honored bythekernel, and
so a PT_GNU_STACK with PF_X set means the stackstartedout with
execute permission. However, this is not reallytrueif the
dynamic linker is the executable the kernelloaded. For this
case, we must reinitialize_dl_stack_flags to match thedynamic
linker itself. If the dynamic linker was builtwith a
PT_GNU_STACK, then the kernel may have loaded us with a
nonexecutable stack that we will have to make executablewhen we
load the program below unless it has a PT_GNU_STACKindicating
nonexecutable stack is ok. */
for (ph = phdr; ph <&phdr[phnum]; ++ph)
if (ph->p_type == PT_GNU_STACK) //#define PT_GNU_STACK0x6474e551/* Indicates stack executability */-----elf/elf.h
{
GL(dl_stack_flags) = ph->p_flags;
break;
}
if (__builtin_expect (mode, normal)== verify)//ld.so的工作模式为verify
{
const char *objname;
const char *err_str = NULL;
struct map_args args;
bool malloced;
args.str = rtld_progname;
args.loader = NULL;
args.is_preloaded = 0;
args.mode = __RTLD_OPENEXEC;//工作模式
(void) _dl_catch_error (&objname,&err_str,&malloced, map_doit,
&args);
这个函数通过调用下面标红的_dl_map_object(这个函数也是dl_open的一部分)将obj映射入内存,下面插入对这个函数的解析
==============================_dl_map_objectstart============================
/* Map in theshared object file NAME. */
struct link_map *
internal_function
_dl_map_object (struct link_map *loader, const char*name, int preloaded,
int type, int trace_mode, int mode, Lmid_t nsid)
{
int fd;
char *realname;
char *name_copy;
struct link_map *l;
struct filebuf fb;
assert (nsid >= 0);
assert (nsid < DL_NNS);
/* Look for this name among those already loaded. */
因为一个可执行文件依赖的多个库可能依赖同一个库,所以先搜索已经加载的.so,看是否能找到匹配
for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
{
/* If the requested name matches the soname of a loadedobject,
use that object. Elide this check for names that have not
yet been opened. */
if (__builtin_expect (l->l_faked, 0) != 0
|| __builtin_expect (l->l_removed, 0) != 0)
continue;
if (!_dl_name_match_p (name, l))
{
const char *soname;
if (__builtin_expect (l->l_soname_added, 1)
|| l->l_info[DT_SONAME] == NULL)
continue;
soname = ((const char *) D_PTR (l, l_info[DT_STRTAB])
+ l->l_info[DT_SONAME]->d_un.d_val);
if (strcmp (name, soname) != 0)
continue;
/* We have a match on a new name -- cache it. */
add_name_to_object (l, soname);
l->l_soname_added = 1;
}
/* We have a match. */,找到match,返回。
return l;
}
/* Display information if we are debugging. */
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)
&& loader != NULL)
_dl_debug_printf ("\nfile=%s [%lu]; needed by %s[%lu]\n", name, nsid,
loader->l_name[0]
? loader->l_name : rtld_progname, loader->l_ns);
/* Will be true if we found a DSO which is of the other ELF class. */
bool found_other_class = false;
if (strchr (name, '/') == NULL)
{
/* Search for NAME in several places. */
size_t namelen = strlen (name) + 1;
if (__builtin_expect (GLRO(dl_debug_mask) &DL_DEBUG_LIBS, 0))
_dl_debug_printf ("find library=%s [%lu]; searching\n", name, nsid);
fd = -1;
/* When the object has the RUNPATH information we don'tuse any
RPATHs. */
RUNPATH不存在,搜索RUNPATH
if (loader == NULL || loader->l_info[DT_RUNPATH] ==NULL)
{
/* This is the executable's map (if there is one). Make sure that
we do not look at it twice. */
struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
bool did_main_map = false;
/* First try the DT_RPATH of the dependent object that caused NAME
to be loaded. Then that object's dependent, and on up. */
for (l = loader; l; l = l->l_loader)
if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH,"RPATH"))
{
fd = open_path (name, namelen, preloaded,&l->l_rpath_dirs,
&realname, &fb, loader, LA_SER_RUNPATH,
&found_other_class);
if (fd != -1)
break;
did_main_map |= l == main_map;
}
没找到,继续搜寻DT-RPATH
/* If dynamically linked, try the DT_RPATH of the executable
itself. NB: we do thisfor lookups in any namespace. */
if (fd == -1 && !did_main_map
&& main_map != NULL && main_map->l_type!= lt_loaded
&& cache_rpath (main_map,&main_map->l_rpath_dirs, DT_RPATH,
"RPATH"))
fd = open_path (name, namelen, preloaded,&main_map->l_rpath_dirs,
&realname, &fb, loader ?: main_map, LA_SER_RUNPATH,
&found_other_class);
}
还没找到,继续搜寻LD_LIBRARY_PATH
/* Try the LD_LIBRARY_PATH environment variable. */
if (fd == -1 && env_path_list.dirs != (void *) -1)
fd = open_path (name, namelen, preloaded,&env_path_list,
&realname, &fb,
loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
LA_SER_LIBPATH, &found_other_class);
搜寻RUNPATH
/* Look at the RUNPATH information for this binary. */
if (fd == -1 && loader != NULL
&& cache_rpath (loader, &loader->l_runpath_dirs,
DT_RUNPATH, "RUNPATH"))
fd = open_path (name, namelen, preloaded,
&loader->l_runpath_dirs, &realname, &fb, loader,
LA_SER_RUNPATH, &found_other_class);
还是没有找到,check/etc/ld.so.cache。
if (fd == -1
&& (__builtin_expect (! preloaded, 1)
|| ! INTUSE(__libc_enable_secure)))
{
/* Check the list of libraries in the file /etc/ld.so.cache,
for compatibility with Linux's ldconfig program. */
const char *cached = _dl_load_cache_lookup (name);
_dl_load_cache_lookup执行找寻过程,没有找到返回NULL
if (cached != NULL)
{
#ifdef SHARED
// XXX Correct to unconditionally default to namespace 0?
l = loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded;
#else
l = loader;
#endif
/* If the loader has the DF_1_NODEFLIB flag set we must not
use a cache entry from any of these directories. */
if (
#ifndef SHARED
/* 'l' is always != NULL for dynamically linked objects. */
l != NULL &&
#endif
__builtin_expect (l->l_flags_1 & DF_1_NODEFLIB, 0))
{
const char *dirp = system_dirs;
unsigned int cnt = 0;
do
{
if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
{
/* The prefix matches. Don't use the entry. */
cached = NULL;
break;
}
dirp += system_dirs_len[cnt] + 1;
++cnt;
}
while (cnt < nsystem_dirs_len);
}
if (cached != NULL)
{
fd = open_verify (cached,
&fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
LA_SER_CONFIG, &found_other_class, false);
if (__builtin_expect (fd != -1, 1))
{
realname = local_strdup (cached);
if (realname == NULL)
{
__close (fd);
fd = -1;
}
}
}
}
}
如果上面都没找到,搜寻默认路径
/* Finally, try the default path. */
if (fd == -1
&& ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
|| __builtin_expect (!(l->l_flags_1 &DF_1_NODEFLIB), 1))
&& rtld_search_dirs.dirs != (void *) -1)
fd = open_path (name, namelen, preloaded, &rtld_search_dirs,
&realname, &fb, l, LA_SER_DEFAULT, &found_other_class);
/* Add another newline when we are tracing the libraryloading. */
if (__builtin_expect (GLRO(dl_debug_mask) &DL_DEBUG_LIBS, 0))
_dl_debug_printf ("\n");
}
else//非‘\’路径
{
/* The path may contain dynamic string tokens. */
realname = (loader
? expand_dynamic_string_token (loader, name)
: local_strdup (name));
if (realname == NULL)
fd = -1;
else
{
fd = open_verify (realname, &fb,
loader ?: GL(dl_ns)[nsid]._ns_loaded, 0,
&found_other_class, true);
if (__builtin_expect (fd, 0) == -1)
free (realname);
}
}
#ifdef SHARED
no_file:
#endif
/* In case the LOADER information has only been provided to get to
the appropriate RUNPATH/RPATH information we do not need it
anymore. */
if (mode & __RTLD_CALLMAP)
loader = NULL;
if (__builtin_expect (fd, 0) == -1)//没有找到合适的库文件,fake一个
{
if (trace_mode
&& __builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK,0) == 0)
{
/* We haven't found an appropriate library. But since we
are only interested in the list of libraries this isn't
so severe. Fake an entry with all the information we
have. */
static const Elf_Symndx dummy_bucket = STN_UNDEF;
/* Enter the new object in the list of loaded objects. */
if ((name_copy = local_strdup (name)) == NULL
|| (l = _dl_new_object (name_copy, name, type, loader,
mode, nsid)) == NULL)
{
free (name_copy);
_dl_signal_error (ENOMEM, name, NULL,
N_("cannot create shared object descriptor"));
}
/* Signal that this is a faked entry. */
l->l_faked = 1;
/* Since the descriptor is initialized with zero we do not
have do this here.
l->l_reserved = 0; */
l->l_buckets = &dummy_bucket;
l->l_nbuckets = 1;
l->l_relocated = 1;
return l;
}
else if (found_other_class)
_dl_signal_error (0, name, NULL,
ELFW(CLASS) == ELFCLASS32
? N_("wrong ELF class: ELFCLASS64")
: N_("wrong ELF class: ELFCLASS32"));
else
_dl_signal_error (errno, name, NULL,
N_("cannot open shared object file"));
}
void *stack_end = __libc_stack_end;
//_dl_map_object_from_fd函数执行最终的map过程,这里不再深入探究
return _dl_map_object_from_fd (name, fd, &fb, realname, loader,type, mode,
&stack_end, nsid);
}
==============================_dl_map_object end===========================
if (__builtin_expect (err_str != NULL, 0))
/* We don't free the returned string,theprograms stops
anyway. */
_exit (EXIT_FAILURE);出错退出
}
else
{
HP_TIMING_NOW (start);记录起始时间
_dl_map_object (NULL,rtld_progname, 0, lt_library,0,
__RTLD_OPENEXEC, LM_ID_BASE);
HP_TIMING_NOW (stop);记录结束时间
HP_TIMING_DIFF (load_time, start, stop);计算load时间
}
/* Now the map for the mainexecutable is available. */
main_map=GL(dl_ns)[LM_ID_BASE]._ns_loaded;
phdr = main_map->l_phdr;
phnum = main_map->l_phnum;
/* We overwrite here a pointer toamalloc()ed string. But since
the malloc() implementation used at this point isthedummy
implementations which has no real free() function itdoesnot
makes sense to free the old string first. */
main_map->l_name = (char*)"";
*user_entry = main_map->l_entry;
}
else
{
/* Create a link_map fortheexecutable 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);
assert (main_map ==GL(dl_ns)[LM_ID_BASE]._ns_loaded);
main_map->l_phdr = phdr;
main_map->l_phnum = phnum;
main_map->l_entry = *user_entry;
/* At this point we are in a bitoftrouble. We would have to
fill in the values for l_dev and l_ino. Butingeneral we
do not know where the file is. We also do nothandleAT_EXECFD
even if it would be passed up.
We leave the values here defined to 0. Thisisnormally no
problem as the program code itself is normally no shared
object and therefore cannot be loadeddynamically. Nothing
prevent the use of dynamic binaries and inthesesituations
we might get problems. We might not be able tofindout
whether the object is already loaded. Butsincethere is no
easy way out and because the dynamic binary must also not
have an SONAME we ignore this program for now. Ifitbecomes
a problem we can force people using SONAMEs. */
/* We delay initializing thepathstructure 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_LOADheaderentries at all. */
main_map->l_map_start = ~0;
/* And it was opened directly. */
++main_map->l_direct_opencount;
/* Scan the program header table for thedynamicsection. */
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 bytheprogram loader to
find the program interpreter, which is thisprogramitself, the
dynamic linker. We note what name findsus,so that a future
dlopen call or DT_NEEDED entry, for somethingthatwants to link
against the dynamic linker as a sharedlibrary,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 fortheloader from
our DT_SONAME. This can't happen if wewereactually linked as
a static executable (detect this case when wehaveno DYNAMIC).
If so, assume the filename component oftheinterpreter path to
be our SONAME, and add it to our namelist. */
if (GL(dl_rtld_map).l_ld == NULL)
{
const char *p = NULL;
const char *cp = _dl_rtld_libname.name;
/* Find the filename part of thepath. */
while (*cp != '\0')
if (*cp++ == '/')
p = cp;
if (p != NULL)
{
_dl_rtld_libname2.name = p;
/* _dl_rtld_libname2.next = NULL; Alreadyzero. */
_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 inmemory. */
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 dynamiclinkerwe duplicate work
here since we read the PT_TLSentryalready in
_dl_start_final. But theresultis repeatable so do not
check for this specialbutunimportant 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;
}
/* Adjust the address of the TLSinitializationimage in case
the executable is actually anET_DYNobject. */
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, sotheprogram 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); /*Howelse did we get here? */
/* If the current libname is different fromtheSONAME, 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 sinceotherwiseloading audit modules
will fail since they reuse theverysame ld.so. */
assert (GL(dl_rtld_map).l_relocated);
if (! rtld_is_main)
{
/* Extract the contents ofthedynamic section for easy access. */
elf_get_dynamic_info(main_map,NULL);
/* Set up our cache of pointersintothe hash table. */
_dl_setup_hash (main_map);
}
if (__builtin_expect (mode, normal) == verify)
{
/* We were called just to verifythatthis is a dynamic
executable using us as the program interpreter. Exitwith an
error if we were not able to load the binary ornointerpreter
is specified (i.e., this is no dynamically linkedbinary. */
if (main_map->l_ld == NULL)
_exit (1);
/* We allow here some platformspecificcode. */
#ifdef DISTINGUISH_LIB_VERSIONS
DISTINGUISH_LIB_VERSIONS;
#endif
_exit (has_interp ? 0 : 2);
}
struct link_map **first_preload=&GL(dl_rtld_map).l_next;
#if defined NEED_DL_SYSINFO || definedNEED_DL_SYSINFO_DSO
/* Set up the data structures forthesystem-supplied DSO early,
so they can influence_dl_init_paths. */
if (GLRO(dl_sysinfo_dso) != NULL)
{
/* Do an abridged version of thework_dl_map_object_from_fd would do
to map in the object. It's already mappedandprelinked (and
better be, since it's read-only and so wecouldn'trelocate it).
We just want our data structures to describe it as ifwehad just
mapped and relocated it normally. */
struct link_map *l =_dl_new_object((char *) "", "", lt_library, NULL,
0, LM_ID_BASE);
if (__builtin_expect (l != NULL, 1))
{
static ElfW(Dyn)dyn_temp[DL_RO_DYN_TEMP_CNT]attribute_relro;
l->l_phdr = ((const void *) GLRO(dl_sysinfo_dso)
+ GLRO(dl_sysinfo_dso)->e_phoff);
l->l_phnum = GLRO(dl_sysinfo_dso)->e_phnum;
for (uint_fast16_t i = 0; i < l->l_phnum;++i)
{
const ElfW(Phdr) *const ph=&l->l_phdr[i];
if (ph->p_type == PT_DYNAMIC)
{
l->l_ld = (void *) ph->p_vaddr;
l->l_ldnum = ph->p_memsz / sizeof(ElfW(Dyn));
}
else if (ph->p_type == PT_LOAD)
{
if (! l->l_addr)
l->l_addr = ph->p_vaddr;
if (ph->p_vaddr + ph->p_memsz>=l->l_map_end)
l->l_map_end = ph->p_vaddr+ph->p_memsz;
if ((ph->p_flags & PF_X)
&& ph->p_vaddr + ph->p_memsz>=l->l_text_end)
l->l_text_end = ph->p_vaddr+ph->p_memsz;
}
else
/* There must be no TLS segment. */
assert (ph->p_type != PT_TLS);
}
l->l_map_start =(ElfW(Addr))GLRO(dl_sysinfo_dso);
l->l_addr = l->l_map_start - l->l_addr;
l->l_map_end += l->l_addr;
l->l_text_end += l->l_addr;
l->l_ld = (void *) ((ElfW(Addr)) l->l_ld+l->l_addr);
elf_get_dynamic_info (l, dyn_temp);
_dl_setup_hash (l);
l->l_relocated = 1;
/* Initialize l_local_scope to contain justthismap. This allows
the use of dl_lookup_symbol_x toresolvesymbols within the vdso.
So we create a single entry list pointingtol_real as its only
element */
l->l_local_scope[0]->r_nlist = 1;
l->l_local_scope[0]->r_list=&l->l_real;
/* Now that we have the info handy, use theDSOimage's soname
so this object can be looked up byname. Note that we do not
set l_name here. That field givesthefile name of the DSO,
and this DSO is not associated withanyfile. */
if (l->l_info[DT_SONAME] != NULL)
{
/* Work around a kernelproblem. The kernel cannot handle
addresses in the vsyscall DSO pages in writev()calls. */
const char *dsoname = ((char *)D_PTR(l, l_info[DT_STRTAB])
+ l->l_info[DT_SONAME]->d_un.d_val);
size_t len = strlen (dsoname);
char *copy = malloc (len);
if (copy == NULL)
_dl_fatal_printf ("out of memory\n");
l->l_libname->name =memcpy(copy, dsoname, len);
}
/* Rearrange the list so this DSO appearsafterrtld_map. */
assert (l->l_next == NULL);
assert (l->l_prev == main_map);
GL(dl_rtld_map).l_next = l;
l->l_prev = &GL(dl_rtld_map);
first_preload = &l->l_next;
/* We have a prelinked DSO preloaded by thesystem. */
GLRO(dl_sysinfo_map) = l;
# ifdef NEED_DL_SYSINFO
if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT)
GLRO(dl_sysinfo)=GLRO(dl_sysinfo_dso)->e_entry + l->l_addr;
# endif
}
}
#endif
#ifdef DL_SYSDEP_OSCHECK
DL_SYSDEP_OSCHECK (dl_fatal);
#endif
/* Initialize the data structures for thesearchpaths 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 chainsoit can be found by
name. Note that at thispointthe global chain of link maps contains
exactly one element, which ispointedto by dl_loaded. */
if (! GL(dl_rtld_map).l_name)
/* If not invoked directly, thedynamiclinker shared object file was
found by the PT_INTERPname. */
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 notbeenseen, default
to not using bias fornon-prelinkedPIEs and libraries
and using it for executablesorprelinked 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 forthedynamic linker
itself. It is needed inthedl_iterate_phdr() callbacks. */
ElfW(Ehdr) *rtld_ehdr = (ElfW(Ehdr)*)GL(dl_rtld_map).l_map_start;
ElfW(Phdr) *rtld_phdr = (ElfW(Phdr)*)(GL(dl_rtld_map).l_map_start
+ 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 ifitalso uses TLS. */
if (GL(dl_rtld_map).l_tls_blocksize != 0)
/* Assign a module ID. Do thisbeforeloading any audit modules. */
GL(dl_rtld_map).l_tls_modid=_dl_next_tls_modid ();
/* If we have auditing DSOs to load, do it now. */
if (__builtin_expect (audit_list != NULL, 0))
{
/* Iterate over all entries inthelist. The order is important. */
struct audit_ifaces *last_audit=NULL;
struct audit_list *al=audit_list->next;
/* Since we start using theauditingDSOs right away we need to
initialize the data structures now. */
tcbp = init_tls ();
/* Initialize securityfeatures. We need to do it this early
since otherwise the constructors of the auditlibrarieswill
use different values (especially the pointer guard)andwill
fail later on. */
security_init ();
do
{
int tls_idx = GL(dl_tls_max_dtv_idx);
/* Now it is time to determine the layout ofthestatic TLS
block and allocate it for theinitialthread. Note that we
always allocate the static block, weneverdefer it even if
no DF_STATIC_TLS bit is set. Thereason is that we know
glibc will use the static model. */
struct dlmopen_args dlmargs;
dlmargs.fname = al->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 (__builtin_expect (err_str != NULL, 0))
{
not_loaded:
_dl_error_printf ("\
ERROR: ld.so: object '%s' cannot be loaded asauditinterface: %s; ignored.\n",
al->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 interfaceversionmatches. */
(void) _dl_catch_error(&objname,&err_str, &malloced,
lookup_doit, &largs);
unsigned int (*laversion)(unsignedint);
unsigned int lav;
if (err_str == NULL
&& (laversion = largs.result) != NULL
&& (lav = laversion (LAV_CURRENT)) > 0
&& lav <= LAV_CURRENT)
{
/* Allocate structure for the callbackfunctionpointers.
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. Allinone
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 tothelist. */
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 expectssomething
more recent. */
#ifndef NDEBUG
Lmid_t ns = dlmargs.map->l_ns;
#endif
_dl_close (dlmargs.map);
/* Make sure the namespace has beenclearedentirely. */
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;
}
}
al = al->next;
}
while (al != audit_list->next);
/* If we have any auditingmodules,announce that we already
have two objects loaded. */
if (__builtin_expect(GLRO(dl_naudit)> 0, 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
{
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;
}
}
}
}
/* Set up debugging before the debugger isnotifiedfor the first time. */
#ifdef ELF_MACHINE_DEBUG_SETUP
/* Some machines (e.g. MIPS) don't use DT_DEBUGinthis 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 thedynamicsection. Fill it in
with the run-time addressofthe r_debug structure */
main_map->l_info[DT_DEBUG]->d_un.d_ptr= (ElfW(Addr)) r;
/* Fill in the pointer in the dynamic linker'sowndynamic section, in
case you run gdb on the dynamiclinkerdirectly. */
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 ();
/* Auditing checkpoint: we are ready to signalthatthe initial map
is being constructed. */
if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
{
struct audit_ifaces *afct=GLRO(dl_audit);
for (unsigned int cnt = 0; 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:viaenvironment
variable and via thefile/etc/ld.so.preload. The latter can also
be used when security isenabled. */
assert (*first_preload == NULL);
struct link_map **preloads = NULL;
unsigned int npreloads = 0;
if (__builtin_expect (preloadlist != NULL, 0))
{
/* The LD_PRELOADenvironmentvariable gives list of libraries
separated by white space or colons that are loadedbeforethe
executable's dependencies and prepended to theglobalscope
list. If the binary is running setuid all elements
containing a '/' are ignored since it isinsecure. */
char *list = strdupa (preloadlist);
char *p;
HP_TIMING_NOW (start);
/* Prevent optimizingstrsep. Speed is not important here. */
while ((p = (strsep)(&list," :")) != NULL)
if (p[0] != '\0'
&& (__builtin_expect(!INTUSE(__libc_enable_secure), 1)
|| strchr (p, '/') == NULL))
npreloads += do_preload (p,main_map,"LD_PRELOAD");
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,itshould only be used
for emergencies and testing. Sothe open call etc should usually
fail. Using access() onanon-existing file is faster than using
open(). So we do thisfirst. If it succeeds we do almost twice
the work but this does notmatter,since it is not for production
use. */
static const char preload_file[]="/etc/ld.so.preload";
if (__builtin_expect (__access (preload_file,R_OK)== 0, 0))
{
/* Read the contents of thefile. */
file =_dl_sysdep_read_whole_file(preload_file, &file_size,
PROT_READ | PROT_WRITE);
if (__builtin_expect (file!=MAP_FAILED, 0))
{
/* Parse the file. It contains namesoflibraries to be loaded,
separated by white spaces or `:'. Itmay 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 anameat the end of
the file without a trailingterminatingcharacters, we cannot
place the \0. Handle thecaseseparately. */
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 (__builtin_expect (*first_preload != NULL, 0))
{
/* Set up PRELOADS with a vectorofthe 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 byDT_NEEDEDentries. If LD_PRELOAD
specified some libraries toload,these are inserted before the actual
dependencies in theexecutable'ssearchlist 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 globalscope. */
for (i = main_map->l_searchlist.r_nlist; i>0; )
main_map->l_searchlist.r_list[--i]->l_global = 1;
#ifndef MAP_ANON
/* We are done mapping things, so closethezero-fill descriptor. */
__close (_dl_zerofd);
_dl_zerofd = -1;
#endif
/* 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
if (main_map->l_searchlist.r_list[i]==&GL(dl_rtld_map))
break;
bool rtld_multiple_ref = false;
if (__builtin_expect (i
{
/* Some DT_NEEDED entry referredtothe interpreter object itself, so
put it back in the list of visible objects. Weinsert it into the
chain in symbol search order because gdb uses thechain'sorder 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_list[i + 1]
: NULL);
#if defined NEED_DL_SYSINFO || definedNEED_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(whichwe
could not find) after the previous one inthesearch list.
In this case it doesn't matter much where weputthe
interpreter object, so we just initialize thelistpointer 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 librariesareavailable 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 theTLSfunctionality unless any of the
initial modules uses TLS. Thismakes dynamic loading of modules with
TLS impossible, but to supportitrequires either eagerly doing setup
now or lazily doing itlater. Doing it now makes us incompatible with
an old kernel that can'tperformTLS_INIT_TP, even if no TLS is ever
used. Trying to do it lazilyistoo hairy to try when there could be
multiple threads (from anon-TLS-usinglibpthread). */
bool was_tls_init_tp_called = tls_init_tp_called;
if (tcbp == NULL)
tcbp = init_tls ();
if (__builtin_expect (audit_list == NULL, 1))
/* Initialize security features. Butonly if we have not done it
earlier. */
security_init ();
if (__builtin_expect (mode, normal) != normal)
{
/* We were run just to listtheshared libraries. It is
important that we do this before real relocation,becausethe
functions we call below for output may no longerworkproperly
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 => notfound\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)",
l->l_libname->name[0]?l->l_libname->name
: rtld_progname ?: "
l->l_name[0] ? l->l_name
: rtld_progname ?: "
(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 themainexecutable
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, .lazy=GLRO(dl_lazy) };
_dl_receive_error (print_unresolved,relocate_doit,&args);
/* This loop depends on the dependencies oftheexecutable to
correspond in number and order totheDT_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;
if (!l->l_used)
{
if (first)
{
_dl_printf ("Unused directdependencies:\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 =>notfound\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(INTUSE(_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);
_dl_printf ("%s found at 0x%0*Zdinobject at 0x%0*Zd\n",
INTUSE(_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 undefinedsymbols. */
if (GLRO(dl_lazy) >= 0&&GLRO(dl_verbose))
{
/* We have to do symboldependencytesting. */
struct relocate_args args;
struct link_map *l;
args.lazy = GLRO(dl_lazy);
l = main_map;
while (l->l_next != NULL)
l = l->l_next;
do
{
if (l != &GL(dl_rtld_map) &&!l->l_faked)
{
args.l = l;
_dl_receive_error(print_unresolved,relocate_doit,
&args);
}
l = l->l_prev;
}
while (l != NULL);
if ((GLRO(dl_debug_mask)&DL_DEBUG_PRELINK)
&& rtld_multiple_ref)
{
/* Mark the link map as not yet relocatedagain. */
GL(dl_rtld_map).l_relocated = 0;
_dl_relocate_object (&GL(dl_rtld_map),
main_map->l_scope, 0, 0);
}
}
#define VERNEEDTAG (DT_NUM + DT_THISPROCNUM+DT_VERSIONTAGIDX (DT_VERNEED))
if (version_info)
{
/* Print more information. Thismeans 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\tVersioninformation:\n");
first = 0;
}
_dl_printf ("\t%s:\n",
map->l_name[0] ? map->l_name:rtld_progname);
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
{
l = *r_list;
if (l == main_map)
continue;
/* If the library is not mapped where itshould,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(__builtin_expect(GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
_dl_debug_printf ("\nprelink checking: %s\n",
prelinked ? "ok" : "failed");
}
/* Now set up the variable which helpstheassembler startup code. */
GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist=&main_map->l_searchlist;
/* Save the information about the originalglobalscope list since
we need it in the memory handlinglater. */
GLRO(dl_initial_searchlist)=*GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist;
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 weknowthey 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 ifnecessasy. */
if (l->l_tls_blocksize != 0&&tls_init_tp_called)
_dl_add_to_slotinfo (l);
}
_dl_sysdep_start_cleanup ();
}
else
{
/* Now we have all theobjectsloaded. Relocate them all except for
the dynamic linker itself. We do this in reverseorderso that copy
relocs of earlier objects overwrite the data writtenbylater
objects. We do not re-relocate the dynamiclinkeritself in this
loop because that could result in the GOT entriesforfunctions we
call being changed, and that would break us. Itissafe to relocate
the dynamic linker out of order because it has nocopyrelocs (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 alsomustdo lazy reloaction. */
GLRO(dl_lazy) |= consider_profiling;
struct link_map *l = main_map;
while (l->l_next)
l = l->l_next;
HP_TIMING_NOW (start);
do
{
/* While we are at it, help the memory handlingabit. We have to
mark some data structures as allocatedwiththe 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;
}
if (l != &GL(dl_rtld_map))
_dl_relocate_object (l,l->l_scope,GLRO(dl_lazy),
consider_profiling);
/* Add object to slot information data ifnecessasy. */
if (l->l_tls_blocksize != 0&&tls_init_tp_called)
_dl_add_to_slotinfo (l);
l = l->l_prev;
}
while (l);
HP_TIMING_NOW (stop);
HP_TIMING_DIFF (relocate_time,start,stop);
/* Do any necessary cleanups forthestartup OS interface code.
We do these now so that no calls are made afterrtldre-relocation
which might be resolved to different functions thanweexpect.
We cannot do this before relocating the otherobjectsbecause
_dl_relocate_object might need to call `mprotect'forDT_TEXTREL. */
_dl_sysdep_start_cleanup ();
/* Now enable profiling ifneeded. Like the previous call,
this has to go here because the calls it makes shouldusethe
rtld versions of the functions (particularlycalloc()),but it
needs to have _dl_profile_map set up by therelocator. */
if(__builtin_expect(GL(dl_profile_map) != NULL, 0))
/* We must prepare the profiling. */
_dl_start_profile ();
}
#ifndef NONTLS_INIT_TP
# define NONTLS_INIT_TP do { } while (0)
#endif
if (!was_tls_init_tp_called&&GL(dl_tls_max_dtv_idx) > 0)
++GL(dl_tls_generation);
/* Now that we have completed relocation,theinitializer data
for the TLS blocks has itsfinalvalues and we can copy them
into the main thread's TLS area,whichwe allocated above. */
_dl_allocate_tls_init (tcbp);
/* And finally install it for the mainthread. If ld.so itself uses
TLS we know the thread pointerwasinitialized earlier. */
if (! tls_init_tp_called)
{
const char *lossage =TLS_INIT_TP(tcbp, USE___THREAD);
if (__builtin_expect (lossage!=NULL, 0))
_dl_fatal_printf ("cannot set upthread-localstorage: %s\n",
lossage);
}
if (! prelinked && rtld_multiple_ref)
{
/* There was an explicit ref tothedynamic linker as a shared lib.
Re-relocate ourselves with user-controlledsymboldefinitions.
We must do this after TLS initialization in caseafterthis
re-relocation, we might call a user-supplied function
(e.g. calloc from _dl_relocate_object) that uses TLSdata. */
#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 notyetrelocated 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);
}
#ifdef SHARED
/* Auditing checkpoint: we have added allobjects. */
if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
{
struct link_map *head=GL(dl_ns)[LM_ID_BASE]._ns_loaded;
/* Do not call the functions foranyauditing object. */
if (head->l_auditing == 0)
{
struct audit_ifaces *afct = GLRO(dl_audit);
for (unsigned int cnt = 0; 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 arenowready to go. We must re-get
the address since by now thevariablemight be in another object. */
r = _dl_debug_initialize (0, LM_ID_BASE);
r->r_state = RT_CONSISTENT;
_dl_debug_state ();
#ifndef MAP_COPY
/* We must munmap() the cache file. */
_dl_unload_cache ();
#endif
/* Once we return, _dl_sysdep_start will invoke
the DT_INIT functions andthen*USER_ENTRY. */
}