diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2003-04-17 03:40:50 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2003-04-17 03:40:50 +0000 |
commit | ddb3879f13964f6a99cf09d238b853194cbab068 (patch) | |
tree | 54937282db398272cbfad8524d0e399eb7c22450 /libexec/ld.so/library_mquery.c | |
parent | f68593d2ce69e6761dc27dc410c13f7afc2883c3 (diff) |
mquery support, currently for i386 only.
Diffstat (limited to 'libexec/ld.so/library_mquery.c')
-rw-r--r-- | libexec/ld.so/library_mquery.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/libexec/ld.so/library_mquery.c b/libexec/ld.so/library_mquery.c new file mode 100644 index 00000000000..cd78f33d19a --- /dev/null +++ b/libexec/ld.so/library_mquery.c @@ -0,0 +1,569 @@ +/* $OpenBSD: library_mquery.c,v 1.1 2003/04/17 03:40:45 drahn Exp $ */ + +/* + * Copyright (c) 2002 Dale Rahn + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed under OpenBSD by + * Per Fogelstrom, Opsycon AB, Sweden. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 <sys/syslimits.h> +#include <sys/param.h> +#include <fcntl.h> +#include <nlist.h> +#include <link.h> +#include <sys/mman.h> +#include <dirent.h> + +#include "syscall.h" +#include "archdep.h" +#include "resolve.h" +#include "dir.h" +#include "sod.h" + +#define DEFAULT_PATH "/usr/lib" + +#define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \ + (((X) & PF_W) ? PROT_WRITE : 0) | \ + (((X) & PF_X) ? PROT_EXEC : 0)) + +elf_object_t *_dl_tryload_shlib(const char *libname, int type); +static void _dl_link_sub(elf_object_t *dep, elf_object_t *p); + +/* + * _dl_match_file() + * + * This fucntion determines if a given name matches what is specified + * in a struct sod. The major must match exactly, and the minor must + * be same or larger. + * + * sodp is updated with the minor if this matches. + */ + +int +_dl_match_file(struct sod *sodp, char *name, int namelen) +{ + int match; + struct sod lsod; + char *lname; + + lname = name; + if (sodp->sod_library) { + if (_dl_strncmp(name, "lib", 3)) + return 0; + lname += 3; + } + if (_dl_strncmp(lname, (char *)sodp->sod_name, + _dl_strlen((char *)sodp->sod_name))) + return 0; + + _dl_build_sod(name, &lsod); + + match = 0; + if ((_dl_strcmp((char *)lsod.sod_name, (char *)sodp->sod_name) == 0) && + (lsod.sod_library == sodp->sod_library) && + (sodp->sod_major == lsod.sod_major) && + ((sodp->sod_minor == -1) || + (lsod.sod_minor >= sodp->sod_minor))) { + match = 1; + + /* return version matched */ + sodp->sod_minor = lsod.sod_minor; + } + _dl_free((char *)lsod.sod_name); + + return match; +} + +char _dl_hint_store[MAXPATHLEN]; + +char * +_dl_find_shlib(struct sod *sodp, const char *searchpath, int nohints) +{ + char *hint, lp[PATH_MAX + 10], *path; + struct dirent *dp; + const char *pp; + int match, len; + DIR *dd; + + /* if we are to search default directories, and hints + * are not to be used, search the standard path from ldconfig + * (_dl_hint_search_path) or use the default path + */ + if (nohints) + goto nohints; + + if (searchpath == NULL) { + /* search 'standard' locations, find any match in the hints */ + hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major, + sodp->sod_minor, NULL); + if (hint) + return hint; + } else { + /* search hints requesting matches for only + * the searchpath directories, + */ + pp = searchpath; + while (pp) { + path = lp; + while (path < lp + PATH_MAX && + *pp && *pp != ':' && *pp != ';') + *path++ = *pp++; + *path = 0; + + hint = _dl_findhint((char *)sodp->sod_name, + sodp->sod_major, sodp->sod_minor, lp); + if (hint != NULL) + return hint; + + if (*pp) /* Try curdir if ':' at end */ + pp++; + else + pp = 0; + } + } + + /* + * For each directory in the searchpath, read the directory + * entries looking for a match to sod. filename compare is + * done by _dl_match_file() + */ +nohints: + if (searchpath == NULL) { + if (_dl_hint_search_path != NULL) + searchpath = _dl_hint_search_path; + else + searchpath = DEFAULT_PATH; + } + pp = searchpath; + while (pp) { + path = lp; + while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';') + *path++ = *pp++; + *path = 0; + + if ((dd = _dl_opendir(lp)) != NULL) { + match = 0; + while ((dp = _dl_readdir(dd)) != NULL) { + if (_dl_match_file(sodp, dp->d_name, + dp->d_namlen)) { + /* + * When a match is found, sodp is + * updated with the minor found. + * We continue looking at this + * directory, thus this will find + * the largest matching library + * in this directory. + * we save off the d_name now + * so that it doesn't have to be + * recreated from the hint. + */ + match = 1; + len = _dl_strlcpy(_dl_hint_store, lp, + MAXPATHLEN); + if (lp[len-1] != '/') { + _dl_hint_store[len] = '/'; + len++; + } + _dl_strlcpy(&_dl_hint_store[len], + dp->d_name, MAXPATHLEN-len); + } + } + _dl_closedir(dd); + if (match) + return (_dl_hint_store); + } + + if (*pp) /* Try curdir if ':' at end */ + pp++; + else + pp = 0; + } + return NULL; +} + +/* + * Load a shared object. Search order is: + * If the name contains a '/' use the name exactly as is. (only) + * try the LD_LIBRARY_PATH specification (if present) + * search hints for match in LD_LIBRARY_PATH dirs + * this will only match specific libary version. + * search LD_LIBRARY_PATH dirs for match. + * this will find largest minor version in first dir found. + * check DT_RPATH paths, (if present) + * search hints for match in DT_RPATH dirs + * this will only match specific libary version. + * search DT_RPATH dirs for match. + * this will find largest minor version in first dir found. + * last look in default search directory, either as specified + * by ldconfig or default to '/usr/lib' + */ + +elf_object_t * +_dl_load_shlib(const char *libname, elf_object_t *parent, int type) +{ + int try_any_minor, ignore_hints; + struct sod sod, req_sod; + elf_object_t *object; + char *hint; + + try_any_minor = 0; + ignore_hints = 0; + + if (_dl_strchr(libname, '/')) { + object = _dl_tryload_shlib(libname, type); + _dl_link_sub(object, parent); + return(object); + } + + _dl_build_sod(libname, &sod); + req_sod = sod; + +again: + /* + * No '/' in name. Scan the known places, LD_LIBRARY_PATH first. + */ + if (_dl_libpath != NULL) { + hint = _dl_find_shlib(&req_sod, _dl_libpath, ignore_hints); + if (hint != NULL) { + if (req_sod.sod_minor < sod.sod_minor) + _dl_printf("warning: lib%s.so.%d.%d: " + "minor version >= %d expected, " + "using it anyway\n", + sod.sod_name, sod.sod_major, + sod.sod_minor, req_sod.sod_minor); + object = _dl_tryload_shlib(hint, type); + if (object != NULL) { + _dl_link_sub(object, parent); + _dl_free((char *)sod.sod_name); + return (object); + } + } + } + + /* + * Check DT_RPATH. + */ + if (parent->dyn.rpath != NULL) { + hint = _dl_find_shlib(&req_sod, parent->dyn.rpath, + ignore_hints); + if (hint != NULL) { + if (req_sod.sod_minor < sod.sod_minor) + _dl_printf("warning: lib%s.so.%d.%d: " + "minor version >= %d expected, " + "using it anyway\n", + sod.sod_name, sod.sod_major, + sod.sod_minor, req_sod.sod_minor); + object = _dl_tryload_shlib(hint, type); + if (object != NULL) { + _dl_link_sub(object, parent); + _dl_free((char *)sod.sod_name); + return (object); + } + } + } + + /* check 'standard' locations */ + hint = _dl_find_shlib(&req_sod, NULL, ignore_hints); + if (hint != NULL) { + if (req_sod.sod_minor < sod.sod_minor) + _dl_printf("warning: lib%s.so.%d.%d: " + "minor version >= %d expected, " + "using it anyway\n", + sod.sod_name, sod.sod_major, + sod.sod_minor, req_sod.sod_minor); + object = _dl_tryload_shlib(hint, type); + if (object != NULL) { + _dl_link_sub(object, parent); + _dl_free((char *)sod.sod_name); + return(object); + } + } + + if (try_any_minor == 0) { + try_any_minor = 1; + ignore_hints = 1; + req_sod.sod_minor = -1; + goto again; + } + _dl_free((char *)sod.sod_name); + _dl_errno = DL_NOT_FOUND; + return(0); +} + +void +_dl_load_list_free(struct load_list *load_list) +{ + struct load_list *next; + int align = _dl_pagesz - 1; + + while (load_list != NULL) { + if (load_list->start != NULL) + _dl_munmap(load_list->start, + ((load_list->size) + align) & ~align); + next = load_list->next; + _dl_free(load_list); + load_list = next; + } +} + +void _dl_run_dtors(elf_object_t *object); + +void +_dl_unload_shlib(elf_object_t *object) +{ + if (--object->refcount == 0) { + _dl_run_dtors(object); + _dl_load_list_free(object->load_list); + _dl_remove_object(object); + } +} + + +elf_object_t * +_dl_tryload_shlib(const char *libname, int type) +{ + int libfile, i, align = _dl_pagesz - 1; + struct load_list *ld, *lowld = NULL; + elf_object_t *object; + char hbuf[4096]; + Elf_Dyn *dynp = 0; + Elf_Ehdr *ehdr; + Elf_Phdr *phdp; + int off; + int size; + Elf_Addr load_end = 0; + +#define ROUND_PG(x) (((x) + align) & ~(align)) +#define TRUNC_PG(x) ((x) & ~(align)) + + object = _dl_lookup_object(libname); + if (object) { + object->refcount++; + return(object); /* Already loaded */ + } + + libfile = _dl_open(libname, O_RDONLY); + if (libfile < 0) { + _dl_errno = DL_CANT_OPEN; + return(0); + } + + _dl_read(libfile, hbuf, sizeof(hbuf)); + ehdr = (Elf_Ehdr *)hbuf; + if (ehdr->e_ident[0] != ELFMAG0 || ehdr->e_ident[1] != ELFMAG1 || + ehdr->e_ident[2] != ELFMAG2 || ehdr->e_ident[3] != ELFMAG3 || + ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) { + _dl_close(libfile); + _dl_errno = DL_NOT_ELF; + return(0); + } + + /* Insertion sort */ +#define LDLIST_INSERT(ld) do { \ + struct load_list **_ld; \ + for (_ld = &lowld; *_ld != NULL; _ld = &(*_ld)->next) \ + if ((*_ld)->moff > ld->moff) \ + break; \ + ld->next = *_ld; \ + *_ld = ld; \ +} while (0) + /* + * Alright, we might have a winner! + * Figure out how much VM space we need and set up the load + * list that we'll use to find free VM space. + */ + phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); + for (i = 0; i < ehdr->e_phnum; i++, phdp++) { + switch (phdp->p_type) { + case PT_LOAD: + off = (phdp->p_vaddr & align); + size = off + phdp->p_filesz; + + ld = _dl_malloc(sizeof(struct load_list)); + ld->start = NULL; + ld->size = size; + ld->moff = TRUNC_PG(phdp->p_vaddr); + ld->foff = TRUNC_PG(phdp->p_offset); + ld->prot = PFLAGS(phdp->p_flags); + LDLIST_INSERT(ld); + + if ((ld->prot & PROT_WRITE) == 0 || + ROUND_PG(size) == ROUND_PG(off + phdp->p_memsz)) + break; + /* This phdr has a zfod section */ + ld = _dl_malloc(sizeof(struct load_list)); + ld->start = NULL; + ld->size = ROUND_PG(off + phdp->p_memsz) - + ROUND_PG(size); + ld->moff = TRUNC_PG(phdp->p_vaddr) + + ROUND_PG(size); + ld->foff = -1; + ld->prot = PFLAGS(phdp->p_flags); + LDLIST_INSERT(ld); + break; + case PT_DYNAMIC: + dynp = (Elf_Dyn *)phdp->p_vaddr; + break; + default: + break; + } + } + +#define LOFF ((Elf_Addr)lowld->start - lowld->moff) + +retry: + for (ld = lowld; ld != NULL; ld = ld->next) { + off_t foff; + int fd, flags; + int error; + + /* + * We don't want to provide the fd/off hint for anything + * but the first mapping, all other might have + * cache-incoherent aliases and will cause this code to + * loop forever. + */ + if (ld == lowld) { + fd = libfile; + foff = ld->foff; + flags = 0; + } else { + fd = -1; + foff = 0; + flags = MAP_FIXED; + } + + ld->start = (void *)(LOFF + ld->moff); + if (ld->start == 0) { + /* we dont want the first > MAXDSIZ address + * we want the first available address + * skip the first 16 pages for page 0, + * possible sigtramp and some exe. + */ + ld->start = (void *)(long)(_dl_pagesz * 16); + } + + /* + * Magic here. + * The first mquery is done with MAP_FIXED to see if + * the mapping we want is free. If it's not, we redo the + * mquery without MAP_FIXED to get the next free mapping, + * adjust the base mapping address to match this free mapping + * and restart the process again. + */ + error = _dl_mquery(flags, &ld->start, ROUND_PG(ld->size), fd, + foff); + if (_dl_check_error(error)) { + ld->start = (void *)(LOFF + ld->moff); + error = _dl_mquery(0, &ld->start, ROUND_PG(ld->size), + fd, foff); + if (_dl_check_error(error)) + goto fail; + } + + if (ld->start != (void *)(LOFF + ld->moff)) { + lowld->start = ld->start - ld->moff + lowld->moff; + goto retry; + } + /* + * XXX - we need some kind of boundary condition here, + * or fix mquery to not run into the stack + */ + } + + for (ld = lowld; ld != NULL; ld = ld->next) { + off_t foff; + int fd, flags; + void *res; + + if (ld->foff < 0) { + fd = -1; + foff = 0; + flags = MAP_FIXED|MAP_PRIVATE|MAP_ANON; + } else { + fd = libfile; + foff = ld->foff; + flags = MAP_FIXED|MAP_PRIVATE; + } + res = _dl_mmap(ld->start, ROUND_PG(ld->size), ld->prot, flags, + fd, foff); + if (_dl_check_error((long)res)) + goto fail; + /* Zero out everything past the EOF */ + if ((ld->prot & PROT_WRITE) != 0 && (ld->size & align) != 0) + _dl_memset((char *)ld->start + ld->size, 0, + _dl_pagesz - (ld->size & align)); + load_end = (Elf_Addr)ld->start + ROUND_PG(ld->size); + } + _dl_close(libfile); + + dynp = (Elf_Dyn *)((unsigned long)dynp + LOFF); + object = _dl_add_object(libname, dynp, 0, type, + (Elf_Addr)lowld->start, LOFF); + if (object) { + object->load_size = (Elf_Addr)lowld->start - (Elf_Addr)load_end; + object->load_list = lowld; + } else { + _dl_load_list_free(lowld); + } + return(object); +fail: + _dl_printf("%s: rtld mmap failed mapping %s.\n", + _dl_progname, libname); + _dl_close(libfile); + _dl_errno = DL_CANT_MMAP; + _dl_load_list_free(lowld); + return(0); +} + +static void +_dl_link_sub(elf_object_t *dep, elf_object_t *p) +{ + struct dep_node *n; + + n = _dl_malloc(sizeof *n); + if (n == NULL) + _dl_exit(5); + n->data = dep; + n->next_sibling = NULL; + if (p->first_child) { + p->last_child->next_sibling = n; + p->last_child = n; + } else + p->first_child = p->last_child = n; + + DL_DEB(("linking dep %s as child of %s\n", dep->load_name, + p->load_name)); +} + + |