[Midnightbsd-cvs] src: rtld.c: Merge
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Thu Nov 20 13:09:18 EST 2008
Log Message:
-----------
Merge
Modified Files:
--------------
src/libexec/rtld-elf:
rtld.c (r1.4 -> r1.5)
-------------- next part --------------
Index: rtld.c
===================================================================
RCS file: /home/cvs/src/libexec/rtld-elf/rtld.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -L libexec/rtld-elf/rtld.c -L libexec/rtld-elf/rtld.c -u -r1.4 -r1.5
--- libexec/rtld-elf/rtld.c
+++ libexec/rtld-elf/rtld.c
@@ -23,7 +23,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: src/libexec/rtld-elf/rtld.c,v 1.106.2.2 2005/12/30 22:13:56 marcel Exp $
+ * $FreeBSD: src/libexec/rtld-elf/rtld.c,v 1.124 2007/05/17 18:00:27 csjp Exp $
*/
/*
@@ -40,6 +40,8 @@
#include <sys/mount.h>
#include <sys/mman.h>
#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/ktrace.h>
#include <dlfcn.h>
#include <err.h>
@@ -80,10 +82,11 @@
* Function declarations.
*/
static const char *basename(const char *);
-static void die(void);
+static void die(void) __dead2;
static void digest_dynamic(Obj_Entry *, int);
static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
static Obj_Entry *dlcheck(void *);
+static Obj_Entry *do_load_object(int, const char *, char *, struct stat *);
static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
static bool donelist_check(DoneList *, const Obj_Entry *);
static void errmsg_restore(char *);
@@ -92,17 +95,16 @@
static char *find_library(const char *, const Obj_Entry *);
static const char *gethints(void);
static void init_dag(Obj_Entry *);
-static void init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *);
+static void init_dag1(Obj_Entry *, Obj_Entry *, DoneList *);
static void init_rtld(caddr_t);
-static void initlist_add_neededs(Needed_Entry *needed, Objlist *list);
-static void initlist_add_objects(Obj_Entry *obj, Obj_Entry **tail,
- Objlist *list);
+static void initlist_add_neededs(Needed_Entry *, Objlist *);
+static void initlist_add_objects(Obj_Entry *, Obj_Entry **, Objlist *);
static bool is_exported(const Elf_Sym *);
static void linkmap_add(Obj_Entry *);
static void linkmap_delete(Obj_Entry *);
static int load_needed_objects(Obj_Entry *);
static int load_preload_objects(void);
-static Obj_Entry *load_object(char *);
+static Obj_Entry *load_object(const char *, const Obj_Entry *);
static Obj_Entry *obj_from_addr(const void *);
static void objlist_call_fini(Objlist *);
static void objlist_call_init(Objlist *);
@@ -118,19 +120,27 @@
static int rtld_dirname(const char *, char *);
static void rtld_exit(void);
static char *search_library_path(const char *, const char *);
-static const void **get_program_var_addr(const char *name);
+static const void **get_program_var_addr(const char *);
static void set_program_var(const char *, const void *);
-static const Elf_Sym *symlook_default(const char *, unsigned long hash,
- const Obj_Entry *refobj, const Obj_Entry **defobj_out, bool in_plt);
-static const Elf_Sym *symlook_list(const char *, unsigned long,
- Objlist *, const Obj_Entry **, bool in_plt, DoneList *);
-static void trace_loaded_objects(Obj_Entry *obj);
+static const Elf_Sym *symlook_default(const char *, unsigned long,
+ const Obj_Entry *, const Obj_Entry **, const Ver_Entry *, int);
+static const Elf_Sym *symlook_list(const char *, unsigned long, const Objlist *,
+ const Obj_Entry **, const Ver_Entry *, int, DoneList *);
+static const Elf_Sym *symlook_needed(const char *, unsigned long,
+ const Needed_Entry *, const Obj_Entry **, const Ver_Entry *,
+ int, DoneList *);
+static void trace_loaded_objects(Obj_Entry *);
static void unlink_object(Obj_Entry *);
static void unload_object(Obj_Entry *);
static void unref_dag(Obj_Entry *);
static void ref_dag(Obj_Entry *);
+static int rtld_verify_versions(const Objlist *);
+static int rtld_verify_object_versions(Obj_Entry *);
+static void object_add_name(Obj_Entry *, const char *);
+static int object_match_name(const Obj_Entry *, const char *);
+static void ld_utrace_log(int, void *, void *, size_t, int, const char *);
-void r_debug_state(struct r_debug*, struct link_map*);
+void r_debug_state(struct r_debug *, struct link_map *);
/*
* Data declarations.
@@ -148,11 +158,13 @@
static char *ld_preload; /* Environment variable for libraries to
load first */
static char *ld_tracing; /* Called from ldd to print libs */
+static char *ld_utrace; /* Use utrace() to log events. */
static Obj_Entry *obj_list; /* Head of linked list of shared objects */
static Obj_Entry **obj_tail; /* Link field of last object in list */
static Obj_Entry *obj_main; /* The main program shared object */
static Obj_Entry obj_rtld; /* The dynamic linker shared object */
static unsigned int obj_count; /* Number of objects in obj_list */
+static unsigned int obj_loads; /* Number of objects in obj_list */
static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */
STAILQ_HEAD_INITIALIZER(list_global);
@@ -182,6 +194,7 @@
(func_ptr_type) &dlerror,
(func_ptr_type) &dlopen,
(func_ptr_type) &dlsym,
+ (func_ptr_type) &dlvsym,
(func_ptr_type) &dladdr,
(func_ptr_type) &dllockinit,
(func_ptr_type) &dlinfo,
@@ -192,6 +205,7 @@
(func_ptr_type) &__tls_get_addr,
(func_ptr_type) &_rtld_allocate_tls,
(func_ptr_type) &_rtld_free_tls,
+ (func_ptr_type) &dl_iterate_phdr,
NULL
};
@@ -222,6 +236,53 @@
(dlp)->num_alloc = obj_count, \
(dlp)->num_used = 0)
+#define UTRACE_DLOPEN_START 1
+#define UTRACE_DLOPEN_STOP 2
+#define UTRACE_DLCLOSE_START 3
+#define UTRACE_DLCLOSE_STOP 4
+#define UTRACE_LOAD_OBJECT 5
+#define UTRACE_UNLOAD_OBJECT 6
+#define UTRACE_ADD_RUNDEP 7
+#define UTRACE_PRELOAD_FINISHED 8
+#define UTRACE_INIT_CALL 9
+#define UTRACE_FINI_CALL 10
+
+struct utrace_rtld {
+ char sig[4]; /* 'RTLD' */
+ int event;
+ void *handle;
+ void *mapbase; /* Used for 'parent' and 'init/fini' */
+ size_t mapsize;
+ int refcnt; /* Used for 'mode' */
+ char name[MAXPATHLEN];
+};
+
+#define LD_UTRACE(e, h, mb, ms, r, n) do { \
+ if (ld_utrace != NULL) \
+ ld_utrace_log(e, h, mb, ms, r, n); \
+} while (0)
+
+static void
+ld_utrace_log(int event, void *handle, void *mapbase, size_t mapsize,
+ int refcnt, const char *name)
+{
+ struct utrace_rtld ut;
+
+ ut.sig[0] = 'R';
+ ut.sig[1] = 'T';
+ ut.sig[2] = 'L';
+ ut.sig[3] = 'D';
+ ut.event = event;
+ ut.handle = handle;
+ ut.mapbase = mapbase;
+ ut.mapsize = mapsize;
+ ut.refcnt = refcnt;
+ bzero(ut.name, sizeof(ut.name));
+ if (name)
+ strlcpy(ut.name, name, sizeof(ut.name));
+ utrace(&ut, sizeof(ut));
+}
+
/*
* Main entry point for dynamic linking. The first argument is the
* stack pointer. The stack is expected to be laid out as described
@@ -290,17 +351,28 @@
trust = !issetugid();
ld_bind_now = getenv(LD_ "BIND_NOW");
- if (trust) {
- ld_debug = getenv(LD_ "DEBUG");
- libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL;
- libmap_override = getenv(LD_ "LIBMAP");
- ld_library_path = getenv(LD_ "LIBRARY_PATH");
- ld_preload = getenv(LD_ "PRELOAD");
- dangerous_ld_env = libmap_disable || (libmap_override != NULL) ||
- (ld_library_path != NULL) || (ld_preload != NULL);
- } else
- dangerous_ld_env = 0;
+ /*
+ * If the process is tainted, then we un-set the dangerous environment
+ * variables. The process will be marked as tainted until setuid(2)
+ * is called. If any child process calls setuid(2) we do not want any
+ * future processes to honor the potentially un-safe variables.
+ */
+ if (!trust) {
+ unsetenv(LD_ "PRELOAD");
+ unsetenv(LD_ "LIBMAP");
+ unsetenv(LD_ "LIBRARY_PATH");
+ unsetenv(LD_ "LIBMAP_DISABLE");
+ unsetenv(LD_ "DEBUG");
+ }
+ ld_debug = getenv(LD_ "DEBUG");
+ libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL;
+ libmap_override = getenv(LD_ "LIBMAP");
+ ld_library_path = getenv(LD_ "LIBRARY_PATH");
+ ld_preload = getenv(LD_ "PRELOAD");
+ dangerous_ld_env = libmap_disable || (libmap_override != NULL) ||
+ (ld_library_path != NULL) || (ld_preload != NULL);
ld_tracing = getenv(LD_ "TRACE_LOADED_OBJECTS");
+ ld_utrace = getenv(LD_ "UTRACE");
if (ld_debug != NULL && *ld_debug != '\0')
debug = 1;
@@ -363,6 +435,7 @@
*obj_tail = obj_main;
obj_tail = &obj_main->next;
obj_count++;
+ obj_loads++;
/* Make sure we don't call the main program's init and fini functions. */
obj_main->init = obj_main->fini = (Elf_Addr)NULL;
@@ -388,6 +461,10 @@
obj->refcount++;
}
+ dbg("checking for required versions");
+ if (rtld_verify_versions(&list_main) == -1 && !ld_tracing)
+ die();
+
if (ld_tracing) { /* We're done */
trace_loaded_objects(obj_main);
exit(0);
@@ -555,6 +632,7 @@
const Elf_Dyn *dynp;
Needed_Entry **needed_tail = &obj->needed;
const Elf_Dyn *dyn_rpath = NULL;
+ const Elf_Dyn *dyn_soname = NULL;
int plttype = DT_REL;
obj->bind_now = false;
@@ -616,6 +694,29 @@
obj->strsize = dynp->d_un.d_val;
break;
+ case DT_VERNEED:
+ obj->verneed = (const Elf_Verneed *) (obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
+ case DT_VERNEEDNUM:
+ obj->verneednum = dynp->d_un.d_val;
+ break;
+
+ case DT_VERDEF:
+ obj->verdef = (const Elf_Verdef *) (obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
+ case DT_VERDEFNUM:
+ obj->verdefnum = dynp->d_un.d_val;
+ break;
+
+ case DT_VERSYM:
+ obj->versyms = (const Elf_Versym *)(obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
case DT_HASH:
{
const Elf_Hashelt *hashtab = (const Elf_Hashelt *)
@@ -661,7 +762,7 @@
break;
case DT_SONAME:
- /* Not used by the dynamic linker. */
+ dyn_soname = dynp;
break;
case DT_INIT:
@@ -715,6 +816,9 @@
if (dyn_rpath != NULL)
obj->rpath = obj->strtab + dyn_rpath->d_un.d_val;
+
+ if (dyn_soname != NULL)
+ object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val);
}
/*
@@ -902,11 +1006,12 @@
*/
const Elf_Sym *
find_symdef(unsigned long symnum, const Obj_Entry *refobj,
- const Obj_Entry **defobj_out, bool in_plt, SymCache *cache)
+ const Obj_Entry **defobj_out, int flags, SymCache *cache)
{
const Elf_Sym *ref;
const Elf_Sym *def;
const Obj_Entry *defobj;
+ const Ver_Entry *ventry;
const char *name;
unsigned long hash;
@@ -938,8 +1043,9 @@
_rtld_error("%s: Bogus symbol table entry %lu", refobj->path,
symnum);
}
+ ventry = fetch_ventry(refobj, symnum);
hash = elf_hash(name);
- def = symlook_default(name, hash, refobj, &defobj, in_plt);
+ def = symlook_default(name, hash, refobj, &defobj, ventry, flags);
} else {
def = ref;
defobj = refobj;
@@ -1164,18 +1270,9 @@
Needed_Entry *needed;
for (needed = obj->needed; needed != NULL; needed = needed->next) {
- const char *name = obj->strtab + needed->name;
- char *path = find_library(name, obj);
-
- needed->obj = NULL;
- if (path == NULL && !ld_tracing)
+ needed->obj = load_object(obj->strtab + needed->name, obj);
+ if (needed->obj == NULL && !ld_tracing)
return -1;
-
- if (path) {
- needed->obj = load_object(path);
- if (needed->obj == NULL && !ld_tracing)
- return -1; /* XXX - cleanup */
- }
}
}
@@ -1194,41 +1291,41 @@
p += strspn(p, delim);
while (*p != '\0') {
size_t len = strcspn(p, delim);
- char *path;
char savech;
savech = p[len];
p[len] = '\0';
- if ((path = find_library(p, NULL)) == NULL)
- return -1;
- if (load_object(path) == NULL)
+ if (load_object(p, NULL) == NULL)
return -1; /* XXX - cleanup */
p[len] = savech;
p += len;
p += strspn(p, delim);
}
+ LD_UTRACE(UTRACE_PRELOAD_FINISHED, NULL, NULL, 0, 0, NULL);
return 0;
}
/*
- * Load a shared object into memory, if it is not already loaded. The
- * argument must be a string allocated on the heap. This function assumes
- * responsibility for freeing it when necessary.
+ * Load a shared object into memory, if it is not already loaded.
*
* Returns a pointer to the Obj_Entry for the object. Returns NULL
* on failure.
*/
static Obj_Entry *
-load_object(char *path)
+load_object(const char *name, const Obj_Entry *refobj)
{
Obj_Entry *obj;
int fd = -1;
struct stat sb;
- struct statfs fs;
+ char *path;
for (obj = obj_list->next; obj != NULL; obj = obj->next)
- if (strcmp(obj->path, path) == 0)
- break;
+ if (object_match_name(obj, name))
+ return obj;
+
+ path = find_library(name, refobj);
+ if (path == NULL)
+ return NULL;
/*
* If we didn't find a match by pathname, open the file and check
@@ -1238,63 +1335,80 @@
* To avoid a race, we open the file and use fstat() rather than
* using stat().
*/
- if (obj == NULL) {
- if ((fd = open(path, O_RDONLY)) == -1) {
- _rtld_error("Cannot open \"%s\"", path);
- return NULL;
- }
- if (fstat(fd, &sb) == -1) {
- _rtld_error("Cannot fstat \"%s\"", path);
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ _rtld_error("Cannot open \"%s\"", path);
+ free(path);
+ return NULL;
+ }
+ if (fstat(fd, &sb) == -1) {
+ _rtld_error("Cannot fstat \"%s\"", path);
+ close(fd);
+ free(path);
+ return NULL;
+ }
+ for (obj = obj_list->next; obj != NULL; obj = obj->next) {
+ if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) {
close(fd);
- return NULL;
- }
- for (obj = obj_list->next; obj != NULL; obj = obj->next) {
- if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) {
- close(fd);
- break;
- }
+ break;
}
}
+ if (obj != NULL) {
+ object_add_name(obj, name);
+ free(path);
+ close(fd);
+ return obj;
+ }
- if (obj == NULL) { /* First use of this object, so we must map it in */
- /*
- * but first, make sure that environment variables haven't been
- * used to circumvent the noexec flag on a filesystem.
- */
- if (dangerous_ld_env) {
- if (fstatfs(fd, &fs) != 0) {
- _rtld_error("Cannot fstatfs \"%s\"", path);
- close(fd);
- return NULL;
- }
- if (fs.f_flags & MNT_NOEXEC) {
- _rtld_error("Cannot execute objects on %s\n", fs.f_mntonname);
- close(fd);
+ /* First use of this object, so we must map it in */
+ obj = do_load_object(fd, name, path, &sb);
+ if (obj == NULL)
+ free(path);
+ close(fd);
+
+ return obj;
+}
+
+static Obj_Entry *
+do_load_object(int fd, const char *name, char *path, struct stat *sbp)
+{
+ Obj_Entry *obj;
+ struct statfs fs;
+
+ /*
+ * but first, make sure that environment variables haven't been
+ * used to circumvent the noexec flag on a filesystem.
+ */
+ if (dangerous_ld_env) {
+ if (fstatfs(fd, &fs) != 0) {
+ _rtld_error("Cannot fstatfs \"%s\"", path);
return NULL;
- }
}
- dbg("loading \"%s\"", path);
- obj = map_object(fd, path, &sb);
- close(fd);
- if (obj == NULL) {
- free(path);
+ if (fs.f_flags & MNT_NOEXEC) {
+ _rtld_error("Cannot execute objects on %s\n", fs.f_mntonname);
return NULL;
}
+ }
+ dbg("loading \"%s\"", path);
+ obj = map_object(fd, path, sbp);
+ if (obj == NULL)
+ return NULL;
+
+ object_add_name(obj, name);
+ obj->path = path;
+ digest_dynamic(obj, 0);
- obj->path = path;
- digest_dynamic(obj, 0);
+ *obj_tail = obj;
+ obj_tail = &obj->next;
+ obj_count++;
+ obj_loads++;
+ linkmap_add(obj); /* for GDB & dlinfo() */
- *obj_tail = obj;
- obj_tail = &obj->next;
- obj_count++;
- linkmap_add(obj); /* for GDB & dlinfo() */
-
- dbg(" %p .. %p: %s", obj->mapbase,
- obj->mapbase + obj->mapsize - 1, obj->path);
- if (obj->textrel)
- dbg(" WARNING: %s has impure text", obj->path);
- } else
- free(path);
+ dbg(" %p .. %p: %s", obj->mapbase,
+ obj->mapbase + obj->mapsize - 1, obj->path);
+ if (obj->textrel)
+ dbg(" WARNING: %s has impure text", obj->path);
+ LD_UTRACE(UTRACE_LOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
+ obj->path);
return obj;
}
@@ -1333,6 +1447,8 @@
if (elm->obj->refcount == 0) {
dbg("calling fini function for %s at %p", elm->obj->path,
(void *)elm->obj->fini);
+ LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)elm->obj->fini, 0, 0,
+ elm->obj->path);
call_initfini_pointer(elm->obj, elm->obj->fini);
}
}
@@ -1358,6 +1474,8 @@
STAILQ_FOREACH(elm, list, link) {
dbg("calling init function for %s at %p", elm->obj->path,
(void *)elm->obj->init);
+ LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)elm->obj->init, 0, 0,
+ elm->obj->path);
call_initfini_pointer(elm->obj, elm->obj->init);
}
errmsg_restore(saved_msg);
@@ -1631,6 +1749,8 @@
wlock_release(rtld_bind_lock, lockstate);
return -1;
}
+ LD_UTRACE(UTRACE_DLCLOSE_START, handle, NULL, 0, root->dl_refcount,
+ root->path);
/* Unreference the object and its dependencies. */
root->dl_refcount--;
@@ -1652,6 +1772,7 @@
unload_object(root);
GDB_STATE(RT_CONSISTENT,NULL);
}
+ LD_UTRACE(UTRACE_DLCLOSE_STOP, handle, NULL, 0, 0, NULL);
wlock_release(rtld_bind_lock, lockstate);
return 0;
}
@@ -1694,6 +1815,7 @@
Objlist initlist;
int result, lockstate;
+ LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name);
ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1";
if (ld_tracing != NULL)
environ = (char **)*get_program_var_addr("environ");
@@ -1709,9 +1831,7 @@
obj = obj_main;
obj->refcount++;
} else {
- char *path = find_library(name, obj_main);
- if (path != NULL)
- obj = load_object(path);
+ obj = load_object(name, obj_main);
}
if (obj) {
@@ -1721,14 +1841,14 @@
mode &= RTLD_MODEMASK;
if (*old_obj_tail != NULL) { /* We loaded something new. */
assert(*old_obj_tail == obj);
-
result = load_needed_objects(obj);
+ init_dag(obj);
+ if (result != -1)
+ result = rtld_verify_versions(&obj->dagmembers);
if (result != -1 && ld_tracing)
goto trace;
-
if (result == -1 ||
- (init_dag(obj), relocate_objects(obj, mode == RTLD_NOW,
- &obj_rtld)) == -1) {
+ (relocate_objects(obj, mode == RTLD_NOW, &obj_rtld)) == -1) {
obj->dl_refcount--;
unref_dag(obj);
if (obj->refcount == 0)
@@ -1748,6 +1868,8 @@
}
}
+ LD_UTRACE(UTRACE_DLOPEN_STOP, obj, NULL, 0, obj ? obj->dl_refcount : 0,
+ name);
GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL);
/* Call the init functions with no locks held. */
@@ -1763,46 +1885,46 @@
exit(0);
}
-void *
-dlsym(void *handle, const char *name)
+static void *
+do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
+ int flags)
{
- const Obj_Entry *obj;
- unsigned long hash;
+ DoneList donelist;
+ const Obj_Entry *obj, *defobj;
const Elf_Sym *def;
- const Obj_Entry *defobj;
+ unsigned long hash;
int lockstate;
hash = elf_hash(name);
def = NULL;
defobj = NULL;
+ flags |= SYMLOOK_IN_PLT;
lockstate = rlock_acquire(rtld_bind_lock);
if (handle == NULL || handle == RTLD_NEXT ||
handle == RTLD_DEFAULT || handle == RTLD_SELF) {
- void *retaddr;
- retaddr = __builtin_return_address(0); /* __GNUC__ only */
if ((obj = obj_from_addr(retaddr)) == NULL) {
_rtld_error("Cannot determine caller's shared object");
rlock_release(rtld_bind_lock, lockstate);
return NULL;
}
if (handle == NULL) { /* Just the caller's shared object. */
- def = symlook_obj(name, hash, obj, true);
+ def = symlook_obj(name, hash, obj, ve, flags);
defobj = obj;
} else if (handle == RTLD_NEXT || /* Objects after caller's */
handle == RTLD_SELF) { /* ... caller included */
if (handle == RTLD_NEXT)
obj = obj->next;
for (; obj != NULL; obj = obj->next) {
- if ((def = symlook_obj(name, hash, obj, true)) != NULL) {
+ if ((def = symlook_obj(name, hash, obj, ve, flags)) != NULL) {
defobj = obj;
break;
}
}
} else {
assert(handle == RTLD_DEFAULT);
- def = symlook_default(name, hash, obj, &defobj, true);
+ def = symlook_default(name, hash, obj, &defobj, ve, flags);
}
} else {
if ((obj = dlcheck(handle)) == NULL) {
@@ -1810,20 +1932,20 @@
return NULL;
}
+ donelist_init(&donelist);
if (obj->mainprog) {
- DoneList donelist;
-
/* Search main program and all libraries loaded by it. */
- donelist_init(&donelist);
- def = symlook_list(name, hash, &list_main, &defobj, true,
- &donelist);
+ def = symlook_list(name, hash, &list_main, &defobj, ve, flags,
+ &donelist);
} else {
- /*
- * XXX - This isn't correct. The search should include the whole
- * DAG rooted at the given object.
- */
- def = symlook_obj(name, hash, obj, true);
- defobj = obj;
+ Needed_Entry fake;
+
+ /* Search the whole DAG rooted at the given object. */
+ fake.next = NULL;
+ fake.obj = (Obj_Entry *)obj;
+ fake.name = 0;
+ def = symlook_needed(name, hash, &fake, &defobj, ve, flags,
+ &donelist);
}
}
@@ -1849,6 +1971,26 @@
return NULL;
}
+void *
+dlsym(void *handle, const char *name)
+{
+ return do_dlsym(handle, name, __builtin_return_address(0), NULL,
+ SYMLOOK_DLSYM);
+}
+
+void *
+dlvsym(void *handle, const char *name, const char *version)
+{
+ Ver_Entry ventry;
+
+ ventry.name = version;
+ ventry.file = NULL;
+ ventry.hash = elf_hash(version);
+ ventry.flags= 0;
+ return do_dlsym(handle, name, __builtin_return_address(0), &ventry,
+ SYMLOOK_DLSYM);
+}
+
int
dladdr(const void *addr, Dl_info *info)
{
@@ -1951,6 +2093,37 @@
return (error);
}
+int
+dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param)
+{
+ struct dl_phdr_info phdr_info;
+ const Obj_Entry *obj;
+ int error, lockstate;
+
+ lockstate = rlock_acquire(rtld_bind_lock);
+
+ error = 0;
+
+ for (obj = obj_list; obj != NULL; obj = obj->next) {
+ phdr_info.dlpi_addr = (Elf_Addr)obj->relocbase;
+ phdr_info.dlpi_name = STAILQ_FIRST(&obj->names) ?
+ STAILQ_FIRST(&obj->names)->name : obj->path;
+ phdr_info.dlpi_phdr = obj->phdr;
+ phdr_info.dlpi_phnum = obj->phsize / sizeof(obj->phdr[0]);
+ phdr_info.dlpi_tls_modid = obj->tlsindex;
+ phdr_info.dlpi_tls_data = obj->tlsinit;
+ phdr_info.dlpi_adds = obj_loads;
+ phdr_info.dlpi_subs = obj_loads - obj_count;
+
+ if ((error = callback(&phdr_info, sizeof phdr_info, param)) != 0)
+ break;
+
+ }
+ rlock_release(rtld_bind_lock, lockstate);
+
+ return (error);
+}
+
struct fill_search_info_args {
int request;
unsigned int flags;
@@ -2163,7 +2336,7 @@
for (obj = obj_main; obj != NULL; obj = obj->next) {
const Elf_Sym *def;
- if ((def = symlook_obj(name, hash, obj, false)) != NULL) {
+ if ((def = symlook_obj(name, hash, obj, NULL, 0)) != NULL) {
const void **addr;
addr = (const void **)(obj->relocbase + def->st_value);
@@ -2196,8 +2369,8 @@
* defining object via the reference parameter DEFOBJ_OUT.
*/
static const Elf_Sym *
-symlook_default(const char *name, unsigned long hash,
- const Obj_Entry *refobj, const Obj_Entry **defobj_out, bool in_plt)
+symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj,
+ const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags)
{
DoneList donelist;
const Elf_Sym *def;
@@ -2211,7 +2384,7 @@
/* Look first in the referencing object if linked symbolically. */
if (refobj->symbolic && !donelist_check(&donelist, refobj)) {
- symp = symlook_obj(name, hash, refobj, in_plt);
+ symp = symlook_obj(name, hash, refobj, ventry, flags);
if (symp != NULL) {
def = symp;
defobj = refobj;
@@ -2220,7 +2393,8 @@
/* Search all objects loaded at program start up. */
if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
- symp = symlook_list(name, hash, &list_main, &obj, in_plt, &donelist);
+ symp = symlook_list(name, hash, &list_main, &obj, ventry, flags,
+ &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -2232,8 +2406,8 @@
STAILQ_FOREACH(elm, &list_global, link) {
if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK)
break;
- symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt,
- &donelist);
+ symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, ventry,
+ flags, &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -2245,8 +2419,8 @@
STAILQ_FOREACH(elm, &refobj->dldags, link) {
if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK)
break;
- symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt,
- &donelist);
+ symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, ventry,
+ flags, &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -2261,7 +2435,7 @@
* in the "exports" array can be resolved from the dynamic linker.
*/
if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
- symp = symlook_obj(name, hash, &obj_rtld, in_plt);
+ symp = symlook_obj(name, hash, &obj_rtld, ventry, flags);
if (symp != NULL && is_exported(symp)) {
def = symp;
defobj = &obj_rtld;
@@ -2274,8 +2448,9 @@
}
static const Elf_Sym *
-symlook_list(const char *name, unsigned long hash, Objlist *objlist,
- const Obj_Entry **defobj_out, bool in_plt, DoneList *dlp)
+symlook_list(const char *name, unsigned long hash, const Objlist *objlist,
+ const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags,
+ DoneList *dlp)
{
const Elf_Sym *symp;
const Elf_Sym *def;
@@ -2287,7 +2462,7 @@
STAILQ_FOREACH(elm, objlist, link) {
if (donelist_check(dlp, elm->obj))
continue;
- if ((symp = symlook_obj(name, hash, elm->obj, in_plt)) != NULL) {
+ if ((symp = symlook_obj(name, hash, elm->obj, ventry, flags)) != NULL) {
if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) {
def = symp;
defobj = elm->obj;
@@ -2302,38 +2477,174 @@
}
/*
- * Search the symbol table of a single shared object for a symbol of
- * the given name. Returns a pointer to the symbol, or NULL if no
+ * Search the symbol table of a shared object and all objects needed
+ * by it for a symbol of the given name. Search order is
+ * breadth-first. Returns a pointer to the symbol, or NULL if no
* definition was found.
+ */
+static const Elf_Sym *
+symlook_needed(const char *name, unsigned long hash, const Needed_Entry *needed,
+ const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags,
+ DoneList *dlp)
+{
+ const Elf_Sym *def, *def_w;
+ const Needed_Entry *n;
+ const Obj_Entry *obj, *defobj, *defobj1;
+
+ def = def_w = NULL;
+ defobj = NULL;
+ for (n = needed; n != NULL; n = n->next) {
+ if ((obj = n->obj) == NULL ||
+ donelist_check(dlp, obj) ||
+ (def = symlook_obj(name, hash, obj, ventry, flags)) == NULL)
+ continue;
+ defobj = obj;
+ if (ELF_ST_BIND(def->st_info) != STB_WEAK) {
+ *defobj_out = defobj;
+ return (def);
+ }
+ }
+ /*
+ * There we come when either symbol definition is not found in
+ * directly needed objects, or found symbol is weak.
+ */
+ for (n = needed; n != NULL; n = n->next) {
+ if ((obj = n->obj) == NULL)
+ continue;
+ def_w = symlook_needed(name, hash, obj->needed, &defobj1,
+ ventry, flags, dlp);
+ if (def_w == NULL)
+ continue;
+ if (def == NULL || ELF_ST_BIND(def_w->st_info) != STB_WEAK) {
+ def = def_w;
+ defobj = defobj1;
+ }
+ if (ELF_ST_BIND(def_w->st_info) != STB_WEAK)
+ break;
+ }
+ if (def != NULL)
+ *defobj_out = defobj;
+ return (def);
+}
+
+/*
+ * Search the symbol table of a single shared object for a symbol of
+ * the given name and version, if requested. Returns a pointer to the
+ * symbol, or NULL if no definition was found.
*
* The symbol's hash value is passed in for efficiency reasons; that
* eliminates many recomputations of the hash value.
*/
const Elf_Sym *
symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
- bool in_plt)
+ const Ver_Entry *ventry, int flags)
{
- if (obj->buckets != NULL) {
- unsigned long symnum = obj->buckets[hash % obj->nbuckets];
+ unsigned long symnum;
+ const Elf_Sym *vsymp;
+ Elf_Versym verndx;
+ int vcount;
+
+ if (obj->buckets == NULL)
+ return NULL;
- while (symnum != STN_UNDEF) {
- const Elf_Sym *symp;
- const char *strp;
+ vsymp = NULL;
+ vcount = 0;
+ symnum = obj->buckets[hash % obj->nbuckets];
+
+ for (; symnum != STN_UNDEF; symnum = obj->chains[symnum]) {
+ const Elf_Sym *symp;
+ const char *strp;
- if (symnum >= obj->nchains)
+ if (symnum >= obj->nchains)
return NULL; /* Bad object */
- symp = obj->symtab + symnum;
- strp = obj->strtab + symp->st_name;
- if (name[0] == strp[0] && strcmp(name, strp) == 0)
- return symp->st_shndx != SHN_UNDEF ||
- (!in_plt && symp->st_value != 0 &&
- ELF_ST_TYPE(symp->st_info) == STT_FUNC) ? symp : NULL;
+ symp = obj->symtab + symnum;
+ strp = obj->strtab + symp->st_name;
+
+ switch (ELF_ST_TYPE(symp->st_info)) {
+ case STT_FUNC:
+ case STT_NOTYPE:
+ case STT_OBJECT:
+ if (symp->st_value == 0)
+ continue;
+ /* fallthrough */
+ case STT_TLS:
+ if (symp->st_shndx != SHN_UNDEF ||
+ ((flags & SYMLOOK_IN_PLT) == 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC))
+ break;
+ /* fallthrough */
+ default:
+ continue;
+ }
+ if (name[0] != strp[0] || strcmp(name, strp) != 0)
+ continue;
- symnum = obj->chains[symnum];
+ if (ventry == NULL) {
+ if (obj->versyms != NULL) {
+ verndx = VER_NDX(obj->versyms[symnum]);
+ if (verndx > obj->vernum) {
+ _rtld_error("%s: symbol %s references wrong version %d",
+ obj->path, obj->strtab + symnum, verndx);
+ continue;
+ }
+ /*
+ * If we are not called from dlsym (i.e. this is a normal
+ * relocation from unversioned binary, accept the symbol
+ * immediately if it happens to have first version after
+ * this shared object became versioned. Otherwise, if
+ * symbol is versioned and not hidden, remember it. If it
+ * is the only symbol with this name exported by the
+ * shared object, it will be returned as a match at the
+ * end of the function. If symbol is global (verndx < 2)
+ * accept it unconditionally.
+ */
+ if ((flags & SYMLOOK_DLSYM) == 0 && verndx == VER_NDX_GIVEN)
+ return symp;
+ else if (verndx >= VER_NDX_GIVEN) {
+ if ((obj->versyms[symnum] & VER_NDX_HIDDEN) == 0) {
+ if (vsymp == NULL)
+ vsymp = symp;
+ vcount ++;
+ }
+ continue;
+ }
+ }
+ return symp;
+ } else {
+ if (obj->versyms == NULL) {
+ if (object_match_name(obj, ventry->name)) {
+ _rtld_error("%s: object %s should provide version %s for "
+ "symbol %s", obj_rtld.path, obj->path, ventry->name,
+ obj->strtab + symnum);
+ continue;
+ }
+ } else {
+ verndx = VER_NDX(obj->versyms[symnum]);
+ if (verndx > obj->vernum) {
+ _rtld_error("%s: symbol %s references wrong version %d",
+ obj->path, obj->strtab + symnum, verndx);
+ continue;
+ }
+ if (obj->vertab[verndx].hash != ventry->hash ||
+ strcmp(obj->vertab[verndx].name, ventry->name)) {
+ /*
+ * Version does not match. Look if this is a global symbol
+ * and if it is not hidden. If global symbol (verndx < 2)
+ * is available, use it. Do not return symbol if we are
+ * called by dlvsym, because dlvsym looks for a specific
+ * version and default one is not what dlvsym wants.
+ */
+ if ((flags & SYMLOOK_DLSYM) ||
+ (obj->versyms[symnum] & VER_NDX_HIDDEN) ||
+ (verndx >= VER_NDX_GIVEN))
+ continue;
+ }
+ }
+ return symp;
}
}
- return NULL;
+ return (vcount == 1) ? vsymp : NULL;
}
static void
@@ -2454,6 +2765,8 @@
linkp = &obj_list->next;
while ((obj = *linkp) != NULL) {
if (obj->refcount == 0) {
+ LD_UTRACE(UTRACE_UNLOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
+ obj->path);
dbg("unloading \"%s\"", obj->path);
munmap(obj->mapbase, obj->mapsize);
linkmap_delete(obj);
@@ -2476,7 +2789,7 @@
objlist_remove(&list_global, root);
/* Remove the object from all objects' DAG lists. */
- STAILQ_FOREACH(elm, &root->dagmembers , link) {
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
objlist_remove(&elm->obj->dldags, root);
if (elm->obj != root)
unlink_object(elm->obj);
@@ -2489,7 +2802,7 @@
{
Objlist_Entry *elm;
- STAILQ_FOREACH(elm, &root->dagmembers , link)
+ STAILQ_FOREACH(elm, &root->dagmembers, link)
elm->obj->refcount++;
}
@@ -2498,7 +2811,7 @@
{
Objlist_Entry *elm;
- STAILQ_FOREACH(elm, &root->dagmembers , link)
+ STAILQ_FOREACH(elm, &root->dagmembers, link)
elm->obj->refcount--;
}
@@ -2542,56 +2855,46 @@
/* XXX not sure what variants to use for arm. */
-#if defined(__ia64__) || defined(__alpha__) || defined(__powerpc__)
+#if defined(__ia64__) || defined(__powerpc__)
/*
* Allocate Static TLS using the Variant I method.
*/
void *
-allocate_tls(Obj_Entry *objs, void *oldtls, size_t tcbsize, size_t tcbalign)
+allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
{
Obj_Entry *obj;
- size_t size;
- char *tls;
- Elf_Addr *dtv, *olddtv;
+ char *tcb;
+ Elf_Addr **tls;
+ Elf_Addr *dtv;
Elf_Addr addr;
int i;
- size = tls_static_space;
-
- tls = malloc(size);
- dtv = calloc(1, (tls_max_index + 2) * sizeof(Elf_Addr));
-
- *(Elf_Addr**) tls = dtv;
-
- dtv[0] = tls_dtv_generation;
- dtv[1] = tls_max_index;
-
- if (oldtls) {
- /*
- * Copy the static TLS block over whole.
- */
- memcpy(tls + tcbsize, oldtls + tcbsize, tls_static_space - tcbsize);
+ if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
+ return (oldtcb);
- /*
- * If any dynamic TLS blocks have been created tls_get_addr(),
- * move them over.
- */
- olddtv = *(Elf_Addr**) oldtls;
- for (i = 0; i < olddtv[1]; i++) {
- if (olddtv[i+2] < (Elf_Addr)oldtls ||
- olddtv[i+2] > (Elf_Addr)oldtls + tls_static_space) {
- dtv[i+2] = olddtv[i+2];
- olddtv[i+2] = 0;
+ assert(tcbsize >= TLS_TCB_SIZE);
+ tcb = calloc(1, tls_static_space - TLS_TCB_SIZE + tcbsize);
+ tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE);
+
+ if (oldtcb != NULL) {
+ memcpy(tls, oldtcb, tls_static_space);
+ free(oldtcb);
+
+ /* Adjust the DTV. */
+ dtv = tls[0];
+ for (i = 0; i < dtv[1]; i++) {
+ if (dtv[i+2] >= (Elf_Addr)oldtcb &&
+ dtv[i+2] < (Elf_Addr)oldtcb + tls_static_space) {
+ dtv[i+2] = dtv[i+2] - (Elf_Addr)oldtcb + (Elf_Addr)tls;
}
}
-
- /*
- * We assume that all tls blocks are allocated with the same
- * size and alignment.
- */
- free_tls(oldtls, tcbsize, tcbalign);
} else {
+ dtv = calloc(tls_max_index + 2, sizeof(Elf_Addr));
+ tls[0] = dtv;
+ dtv[0] = tls_dtv_generation;
+ dtv[1] = tls_max_index;
+
for (obj = objs; obj; obj = obj->next) {
if (obj->tlsoffset) {
addr = (Elf_Addr)tls + obj->tlsoffset;
@@ -2605,35 +2908,30 @@
}
}
- return tls;
+ return (tcb);
}
void
-free_tls(void *tls, size_t tcbsize, size_t tcbalign)
+free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
{
- size_t size;
- Elf_Addr* dtv;
- int dtvsize, i;
+ Elf_Addr *dtv;
Elf_Addr tlsstart, tlsend;
+ int dtvsize, i;
- /*
- * Figure out the size of the initial TLS block so that we can
- * find stuff which __tls_get_addr() allocated dynamically.
- */
- size = tls_static_space;
+ assert(tcbsize >= TLS_TCB_SIZE);
+
+ tlsstart = (Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE;
+ tlsend = tlsstart + tls_static_space;
- dtv = ((Elf_Addr**)tls)[0];
+ dtv = *(Elf_Addr **)tlsstart;
dtvsize = dtv[1];
- tlsstart = (Elf_Addr) tls;
- tlsend = tlsstart + size;
for (i = 0; i < dtvsize; i++) {
- if (dtv[i+2] && (dtv[i+2] < tlsstart || dtv[i+2] > tlsend)) {
- free((void*) dtv[i+2]);
+ if (dtv[i+2] && (dtv[i+2] < tlsstart || dtv[i+2] >= tlsend)) {
+ free((void*)dtv[i+2]);
}
}
-
- free((void*) tlsstart);
- free((void*) dtv);
+ free(dtv);
+ free(tcb);
}
#endif
@@ -2657,7 +2955,7 @@
size = round(tls_static_space, tcbalign);
assert(tcbsize >= 2*sizeof(Elf_Addr));
- tls = malloc(size + tcbsize);
+ tls = calloc(1, size + tcbsize);
dtv = calloc(1, (tls_max_index + 2) * sizeof(Elf_Addr));
segbase = (Elf_Addr)(tls + size);
@@ -2811,7 +3109,7 @@
* block, we give our space back to the 'allocator'. This is a
* simplistic workaround to allow libGL.so.1 to be loaded and
* unloaded multiple times. We only handle the Variant II
- * mechanism for now - this really needs a proper allocator.
+ * mechanism for now - this really needs a proper allocator.
*/
if (calculate_tls_end(obj->tlsoffset, obj->tlssize)
== calculate_tls_end(tls_last_offset, tls_last_size)) {
@@ -2842,3 +3140,235 @@
free_tls(tcb, tcbsize, tcbalign);
wlock_release(rtld_bind_lock, lockstate);
}
+
+static void
+object_add_name(Obj_Entry *obj, const char *name)
+{
+ Name_Entry *entry;
+ size_t len;
+
+ len = strlen(name);
+ entry = malloc(sizeof(Name_Entry) + len);
+
+ if (entry != NULL) {
+ strcpy(entry->name, name);
+ STAILQ_INSERT_TAIL(&obj->names, entry, link);
+ }
+}
+
+static int
+object_match_name(const Obj_Entry *obj, const char *name)
+{
+ Name_Entry *entry;
+
+ STAILQ_FOREACH(entry, &obj->names, link) {
+ if (strcmp(name, entry->name) == 0)
+ return (1);
+ }
+ return (0);
+}
+
+static Obj_Entry *
+locate_dependency(const Obj_Entry *obj, const char *name)
+{
+ const Objlist_Entry *entry;
+ const Needed_Entry *needed;
+
+ STAILQ_FOREACH(entry, &list_main, link) {
+ if (object_match_name(entry->obj, name))
+ return entry->obj;
+ }
+
+ for (needed = obj->needed; needed != NULL; needed = needed->next) {
+ if (needed->obj == NULL)
+ continue;
+ if (object_match_name(needed->obj, name))
+ return needed->obj;
+ }
+ _rtld_error("%s: Unexpected inconsistency: dependency %s not found",
+ obj->path, name);
+ die();
+}
+
+static int
+check_object_provided_version(Obj_Entry *refobj, const Obj_Entry *depobj,
+ const Elf_Vernaux *vna)
+{
+ const Elf_Verdef *vd;
+ const char *vername;
+
+ vername = refobj->strtab + vna->vna_name;
+ vd = depobj->verdef;
+ if (vd == NULL) {
+ _rtld_error("%s: version %s required by %s not defined",
+ depobj->path, vername, refobj->path);
+ return (-1);
+ }
+ for (;;) {
+ if (vd->vd_version != VER_DEF_CURRENT) {
+ _rtld_error("%s: Unsupported version %d of Elf_Verdef entry",
+ depobj->path, vd->vd_version);
+ return (-1);
+ }
+ if (vna->vna_hash == vd->vd_hash) {
+ const Elf_Verdaux *aux = (const Elf_Verdaux *)
+ ((char *)vd + vd->vd_aux);
+ if (strcmp(vername, depobj->strtab + aux->vda_name) == 0)
+ return (0);
+ }
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+ }
+ if (vna->vna_flags & VER_FLG_WEAK)
+ return (0);
+ _rtld_error("%s: version %s required by %s not found",
+ depobj->path, vername, refobj->path);
+ return (-1);
+}
+
+static int
+rtld_verify_object_versions(Obj_Entry *obj)
+{
+ const Elf_Verneed *vn;
+ const Elf_Verdef *vd;
+ const Elf_Verdaux *vda;
+ const Elf_Vernaux *vna;
+ const Obj_Entry *depobj;
+ int maxvernum, vernum;
+
+ maxvernum = 0;
+ /*
+ * Walk over defined and required version records and figure out
+ * max index used by any of them. Do very basic sanity checking
+ * while there.
+ */
+ vn = obj->verneed;
+ while (vn != NULL) {
+ if (vn->vn_version != VER_NEED_CURRENT) {
+ _rtld_error("%s: Unsupported version %d of Elf_Verneed entry",
+ obj->path, vn->vn_version);
+ return (-1);
+ }
+ vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux);
+ for (;;) {
+ vernum = VER_NEED_IDX(vna->vna_other);
+ if (vernum > maxvernum)
+ maxvernum = vernum;
+ if (vna->vna_next == 0)
+ break;
+ vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next);
+ }
+ if (vn->vn_next == 0)
+ break;
+ vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next);
+ }
+
+ vd = obj->verdef;
+ while (vd != NULL) {
+ if (vd->vd_version != VER_DEF_CURRENT) {
+ _rtld_error("%s: Unsupported version %d of Elf_Verdef entry",
+ obj->path, vd->vd_version);
+ return (-1);
+ }
+ vernum = VER_DEF_IDX(vd->vd_ndx);
+ if (vernum > maxvernum)
+ maxvernum = vernum;
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+ }
+
+ if (maxvernum == 0)
+ return (0);
+
+ /*
+ * Store version information in array indexable by version index.
+ * Verify that object version requirements are satisfied along the
+ * way.
+ */
+ obj->vernum = maxvernum + 1;
+ obj->vertab = calloc(obj->vernum, sizeof(Ver_Entry));
+
+ vd = obj->verdef;
+ while (vd != NULL) {
+ if ((vd->vd_flags & VER_FLG_BASE) == 0) {
+ vernum = VER_DEF_IDX(vd->vd_ndx);
+ assert(vernum <= maxvernum);
+ vda = (const Elf_Verdaux *)((char *)vd + vd->vd_aux);
+ obj->vertab[vernum].hash = vd->vd_hash;
+ obj->vertab[vernum].name = obj->strtab + vda->vda_name;
+ obj->vertab[vernum].file = NULL;
+ obj->vertab[vernum].flags = 0;
+ }
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+ }
+
+ vn = obj->verneed;
+ while (vn != NULL) {
+ depobj = locate_dependency(obj, obj->strtab + vn->vn_file);
+ vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux);
+ for (;;) {
+ if (check_object_provided_version(obj, depobj, vna))
+ return (-1);
+ vernum = VER_NEED_IDX(vna->vna_other);
+ assert(vernum <= maxvernum);
+ obj->vertab[vernum].hash = vna->vna_hash;
+ obj->vertab[vernum].name = obj->strtab + vna->vna_name;
+ obj->vertab[vernum].file = obj->strtab + vn->vn_file;
+ obj->vertab[vernum].flags = (vna->vna_other & VER_NEED_HIDDEN) ?
+ VER_INFO_HIDDEN : 0;
+ if (vna->vna_next == 0)
+ break;
+ vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next);
+ }
+ if (vn->vn_next == 0)
+ break;
+ vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next);
+ }
+ return 0;
+}
+
+static int
+rtld_verify_versions(const Objlist *objlist)
+{
+ Objlist_Entry *entry;
+ int rc;
+
+ rc = 0;
+ STAILQ_FOREACH(entry, objlist, link) {
+ /*
+ * Skip dummy objects or objects that have their version requirements
+ * already checked.
+ */
+ if (entry->obj->strtab == NULL || entry->obj->vertab != NULL)
+ continue;
+ if (rtld_verify_object_versions(entry->obj) == -1) {
+ rc = -1;
+ if (ld_tracing == NULL)
+ break;
+ }
+ }
+ if (rc == 0 || ld_tracing != NULL)
+ rc = rtld_verify_object_versions(&obj_rtld);
+ return rc;
+}
+
+const Ver_Entry *
+fetch_ventry(const Obj_Entry *obj, unsigned long symnum)
+{
+ Elf_Versym vernum;
+
+ if (obj->vertab) {
+ vernum = VER_NDX(obj->versyms[symnum]);
+ if (vernum >= obj->vernum) {
+ _rtld_error("%s: symbol %s has wrong verneed value %d",
+ obj->path, obj->strtab + symnum, vernum);
+ } else if (obj->vertab[vernum].hash != 0) {
+ return &obj->vertab[vernum];
+ }
+ }
+ return NULL;
+}
More information about the Midnightbsd-cvs
mailing list