下载了libc的源码,现在就开始libc源码的学习,最近了解到了linux动态库的相关知识,那么就从linux动态库加载函数dlopen进行梳理学习吧。
如果还没下载libc源码,可通过
https://blog.csdn.net/SweeNeil/article/details/83744069
来查看自己需要的libc版本并进行下载。在这里我使用的是glibc-2.15
一、glibc/dlfcn/dlopen.c
解压源代码,首先进入glibc/dlfcn目录下,我们看到在该目录下有很多的dlxxx文件
其中dlopen.c就是我们需要分析的dlopen函数的定义地址,进入到dlopen.c中
dlopen函数如下:
dlopen (const char *file, int mode) return __dlopen (file, mode, RETURN_ADDRESS (0));
它实际上是调用了__dlopen函数,我们在进入到__dlopen函数中
__dlopen (const char *file, int mode DL_CALLER_DECL) if (__builtin_expect (_dlfcn_hook != NULL, 0)) return _dlfcn_hook->dlopen (file, mode, DL_CALLER); return _dlerror_run (dlopen_doit, &args) ? NULL : args.new; if (_dlerror_run (dlopen_doit, &args)) __libc_register_dl_open_hook ((struct link_map *) args.new); __libc_register_dlfcn_hook ((struct link_map *) args.new);
该函数主要对args进行了赋值,比较关键的就是args.new,它作为了返回值被返回,而这个args.new是通过dlopen_doit函数进行赋值的。
先来了解一个这个args到底是什么
/* The arguments for dlopen_doit. */ /* The return value of dlopen_doit. */ /* Address of the caller. */
它由上述所示的四个字段组成,这个void *new就是最终的返回值,它在dlopen_doit中被返回到__dlopen。进入到dlopen_doit函数中
struct dlopen_args *args = (struct dlopen_args *) a; if (args->mode & ~(RTLD_BINDING_MASK | RTLD_NOLOAD | RTLD_DEEPBIND | RTLD_GLOBAL | RTLD_LOCAL | RTLD_NODELETE GLRO(dl_signal_error) (0, NULL, NULL, _("invalid mode parameter")); args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN, args->file == NULL ? LM_ID_BASE : NS, __dlfcn_argc, __dlfcn_argv, __environ);
上述代码主要是对args->new进行赋值,不过是通过_dl_open函数来进行的,这个GLRO(dl_open)实际上就是_dl_open函数,因此下一步又需要去查看_dl_open函数,此时_dl_open函数就已经不再是定义在dlopen.c中了,而是dl-open.c中。
暂且将dlopen.c文件中的调用关系称为第一层调用吧,其具体图示如下所示:
二、glibc/elf/dl-open.c
在dl-open.c下也有着大量的关于dl-xxx的具体实现
我们需要了解的是其中的dl-open,打开dl-open文件,里面有_dl_open函数的具体实现,省略号部分表示省略了部分代码。
_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, int argc, char *argv[], char *env[]) /* Never allow loading a DSO in a namespace which is empty. Such direct placements is only causing problems. Also don't allow loading into a namespace used for auditing. */ else if (__builtin_expect (nsid != LM_ID_BASE && nsid != __LM_ID_CALLER, 0) && (GL(dl_ns)[nsid]._ns_nloaded == 0 || GL(dl_ns)[nsid]._ns_loaded->l_auditing)) _dl_signal_error (EINVAL, file, NULL, N_("invalid target namespace in dlmopen()")); else if ((nsid == LM_ID_BASE || nsid == __LM_ID_CALLER) && GL(dl_ns)[LM_ID_BASE]._ns_loaded == NULL struct dl_open_args args; args.caller_dlopen = caller_dlopen; args.caller_dl_open = RETURN_ADDRESS (0); int errcode = _dl_catch_error (&objname, &errstring, &malloced, /* We must munmap() the cache file. */ /* See if an error occurred during loading. */ if (__builtin_expect (errstring != NULL, 0)) /* Remove the object from memory. It may be in an inconsistent state if relocation failed, for example. */ /* Maybe some of the modules which were loaded use TLS. Since it will be removed in the following _dl_close call we have to mark the dtv array as having gaps to fill the holes. This is a pessimistic assumption which won't hurt if not true. There is no need to do this when we are loading the auditing DSOs since TLS has not yet been set if ((mode & __RTLD_AUDIT) == 0) GL(dl_tls_dtv_gaps) = true; _dl_close_worker (args.map); assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); __rtld_lock_unlock_recursive (GL(dl_load_lock)); /* Make a local copy of the error string so that we can release the memory allocated for it. */ size_t len_errstring = strlen (errstring) + 1; if (objname == errstring + len_errstring) size_t total_len = len_errstring + strlen (objname) + 1; local_errstring = alloca (total_len); memcpy (local_errstring, errstring, total_len); objname = local_errstring + len_errstring; local_errstring = alloca (len_errstring); memcpy (local_errstring, errstring, len_errstring); free ((char *) errstring); _dl_signal_error (errcode, objname, NULL, local_errstring); assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); __rtld_lock_unlock_recursive (GL(dl_load_lock)); DL_STATIC_INIT (args.map);
在_dl_open函数中定义了一个新的结构体dl_open_args,并且_dl_open中实际上是调用了dl_open_worker函数,然后在dl_open_worker中进行了相关操作。
先来看看dl_open_args结构
/* This is the caller of the dlopen() function. */ const void *caller_dlopen; /* This is the caller if _dl_open(). */ const void *caller_dl_open; /* Original parameters to the program and the current environment. */
dl_open_args结构体定义了一些dl_open阶段的参数,其中最重要的就是struct link_map这个结构体,它作为了_dl_open的返回值对应了dlopen_doit中的char * new。
关于struct link_map想另开一篇进行讲述这里先继续分析函数调用。
在_dl_open函数中定义了这样一个结构之后,这个link_map结构的map值需要从dl_open_worker中进行获取,我们进入到dl_open_worker函数中来查看它到底做了什么。
struct dl_open_args *args = a; const char *file = args->file; struct link_map *call_map = NULL; /* Check whether _dl_open() has been called from a valid DSO. */ if (__check_caller (args->caller_dl_open, allow_libc|allow_libdl|allow_ldso) != 0) _dl_signal_error (0, "dlopen", NULL, N_("invalid caller")); /* Determine the caller's map if necessary. This is needed in case we have a DST, when we don't know the namespace ID we have to put the new object in, or when the file name has no path in which case we need to look along the RUNPATH/RPATH of the caller. */ const char *dst = strchr (file, '$'); if (dst != NULL || args->nsid == __LM_ID_CALLER || strchr (file, '/') == NULL) const void *caller_dlopen = args->caller_dlopen; /* We have to find out from which object the caller is calling. By default we assume this is the main application. */ call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) if (caller_dlopen >= (const void *) l->l_map_start && caller_dlopen < (const void *) l->l_map_end || _dl_addr_inside_object (l, (ElfW(Addr)) caller_dlopen))) if (args->nsid == __LM_ID_CALLER) /* In statically linked apps there might be no loaded object. */ args->nsid = call_map->l_ns; assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); /* Load the named object. */ args->map = new = _dl_map_object (call_map, file, lt_loaded, 0, mode | __RTLD_CALLMAP, args->nsid); /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is set and the object is not already loaded. */ assert (mode & RTLD_NOLOAD); if (__builtin_expect (mode & __RTLD_SPROF, 0)) /* This happens only if we load a DSO for 'sprof'. */ /* This object is directly loaded. */ ++new->l_direct_opencount; /* It was already open. */ if (__builtin_expect (new->l_searchlist.r_list != NULL, 0)) /* Let the user know about the opencount. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", new->l_name, new->l_ns, new->l_direct_opencount); /* If the user requested the object to be in the global namespace but it is not so far, add it now. */ if ((mode & RTLD_GLOBAL) && new->l_global == 0) (void) add_to_global (new); assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); /* Load that object's dependencies. */ _dl_map_object_deps (new, NULL, 0, 0, mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT)); /* So far, so good. Now check the versions. */ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) if (new->l_searchlist.r_list[i]->l_real->l_versions == NULL) (void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real, /* Auditing checkpoint: we have added all objects. */ if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) struct link_map *head = GL(dl_ns)[new->l_ns]._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); /* Notify the debugger all new objects are now ready to go. */ struct r_debug *r = _dl_debug_initialize (0, args->nsid); r->r_state = RT_CONSISTENT; /* Print scope information. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES, 0)) /* Only do lazy relocation if `LD_BIND_NOW' is not set. */ int reloc_mode = mode & __RTLD_AUDIT; reloc_mode |= mode & RTLD_LAZY; /* Relocate the objects loaded. We do this in reverse order so that copy relocs of earlier objects overwrite the data written by later objects. */ struct link_map *l = new; if (! l->l_real->l_relocated) if (__builtin_expect (GLRO(dl_profile) != NULL, 0)) /* If this here is the shared object which we want to profile make sure the profile is started. We can find out whether this is necessary or not by observing the `_dl_profile_map' variable. If was NULL but is not NULL afterwars we must struct link_map *old_profile_map = GL(dl_profile_map); _dl_relocate_object (l, l->l_scope, reloc_mode | RTLD_LAZY, 1); if (old_profile_map == NULL && GL(dl_profile_map) != NULL) /* We must prepare the profiling. */ /* Prevent unloading the object. */ GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE; _dl_relocate_object (l, l->l_scope, reloc_mode, 0); /* If the file is not loaded now as a dependency, add the search list of the newly loaded object to the scope. */ unsigned int first_static_tls = new->l_searchlist.r_nlist; for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) struct link_map *imap = new->l_searchlist.r_list[i]; /* If the initializer has been called already, the object has not been loaded here and now. */ if (imap->l_init_called && imap->l_type == lt_loaded) struct r_scope_elem **runp = imap->l_scope; if (*runp == &new->l_searchlist) if (__builtin_expect (cnt + 1 >= imap->l_scope_max, 0)) /* The 'r_scope' array is too small. Allocate a new one struct r_scope_elem **newp; #define SCOPE_ELEMS(imap) \ (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0])) if (imap->l_scope != imap->l_scope_mem && imap->l_scope_max < SCOPE_ELEMS (imap)) new_size = SCOPE_ELEMS (imap); newp = imap->l_scope_mem; new_size = imap->l_scope_max * 2; newp = (struct r_scope_elem **) malloc (new_size * sizeof (struct r_scope_elem *)); _dl_signal_error (ENOMEM, "dlopen", NULL, N_("cannot create scope list")); memcpy (newp, imap->l_scope, cnt * sizeof (imap->l_scope[0])); struct r_scope_elem **old = imap->l_scope; if (old != imap->l_scope_mem) imap->l_scope_max = new_size; /* First terminate the extended list. Otherwise a thread might use the new last element and then use the garbage imap->l_scope[cnt + 1] = NULL; imap->l_scope[cnt] = &new->l_searchlist; /* Print only new scope information. */ /* Only add TLS memory if this object is loaded now and therefore is not yet initialized. */ else if (! imap->l_init_called /* Only if the module defines thread local data. */ && __builtin_expect (imap->l_tls_blocksize > 0, 0)) /* Now that we know the object is loaded successfully add modules containing TLS data to the slot info table. We might have to increase its size. */ _dl_add_to_slotinfo (imap); if (imap->l_need_tls_init && first_static_tls == new->l_searchlist.r_nlist) /* We have to bump the generation counter. */ /* Print scope information. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES, 0)) _dl_show_scope (imap, from_scope); /* Bump the generation number if necessary. */ if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0)) TLS generation counter wrapped! Please report this.")); /* We need a second pass for static tls data, because _dl_update_slotinfo must not be run while calls to _dl_add_to_slotinfo are still pending. */ for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i) struct link_map *imap = new->l_searchlist.r_list[i]; if (imap->l_need_tls_init && imap->l_tls_blocksize > 0) /* For static TLS we have to allocate the memory here and now. This includes allocating memory in the DTV. But we cannot change any DTV other than our own. So, if we cannot guarantee that there is room in the DTV we don't even try it and fail the load. XXX We could track the minimum DTV slots allocated in if (! RTLD_SINGLE_THREAD_P && imap->l_tls_modid > DTV_SURPLUS) _dl_signal_error (0, "dlopen", NULL, N_("\ cannot load any more object with static TLS")); imap->l_need_tls_init = 0; /* Update the slot information data for at least the generation of the DSO we are allocating data for. */ _dl_update_slotinfo (imap->l_tls_modid); GL(dl_init_static_tls) (imap); assert (imap->l_need_tls_init == 0); /* Run the initializer functions of new objects. */ _dl_init (new, args->argc, args->argv, args->env); /* Now we can make the new map available in the global scope. */ /* Move the object in the global namespace. */ if (add_to_global (new) != 0) /* Mark the object as not deletable if the RTLD_NODELETE flags was if (__builtin_expect (mode & RTLD_NODELETE, 0)) new->l_flags_1 |= DF_1_NODELETE; /* We must be the static _dl_open in libc.a. A static program that has loaded a dynamic object now has competition. */ __libc_multiple_libcs = 1; /* Let the user know about the opencount. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", new->l_name, new->l_ns, new->l_direct_opencount);
从上可以看到,dl_open_worker中的内容非常多,不过在其中我们发现了这样的一条语句
args->map = new = _dl_map_object (call_map, file, lt_loaded, 0, mode | __RTLD_CALLMAP, args->nsid);
从这个语句可知,dl_open_worker主要通过_dl_map_object来进行内容的映射并返回这个map,我们再进入到_dl_map_object中来进行查看
三、glibc/elf/dl-load.c
_dl_map_object函数定义在dl-load.c中
_dl_map_object (struct link_map *loader, const char *name, int type, int trace_mode, int mode, Lmid_t nsid) /* Look for this name among those already loaded. */ for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next) /* If the requested name matches the soname of a loaded object, use that object. Elide this check for names that have not if (__builtin_expect (l->l_faked, 0) != 0 || __builtin_expect (l->l_removed, 0) != 0) if (!_dl_name_match_p (name, l)) if (__builtin_expect (l->l_soname_added, 1) || l->l_info[DT_SONAME] == NULL) soname = ((const char *) D_PTR (l, l_info[DT_STRTAB]) + l->l_info[DT_SONAME]->d_un.d_val); if (strcmp (name, soname) != 0) /* We have a match on a new name -- cache it. */ add_name_to_object (l, soname); fd = open_path (name, namelen, mode & __RTLD_SECURE, &realname, &fb, loader ?: main_map, LA_SER_RUNPATH, l = _dl_new_object (name_copy, name, type, loader, return _dl_map_object_from_fd (name, fd, &fb, realname, loader, type, mode,
_dl_map_object中的内容实在是太多了,这里进行了大量的省略,在_dl_map_object中首先判断这个so是否被其他程序给加载,如果被加载,就直接将这个link_map进行返回,如果没有被使用过再进行下面的步骤。
fd = open_path()主要通过各个标志来确定,我们进入到open_path中查看
此时_dl_map_object -> open_path
open_path (const char *name, size_t namelen, int secure, struct r_search_path_struct *sps, char **realname, struct filebuf *fbp, struct link_map *loader, int whatcode, struct r_search_path_elem **dirs = sps->dirs; const char *current_what = NULL; buf = alloca (max_dirnamelen + max_capstrlen + namelen); struct r_search_path_elem *this_dir = *dirs; edp = (char *) __mempcpy (buf, this_dir->dirname, this_dir->dirnamelen); for (cnt = 0; fd == -1 && cnt < ncapstr; ++cnt) /* Skip this directory if we know it does not exist. */ if (this_dir->status[cnt] == nonexisting) ((char *) __mempcpy (__mempcpy (edp, capstr[cnt].str, /* Print name we try if this is wanted. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) _dl_debug_printf (" trying file=%s\n", buf); fd = open_verify (buf, fbp, loader, whatcode, found_other_class, *realname = (char *) malloc (buflen); memcpy (*realname, buf, buflen); /* No memory for the name, we certainly won't be able if (here_any && (err = errno) != ENOENT && err != EACCES) /* The file exists and is readable, but something went wrong. */ /* Remember whether we found anything. */
可以看出在open_path中同样还未设计到文件的打开与关闭操作,fd还是通过open_verify来进行返回的,可以进入到open_verify函数中查看其内容,目前调用路径为:
_dl_map_object -> open_path->open_verify
open_verify函数内容如下
open_verify (const char *name, struct filebuf *fbp, struct link_map *loader, int whatcode, bool *found_other_class, bool free_name) /* Open the file. We always open files read-only. */ int fd = __open (name, O_RDONLY | O_CLOEXEC); /* We successfully openened the file. Now verify it is a file fbp->len = __libc_read (fd, fbp->buf, sizeof (fbp->buf)); /* This is where the ELF header is loaded. */ assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr))); ehdr = (ElfW(Ehdr) *) fbp->buf; maplength = ehdr->e_phnum * sizeof (ElfW(Phdr)); if (ehdr->e_phoff + maplength <= (size_t) fbp->len) phdr = (void *) (fbp->buf + ehdr->e_phoff); phdr = alloca (maplength); __lseek (fd, ehdr->e_phoff, SEEK_SET); if ((size_t) __libc_read (fd, (void *) phdr, maplength) != maplength) errstring = N_("cannot read file data"); /* Check .note.ABI-tag if present. */ for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph) if (ph->p_type == PT_NOTE && ph->p_filesz >= 32 && ph->p_align >= 4) ElfW(Addr) size = ph->p_filesz; if (ph->p_offset + size <= (size_t) fbp->len) abi_note = (void *) (fbp->buf + ph->p_offset); abi_note = alloca (size); __lseek (fd, ph->p_offset, SEEK_SET); if (__libc_read (fd, (void *) abi_note, size) != size) while (memcmp (abi_note, &expected_note, sizeof (expected_note))) #define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word))) ElfW(Addr) note_size = 3 * sizeof (ElfW(Word)) if (size - 32 < note_size) abi_note = (void *) abi_note + note_size; osversion = (abi_note[5] & 0xff) * 65536 + (abi_note[6] & 0xff) * 256 if (abi_note[4] != __ABI_TAG_OS || (GLRO(dl_osversion) && GLRO(dl_osversion) < osversion))
从上述代码中可以看到,在open_verify中对文件进行了打开和读的操作。在里面使用到了一个fbp的结构体,其内容如下:
# define FILEBUF_SIZE 512 # define FILEBUF_SIZE 832 char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
其中对FILEBUF_SIZE大小进行了定义
在open_verify中存在
fbp->len = __libc_read (fd, fbp->buf, sizeof (fbp->buf));
即将so文件中的部分内容读入到了fbp中,然后进行了一些校验,也就是在这个函数里真正对文件进行了打开和读操作。至此这条调用路径就结束,然后再来看_dl_new_object中的内容。
_dl_new_object主要是一个分配struct link_map* 数据结构并填充一些最基本的参数,其函数内容如下:
_dl_new_object (char *realname, const char *libname, int type, struct link_map *loader, int mode, Lmid_t nsid) size_t libname_len = strlen (libname) + 1; struct libname_list *newname; /* We create the map for the executable before we know whether we have auditing libraries and if yes, how many. Assume the worst. */ unsigned int naudit = GLRO(dl_naudit) ?: ((mode & __RTLD_OPENEXEC) size_t audit_space = naudit * sizeof (new->l_audit[0]); new = (struct link_map *) calloc (sizeof (*new) + audit_space + sizeof (struct link_map *) + sizeof (*newname) + libname_len, 1); new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1) = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1); newname->name = (char *) memcpy (newname + 1, libname, libname_len); /* newname->next = NULL; We use calloc therefore not necessary. */ /* If we set the bit now since we know it is never used we avoid dirtying the cache line later. */ if ((GLRO(dl_debug_mask) & DL_DEBUG_UNUSED) == 0) new->l_tls_offset = NO_TLS_OFFSET; for (unsigned int cnt = 0; cnt < naudit; ++cnt) new->l_audit[cnt].cookie = (uintptr_t) new; /* new->l_audit[cnt].bindflags = 0; */ /* new->l_global = 0; We use calloc therefore not necessary. */ /* Use the 'l_scope_mem' array by default for the 'l_scope' information. If we need more entries we will allocate a large new->l_scope = new->l_scope_mem; new->l_scope_max = sizeof (new->l_scope_mem) / sizeof (new->l_scope_mem[0]); /* Counter for the scopes we have to handle. */ if (GL(dl_ns)[nsid]._ns_loaded != NULL) /* Add the global scope. */ new->l_scope[idx++] = &GL(dl_ns)[nsid]._ns_loaded->l_searchlist; /* If we have no loader the new object acts as it. */ /* Determine the local scope. */ while (loader->l_loader != NULL) loader = loader->l_loader; /* Insert the scope if it isn't the global scope we already added. */ if (idx == 0 || &loader->l_searchlist != new->l_scope[0]) if ((mode & RTLD_DEEPBIND) != 0 && idx != 0) new->l_scope[1] = new->l_scope[0]; new->l_scope[idx] = &loader->l_searchlist; new->l_local_scope[0] = &new->l_searchlist; /* Don't try to find the origin for the main map which has the name "". */ size_t realname_len = strlen (realname) + 1; /* It is an absolute path. Use it. But we have to make a copy since we strip out the trailing slash. */ cp = origin = (char *) malloc (realname_len); size_t len = realname_len; /* Get the current directory name. */ new_origin = (char *) realloc (origin, len); /* We exit the loop. Note that result == NULL. */ while ((result = __getcwd (origin, len - realname_len)) == NULL /* We were not able to determine the current directory. Note that free(origin) is OK if origin == NULL. */ /* Find the end of the path and see whether we have to add a slash. We could use rawmemchr but this need not be cp = (strchr) (origin, '\0'); /* Add the real file name. */ cp = __mempcpy (cp, realname, realname_len); /* Now remove the filename and the slash. Leave the slash if the name is something like "/foo". */ /* Keep the only slash which is the first character. */
_dl_new_object函数如上,为struct link_map 的成员数据赋值。并把新的struct link_map* 加入到一个单链中,这是在以后是很有用的,因为这样在一个执行文件中如果要整体管理它相关的动态链接库,就可以以单链遍历。
如果要加载的动态链接库还没有被映射到进程的虚拟内存空间的话,那只是准备工作,真正的要点在 _dl_map_object_from_fd()这个函数开始的。因为这之后,每一步都有关动态链接库在进程中发挥它的作用而必须的条件。
进入到_dl_map_object_from_fd()中进行查看
进去后发现太长了,准备在(二)中继续进行介绍,这里先介绍到这里,下面是_dl_open后续的调用流程
|