summaryrefslogtreecommitdiff
path: root/libexec/ld.so/library.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/ld.so/library.c')
-rw-r--r--libexec/ld.so/library.c308
1 files changed, 240 insertions, 68 deletions
diff --git a/libexec/ld.so/library.c b/libexec/ld.so/library.c
index 5eb86c9c54b..ec75bcd1b55 100644
--- a/libexec/ld.so/library.c
+++ b/libexec/ld.so/library.c
@@ -1,6 +1,7 @@
-/* $OpenBSD: library.c,v 1.15 2002/06/05 19:34:44 art Exp $ */
+/* $OpenBSD: library.c,v 1.16 2002/07/12 20:18:30 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
@@ -36,119 +37,287 @@
#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);
-void _dl_build_sod(const char *name, struct sod *sodp);
-char *_dl_findhint(char *name, int major, int minor, char *prefered_path);
+static elf_object_t *_dl_tryload_shlib(const char *libname, int type);
+
+/*
+ * _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)
+{
+ int len;
+ char *hint;
+ char lp[PATH_MAX + 10];
+ char *path;
+ const char *pp;
+ DIR *dd;
+ struct dirent *dp;
+ int match;
+
+ /* 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)
- * check /var/run/ld.so.hints cache
- * last look in /usr/lib.
+ * 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)
{
- char lp[PATH_MAX + 10], *hint, *path = lp;
elf_object_t *object;
- const char *pp;
- struct sod sodp;
+ struct sod sod;
+ struct sod req_sod;
+ char *hint;
+ int try_any_minor;
+ int ignore_hints;
+
+ try_any_minor = 0;
+ ignore_hints = 0;
if (_dl_strchr(libname, '/')) {
object = _dl_tryload_shlib(libname, type);
return(object);
}
+
+ _dl_build_sod(libname, &sod);
+ req_sod = sod;
+
+again:
/*
* No '/' in name. Scan the known places, LD_LIBRARY_PATH first.
*/
- pp = _dl_libpath;
- while (pp) {
- const char *ln = libname;
-
- path = lp;
- while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';')
- *path++ = *pp++;
- /* Insert '/' */
- if (path != lp && *(path - 1) != '/')
- *path++ = '/';
-
- while (path < lp + PATH_MAX && (*path++ = *ln++))
- ;
- if (path < lp + PATH_MAX) {
- object = _dl_tryload_shlib(lp, type);
- if (object)
- return(object);
+ 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",
+ sod.sod_name, sod.sod_major,
+ sod.sod_minor, req_sod.sod_minor);
+ object = _dl_tryload_shlib(hint, type);
+ if (object != NULL) {
+ _dl_free((char *)sod.sod_name);
+ return (object);
+ }
}
- if (*pp) /* Try curdir if ':' at end */
- pp++;
- else
- pp = 0;
}
/*
* Check DT_RPATH.
*/
- pp = parent->dyn.rpath;
- while (pp) {
- const char *ln = libname;
-
- path = lp;
- while (path < lp + PATH_MAX && *pp && *pp != ':')
- *path++ = *pp++;
-
- /* Make sure '/' after dir path */
- if (*(path - 1) != '/')
- *path++ = '/';
-
- while (path < lp + PATH_MAX && (*path++ = *ln++))
- ;
- if (path < lp + PATH_MAX) {
- object = _dl_tryload_shlib(lp, type);
- if (object)
- return(object);
+ 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",
+ sod.sod_name, sod.sod_major,
+ sod.sod_minor, req_sod.sod_minor);
+ object = _dl_tryload_shlib(hint, type);
+ if (object != NULL) {
+ _dl_free((char *)sod.sod_name);
+ return (object);
+ }
}
- if (*pp) /* Try curdir if ':' at end */
- pp++;
- else
- pp = 0;
}
- _dl_build_sod(libname, &sodp);
- if ((hint = _dl_findhint((char *)sodp.sod_name, sodp.sod_major,
- sodp.sod_minor, NULL)) != NULL) {
+ /* 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",
+ sod.sod_name, sod.sod_major,
+ sod.sod_minor, req_sod.sod_minor);
object = _dl_tryload_shlib(hint, type);
- return(object);
+ if (object != NULL) {
+ _dl_free((char *)sod.sod_name);
+ return(object);
+ }
}
- /*
- * Check '/usr/lib'
- */
- _dl_strlcpy(lp, "/usr/lib/", sizeof(lp));
- path = lp + sizeof("/usr/lib/") - 1;
- while (path < lp + PATH_MAX && (*path++ = *libname++))
- ;
- if (path < lp + PATH_MAX) {
- object = _dl_tryload_shlib(lp, type);
- if (object)
- 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);
}
@@ -165,10 +334,13 @@ _dl_load_list_free(struct load_list *load_list)
}
}
+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_munmap((void *)object->load_addr, object->load_size);
_dl_remove_object(object);
@@ -176,7 +348,7 @@ _dl_unload_shlib(elf_object_t *object)
}
-elf_object_t *
+static elf_object_t *
_dl_tryload_shlib(const char *libname, int type)
{
int libfile, i, align = _dl_pagesz - 1;