1 /* $OpenBSD: library_subr.c,v 1.26 2006/05/08 20:37:01 deraadt Exp $ */
2
3 /*
4 * Copyright (c) 2002 Dale Rahn
5 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30 #define _DYN_LOADER
31
32 #include <sys/types.h>
33 #include <sys/syslimits.h>
34 #include <sys/param.h>
35 #include <sys/queue.h>
36 #include <dirent.h>
37
38 #include "archdep.h"
39 #include "resolve.h"
40 #include "dir.h"
41 #include "sod.h"
42
43 #define DEFAULT_PATH "/usr/lib"
44
45
46 /* STATIC DATA */
47 struct dlochld _dlopened_child_list;
48
49
50 /* local functions */
51 elf_object_t * _dl_load_shlib_hint(struct sod *sod, struct sod *req_sod,
52 int type, int flags, int use_hints, const char *libpath);
53
54 /*
55 * _dl_match_file()
56 *
57 * This fucntion determines if a given name matches what is specified
58 * in a struct sod. The major must match exactly, and the minor must
59 * be same or larger.
60 *
61 * sodp is updated with the minor if this matches.
62 */
63
64 int
_dl_match_file(struct sod * sodp,char * name,int namelen)65 _dl_match_file(struct sod *sodp, char *name, int namelen)
66 {
67 int match;
68 struct sod lsod;
69 char *lname;
70
71 lname = name;
72 if (sodp->sod_library) {
73 if (_dl_strncmp(name, "lib", 3))
74 return 0;
75 lname += 3;
76 }
77 if (_dl_strncmp(lname, (char *)sodp->sod_name,
78 _dl_strlen((char *)sodp->sod_name)))
79 return 0;
80
81 _dl_build_sod(name, &lsod);
82
83 match = 0;
84 if ((_dl_strcmp((char *)lsod.sod_name, (char *)sodp->sod_name) == 0) &&
85 (lsod.sod_library == sodp->sod_library) &&
86 ((sodp->sod_major == -1) || (sodp->sod_major == lsod.sod_major)) &&
87 ((sodp->sod_minor == -1) ||
88 (lsod.sod_minor >= sodp->sod_minor))) {
89 match = 1;
90
91 /* return version matched */
92 sodp->sod_major = lsod.sod_major;
93 sodp->sod_minor = lsod.sod_minor;
94 }
95 _dl_free((char *)lsod.sod_name);
96 return match;
97 }
98
99 char _dl_hint_store[MAXPATHLEN];
100
101 char *
_dl_find_shlib(struct sod * sodp,const char * searchpath,int nohints)102 _dl_find_shlib(struct sod *sodp, const char *searchpath, int nohints)
103 {
104 char *hint, lp[PATH_MAX + 10], *path;
105 struct dirent *dp;
106 const char *pp;
107 int match, len;
108 DIR *dd;
109 struct sod tsod, bsod; /* transient and best sod */
110
111 /* if we are to search default directories, and hints
112 * are not to be used, search the standard path from ldconfig
113 * (_dl_hint_search_path) or use the default path
114 */
115 if (nohints)
116 goto nohints;
117
118 if (searchpath == NULL) {
119 /* search 'standard' locations, find any match in the hints */
120 hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major,
121 sodp->sod_minor, NULL);
122 if (hint)
123 return hint;
124 } else {
125 /* search hints requesting matches for only
126 * the searchpath directories,
127 */
128 pp = searchpath;
129 while (pp) {
130 path = lp;
131 while (path < lp + PATH_MAX &&
132 *pp && *pp != ':' && *pp != ';')
133 *path++ = *pp++;
134 *path = 0;
135
136 /* interpret "" as curdir "." */
137 if (lp[0] == '\0') {
138 lp[0] = '.';
139 lp[1] = '\0';
140 }
141
142 hint = _dl_findhint((char *)sodp->sod_name,
143 sodp->sod_major, sodp->sod_minor, lp);
144 if (hint != NULL)
145 return hint;
146
147 if (*pp) /* Try curdir if ':' at end */
148 pp++;
149 else
150 pp = 0;
151 }
152 }
153
154 /*
155 * For each directory in the searchpath, read the directory
156 * entries looking for a match to sod. filename compare is
157 * done by _dl_match_file()
158 */
159 nohints:
160 if (searchpath == NULL) {
161 if (_dl_hint_search_path != NULL)
162 searchpath = _dl_hint_search_path;
163 else
164 searchpath = DEFAULT_PATH;
165 }
166 pp = searchpath;
167 while (pp) {
168 path = lp;
169 while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';')
170 *path++ = *pp++;
171 *path = 0;
172
173 /* interpret "" as curdir "." */
174 if (lp[0] == '\0') {
175 lp[0] = '.';
176 lp[1] = '\0';
177 }
178
179 if ((dd = _dl_opendir(lp)) != NULL) {
180 match = 0;
181 while ((dp = _dl_readdir(dd)) != NULL) {
182 tsod = *sodp;
183 if (_dl_match_file(&tsod, dp->d_name,
184 dp->d_namlen)) {
185 /*
186 * When a match is found, tsod is
187 * updated with the major+minor found.
188 * This version is compared with the
189 * largest so far (kept in bsod),
190 * and saved if larger.
191 */
192 if (!match ||
193 tsod.sod_major == -1 ||
194 tsod.sod_major > bsod.sod_major ||
195 ((tsod.sod_major ==
196 bsod.sod_major) &&
197 tsod.sod_minor > bsod.sod_minor)) {
198 bsod = tsod;
199 match = 1;
200 len = _dl_strlcpy(
201 _dl_hint_store, lp,
202 MAXPATHLEN);
203 if (lp[len-1] != '/') {
204 _dl_hint_store[len] =
205 '/';
206 len++;
207 }
208 _dl_strlcpy(
209 &_dl_hint_store[len],
210 dp->d_name,
211 MAXPATHLEN-len);
212 if (tsod.sod_major == -1)
213 break;
214 }
215 }
216 }
217 _dl_closedir(dd);
218 if (match) {
219 *sodp = bsod;
220 return (_dl_hint_store);
221 }
222 }
223
224 if (*pp) /* Try curdir if ':' at end */
225 pp++;
226 else
227 pp = 0;
228 }
229 return NULL;
230 }
231
232 /*
233 * attempt to locate and load a library based on libpath, sod info and
234 * if it needs to respect hints, passing type and flags to perform open
235 */
236 elf_object_t *
_dl_load_shlib_hint(struct sod * sod,struct sod * req_sod,int type,int flags,int use_hints,const char * libpath)237 _dl_load_shlib_hint(struct sod *sod, struct sod *req_sod, int type,
238 int flags, int use_hints, const char *libpath)
239 {
240 elf_object_t *object = NULL;
241 char *hint;
242
243 hint = _dl_find_shlib(req_sod, libpath, use_hints);
244 if (hint != NULL) {
245 if (req_sod->sod_minor < sod->sod_minor)
246 _dl_printf("warning: lib%s.so.%d.%d: "
247 "minor version >= %d expected, "
248 "using it anyway\n",
249 sod->sod_name, sod->sod_major,
250 req_sod->sod_minor, sod->sod_minor);
251 object = _dl_tryload_shlib(hint, type, flags);
252 }
253 return object;
254 }
255
256 /*
257 * Load a shared object. Search order is:
258 * If the name contains a '/' use only the path preceeding the
259 * library name and do not continue on to other methods if not
260 * found.
261 * search hints for match in path preceeding library name
262 * this will only match specific library version.
263 * search path preceeding library name
264 * this will find largest minor version in path provided
265 * try the LD_LIBRARY_PATH specification (if present)
266 * search hints for match in LD_LIBRARY_PATH dirs
267 * this will only match specific libary version.
268 * search LD_LIBRARY_PATH dirs for match.
269 * this will find largest minor version in first dir found.
270 * check DT_RPATH paths, (if present)
271 * search hints for match in DT_RPATH dirs
272 * this will only match specific libary version.
273 * search DT_RPATH dirs for match.
274 * this will find largest minor version in first dir found.
275 * last look in default search directory, either as specified
276 * by ldconfig or default to '/usr/lib'
277 */
278
279
280 elf_object_t *
_dl_load_shlib(const char * libname,elf_object_t * parent,int type,int flags)281 _dl_load_shlib(const char *libname, elf_object_t *parent, int type, int flags)
282 {
283 int try_any_minor, ignore_hints;
284 struct sod sod, req_sod;
285 elf_object_t *object = NULL;
286
287 try_any_minor = 0;
288 ignore_hints = 0;
289
290 if (_dl_strchr(libname, '/')) {
291 char *lpath, *lname;
292 lpath = _dl_strdup(libname);
293 lname = _dl_strrchr(lpath, '/');
294 if (lname == NULL) {
295 _dl_free(lpath);
296 _dl_errno = DL_NOT_FOUND;
297 return (object);
298 }
299 *lname = '\0';
300 lname++;
301 if (*lname == '\0') {
302 _dl_free(lpath);
303 _dl_errno = DL_NOT_FOUND;
304 return (object);
305 }
306
307 _dl_build_sod(lname, &sod);
308 req_sod = sod;
309
310 fullpathagain:
311 object = _dl_load_shlib_hint(&sod, &req_sod, type, flags,
312 ignore_hints, lpath);
313 if (object != NULL)
314 goto fullpathdone;
315
316 if (try_any_minor == 0) {
317 try_any_minor = 1;
318 ignore_hints = 1;
319 req_sod.sod_minor = -1;
320 goto fullpathagain;
321 }
322 _dl_errno = DL_NOT_FOUND;
323 fullpathdone:
324 _dl_free(lpath);
325 _dl_free((char *)sod.sod_name);
326 return (object);
327 }
328
329 _dl_build_sod(libname, &sod);
330 req_sod = sod;
331
332 again:
333 /* No '/' in name. Scan the known places, LD_LIBRARY_PATH first. */
334 if (_dl_libpath != NULL) {
335 object = _dl_load_shlib_hint(&sod, &req_sod, type, flags,
336 ignore_hints, _dl_libpath);
337 if (object != NULL)
338 goto done;
339 }
340
341 /* Check DT_RPATH. */
342 if (parent->dyn.rpath != NULL) {
343 object = _dl_load_shlib_hint(&sod, &req_sod, type, flags,
344 ignore_hints, parent->dyn.rpath);
345 if (object != NULL)
346 goto done;
347 }
348
349 /* Check main program's DT_RPATH, if parent != main program */
350 if (parent != _dl_objects && _dl_objects->dyn.rpath != NULL) {
351 object = _dl_load_shlib_hint(&sod, &req_sod, type, flags,
352 ignore_hints, _dl_objects->dyn.rpath);
353 if (object != NULL)
354 goto done;
355 }
356
357 /* check 'standard' locations */
358 object = _dl_load_shlib_hint(&sod, &req_sod, type, flags,
359 ignore_hints, NULL);
360 if (object != NULL)
361 goto done;
362
363 if (try_any_minor == 0) {
364 try_any_minor = 1;
365 ignore_hints = 1;
366 req_sod.sod_minor = -1;
367 goto again;
368 }
369 _dl_errno = DL_NOT_FOUND;
370 done:
371 _dl_free((char *)sod.sod_name);
372 return(object);
373 }
374
375
376 void
_dl_link_dlopen(elf_object_t * dep)377 _dl_link_dlopen(elf_object_t *dep)
378 {
379 struct dep_node *n;
380
381 dep->opencount++;
382
383 if (OBJECT_DLREF_CNT(dep) > 1)
384 return;
385
386 n = _dl_malloc(sizeof *n);
387 if (n == NULL)
388 _dl_exit(5);
389
390 n->data = dep;
391 TAILQ_INSERT_TAIL(&_dlopened_child_list, n, next_sib);
392
393 DL_DEB(("linking %s as dlopen()ed\n", dep->load_name));
394 }
395
396 void
_dl_child_refcnt_decrement(elf_object_t * object)397 _dl_child_refcnt_decrement(elf_object_t *object)
398 {
399 struct dep_node *n;
400
401 object->refcount--;
402 if (OBJECT_REF_CNT(object) == 0)
403 TAILQ_FOREACH(n, &object->child_list, next_sib)
404 _dl_child_refcnt_decrement(n->data);
405 }
406
407 void
_dl_notify_unload_shlib(elf_object_t * object)408 _dl_notify_unload_shlib(elf_object_t *object)
409 {
410 struct dep_node *n;
411
412 if (OBJECT_REF_CNT(object) == 0)
413 TAILQ_FOREACH(n, &object->child_list, next_sib)
414 _dl_child_refcnt_decrement(n->data);
415
416 if (OBJECT_DLREF_CNT(object) == 0) {
417 TAILQ_FOREACH(n, &object->grpref_list, next_sib) {
418 n->data->grprefcount--;
419 _dl_notify_unload_shlib(n->data);
420 }
421 }
422 }
423
424 void
_dl_unload_dlopen(void)425 _dl_unload_dlopen(void)
426 {
427 struct dep_node *node;
428
429 TAILQ_FOREACH_REVERSE(node, &_dlopened_child_list, dlochld, next_sib) {
430 /* dont dlclose the main program */
431 if (node->data == _dl_objects)
432 continue;
433
434 while (node->data->opencount > 0) {
435 node->data->opencount--;
436 _dl_notify_unload_shlib(node->data);
437 _dl_run_all_dtors();
438 }
439 }
440 }
441
442 void
_dl_link_grpref(elf_object_t * load_group,elf_object_t * load_object)443 _dl_link_grpref(elf_object_t *load_group, elf_object_t *load_object)
444 {
445 struct dep_node *n;
446
447 n = _dl_malloc(sizeof *n);
448 if (n == NULL)
449 _dl_exit(7);
450 n->data = load_group;
451 TAILQ_INSERT_TAIL(&load_object->grpref_list, n, next_sib);
452 load_group->grprefcount++;
453 }
454
455 void
_dl_link_child(elf_object_t * dep,elf_object_t * p)456 _dl_link_child(elf_object_t *dep, elf_object_t *p)
457 {
458 struct dep_node *n;
459
460 n = _dl_malloc(sizeof *n);
461 if (n == NULL)
462 _dl_exit(7);
463 n->data = dep;
464 TAILQ_INSERT_TAIL(&p->child_list, n, next_sib);
465
466 dep->refcount++;
467
468 DL_DEB(("linking dep %s as child of %s\n", dep->load_name,
469 p->load_name));
470 }
471
472 void
_dl_link_grpsym(elf_object_t * object)473 _dl_link_grpsym(elf_object_t *object)
474 {
475 struct dep_node *n;
476
477 TAILQ_FOREACH(n, &_dl_loading_object->grpsym_list, next_sib)
478 if (n->data == object)
479 return; /* found, dont bother adding */
480
481 n = _dl_malloc(sizeof *n);
482 if (n == NULL)
483 _dl_exit(8);
484 n->data = object;
485 TAILQ_INSERT_TAIL(&_dl_loading_object->grpsym_list, n, next_sib);
486 }
487
488 void
_dl_cache_grpsym_list(elf_object_t * object)489 _dl_cache_grpsym_list(elf_object_t *object)
490 {
491 struct dep_node *n;
492
493 /*
494 * grpsym_list is an ordered list of all child libs of the
495 * _dl_loading_object with no dups. The order is equalivant
496 * to a breath-first traversal of the child list without dups.
497 */
498
499 TAILQ_FOREACH(n, &object->child_list, next_sib)
500 _dl_link_grpsym(n->data);
501
502 TAILQ_FOREACH(n, &object->child_list, next_sib)
503 _dl_cache_grpsym_list(n->data);
504 }
505