1 /**	$MirOS: src/libexec/ld.so/dlfcn.c,v 1.8 2006/08/30 04:28:24 tg Exp $ */
2 /*	$OpenBSD: dlfcn.c,v 1.73 2006/05/08 20:34:36 deraadt Exp $ */
3 
4 /*
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 <nlist.h>
34 #include <link.h>
35 #include <dlfcn.h>
36 #include <unistd.h>
37 
38 #include "syscall.h"
39 #include "archdep.h"
40 #include "resolve.h"
41 
42 __RCSID("$MirOS: src/libexec/ld.so/dlfcn.c,v 1.8 2006/08/30 04:28:24 tg Exp $");
43 
44 int _dl_errno;
45 
46 int _dl_real_close(void *handle);
47 void (*_dl_thread_fnc)(int) = NULL;
48 static elf_object_t *obj_from_addr(const void *addr);
49 
50 void *
dlopen(const char * libname,int flags)51 dlopen(const char *libname, int flags)
52 {
53 	elf_object_t *object;
54 	int failed = 0;
55 
56 	if (libname == NULL)
57 		return RTLD_DEFAULT;
58 
59 	if ((flags & RTLD_TRACE) == RTLD_TRACE)
60 		_dl_traceld = true;
61 
62 	DL_DEB(("dlopen: loading: %s\n", libname));
63 
64 	_dl_thread_kern_stop();
65 
66 	if (_dl_debug_map->r_brk) {
67 		_dl_debug_map->r_state = RT_ADD;
68 		(*((void (*)(void))_dl_debug_map->r_brk))();
69 	}
70 
71 	_dl_loading_object = NULL;
72 
73 	object = _dl_load_shlib(libname, _dl_objects, OBJTYPE_DLO, flags);
74 	if (object == 0) {
75 		DL_DEB(("dlopen: failed to open %s\n", libname));
76 		failed = 1;
77 		goto loaded;
78 	}
79 
80 	_dl_link_dlopen(object);
81 
82 	if (OBJECT_REF_CNT(object) > 1) {
83 		/* if opened but grpsym_list has not been created */
84 		if (OBJECT_DLREF_CNT(object) == 1) {
85 			/* add first object manually */
86 			_dl_link_grpsym(object);
87 			_dl_cache_grpsym_list(object);
88 		}
89 		goto loaded;
90 	}
91 
92 	/* this add_object should not be here, XXX */
93 	_dl_add_object(object);
94 
95 	DL_DEB(("head [%s]\n", object->load_name ));
96 
97 	if ((failed = _dl_load_dep_libs(object, flags, 0)) == 1) {
98 		_dl_real_close(object);
99 		object = NULL;
100 		_dl_errno = DL_CANT_LOAD_OBJ;
101 	} else {
102 		int err;
103 		DL_DEB(("tail %s\n", object->load_name ));
104 		if (_dl_traceld) {
105 			_dl_show_objects();
106 			_dl_unload_shlib(object);
107 			_dl_exit(0);
108 		}
109 		err = _dl_rtld(object);
110 		if (err != 0) {
111 			_dl_real_close(object);
112 			_dl_errno = DL_CANT_LOAD_OBJ;
113 			object = 0;
114 			failed = 1;
115 		} else {
116 			_dl_call_init(object);
117 		}
118 	}
119 
120 loaded:
121 	_dl_loading_object = NULL;
122 
123 	if (_dl_debug_map->r_brk) {
124 		_dl_debug_map->r_state = RT_CONSISTENT;
125 		(*((void (*)(void))_dl_debug_map->r_brk))();
126 	}
127 
128 	_dl_thread_kern_go();
129 
130 	DL_DEB(("dlopen: %s: done (%s).\n", libname,
131 	    failed ? "failed" : "success"));
132 
133 	return((void *)object);
134 }
135 
136 void *
dlsym(void * handle,const char * name)137 dlsym(void *handle, const char *name)
138 {
139 	elf_object_t	*object;
140 	elf_object_t	*dynobj;
141 	const elf_object_t	*pobj;
142 	void		*retval;
143 	const Elf_Sym	*sym = NULL;
144 	int flags;
145 
146 	if (handle == NULL || handle == RTLD_NEXT ||
147 	    handle == RTLD_SELF || handle == RTLD_DEFAULT) {
148 		void *retaddr;
149 
150 		retaddr = __builtin_return_address(0);	/* __GNUC__ only */
151 
152 		if ((object = obj_from_addr(retaddr)) == NULL) {
153 			_dl_errno = DL_CANT_FIND_OBJ;
154 			return(0);
155 		}
156 
157 		if (handle == RTLD_NEXT)
158 			flags = SYM_SEARCH_NEXT|SYM_PLT;
159 		else if (handle == RTLD_SELF)
160 			flags = SYM_SEARCH_SELF|SYM_PLT;
161 		else if (handle == RTLD_DEFAULT)
162 			flags = SYM_SEARCH_ALL|SYM_PLT;
163 		else
164 			flags = SYM_DLSYM|SYM_PLT;
165 
166 	} else {
167 		object = (elf_object_t *)handle;
168 		flags = SYM_DLSYM|SYM_PLT;
169 
170 		dynobj = _dl_objects;
171 		while (dynobj && dynobj != object)
172 			dynobj = dynobj->next;
173 
174 		if (!dynobj || object != dynobj) {
175 			_dl_errno = DL_INVALID_HANDLE;
176 			return(0);
177 		}
178 	}
179 
180 	retval = (void *)_dl_find_symbol(name, &sym,
181 	    flags|SYM_NOWARNNOTFOUND, NULL, object, &pobj);
182 
183 	if (sym != NULL) {
184 		retval += sym->st_value;
185 #ifdef __hppa__
186 		if (ELF_ST_TYPE(sym->st_info) == STT_FUNC)
187 			retval = (void *)_dl_md_plabel((Elf_Addr)retval,
188 			    pobj->dyn.pltgot);
189 #endif
190 		DL_DEB(("dlsym: %s in %s: %p\n",
191 		    name, object->load_name, retval));
192 	} else
193 		_dl_errno = DL_NO_SYMBOL;
194 	return (retval);
195 }
196 
197 int
dlctl(void * handle,int command,void * data)198 dlctl(void *handle, int command, void *data)
199 {
200 	int retval;
201 
202 	switch (command) {
203 	case DL_SETTHREADLCK:
204 		DL_DEB(("dlctl: _dl_thread_fnc set to %p\n", data));
205 		_dl_thread_fnc = data;
206 		retval = 0;
207 		break;
208 	case 0x20:
209 		_dl_show_objects();
210 		retval = 0;
211 		break;
212 	case 0x21:
213 	{
214 		struct dep_node *n, *m;
215 		elf_object_t *obj;
216 		_dl_printf("Load Groups:\n");
217 
218 		TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) {
219 			obj = n->data;
220 			_dl_printf("%s\n", obj->load_name);
221 
222 			_dl_printf("  children\n");
223 			TAILQ_FOREACH(m, &obj->child_list, next_sib)
224 				_dl_printf("\t[%s]\n", m->data->load_name);
225 
226 			_dl_printf("  grpref\n");
227 			TAILQ_FOREACH(m, &obj->grpref_list, next_sib)
228 				_dl_printf("\t[%s]\n", m->data->load_name);
229 			_dl_printf("\n");
230 		}
231 		retval = 0;
232 		break;
233 	}
234 	default:
235 		_dl_errno = DL_INVALID_CTL;
236 		retval = -1;
237 		break;
238 	}
239 	return (retval);
240 }
241 
242 int
dlclose(void * handle)243 dlclose(void *handle)
244 {
245 	int retval;
246 
247 	if (handle == RTLD_DEFAULT)
248 		return 0;
249 
250 	_dl_thread_kern_stop();
251 
252 	if (_dl_debug_map->r_brk) {
253 		_dl_debug_map->r_state = RT_DELETE;
254 		(*((void (*)(void))_dl_debug_map->r_brk))();
255 	}
256 
257 	retval = _dl_real_close(handle);
258 
259 
260 	if (_dl_debug_map->r_brk) {
261 		_dl_debug_map->r_state = RT_CONSISTENT;
262 		(*((void (*)(void))_dl_debug_map->r_brk))();
263 	}
264 	_dl_thread_kern_go();
265 	return (retval);
266 }
267 
268 int
_dl_real_close(void * handle)269 _dl_real_close(void *handle)
270 {
271 	elf_object_t	*object;
272 	elf_object_t	*dynobj;
273 
274 	object = (elf_object_t *)handle;
275 
276 	dynobj = _dl_objects;
277 	while (dynobj && dynobj != object)
278 		dynobj = dynobj->next;
279 
280 	if (!dynobj || object != dynobj) {
281 		_dl_errno = DL_INVALID_HANDLE;
282 		return (1);
283 	}
284 
285 	if (object->opencount == 0) {
286 		_dl_errno = DL_INVALID_HANDLE;
287 		return (1);
288 	}
289 
290 	object->opencount--;
291 	_dl_notify_unload_shlib(object);
292 	_dl_run_all_dtors();
293 	_dl_unload_shlib(object);
294 	_dl_cleanup_objects();
295 	return (0);
296 }
297 
298 
299 /*
300  * Return a character string describing the last dl... error occurred.
301  */
302 const char *
dlerror(void)303 dlerror(void)
304 {
305 	const char *errmsg;
306 
307 	switch (_dl_errno) {
308 	case 0:	/* NO ERROR */
309 		errmsg = NULL;
310 		break;
311 	case DL_NOT_FOUND:
312 		errmsg = "File not found";
313 		break;
314 	case DL_CANT_OPEN:
315 		errmsg = "Can't open file";
316 		break;
317 	case DL_NOT_ELF:
318 		errmsg = "File not an ELF object";
319 		break;
320 	case DL_CANT_OPEN_REF:
321 		errmsg = "Can't open referenced object";
322 		break;
323 	case DL_CANT_MMAP:
324 		errmsg = "Can't map ELF object";
325 		break;
326 	case DL_INVALID_HANDLE:
327 		errmsg = "Invalid handle";
328 		break;
329 	case DL_NO_SYMBOL:
330 		errmsg = "Unable to resolve symbol";
331 		break;
332 	case DL_INVALID_CTL:
333 		errmsg = "Invalid dlctl() command";
334 		break;
335 	case DL_NO_OBJECT:
336 		errmsg = "No shared object contains address";
337 		break;
338 	case DL_CANT_FIND_OBJ:
339 		errmsg = "Cannot determine caller's shared object";
340 		break;
341 	case DL_CANT_LOAD_OBJ:
342 		errmsg = "Cannot load specified object";
343 		break;
344 	default:
345 		errmsg = "Unknown error";
346 	}
347 
348 	_dl_errno = 0;
349 	return (errmsg);
350 }
351 
352 void
_dl_show_objects(void)353 _dl_show_objects(void)
354 {
355 	elf_object_t *object;
356 	char *objtypename;
357 	int outputfd;
358 
359 	object = _dl_objects;
360 	outputfd = _dl_traceld ? STDOUT_FILENO : STDERR_FILENO;
361 
362 	if (sizeof(long) == 8)
363 		_dl_fdprintf(outputfd, "\tStart            End              Type Open Ref GrpRef Name\n");
364 	else
365 		_dl_fdprintf(outputfd, "\tStart    End      Type Open Ref GrpRef Name\n");
366 
367 	while (object) {
368 		switch (object->obj_type) {
369 		case OBJTYPE_LDR:
370 			objtypename = "rtld";
371 			break;
372 		case OBJTYPE_EXE:
373 			if (!_dl_debug)
374 				goto _dl_show_objects_skip;
375 			objtypename = "exe ";
376 			break;
377 		case OBJTYPE_LIB:
378 			objtypename = "rlib";
379 			break;
380 		case OBJTYPE_DLO:
381 			if (!_dl_debug)
382 				goto _dl_show_objects_skip;
383 			objtypename = "dlib";
384 			break;
385 		default:
386 			objtypename = "????";
387 			break;
388 		}
389 		_dl_fdprintf(outputfd, "\t%lX %lX %s %d    %d   %d      %s\n",
390 		    (void *)object->load_addr,
391 		    (void *)(object->load_addr + object->load_size),
392 		    objtypename, object->opencount, object->refcount,
393 		    object->grprefcount, object->load_name);
394  _dl_show_objects_skip:
395 		object = object->next;
396 	}
397 
398 	if (_dl_symcachestat_lookups != 0)
399 		DL_DEB(("symcache lookups %d hits %d ratio %d% hits\n",
400 		    _dl_symcachestat_lookups, _dl_symcachestat_hits,
401 		    (_dl_symcachestat_hits * 100) /
402 		    _dl_symcachestat_lookups));
403 }
404 
405 void
_dl_thread_kern_stop(void)406 _dl_thread_kern_stop(void)
407 {
408 	if (_dl_thread_fnc != NULL)
409 		(*_dl_thread_fnc)(0);
410 }
411 
412 void
_dl_thread_kern_go(void)413 _dl_thread_kern_go(void)
414 {
415 	if (_dl_thread_fnc != NULL)
416 		(*_dl_thread_fnc)(1);
417 }
418 
419 int
dl_iterate_phdr(int (* callback)(struct dl_phdr_info *,size_t,void * data),void * data)420 dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *data),
421 	void *data)
422 {
423 	elf_object_t *object;
424 	Elf_Ehdr *ehdr;
425 	struct dl_phdr_info info;
426 	int retval = -1;
427 
428 	for (object = _dl_objects; object != NULL; object = object->next) {
429 		ehdr = (Elf_Ehdr *)object->load_addr;
430 		if (object->phdrp == NULL && ehdr == NULL)
431 			continue;
432 
433 		info.dlpi_addr = object->load_addr;
434 		info.dlpi_name = object->load_name;
435 		info.dlpi_phdr = object->phdrp;
436 		info.dlpi_phnum = object->phdrc;
437 		if (info.dlpi_phdr == NULL) {
438 		    info.dlpi_phdr = (Elf_Phdr *)
439 			((char *)object->load_addr + ehdr->e_phoff);
440 		    info.dlpi_phnum = ehdr->e_phnum;
441 		}
442 		retval = callback(&info, sizeof (struct dl_phdr_info), data);
443 		if (retval)
444 			break;
445 	}
446 
447 	return retval;
448 }
449 
450 static elf_object_t *
obj_from_addr(const void * addr)451 obj_from_addr(const void *addr)
452 {
453 	elf_object_t *dynobj;
454 	Elf_Ehdr *ehdr;
455 	Elf_Phdr *phdr;
456 	Elf_Addr start;
457 	Elf_Addr end;
458 	u_int32_t symoffset;
459 	const Elf_Sym *sym;
460 	int i;
461 
462 	for (dynobj = _dl_objects; dynobj != NULL; dynobj = dynobj->next) {
463 		ehdr = (Elf_Ehdr *)dynobj->load_addr;
464 		if (ehdr == NULL)
465 			continue;
466 
467 		phdr = (Elf_Phdr *)((char *)dynobj->load_addr + ehdr->e_phoff);
468 
469 		for (i = 0; i < ehdr->e_phnum; i++) {
470 			switch (phdr[i].p_type) {
471 			case PT_LOAD:
472 				start = phdr[i].p_vaddr + dynobj->load_addr;
473 				if ((Elf_Addr)addr >= start &&
474 				    (Elf_Addr)addr < start + phdr[i].p_memsz)
475 					return dynobj;
476 				break;
477 			default:
478 				break;
479 			}
480 		}
481 	}
482 
483 	/* find the lowest & highest symbol address in the main exe */
484 	start = -1;
485 	end = 0;
486 
487 	for (symoffset = 0; symoffset < _dl_objects->nchains; symoffset++) {
488 		sym = _dl_objects->dyn.symtab + symoffset;
489 
490 		/*
491 		 * For skip the symbol if st_shndx is either SHN_UNDEF or
492 		 * SHN_COMMON.
493 		 */
494 		if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON)
495 			continue;
496 
497 		if (sym->st_value < start)
498 			start = sym->st_value;
499 
500 		if (sym->st_value > end)
501 			end = sym->st_value;
502 	}
503 
504 	if (end && (Elf_Addr) addr >= start && (Elf_Addr) addr <= end)
505 		return _dl_objects;
506 	else
507 		return NULL;
508 }
509 
510 int
dladdr(const void * addr,Dl_info * info)511 dladdr(const void *addr, Dl_info *info)
512 {
513 	const elf_object_t *object;
514 	const Elf_Sym *sym;
515 	void *symbol_addr;
516 	u_int32_t symoffset;
517 
518 	object = obj_from_addr(addr);
519 
520 	if (object == NULL) {
521 		_dl_errno = DL_NO_OBJECT;
522 		return 0;
523 	}
524 
525 	info->dli_fname = (char *)object->load_name;
526 	info->dli_fbase = (void *)object->load_addr;
527 	info->dli_sname = NULL;
528 	info->dli_saddr = (void *)0;
529 
530 	/*
531 	 * Walk the symbol list looking for the symbol whose address is
532 	 * closest to the address sent in.
533 	 */
534 	for (symoffset = 0; symoffset < object->nchains; symoffset++) {
535 		sym = object->dyn.symtab + symoffset;
536 
537 		/*
538 		 * For skip the symbol if st_shndx is either SHN_UNDEF or
539 		 * SHN_COMMON.
540 		 */
541 		if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON)
542 			continue;
543 
544 		/*
545 		 * If the symbol is greater than the specified address, or if
546 		 * it is further away from addr than the current nearest
547 		 * symbol, then reject it.
548 		 */
549 		symbol_addr = (void *)(object->load_addr + sym->st_value);
550 		if (symbol_addr > addr || symbol_addr < info->dli_saddr)
551 			continue;
552 
553 		/* Update our idea of the nearest symbol. */
554 		info->dli_sname = object->dyn.strtab + sym->st_name;
555 		info->dli_saddr = symbol_addr;
556 
557 		/* Exact match? */
558 		if (info->dli_saddr == addr)
559 			break;
560 	}
561 
562 	return 1;
563 }
564