分享

Linux动态库加载函数dlopen源码梳理(一)

 astrotycoon 2019-06-05

下载了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函数如下:

  1. void *
  2. dlopen (const char *file, int mode)
  3. {
  4. return __dlopen (file, mode, RETURN_ADDRESS (0));
  5. }

它实际上是调用了__dlopen函数,我们在进入到__dlopen函数中

  1. void *
  2. __dlopen (const char *file, int mode DL_CALLER_DECL)
  3. {
  4. # ifdef SHARED
  5. if (__builtin_expect (_dlfcn_hook != NULL, 0))
  6. return _dlfcn_hook->dlopen (file, mode, DL_CALLER);
  7. # endif
  8. struct dlopen_args args;
  9. args.file = file;
  10. args.mode = mode;
  11. args.caller = DL_CALLER;
  12. # ifdef SHARED
  13. return _dlerror_run (dlopen_doit, &args) ? NULL : args.new;
  14. # else
  15. if (_dlerror_run (dlopen_doit, &args))
  16. return NULL;
  17. __libc_register_dl_open_hook ((struct link_map *) args.new);
  18. __libc_register_dlfcn_hook ((struct link_map *) args.new);
  19. return args.new;
  20. # endif
  21. }

该函数主要对args进行了赋值,比较关键的就是args.new,它作为了返回值被返回,而这个args.new是通过dlopen_doit函数进行赋值的。

先来了解一个这个args到底是什么

  1. struct dlopen_args
  2. {
  3. /* The arguments for dlopen_doit. */
  4. const char *file;
  5. int mode;
  6. /* The return value of dlopen_doit. */
  7. void *new;
  8. /* Address of the caller. */
  9. const void *caller;
  10. };

它由上述所示的四个字段组成,这个void *new就是最终的返回值,它在dlopen_doit中被返回到__dlopen。进入到dlopen_doit函数中

  1. static void
  2. dlopen_doit (void *a)
  3. {
  4. struct dlopen_args *args = (struct dlopen_args *) a;
  5. if (args->mode & ~(RTLD_BINDING_MASK | RTLD_NOLOAD | RTLD_DEEPBIND
  6. | RTLD_GLOBAL | RTLD_LOCAL | RTLD_NODELETE
  7. | __RTLD_SPROF))
  8. GLRO(dl_signal_error) (0, NULL, NULL, _("invalid mode parameter"));
  9. args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN,
  10. args->caller,
  11. args->file == NULL ? LM_ID_BASE : NS,
  12. __dlfcn_argc, __dlfcn_argv, __environ);
  13. }

上述代码主要是对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函数的具体实现,省略号部分表示省略了部分代码。

  1. void *
  2. _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
  3. int argc, char *argv[], char *env[])
  4. {
  5. ……
  6. ……
  7. /* Never allow loading a DSO in a namespace which is empty. Such
  8. direct placements is only causing problems. Also don't allow
  9. loading into a namespace used for auditing. */
  10. else if (__builtin_expect (nsid != LM_ID_BASE && nsid != __LM_ID_CALLER, 0)
  11. && (GL(dl_ns)[nsid]._ns_nloaded == 0
  12. || GL(dl_ns)[nsid]._ns_loaded->l_auditing))
  13. _dl_signal_error (EINVAL, file, NULL,
  14. N_("invalid target namespace in dlmopen()"));
  15. #ifndef SHARED
  16. else if ((nsid == LM_ID_BASE || nsid == __LM_ID_CALLER)
  17. && GL(dl_ns)[LM_ID_BASE]._ns_loaded == NULL
  18. && GL(dl_nns) == 0)
  19. GL(dl_nns) = 1;
  20. #endif
  21. struct dl_open_args args;
  22. args.file = file;
  23. args.mode = mode;
  24. args.caller_dlopen = caller_dlopen;
  25. args.caller_dl_open = RETURN_ADDRESS (0);
  26. args.map = NULL;
  27. args.nsid = nsid;
  28. args.argc = argc;
  29. args.argv = argv;
  30. args.env = env;
  31. const char *objname;
  32. const char *errstring;
  33. bool malloced;
  34. int errcode = _dl_catch_error (&objname, &errstring, &malloced,
  35. dl_open_worker, &args);
  36. #ifndef MAP_COPY
  37. /* We must munmap() the cache file. */
  38. _dl_unload_cache ();
  39. #endif
  40. /* See if an error occurred during loading. */
  41. if (__builtin_expect (errstring != NULL, 0))
  42. {
  43. /* Remove the object from memory. It may be in an inconsistent
  44. state if relocation failed, for example. */
  45. if (args.map)
  46. {
  47. /* Maybe some of the modules which were loaded use TLS.
  48. Since it will be removed in the following _dl_close call
  49. we have to mark the dtv array as having gaps to fill the
  50. holes. This is a pessimistic assumption which won't hurt
  51. if not true. There is no need to do this when we are
  52. loading the auditing DSOs since TLS has not yet been set
  53. up. */
  54. if ((mode & __RTLD_AUDIT) == 0)
  55. GL(dl_tls_dtv_gaps) = true;
  56. _dl_close_worker (args.map);
  57. }
  58. assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
  59. /* Release the lock. */
  60. __rtld_lock_unlock_recursive (GL(dl_load_lock));
  61. /* Make a local copy of the error string so that we can release the
  62. memory allocated for it. */
  63. size_t len_errstring = strlen (errstring) + 1;
  64. char *local_errstring;
  65. if (objname == errstring + len_errstring)
  66. {
  67. size_t total_len = len_errstring + strlen (objname) + 1;
  68. local_errstring = alloca (total_len);
  69. memcpy (local_errstring, errstring, total_len);
  70. objname = local_errstring + len_errstring;
  71. }
  72. else
  73. {
  74. local_errstring = alloca (len_errstring);
  75. memcpy (local_errstring, errstring, len_errstring);
  76. }
  77. if (malloced)
  78. free ((char *) errstring);
  79. /* Reraise the error. */
  80. _dl_signal_error (errcode, objname, NULL, local_errstring);
  81. }
  82. assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
  83. /* Release the lock. */
  84. __rtld_lock_unlock_recursive (GL(dl_load_lock));
  85. #ifndef SHARED
  86. DL_STATIC_INIT (args.map);
  87. #endif
  88. return args.map;
  89. }

在_dl_open函数中定义了一个新的结构体dl_open_args,并且_dl_open中实际上是调用了dl_open_worker函数,然后在dl_open_worker中进行了相关操作。

先来看看dl_open_args结构

  1. struct dl_open_args
  2. {
  3. const char *file;
  4. int mode;
  5. /* This is the caller of the dlopen() function. */
  6. const void *caller_dlopen;
  7. /* This is the caller if _dl_open(). */
  8. const void *caller_dl_open;
  9. struct link_map *map;
  10. /* Namespace ID. */
  11. Lmid_t nsid;
  12. /* Original parameters to the program and the current environment. */
  13. int argc;
  14. char **argv;
  15. char **env;
  16. };

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函数中来查看它到底做了什么。

  1. static void
  2. dl_open_worker (void *a)
  3. {
  4. struct dl_open_args *args = a;
  5. const char *file = args->file;
  6. int mode = args->mode;
  7. struct link_map *call_map = NULL;
  8. /* Check whether _dl_open() has been called from a valid DSO. */
  9. if (__check_caller (args->caller_dl_open,
  10. allow_libc|allow_libdl|allow_ldso) != 0)
  11. _dl_signal_error (0, "dlopen", NULL, N_("invalid caller"));
  12. /* Determine the caller's map if necessary. This is needed in case
  13. we have a DST, when we don't know the namespace ID we have to put
  14. the new object in, or when the file name has no path in which
  15. case we need to look along the RUNPATH/RPATH of the caller. */
  16. const char *dst = strchr (file, '$');
  17. if (dst != NULL || args->nsid == __LM_ID_CALLER
  18. || strchr (file, '/') == NULL)
  19. {
  20. const void *caller_dlopen = args->caller_dlopen;
  21. /* We have to find out from which object the caller is calling.
  22. By default we assume this is the main application. */
  23. call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
  24. struct link_map *l;
  25. for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
  26. for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
  27. if (caller_dlopen >= (const void *) l->l_map_start
  28. && caller_dlopen < (const void *) l->l_map_end
  29. && (l->l_contiguous
  30. || _dl_addr_inside_object (l, (ElfW(Addr)) caller_dlopen)))
  31. {
  32. assert (ns == l->l_ns);
  33. call_map = l;
  34. goto found_caller;
  35. }
  36. found_caller:
  37. if (args->nsid == __LM_ID_CALLER)
  38. {
  39. #ifndef SHARED
  40. /* In statically linked apps there might be no loaded object. */
  41. if (call_map == NULL)
  42. args->nsid = LM_ID_BASE;
  43. else
  44. #endif
  45. args->nsid = call_map->l_ns;
  46. }
  47. }
  48. assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
  49. /* Load the named object. */
  50. struct link_map *new;
  51. args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,
  52. mode | __RTLD_CALLMAP, args->nsid);
  53. /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is
  54. set and the object is not already loaded. */
  55. if (new == NULL)
  56. {
  57. assert (mode & RTLD_NOLOAD);
  58. return;
  59. }
  60. if (__builtin_expect (mode & __RTLD_SPROF, 0))
  61. /* This happens only if we load a DSO for 'sprof'. */
  62. return;
  63. /* This object is directly loaded. */
  64. ++new->l_direct_opencount;
  65. /* It was already open. */
  66. if (__builtin_expect (new->l_searchlist.r_list != NULL, 0))
  67. {
  68. /* Let the user know about the opencount. */
  69. if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
  70. _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
  71. new->l_name, new->l_ns, new->l_direct_opencount);
  72. /* If the user requested the object to be in the global namespace
  73. but it is not so far, add it now. */
  74. if ((mode & RTLD_GLOBAL) && new->l_global == 0)
  75. (void) add_to_global (new);
  76. assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
  77. return;
  78. }
  79. /* Load that object's dependencies. */
  80. _dl_map_object_deps (new, NULL, 0, 0,
  81. mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
  82. /* So far, so good. Now check the versions. */
  83. for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
  84. if (new->l_searchlist.r_list[i]->l_real->l_versions == NULL)
  85. (void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real,
  86. 0, 0);
  87. #ifdef SHARED
  88. /* Auditing checkpoint: we have added all objects. */
  89. if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
  90. {
  91. struct link_map *head = GL(dl_ns)[new->l_ns]._ns_loaded;
  92. /* Do not call the functions for any auditing object. */
  93. if (head->l_auditing == 0)
  94. {
  95. struct audit_ifaces *afct = GLRO(dl_audit);
  96. for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
  97. {
  98. if (afct->activity != NULL)
  99. afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT);
  100. afct = afct->next;
  101. }
  102. }
  103. }
  104. #endif
  105. /* Notify the debugger all new objects are now ready to go. */
  106. struct r_debug *r = _dl_debug_initialize (0, args->nsid);
  107. r->r_state = RT_CONSISTENT;
  108. _dl_debug_state ();
  109. /* Print scope information. */
  110. if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES, 0))
  111. _dl_show_scope (new, 0);
  112. /* Only do lazy relocation if `LD_BIND_NOW' is not set. */
  113. int reloc_mode = mode & __RTLD_AUDIT;
  114. if (GLRO(dl_lazy))
  115. reloc_mode |= mode & RTLD_LAZY;
  116. /* Relocate the objects loaded. We do this in reverse order so that copy
  117. relocs of earlier objects overwrite the data written by later objects. */
  118. struct link_map *l = new;
  119. while (l->l_next)
  120. l = l->l_next;
  121. while (1)
  122. {
  123. if (! l->l_real->l_relocated)
  124. {
  125. #ifdef SHARED
  126. if (__builtin_expect (GLRO(dl_profile) != NULL, 0))
  127. {
  128. /* If this here is the shared object which we want to profile
  129. make sure the profile is started. We can find out whether
  130. this is necessary or not by observing the `_dl_profile_map'
  131. variable. If was NULL but is not NULL afterwars we must
  132. start the profiling. */
  133. struct link_map *old_profile_map = GL(dl_profile_map);
  134. _dl_relocate_object (l, l->l_scope, reloc_mode | RTLD_LAZY, 1);
  135. if (old_profile_map == NULL && GL(dl_profile_map) != NULL)
  136. {
  137. /* We must prepare the profiling. */
  138. _dl_start_profile ();
  139. /* Prevent unloading the object. */
  140. GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE;
  141. }
  142. }
  143. else
  144. #endif
  145. _dl_relocate_object (l, l->l_scope, reloc_mode, 0);
  146. }
  147. if (l == new)
  148. break;
  149. l = l->l_prev;
  150. }
  151. /* If the file is not loaded now as a dependency, add the search
  152. list of the newly loaded object to the scope. */
  153. bool any_tls = false;
  154. unsigned int first_static_tls = new->l_searchlist.r_nlist;
  155. for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
  156. {
  157. struct link_map *imap = new->l_searchlist.r_list[i];
  158. int from_scope = 0;
  159. /* If the initializer has been called already, the object has
  160. not been loaded here and now. */
  161. if (imap->l_init_called && imap->l_type == lt_loaded)
  162. {
  163. struct r_scope_elem **runp = imap->l_scope;
  164. size_t cnt = 0;
  165. while (*runp != NULL)
  166. {
  167. if (*runp == &new->l_searchlist)
  168. break;
  169. ++cnt;
  170. ++runp;
  171. }
  172. if (*runp != NULL)
  173. /* Avoid duplicates. */
  174. continue;
  175. if (__builtin_expect (cnt + 1 >= imap->l_scope_max, 0))
  176. {
  177. /* The 'r_scope' array is too small. Allocate a new one
  178. dynamically. */
  179. size_t new_size;
  180. struct r_scope_elem **newp;
  181. #define SCOPE_ELEMS(imap) \
  182. (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0]))
  183. if (imap->l_scope != imap->l_scope_mem
  184. && imap->l_scope_max < SCOPE_ELEMS (imap))
  185. {
  186. new_size = SCOPE_ELEMS (imap);
  187. newp = imap->l_scope_mem;
  188. }
  189. else
  190. {
  191. new_size = imap->l_scope_max * 2;
  192. newp = (struct r_scope_elem **)
  193. malloc (new_size * sizeof (struct r_scope_elem *));
  194. if (newp == NULL)
  195. _dl_signal_error (ENOMEM, "dlopen", NULL,
  196. N_("cannot create scope list"));
  197. }
  198. memcpy (newp, imap->l_scope, cnt * sizeof (imap->l_scope[0]));
  199. struct r_scope_elem **old = imap->l_scope;
  200. imap->l_scope = newp;
  201. if (old != imap->l_scope_mem)
  202. _dl_scope_free (old);
  203. imap->l_scope_max = new_size;
  204. }
  205. /* First terminate the extended list. Otherwise a thread
  206. might use the new last element and then use the garbage
  207. at offset IDX+1. */
  208. imap->l_scope[cnt + 1] = NULL;
  209. atomic_write_barrier ();
  210. imap->l_scope[cnt] = &new->l_searchlist;
  211. /* Print only new scope information. */
  212. from_scope = cnt;
  213. }
  214. /* Only add TLS memory if this object is loaded now and
  215. therefore is not yet initialized. */
  216. else if (! imap->l_init_called
  217. /* Only if the module defines thread local data. */
  218. && __builtin_expect (imap->l_tls_blocksize > 0, 0))
  219. {
  220. /* Now that we know the object is loaded successfully add
  221. modules containing TLS data to the slot info table. We
  222. might have to increase its size. */
  223. _dl_add_to_slotinfo (imap);
  224. if (imap->l_need_tls_init
  225. && first_static_tls == new->l_searchlist.r_nlist)
  226. first_static_tls = i;
  227. /* We have to bump the generation counter. */
  228. any_tls = true;
  229. }
  230. /* Print scope information. */
  231. if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES, 0))
  232. _dl_show_scope (imap, from_scope);
  233. }
  234. /* Bump the generation number if necessary. */
  235. if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0))
  236. _dl_fatal_printf (N_("\
  237. TLS generation counter wrapped! Please report this."));
  238. /* We need a second pass for static tls data, because _dl_update_slotinfo
  239. must not be run while calls to _dl_add_to_slotinfo are still pending. */
  240. for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i)
  241. {
  242. struct link_map *imap = new->l_searchlist.r_list[i];
  243. if (imap->l_need_tls_init
  244. && ! imap->l_init_called
  245. && imap->l_tls_blocksize > 0)
  246. {
  247. /* For static TLS we have to allocate the memory here and
  248. now. This includes allocating memory in the DTV. But we
  249. cannot change any DTV other than our own. So, if we
  250. cannot guarantee that there is room in the DTV we don't
  251. even try it and fail the load.
  252. XXX We could track the minimum DTV slots allocated in
  253. all threads. */
  254. if (! RTLD_SINGLE_THREAD_P && imap->l_tls_modid > DTV_SURPLUS)
  255. _dl_signal_error (0, "dlopen", NULL, N_("\
  256. cannot load any more object with static TLS"));
  257. imap->l_need_tls_init = 0;
  258. #ifdef SHARED
  259. /* Update the slot information data for at least the
  260. generation of the DSO we are allocating data for. */
  261. _dl_update_slotinfo (imap->l_tls_modid);
  262. #endif
  263. GL(dl_init_static_tls) (imap);
  264. assert (imap->l_need_tls_init == 0);
  265. }
  266. }
  267. /* Run the initializer functions of new objects. */
  268. _dl_init (new, args->argc, args->argv, args->env);
  269. /* Now we can make the new map available in the global scope. */
  270. if (mode & RTLD_GLOBAL)
  271. /* Move the object in the global namespace. */
  272. if (add_to_global (new) != 0)
  273. /* It failed. */
  274. return;
  275. /* Mark the object as not deletable if the RTLD_NODELETE flags was
  276. passed. */
  277. if (__builtin_expect (mode & RTLD_NODELETE, 0))
  278. new->l_flags_1 |= DF_1_NODELETE;
  279. #ifndef SHARED
  280. /* We must be the static _dl_open in libc.a. A static program that
  281. has loaded a dynamic object now has competition. */
  282. __libc_multiple_libcs = 1;
  283. #endif
  284. /* Let the user know about the opencount. */
  285. if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
  286. _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
  287. new->l_name, new->l_ns, new->l_direct_opencount);
  288. }

从上可以看到,dl_open_worker中的内容非常多,不过在其中我们发现了这样的一条语句

  1. struct link_map *new;
  2. args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,
  3. 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中

  1. struct link_map *
  2. internal_function
  3. _dl_map_object (struct link_map *loader, const char *name,
  4. int type, int trace_mode, int mode, Lmid_t nsid)
  5. {
  6. int fd;
  7. char *realname;
  8. char *name_copy;
  9. struct link_map *l;
  10. struct filebuf fb;
  11. ……
  12. /* Look for this name among those already loaded. */
  13. for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
  14. {
  15. /* If the requested name matches the soname of a loaded object,
  16. use that object. Elide this check for names that have not
  17. yet been opened. */
  18. if (__builtin_expect (l->l_faked, 0) != 0
  19. || __builtin_expect (l->l_removed, 0) != 0)
  20. continue;
  21. if (!_dl_name_match_p (name, l))
  22. {
  23. const char *soname;
  24. if (__builtin_expect (l->l_soname_added, 1)
  25. || l->l_info[DT_SONAME] == NULL)
  26. continue;
  27. soname = ((const char *) D_PTR (l, l_info[DT_STRTAB])
  28. + l->l_info[DT_SONAME]->d_un.d_val);
  29. if (strcmp (name, soname) != 0)
  30. continue;
  31. /* We have a match on a new name -- cache it. */
  32. add_name_to_object (l, soname);
  33. l->l_soname_added = 1;
  34. }
  35. /* We have a match. */
  36. return l;
  37. }
  38. ……
  39. fd = open_path (name, namelen, mode & __RTLD_SECURE,
  40. &main_map->l_rpath_dirs,
  41. &realname, &fb, loader ?: main_map, LA_SER_RUNPATH,
  42. &found_other_class);
  43. ……
  44. l = _dl_new_object (name_copy, name, type, loader,
  45. mode, nsid))
  46. return _dl_map_object_from_fd (name, fd, &fb, realname, loader, type, mode,
  47. &stack_end, nsid);
  48. }

_dl_map_object中的内容实在是太多了,这里进行了大量的省略,在_dl_map_object中首先判断这个so是否被其他程序给加载,如果被加载,就直接将这个link_map进行返回,如果没有被使用过再进行下面的步骤。

fd = open_path()主要通过各个标志来确定,我们进入到open_path中查看

此时_dl_map_object -> open_path

  1. static int
  2. open_path (const char *name, size_t namelen, int secure,
  3. struct r_search_path_struct *sps, char **realname,
  4. struct filebuf *fbp, struct link_map *loader, int whatcode,
  5. bool *found_other_class)
  6. {
  7. struct r_search_path_elem **dirs = sps->dirs;
  8. char *buf;
  9. int fd = -1;
  10. const char *current_what = NULL;
  11. int any = 0;
  12. buf = alloca (max_dirnamelen + max_capstrlen + namelen);
  13. do
  14. {
  15. struct r_search_path_elem *this_dir = *dirs;
  16. size_t buflen = 0;
  17. size_t cnt;
  18. char *edp;
  19. int here_any = 0;
  20. int err;
  21. ……
  22. edp = (char *) __mempcpy (buf, this_dir->dirname, this_dir->dirnamelen);
  23. for (cnt = 0; fd == -1 && cnt < ncapstr; ++cnt)
  24. {
  25. /* Skip this directory if we know it does not exist. */
  26. if (this_dir->status[cnt] == nonexisting)
  27. continue;
  28. buflen =
  29. ((char *) __mempcpy (__mempcpy (edp, capstr[cnt].str,
  30. capstr[cnt].len),
  31. name, namelen)
  32. - buf);
  33. /* Print name we try if this is wanted. */
  34. if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
  35. _dl_debug_printf (" trying file=%s\n", buf);
  36. fd = open_verify (buf, fbp, loader, whatcode, found_other_class,
  37. false);
  38. ……
  39. }
  40. if (fd != -1)
  41. {
  42. *realname = (char *) malloc (buflen);
  43. if (*realname != NULL)
  44. {
  45. memcpy (*realname, buf, buflen);
  46. return fd;
  47. }
  48. else
  49. {
  50. /* No memory for the name, we certainly won't be able
  51. to load and link it. */
  52. __close (fd);
  53. return -1;
  54. }
  55. }
  56. if (here_any && (err = errno) != ENOENT && err != EACCES)
  57. /* The file exists and is readable, but something went wrong. */
  58. return -1;
  59. /* Remember whether we found anything. */
  60. any |= here_any;
  61. }
  62. while (*++dirs != NULL);
  63. return -1;
  64. }

可以看出在open_path中同样还未设计到文件的打开与关闭操作,fd还是通过open_verify来进行返回的,可以进入到open_verify函数中查看其内容,目前调用路径为:

_dl_map_object -> open_path->open_verify

open_verify函数内容如下

  1. static int
  2. open_verify (const char *name, struct filebuf *fbp, struct link_map *loader,
  3. int whatcode, bool *found_other_class, bool free_name)
  4. {
  5. ……
  6. /* Open the file. We always open files read-only. */
  7. int fd = __open (name, O_RDONLY | O_CLOEXEC);
  8. if (fd != -1)
  9. {
  10. ElfW(Ehdr) *ehdr;
  11. ElfW(Phdr) *phdr, *ph;
  12. ElfW(Word) *abi_note;
  13. unsigned int osversion;
  14. size_t maplength;
  15. /* We successfully openened the file. Now verify it is a file
  16. we can use. */
  17. __set_errno (0);
  18. fbp->len = __libc_read (fd, fbp->buf, sizeof (fbp->buf));
  19. /* This is where the ELF header is loaded. */
  20. assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
  21. ehdr = (ElfW(Ehdr) *) fbp->buf;
  22. ……
  23. maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
  24. if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
  25. phdr = (void *) (fbp->buf + ehdr->e_phoff);
  26. else
  27. {
  28. phdr = alloca (maplength);
  29. __lseek (fd, ehdr->e_phoff, SEEK_SET);
  30. if ((size_t) __libc_read (fd, (void *) phdr, maplength) != maplength)
  31. {
  32. read_error:
  33. errval = errno;
  34. errstring = N_("cannot read file data");
  35. goto call_lose;
  36. }
  37. }
  38. /* Check .note.ABI-tag if present. */
  39. for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph)
  40. if (ph->p_type == PT_NOTE && ph->p_filesz >= 32 && ph->p_align >= 4)
  41. {
  42. ElfW(Addr) size = ph->p_filesz;
  43. if (ph->p_offset + size <= (size_t) fbp->len)
  44. abi_note = (void *) (fbp->buf + ph->p_offset);
  45. else
  46. {
  47. abi_note = alloca (size);
  48. __lseek (fd, ph->p_offset, SEEK_SET);
  49. if (__libc_read (fd, (void *) abi_note, size) != size)
  50. goto read_error;
  51. }
  52. while (memcmp (abi_note, &expected_note, sizeof (expected_note)))
  53. {
  54. #define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
  55. ElfW(Addr) note_size = 3 * sizeof (ElfW(Word))
  56. + ROUND (abi_note[0])
  57. + ROUND (abi_note[1]);
  58. if (size - 32 < note_size)
  59. {
  60. size = 0;
  61. break;
  62. }
  63. size -= note_size;
  64. abi_note = (void *) abi_note + note_size;
  65. }
  66. if (size == 0)
  67. continue;
  68. osversion = (abi_note[5] & 0xff) * 65536
  69. + (abi_note[6] & 0xff) * 256
  70. + (abi_note[7] & 0xff);
  71. if (abi_note[4] != __ABI_TAG_OS
  72. || (GLRO(dl_osversion) && GLRO(dl_osversion) < osversion))
  73. {
  74. close_and_out:
  75. __close (fd);
  76. __set_errno (ENOENT);
  77. fd = -1;
  78. }
  79. break;
  80. }
  81. }
  82. return fd;
  83. }

从上述代码中可以看到,在open_verify中对文件进行了打开和读的操作。在里面使用到了一个fbp的结构体,其内容如下:

  1. struct filebuf
  2. {
  3. ssize_t len;
  4. #if __WORDSIZE == 32
  5. # define FILEBUF_SIZE 512
  6. #else
  7. # define FILEBUF_SIZE 832
  8. #endif
  9. char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
  10. };

其中对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* 数据结构并填充一些最基本的参数,其函数内容如下:

  1. struct link_map *
  2. internal_function
  3. _dl_new_object (char *realname, const char *libname, int type,
  4. struct link_map *loader, int mode, Lmid_t nsid)
  5. {
  6. size_t libname_len = strlen (libname) + 1;
  7. struct link_map *new;
  8. struct libname_list *newname;
  9. #ifdef SHARED
  10. /* We create the map for the executable before we know whether we have
  11. auditing libraries and if yes, how many. Assume the worst. */
  12. unsigned int naudit = GLRO(dl_naudit) ?: ((mode & __RTLD_OPENEXEC)
  13. ? DL_NNS : 0);
  14. size_t audit_space = naudit * sizeof (new->l_audit[0]);
  15. #else
  16. # define audit_space 0
  17. #endif
  18. new = (struct link_map *) calloc (sizeof (*new) + audit_space
  19. + sizeof (struct link_map *)
  20. + sizeof (*newname) + libname_len, 1);
  21. if (new == NULL)
  22. return NULL;
  23. new->l_real = new;
  24. new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1)
  25. + audit_space);
  26. new->l_libname = newname
  27. = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1);
  28. newname->name = (char *) memcpy (newname + 1, libname, libname_len);
  29. /* newname->next = NULL; We use calloc therefore not necessary. */
  30. newname->dont_free = 1;
  31. new->l_name = realname;
  32. new->l_type = type;
  33. /* If we set the bit now since we know it is never used we avoid
  34. dirtying the cache line later. */
  35. if ((GLRO(dl_debug_mask) & DL_DEBUG_UNUSED) == 0)
  36. new->l_used = 1;
  37. new->l_loader = loader;
  38. #if NO_TLS_OFFSET != 0
  39. new->l_tls_offset = NO_TLS_OFFSET;
  40. #endif
  41. new->l_ns = nsid;
  42. #ifdef SHARED
  43. for (unsigned int cnt = 0; cnt < naudit; ++cnt)
  44. {
  45. new->l_audit[cnt].cookie = (uintptr_t) new;
  46. /* new->l_audit[cnt].bindflags = 0; */
  47. }
  48. #endif
  49. /* new->l_global = 0; We use calloc therefore not necessary. */
  50. /* Use the 'l_scope_mem' array by default for the 'l_scope'
  51. information. If we need more entries we will allocate a large
  52. array dynamically. */
  53. new->l_scope = new->l_scope_mem;
  54. new->l_scope_max = sizeof (new->l_scope_mem) / sizeof (new->l_scope_mem[0]);
  55. /* Counter for the scopes we have to handle. */
  56. int idx = 0;
  57. if (GL(dl_ns)[nsid]._ns_loaded != NULL)
  58. /* Add the global scope. */
  59. new->l_scope[idx++] = &GL(dl_ns)[nsid]._ns_loaded->l_searchlist;
  60. /* If we have no loader the new object acts as it. */
  61. if (loader == NULL)
  62. loader = new;
  63. else
  64. /* Determine the local scope. */
  65. while (loader->l_loader != NULL)
  66. loader = loader->l_loader;
  67. /* Insert the scope if it isn't the global scope we already added. */
  68. if (idx == 0 || &loader->l_searchlist != new->l_scope[0])
  69. {
  70. if ((mode & RTLD_DEEPBIND) != 0 && idx != 0)
  71. {
  72. new->l_scope[1] = new->l_scope[0];
  73. idx = 0;
  74. }
  75. new->l_scope[idx] = &loader->l_searchlist;
  76. }
  77. new->l_local_scope[0] = &new->l_searchlist;
  78. /* Don't try to find the origin for the main map which has the name "". */
  79. if (realname[0] != '\0')
  80. {
  81. size_t realname_len = strlen (realname) + 1;
  82. char *origin;
  83. char *cp;
  84. if (realname[0] == '/')
  85. {
  86. /* It is an absolute path. Use it. But we have to make a
  87. copy since we strip out the trailing slash. */
  88. cp = origin = (char *) malloc (realname_len);
  89. if (origin == NULL)
  90. {
  91. origin = (char *) -1;
  92. goto out;
  93. }
  94. }
  95. else
  96. {
  97. size_t len = realname_len;
  98. char *result = NULL;
  99. /* Get the current directory name. */
  100. origin = NULL;
  101. do
  102. {
  103. char *new_origin;
  104. len += 128;
  105. new_origin = (char *) realloc (origin, len);
  106. if (new_origin == NULL)
  107. /* We exit the loop. Note that result == NULL. */
  108. break;
  109. origin = new_origin;
  110. }
  111. while ((result = __getcwd (origin, len - realname_len)) == NULL
  112. && errno == ERANGE);
  113. if (result == NULL)
  114. {
  115. /* We were not able to determine the current directory.
  116. Note that free(origin) is OK if origin == NULL. */
  117. free (origin);
  118. origin = (char *) -1;
  119. goto out;
  120. }
  121. /* Find the end of the path and see whether we have to add a
  122. slash. We could use rawmemchr but this need not be
  123. fast. */
  124. cp = (strchr) (origin, '\0');
  125. if (cp[-1] != '/')
  126. *cp++ = '/';
  127. }
  128. /* Add the real file name. */
  129. cp = __mempcpy (cp, realname, realname_len);
  130. /* Now remove the filename and the slash. Leave the slash if
  131. the name is something like "/foo". */
  132. do
  133. --cp;
  134. while (*cp != '/');
  135. if (cp == origin)
  136. /* Keep the only slash which is the first character. */
  137. ++cp;
  138. *cp = '\0';
  139. out:
  140. new->l_origin = origin;
  141. }
  142. return new;
  143. }

_dl_new_object函数如上,为struct link_map 的成员数据赋值。并把新的struct link_map* 加入到一个单链中,这是在以后是很有用的,因为这样在一个执行文件中如果要整体管理它相关的动态链接库,就可以以单链遍历。

如果要加载的动态链接库还没有被映射到进程的虚拟内存空间的话,那只是准备工作,真正的要点在 _dl_map_object_from_fd()这个函数开始的。因为这之后,每一步都有关动态链接库在进程中发挥它的作用而必须的条件。

进入到_dl_map_object_from_fd()中进行查看

进去后发现太长了,准备在(二)中继续进行介绍,这里先介绍到这里,下面是_dl_open后续的调用流程

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多