本章分析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