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