Edit

IABSD.fr/src/libexec/ld.so/dlfcn.c

Branch :

  • Show log

    Commit

  • Author : guenther
    Date : 2019-10-04 17:42:16
    Hash : d937a926
    Message : Convert the child_list member from a linked list to a vector. ok mpi@

  • libexec/ld.so/dlfcn.c
  • /*	$OpenBSD: dlfcn.c,v 1.106 2019/10/04 17:42:16 guenther Exp $ */
    
    /*
     * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     *
     */
    
    #define _DYN_LOADER
    
    #include <sys/types.h>
    #include <nlist.h>
    #include <link.h>
    #include <dlfcn.h>
    #include <unistd.h>
    
    #include "syscall.h"
    #include "archdep.h"
    #include "resolve.h"
    #include "sod.h"
    
    int _dl_errno;
    static int _dl_tracelib;
    
    static int _dl_real_close(void *handle);
    static lock_cb *_dl_thread_fnc = NULL;
    static elf_object_t *obj_from_addr(const void *addr);
    
    void *
    dlopen(const char *libname, int flags)
    {
    	elf_object_t *object;
    	lock_cb *cb;
    	int failed = 0;
    	int obj_flags;
    
    	if (flags & ~(RTLD_TRACE|RTLD_LAZY|RTLD_NOW|RTLD_GLOBAL)) {
    		_dl_errno = DL_INVALID_MODE;
    		return NULL;
    	}
    
    	if (libname == NULL)
    		return RTLD_DEFAULT;
    
    	if ((flags & RTLD_TRACE) == RTLD_TRACE) {
    		_dl_traceld = 1;
    		_dl_tracelib = 1;
    	}
    
    	DL_DEB(("dlopen: loading: %s\n", libname));
    
    	cb = _dl_thread_kern_stop();
    
    	if (_dl_debug_map && _dl_debug_map->r_brk) {
    		_dl_debug_map->r_state = RT_ADD;
    		(*((void (*)(void))_dl_debug_map->r_brk))();
    	}
    
    	_dl_loading_object = NULL;
    
    	obj_flags = (flags & RTLD_NOW ? DF_1_NOW : 0)
    	    | (flags & RTLD_GLOBAL ? DF_1_GLOBAL : 0);
    	object = _dl_load_shlib(libname, _dl_objects, OBJTYPE_DLO, obj_flags);
    	if (object == 0) {
    		DL_DEB(("dlopen: failed to open %s\n", libname));
    		failed = 1;
    		goto loaded;
    	}
    
    	_dl_link_dlopen(object);
    
    	if (OBJECT_REF_CNT(object) > 1) {
    		/* if opened but grpsym_vec has not been filled in */
    		if (object->grpsym_vec.len == 0)
    			_dl_cache_grpsym_list_setup(object);
    		goto loaded;
    	}
    
    	/* this add_object should not be here, XXX */
    	_dl_add_object(object);
    
    	DL_DEB(("head [%s]\n", object->load_name ));
    
    	if ((failed = _dl_load_dep_libs(object, obj_flags, 0)) == 1) {
    		_dl_real_close(object);
    		object = NULL;
    		_dl_errno = DL_CANT_LOAD_OBJ;
    	} else {
    		int err;
    		DL_DEB(("tail %s\n", object->load_name ));
    		if (_dl_traceld) {
    			_dl_show_objects();
    			_dl_unload_shlib(object);
    			_dl_exit(0);
    		}
    		err = _dl_rtld(object);
    		if (err != 0) {
    			_dl_real_close(object);
    			_dl_errno = DL_CANT_LOAD_OBJ;
    			object = NULL;
    			failed = 1;
    		} else {
    			_dl_call_init(object);
    		}
    	}
    
    loaded:
    	_dl_loading_object = NULL;
    
    	if (_dl_debug_map && _dl_debug_map->r_brk) {
    		_dl_debug_map->r_state = RT_CONSISTENT;
    		(*((void (*)(void))_dl_debug_map->r_brk))();
    	}
    
    	_dl_thread_kern_go(cb);
    
    	DL_DEB(("dlopen: %s: done (%s).\n", libname,
    	    failed ? "failed" : "success"));
    
    	return((void *)object);
    }
    
    void *
    dlsym(void *handle, const char *name)
    {
    	elf_object_t	*object;
    	elf_object_t	*dynobj;
    	struct sym_res	sr;
    	int		flags;
    	Elf_Addr	addr;
    
    	if (handle == NULL || handle == RTLD_NEXT ||
    	    handle == RTLD_SELF || handle == RTLD_DEFAULT) {
    		void *retaddr;
    
    		retaddr = __builtin_return_address(0);	/* __GNUC__ only */
    
    		if ((object = obj_from_addr(retaddr)) == NULL) {
    			_dl_errno = DL_CANT_FIND_OBJ;
    			return(0);
    		}
    
    		if (handle == RTLD_NEXT)
    			flags = SYM_SEARCH_NEXT|SYM_PLT;
    		else if (handle == RTLD_SELF)
    			flags = SYM_SEARCH_SELF|SYM_PLT;
    		else if (handle == RTLD_DEFAULT)
    			flags = SYM_SEARCH_ALL|SYM_PLT;
    		else
    			flags = SYM_DLSYM|SYM_PLT;
    
    	} else {
    		object = (elf_object_t *)handle;
    		flags = SYM_DLSYM|SYM_PLT;
    
    		dynobj = _dl_objects;
    		while (dynobj && dynobj != object)
    			dynobj = dynobj->next;
    
    		if (!dynobj || object != dynobj) {
    			_dl_errno = DL_INVALID_HANDLE;
    			return(0);
    		}
    	}
    
    	sr = _dl_find_symbol(name, flags|SYM_NOWARNNOTFOUND, NULL, object);
    	if (sr.sym == NULL) {
    		_dl_errno = DL_NO_SYMBOL;
    		return NULL;
    	}
    
    	addr = sr.obj->obj_base + sr.sym->st_value;
    #ifdef __hppa__
    	if (ELF_ST_TYPE(sr.sym->st_info) == STT_FUNC)
    		addr = _dl_md_plabel(addr, sr.obj->dyn.pltgot);
    #endif
    	DL_DEB(("dlsym: %s in %s: %p\n",
    	    name, object->load_name, (void *)addr));
    	return (void *)addr;
    }
    
    int
    dlctl(void *handle, int command, void *data)
    {
    	int retval;
    
    	switch (command) {
    	case DL_SETTHREADLCK:
    		DL_DEB(("dlctl: _dl_thread_fnc set to %p\n", data));
    		_dl_thread_fnc = data;
    		retval = 0;
    		break;
    	case DL_SETBINDLCK:
    		/* made superfluous by kbind */
    		retval = 0;
    		break;
    	case DL_REFERENCE:
    	{
    		elf_object_t *obj;
    
    		obj = obj_from_addr(data);
    		if (obj == NULL) {
    			_dl_errno = DL_CANT_FIND_OBJ;
    			retval = -1;
    			break;
    		}
    		if ((obj->status & STAT_NODELETE) == 0) {
    			obj->opencount++;
    			obj->status |= STAT_NODELETE;
    		}
    		retval = 0;
    		break;
    	}
    	case 0x20:
    		_dl_show_objects();
    		retval = 0;
    		break;
    	case 0x21:
    	{
    		struct object_vector vec;
    		struct dep_node *n, *m;
    		elf_object_t *obj;
    		int i;
    
    		_dl_printf("Load Groups:\n");
    
    		TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) {
    			obj = n->data;
    			_dl_printf("%s\n", obj->load_name);
    
    			_dl_printf("  children\n");
    			for (vec = obj->child_vec, i = 0; i < vec.len; i++)
    				_dl_printf("\t[%s]\n", vec.vec[i]->load_name);
    
    			_dl_printf("  grpref\n");
    			TAILQ_FOREACH(m, &obj->grpref_list, next_sib)
    				_dl_printf("\t[%s]\n", m->data->load_name);
    			_dl_printf("\n");
    		}
    		retval = 0;
    		break;
    	}
    	default:
    		_dl_errno = DL_INVALID_CTL;
    		retval = -1;
    		break;
    	}
    	return (retval);
    }
    __strong_alias(_dlctl,dlctl);
    
    int
    dlclose(void *handle)
    {
    	lock_cb *cb;
    	int retval;
    
    	if (handle == RTLD_DEFAULT)
    		return 0;
    
    	cb = _dl_thread_kern_stop();
    
    	if (_dl_debug_map && _dl_debug_map->r_brk) {
    		_dl_debug_map->r_state = RT_DELETE;
    		(*((void (*)(void))_dl_debug_map->r_brk))();
    	}
    
    	retval = _dl_real_close(handle);
    
    	if (_dl_debug_map && _dl_debug_map->r_brk) {
    		_dl_debug_map->r_state = RT_CONSISTENT;
    		(*((void (*)(void))_dl_debug_map->r_brk))();
    	}
    	_dl_thread_kern_go(cb);
    	return (retval);
    }
    
    int
    _dl_real_close(void *handle)
    {
    	elf_object_t	*object;
    	elf_object_t	*dynobj;
    
    	object = (elf_object_t *)handle;
    
    	dynobj = _dl_objects;
    	while (dynobj && dynobj != object)
    		dynobj = dynobj->next;
    
    	if (!dynobj || object != dynobj) {
    		_dl_errno = DL_INVALID_HANDLE;
    		return (1);
    	}
    
    	if (object->opencount == 0) {
    		_dl_errno = DL_INVALID_HANDLE;
    		return (1);
    	}
    
    	object->opencount--;
    	_dl_notify_unload_shlib(object);
    	_dl_run_all_dtors();
    	_dl_unload_shlib(object);
    	_dl_cleanup_objects();
    	return (0);
    }
    
    
    /*
     * Return a character string describing the last dl... error occurred.
     */
    char *
    dlerror(void)
    {
    	char *errmsg;
    
    	switch (_dl_errno) {
    	case 0:	/* NO ERROR */
    		errmsg = NULL;
    		break;
    	case DL_NOT_FOUND:
    		errmsg = "File not found";
    		break;
    	case DL_CANT_OPEN:
    		errmsg = "Can't open file";
    		break;
    	case DL_NOT_ELF:
    		errmsg = "File not an ELF object";
    		break;
    	case DL_CANT_OPEN_REF:
    		errmsg = "Can't open referenced object";
    		break;
    	case DL_CANT_MMAP:
    		errmsg = "Can't map ELF object";
    		break;
    	case DL_INVALID_HANDLE:
    		errmsg = "Invalid handle";
    		break;
    	case DL_NO_SYMBOL:
    		errmsg = "Unable to resolve symbol";
    		break;
    	case DL_INVALID_CTL:
    		errmsg = "Invalid dlctl() command";
    		break;
    	case DL_NO_OBJECT:
    		errmsg = "No shared object contains address";
    		break;
    	case DL_CANT_FIND_OBJ:
    		errmsg = "Cannot determine caller's shared object";
    		break;
    	case DL_CANT_LOAD_OBJ:
    		errmsg = "Cannot load specified object";
    		break;
    	case DL_INVALID_MODE:
    		errmsg = "Invalid mode";
    		break;
    	default:
    		errmsg = "Unknown error";
    	}
    
    	_dl_errno = 0;
    	return (errmsg);
    }
    
    static void
    _dl_tracefmt(int fd, elf_object_t *object, const char *fmt1, const char *fmt2,
        const char *objtypename)
    {
    	const char *fmt;
    	int i;
    
    	fmt = object->sod.sod_library ? fmt1 : fmt2;
    
    	for (i = 0; fmt[i]; i++) {
    		if (fmt[i] != '%' && fmt[i] != '\\') {
    			_dl_dprintf(fd, "%c", fmt[i]);
    			continue;
    		}
    		if (fmt[i] == '%') {
    			i++;
    			switch (fmt[i]) {
    			case '\0':
    				return;
    			case '%':
    				_dl_dprintf(fd, "%c", '%');
    				break;
    			case 'A':
    				_dl_dprintf(fd, "%s", _dl_traceprog ?
    				    _dl_traceprog : "");
    				break;
    			case 'a':
    				_dl_dprintf(fd, "%s", __progname);
    				break;
    			case 'e':
    				_dl_dprintf(fd, "%lX",
    				    (void *)(object->load_base +
    				    object->load_size));
    				break;
    			case 'g':
    				_dl_dprintf(fd, "%d", object->grprefcount);
    				break;
    			case 'm':
    				_dl_dprintf(fd, "%d", object->sod.sod_major);
    				break;
    			case 'n':
    				_dl_dprintf(fd, "%d", object->sod.sod_minor);
    				break;
    			case 'O':
    				_dl_dprintf(fd, "%d", object->opencount);
    				break;
    			case 'o':
    				_dl_dprintf(fd, "%s", object->sod.sod_name);
    				break;
    			case 'p':
    				_dl_dprintf(fd, "%s", object->load_name);
    				break;
    			case 'r':
    				_dl_dprintf(fd, "%d", object->refcount);
    				break;
    			case 't':
    				_dl_dprintf(fd, "%s", objtypename);
    				break;
    			case 'x':
    				_dl_dprintf(fd, "%lX", object->load_base);
    				break;
    			}
    		}
    		if (fmt[i] == '\\') {
    			i++;
    			switch (fmt[i]) {
    			case '\0':
    				return;
    			case 'n':
    				_dl_dprintf(fd, "%c", '\n');
    				break;
    			case 'r':
    				_dl_dprintf(fd, "%c", '\r');
    				break;
    			case 't':
    				_dl_dprintf(fd, "%c", '\t');
    				break;
    			default:
    				_dl_dprintf(fd, "%c", fmt[i]);
    				break;
    			}
    		}
    	}
    }
    
    void
    _dl_show_objects(void)
    {
    	elf_object_t *object;
    	char *objtypename;
    	int outputfd;
    	char *pad;
    	const char *fmt1, *fmt2;
    
    	object = _dl_objects;
    	if (_dl_traceld)
    		outputfd = STDOUT_FILENO;
    	else
    		outputfd = STDERR_FILENO;
    
    	if (sizeof(long) == 8)
    		pad = "        ";
    	else
    		pad = "";
    
    	fmt1 = _dl_tracefmt1 ? _dl_tracefmt1 :
    	    "\t%x %e %t %O    %r   %g      %p\n";
    	fmt2 = _dl_tracefmt2 ? _dl_tracefmt2 :
    	    "\t%x %e %t %O    %r   %g      %p\n";
    
    	if (_dl_tracefmt1 == NULL && _dl_tracefmt2 == NULL)
    		_dl_dprintf(outputfd, "\tStart   %s End     %s Type  Open Ref GrpRef Name\n",
    		    pad, pad);
    
    	if (_dl_tracelib) {
    		for (; object != NULL; object = object->next)
    			if (object->obj_type == OBJTYPE_LDR) {
    				object = object->next;
    				break;
    			}
    	}
    
    	for (; object != NULL; object = object->next) {
    		switch (object->obj_type) {
    		case OBJTYPE_LDR:
    			objtypename = "ld.so";
    			break;
    		case OBJTYPE_EXE:
    			objtypename = "exe  ";
    			break;
    		case OBJTYPE_LIB:
    			objtypename = "rlib ";
    			break;
    		case OBJTYPE_DLO:
    			objtypename = "dlib ";
    			break;
    		default:
    			objtypename = "?????";
    			break;
    		}
    		_dl_tracefmt(outputfd, object, fmt1, fmt2, objtypename);
    	}
    }
    
    lock_cb *
    _dl_thread_kern_stop(void)
    {
    	lock_cb *cb = _dl_thread_fnc;
    
    	if (cb != NULL)
    		(*cb)(0);
    	return cb;
    }
    
    void
    _dl_thread_kern_go(lock_cb *cb)
    {
    	if (cb != NULL)
    		(*cb)(1);
    }
    
    int
    dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *data),
    	void *data)
    {
    	elf_object_t *object;
    	struct dl_phdr_info info;
    	int retval = -1;
    
    	for (object = _dl_objects; object != NULL; object = object->next) {
    		if (object->phdrp == NULL)
    			continue;
    
    		info.dlpi_addr = object->obj_base;
    		info.dlpi_name = object->load_name;
    		info.dlpi_phdr = object->phdrp;
    		info.dlpi_phnum = object->phdrc;
    		retval = callback(&info, sizeof (struct dl_phdr_info), data);
    		if (retval)
    			break;
    	}
    
    	return retval;
    }
    
    static elf_object_t *
    obj_from_addr(const void *addr)
    {
    	elf_object_t *dynobj;
    	Elf_Phdr *phdrp;
    	int phdrc;
    	Elf_Addr start;
    	int i;
    
    	for (dynobj = _dl_objects; dynobj != NULL; dynobj = dynobj->next) {
    		if (dynobj->phdrp == NULL)
    			continue;
    
    		phdrp = dynobj->phdrp;
    		phdrc = dynobj->phdrc;
    
    		for (i = 0; i < phdrc; i++, phdrp++) {
    			if (phdrp->p_type == PT_LOAD) {
    				start = dynobj->obj_base + phdrp->p_vaddr;
    				if ((Elf_Addr)addr >= start &&
    				    (Elf_Addr)addr < start + phdrp->p_memsz)
    					return dynobj;
    			}
    		}
    	}
    
    	return NULL;
    }
    
    int
    dladdr(const void *addr, Dl_info *info)
    {
    	const elf_object_t *object;
    	const Elf_Sym *sym;
    	void *symbol_addr;
    	u_int32_t symoffset;
    
    	object = obj_from_addr(addr);
    
    	if (object == NULL) {
    		_dl_errno = DL_NO_OBJECT;
    		return 0;
    	}
    
    	info->dli_fname = (char *)object->load_name;
    	info->dli_fbase = (void *)object->load_base;
    	info->dli_sname = NULL;
    	info->dli_saddr = NULL;
    
    	/*
    	 * Walk the symbol list looking for the symbol whose address is
    	 * closest to the address sent in.
    	 */
    	for (symoffset = 0; symoffset < object->nchains; symoffset++) {
    		sym = object->dyn.symtab + symoffset;
    
    		/*
    		 * For skip the symbol if st_shndx is either SHN_UNDEF or
    		 * SHN_COMMON.
    		 */
    		if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON)
    			continue;
    
    		/*
    		 * If the symbol is greater than the specified address, or if
    		 * it is further away from addr than the current nearest
    		 * symbol, then reject it.
    		 */
    		symbol_addr = (void *)(object->obj_base + sym->st_value);
    		if (symbol_addr > addr || symbol_addr < info->dli_saddr)
    			continue;
    
    		/* Update our idea of the nearest symbol. */
    		info->dli_sname = object->dyn.strtab + sym->st_name;
    		info->dli_saddr = symbol_addr;
    
    		/* Exact match? */
    		if (info->dli_saddr == addr)
    			break;
    	}
    
    	return 1;
    }