summaryrefslogtreecommitdiff
path: root/libexec
diff options
context:
space:
mode:
authornonaka <nonaka@NetBSD.org>2011-06-25 05:45:10 +0000
committernonaka <nonaka@NetBSD.org>2011-06-25 05:45:10 +0000
commit07fac769f5abdca4765e54307805a16e2e3ffb14 (patch)
tree1c67ea3e5b6fd5d8757cfc21270ac23fc8066c0d /libexec
parent5f70c711cdde438a2dabe184f7ef9fa760cc9b6e (diff)
PR/45015: ld.elf_so: support ELF symbol versioning
Applied latest patch.
Diffstat (limited to 'libexec')
-rw-r--r--libexec/ld.elf_so/Makefile4
-rw-r--r--libexec/ld.elf_so/headers.c27
-rw-r--r--libexec/ld.elf_so/reloc.c12
-rw-r--r--libexec/ld.elf_so/rtld.c80
-rw-r--r--libexec/ld.elf_so/rtld.h60
-rw-r--r--libexec/ld.elf_so/symbol.c169
-rw-r--r--libexec/ld.elf_so/symver.c317
7 files changed, 594 insertions, 75 deletions
diff --git a/libexec/ld.elf_so/Makefile b/libexec/ld.elf_so/Makefile
index 926d79befdf..25ff8c9987b 100644
--- a/libexec/ld.elf_so/Makefile
+++ b/libexec/ld.elf_so/Makefile
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.107 2011/06/05 23:08:17 christos Exp $
+# $NetBSD: Makefile,v 1.108 2011/06/25 05:45:12 nonaka Exp $
#
# NOTE: when changing ld.so, ensure that ldd still compiles.
#
@@ -63,7 +63,7 @@ CLIBOBJ!= cd ${NETBSDSRCDIR}/lib/libc && ${PRINTOBJDIR}
SRCS+= rtld.c reloc.c symbol.c xmalloc.c xprintf.c debug.c \
map_object.c load.c search.c headers.c paths.c expand.c \
- tls.c
+ tls.c symver.c
.if ${USE_FORT} == "yes"
.PATH.c: ${NETBSDSRCDIR}/lib/libc/misc
diff --git a/libexec/ld.elf_so/headers.c b/libexec/ld.elf_so/headers.c
index 8dcc6c7f2f3..bd7342b2106 100644
--- a/libexec/ld.elf_so/headers.c
+++ b/libexec/ld.elf_so/headers.c
@@ -1,4 +1,4 @@
-/* $NetBSD: headers.c,v 1.40 2011/03/09 23:10:07 joerg Exp $ */
+/* $NetBSD: headers.c,v 1.41 2011/06/25 05:45:12 nonaka Exp $ */
/*
* Copyright 1996 John D. Polstra.
@@ -40,7 +40,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: headers.c,v 1.40 2011/03/09 23:10:07 joerg Exp $");
+__RCSID("$NetBSD: headers.c,v 1.41 2011/06/25 05:45:12 nonaka Exp $");
#endif /* not lint */
#include <err.h>
@@ -136,6 +136,29 @@ _rtld_digest_dynamic(const char *execname, Obj_Entry *obj)
obj->strsize = dynp->d_un.d_val;
break;
+ case DT_VERNEED:
+ obj->verneed = (const Elf_Verneed *)
+ (obj->relocbase + dynp->d_un.d_ptr);
+ 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_ptr);
+ 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_ptr);
+ break;
+
case DT_HASH:
{
const Elf_Symindx *hashtab = (const Elf_Symindx *)
diff --git a/libexec/ld.elf_so/reloc.c b/libexec/ld.elf_so/reloc.c
index c0666b2111c..1730f7f9b7b 100644
--- a/libexec/ld.elf_so/reloc.c
+++ b/libexec/ld.elf_so/reloc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: reloc.c,v 1.103 2010/12/24 12:41:43 skrll Exp $ */
+/* $NetBSD: reloc.c,v 1.104 2011/06/25 05:45:12 nonaka Exp $ */
/*
* Copyright 1996 John D. Polstra.
@@ -39,7 +39,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: reloc.c,v 1.103 2010/12/24 12:41:43 skrll Exp $");
+__RCSID("$NetBSD: reloc.c,v 1.104 2011/06/25 05:45:12 nonaka Exp $");
#endif /* not lint */
#include <err.h>
@@ -73,9 +73,12 @@ _rtld_do_copy_relocation(const Obj_Entry *dstobj, const Elf_Rela *rela)
const Elf_Sym *srcsym = NULL;
Obj_Entry *srcobj;
- for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next)
- if ((srcsym = _rtld_symlook_obj(name, hash, srcobj, false)) != NULL)
+ for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) {
+ srcsym = _rtld_symlook_obj(name, hash, srcobj, 0,
+ _rtld_fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)));
+ if (srcsym != NULL)
break;
+ }
if (srcobj == NULL) {
_rtld_error("Undefined symbol \"%s\" referenced from COPY"
@@ -210,6 +213,7 @@ _rtld_relocate_objects(Obj_Entry *first, bool bind_now)
/* Fill in the dynamic linker entry points. */
obj->dlopen = dlopen;
obj->dlsym = dlsym;
+ obj->dlvsym = dlvsym;
obj->dlerror = dlerror;
obj->dlclose = dlclose;
obj->dladdr = dladdr;
diff --git a/libexec/ld.elf_so/rtld.c b/libexec/ld.elf_so/rtld.c
index 44981c0e426..c37049e43ed 100644
--- a/libexec/ld.elf_so/rtld.c
+++ b/libexec/ld.elf_so/rtld.c
@@ -1,4 +1,4 @@
-/* $NetBSD: rtld.c,v 1.150 2011/04/02 16:49:49 joerg Exp $ */
+/* $NetBSD: rtld.c,v 1.151 2011/06/25 05:45:12 nonaka Exp $ */
/*
* Copyright 1996 John D. Polstra.
@@ -40,7 +40,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: rtld.c,v 1.150 2011/04/02 16:49:49 joerg Exp $");
+__RCSID("$NetBSD: rtld.c,v 1.151 2011/06/25 05:45:12 nonaka Exp $");
#endif /* not lint */
#include <sys/param.h>
@@ -373,9 +373,7 @@ _rtld(Elf_Addr *sp, Elf_Addr relocbase)
char **env, **oenvp;
const AuxInfo *aux;
const AuxInfo *auxp;
-#if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
Obj_Entry *obj;
-#endif
Elf_Addr *const osp = sp;
bool bind_now = 0;
const char *ld_bind_now, *ld_preload, *ld_library_path;
@@ -635,6 +633,12 @@ _rtld(Elf_Addr *sp, Elf_Addr relocbase)
if (_rtld_load_needed_objects(_rtld_objmain, _RTLD_MAIN) == -1)
_rtld_die();
+ dbg(("checking for required versions"));
+ for (obj = _rtld_objlist; obj != NULL; obj = obj->next) {
+ if (_rtld_verify_object_versions(obj) == -1)
+ _rtld_die();
+ }
+
#if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
dbg(("initializing initial Thread Local Storage"));
/*
@@ -930,6 +934,7 @@ dlopen(const char *name, int mode)
bool nodelete;
bool now;
sigset_t mask;
+ int result;
dbg(("dlopen of %s %d", name, mode));
@@ -956,10 +961,18 @@ dlopen(const char *name, int mode)
if (*old_obj_tail != NULL) { /* We loaded something new. */
assert(*old_obj_tail == obj);
- if (_rtld_load_needed_objects(obj, flags) == -1 ||
- (_rtld_init_dag(obj),
- _rtld_relocate_objects(obj,
- (now || obj->z_now))) == -1) {
+ result = _rtld_load_needed_objects(obj, flags);
+ if (result != -1) {
+ Objlist_Entry *entry;
+ _rtld_init_dag(obj);
+ SIMPLEQ_FOREACH(entry, &obj->dagmembers, link) {
+ result = _rtld_verify_object_versions(entry->obj);
+ if (result == -1)
+ break;
+ }
+ }
+ if (result == -1 || _rtld_relocate_objects(obj,
+ (now || obj->z_now)) == -1) {
_rtld_unload_object(&mask, obj, false);
obj->dl_refcount--;
obj = NULL;
@@ -998,8 +1011,8 @@ _rtld_objmain_sym(const char *name)
obj = _rtld_objmain;
_rtld_donelist_init(&donelist);
- def = _rtld_symlook_list(name, hash, &_rtld_list_main, &obj, false,
- &donelist);
+ def = _rtld_symlook_list(name, hash, &_rtld_list_main, &obj, 0,
+ NULL, &donelist);
if (def != NULL)
return obj->relocbase + def->st_value;
@@ -1022,9 +1035,8 @@ hackish_return_address(void)
#define lookup_mutex_exit() _rtld_shared_exit()
#endif
-__strong_alias(__dlsym,dlsym)
-void *
-dlsym(void *handle, const char *name)
+static void *
+do_dlsym(void *handle, const char *name, const Ver_Entry *ventry)
{
const Obj_Entry *obj;
unsigned long hash;
@@ -1032,12 +1044,11 @@ dlsym(void *handle, const char *name)
const Obj_Entry *defobj;
void *retaddr;
DoneList donelist;
+ const u_int flags = SYMLOOK_DLSYM | SYMLOOK_IN_PLT;
#ifdef __HAVE_FUNCTION_DESCRIPTORS
sigset_t mask;
#endif
- dbg(("dlsym of %s in %p", name, handle));
-
lookup_mutex_enter();
hash = _rtld_elf_hash(name);
@@ -1062,7 +1073,7 @@ dlsym(void *handle, const char *name)
switch ((intptr_t)handle) {
case (intptr_t)NULL: /* Just the caller's shared object. */
- def = _rtld_symlook_obj(name, hash, obj, false);
+ def = _rtld_symlook_obj(name, hash, obj, flags, ventry);
defobj = obj;
break;
@@ -1073,7 +1084,7 @@ dlsym(void *handle, const char *name)
case (intptr_t)RTLD_SELF: /* Caller included */
for (; obj; obj = obj->next) {
if ((def = _rtld_symlook_obj(name, hash, obj,
- false)) != NULL) {
+ flags, ventry)) != NULL) {
defobj = obj;
break;
}
@@ -1082,7 +1093,7 @@ dlsym(void *handle, const char *name)
case (intptr_t)RTLD_DEFAULT:
def = _rtld_symlook_default(name, hash, obj, &defobj,
- false);
+ flags, ventry);
break;
default:
@@ -1101,7 +1112,7 @@ dlsym(void *handle, const char *name)
if (obj->mainprog) {
/* Search main program and all libraries loaded by it */
def = _rtld_symlook_list(name, hash, &_rtld_list_main,
- &defobj, false, &donelist);
+ &defobj, flags, ventry, &donelist);
} else {
Needed_Entry fake;
DoneList depth;
@@ -1113,7 +1124,7 @@ dlsym(void *handle, const char *name)
_rtld_donelist_init(&depth);
def = _rtld_symlook_needed(name, hash, &fake, &defobj,
- false, &donelist, &depth);
+ flags, ventry, &donelist, &depth);
}
break;
@@ -1139,6 +1150,35 @@ dlsym(void *handle, const char *name)
return NULL;
}
+__strong_alias(__dlsym,dlsym)
+void *
+dlsym(void *handle, const char *name)
+{
+
+ dbg(("dlsym of %s in %p", name, handle));
+
+ return do_dlsym(handle, name, NULL);
+}
+
+__strong_alias(__dlvsym,dlvsym)
+void *
+dlvsym(void *handle, const char *name, const char *version)
+{
+ Ver_Entry *ventry = NULL;
+ Ver_Entry ver_entry;
+
+ dbg(("dlvsym of %s@%s in %p", name, version ? version : NULL, handle));
+
+ if (version != NULL) {
+ ver_entry.name = version;
+ ver_entry.file = NULL;
+ ver_entry.hash = _rtld_elf_hash(version);
+ ver_entry.flags = 0;
+ ventry = &ver_entry;
+ }
+ return do_dlsym(handle, name, ventry);
+}
+
__strong_alias(__dladdr,dladdr)
int
dladdr(const void *addr, Dl_info *info)
diff --git a/libexec/ld.elf_so/rtld.h b/libexec/ld.elf_so/rtld.h
index 9ad971d4e3a..1458aa4002f 100644
--- a/libexec/ld.elf_so/rtld.h
+++ b/libexec/ld.elf_so/rtld.h
@@ -1,4 +1,4 @@
-/* $NetBSD: rtld.h,v 1.105 2011/03/29 20:56:35 joerg Exp $ */
+/* $NetBSD: rtld.h,v 1.106 2011/06/25 05:45:12 nonaka Exp $ */
/*
* Copyright 1996 John D. Polstra.
@@ -104,6 +104,16 @@ typedef struct _rtld_search_path_t {
size_t sp_pathlen;
} Search_Path;
+typedef struct Struct_Ver_Entry {
+ Elf_Word hash;
+ u_int flags;
+ const char *name;
+ const char *file;
+} Ver_Entry;
+
+/* Ver_Entry.flags */
+#define VER_INFO_HIDDEN 0x01
+
#define RTLD_MAX_ENTRY 10
#define RTLD_MAX_LIBRARY 4
@@ -184,6 +194,7 @@ typedef struct Struct_Obj_Entry {
/* Entry points for dlopen() and friends. */
void *(*dlopen)(const char *, int);
void *(*dlsym)(void *, const char *);
+ void *(*dlvsym)(void *, const char *, const char *);
char *(*dlerror)(void);
int (*dlclose)(void *);
int (*dladdr)(const void *, Dl_info *);
@@ -251,6 +262,17 @@ typedef struct Struct_Obj_Entry {
size_t tlsoffset; /* Offset in the static TLS block */
size_t tlsalign; /* Needed alignment for static TLS */
#endif
+
+ /* symbol versioning */
+ const Elf_Verneed *verneed; /* Required versions. */
+ Elf_Word verneednum; /* Number of entries in verneed table */
+ const Elf_Verdef *verdef; /* Provided versions. */
+ Elf_Word verdefnum; /* Number of entries in verdef table */
+ const Elf_Versym *versyms; /* Symbol versions table */
+
+ Ver_Entry *vertab; /* Versions required/defined by this
+ * object */
+ int vertabnum; /* Number of entries in vertab */
} Obj_Entry;
typedef struct Struct_DoneList {
@@ -279,6 +301,11 @@ extern Elf_Sym _rtld_sym_zero;
#define RTLD_MODEMASK 0x3
+/* Flags to be passed into _rtld_symlook_ family of functions. */
+#define SYMLOOK_IN_PLT 0x01 /* Lookup for PLT symbol */
+#define SYMLOOK_DLSYM 0x02 /* Return newes versioned symbol.
+ Used by dlsym. */
+
/* Flags for _rtld_load_object() and friends. */
#define _RTLD_GLOBAL 0x01 /* Add object to global DAG. */
#define _RTLD_MAIN 0x02
@@ -352,23 +379,44 @@ Obj_Entry *_rtld_load_library(const char *, const Obj_Entry *, int);
/* symbol.c */
unsigned long _rtld_elf_hash(const char *);
const Elf_Sym *_rtld_symlook_obj(const char *, unsigned long,
- const Obj_Entry *, bool);
+ const Obj_Entry *, u_int, const Ver_Entry *);
const Elf_Sym *_rtld_find_symdef(unsigned long, const Obj_Entry *,
- const Obj_Entry **, bool);
+ const Obj_Entry **, u_int);
const Elf_Sym *_rtld_find_plt_symdef(unsigned long, const Obj_Entry *,
const Obj_Entry **, bool);
const Elf_Sym *_rtld_symlook_list(const char *, unsigned long,
- const Objlist *, const Obj_Entry **, bool, DoneList *);
+ const Objlist *, const Obj_Entry **, u_int, const Ver_Entry *, DoneList *);
const Elf_Sym *_rtld_symlook_default(const char *, unsigned long,
- const Obj_Entry *, const Obj_Entry **, bool);
+ const Obj_Entry *, const Obj_Entry **, u_int, const Ver_Entry *);
const Elf_Sym *_rtld_symlook_needed(const char *, unsigned long,
- const Needed_Entry *, const Obj_Entry **, bool,
+ const Needed_Entry *, const Obj_Entry **, u_int, const Ver_Entry *,
DoneList *, DoneList *);
#ifdef COMBRELOC
void _rtld_combreloc_reset(const Obj_Entry *);
#endif
+/* symver.c */
+int _rtld_object_match_name(const Obj_Entry *, const char *);
+int _rtld_verify_object_versions(Obj_Entry *);
+
+static __inline const Ver_Entry *
+_rtld_fetch_ventry(const Obj_Entry *obj, unsigned long symnum)
+{
+ Elf_Half vernum;
+
+ if (obj->vertab) {
+ vernum = VER_NDX(obj->versyms[symnum].vs_vers);
+ if (vernum >= obj->vertabnum) {
+ _rtld_error("%s: symbol %s has wrong verneed value %d",
+ obj->path, &obj->strtab[symnum], vernum);
+ } else if (obj->vertab[vernum].hash) {
+ return &obj->vertab[vernum];
+ }
+ }
+ return NULL;
+}
+
#if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
/* tls.c */
void *_rtld_tls_get_addr(void *, size_t, size_t);
diff --git a/libexec/ld.elf_so/symbol.c b/libexec/ld.elf_so/symbol.c
index 752cf540097..9f101b16917 100644
--- a/libexec/ld.elf_so/symbol.c
+++ b/libexec/ld.elf_so/symbol.c
@@ -1,4 +1,4 @@
-/* $NetBSD: symbol.c,v 1.56 2011/03/12 22:54:36 joerg Exp $ */
+/* $NetBSD: symbol.c,v 1.57 2011/06/25 05:45:12 nonaka Exp $ */
/*
* Copyright 1996 John D. Polstra.
@@ -40,7 +40,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: symbol.c,v 1.56 2011/03/12 22:54:36 joerg Exp $");
+__RCSID("$NetBSD: symbol.c,v 1.57 2011/06/25 05:45:12 nonaka Exp $");
#endif /* not lint */
#include <err.h>
@@ -89,6 +89,7 @@ _rtld_is_exported(const Elf_Sym *def)
(fptr_t)dlopen,
(fptr_t)dlclose,
(fptr_t)dlsym,
+ (fptr_t)dlvsym,
(fptr_t)dlerror,
(fptr_t)dladdr,
(fptr_t)dlinfo,
@@ -139,7 +140,8 @@ _rtld_elf_hash(const char *name)
const Elf_Sym *
_rtld_symlook_list(const char *name, unsigned long hash, const Objlist *objlist,
- const Obj_Entry **defobj_out, bool in_plt, DoneList *dlp)
+ const Obj_Entry **defobj_out, u_int flags, const Ver_Entry *ventry,
+ DoneList *dlp)
{
const Elf_Sym *symp;
const Elf_Sym *def;
@@ -153,8 +155,8 @@ _rtld_symlook_list(const char *name, unsigned long hash, const Objlist *objlist,
continue;
rdbg(("search object %p (%s) for %s", elm->obj, elm->obj->path,
name));
- if ((symp = _rtld_symlook_obj(name, hash, elm->obj, in_plt))
- != NULL) {
+ symp = _rtld_symlook_obj(name, hash, elm->obj, flags, ventry);
+ if (symp != NULL) {
if ((def == NULL) ||
(ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -176,8 +178,8 @@ _rtld_symlook_list(const char *name, unsigned long hash, const Objlist *objlist,
*/
const Elf_Sym *
_rtld_symlook_needed(const char *name, unsigned long hash,
- const Needed_Entry *needed, const Obj_Entry **defobj_out, bool inplt,
- DoneList *breadth, DoneList *depth)
+ const Needed_Entry *needed, const Obj_Entry **defobj_out, u_int flags,
+ const Ver_Entry *ventry, DoneList *breadth, DoneList *depth)
{
const Elf_Sym *def, *def_w;
const Needed_Entry *n;
@@ -190,7 +192,8 @@ _rtld_symlook_needed(const char *name, unsigned long hash,
continue;
if (_rtld_donelist_check(breadth, obj))
continue;
- if ((def = _rtld_symlook_obj(name, hash, obj, inplt)) == NULL)
+ def = _rtld_symlook_obj(name, hash, obj, flags, ventry);
+ if (def == NULL)
continue;
defobj = obj;
if (ELF_ST_BIND(def->st_info) != STB_WEAK) {
@@ -209,7 +212,7 @@ _rtld_symlook_needed(const char *name, unsigned long hash,
if (_rtld_donelist_check(depth, obj))
continue;
def_w = _rtld_symlook_needed(name, hash, obj->needed, &defobj1,
- inplt, breadth, depth);
+ flags, ventry, breadth, depth);
if (def_w == NULL)
continue;
if (def == NULL || ELF_ST_BIND(def_w->st_info) != STB_WEAK) {
@@ -235,9 +238,12 @@ _rtld_symlook_needed(const char *name, unsigned long hash,
*/
const Elf_Sym *
_rtld_symlook_obj(const char *name, unsigned long hash,
- const Obj_Entry *obj, bool in_plt)
+ const Obj_Entry *obj, u_int flags, const Ver_Entry *ventry)
{
unsigned long symnum;
+ const Elf_Sym *vsymp = NULL;
+ Elf_Half verndx;
+ int vcount = 0;
for (symnum = obj->buckets[fast_remainder32(hash, obj->nbuckets,
obj->nbuckets_m, obj->nbuckets_s1, obj->nbuckets_s2)];
@@ -250,29 +256,105 @@ _rtld_symlook_obj(const char *name, unsigned long hash,
symp = obj->symtab + symnum;
strp = obj->strtab + symp->st_name;
rdbg(("check \"%s\" vs \"%s\" in %p", name, strp, obj));
- if (name[1] == strp[1] && !strcmp(name, strp)) {
- if (symp->st_shndx != SHN_UNDEF)
- return symp;
+ if (name[1] != strp[1] || strcmp(name, strp))
+ continue;
+ if (symp->st_shndx != SHN_UNDEF)
+ /* Nothing to do */;
#ifndef __mips__
- /*
- * XXX DANGER WILL ROBINSON!
- * If we have a function pointer in the executable's
- * data section, it points to the executable's PLT
- * slot, and there is NO relocation emitted. To make
- * the function pointer comparable to function pointers
- * in shared libraries, we must resolve data references
- * in the libraries to point to PLT slots in the
- * executable, if they exist.
- */
- else if (!in_plt && symp->st_value != 0 &&
- ELF_ST_TYPE(symp->st_info) == STT_FUNC)
- return symp;
+ /*
+ * XXX DANGER WILL ROBINSON!
+ * If we have a function pointer in the executable's
+ * data section, it points to the executable's PLT
+ * slot, and there is NO relocation emitted. To make
+ * the function pointer comparable to function pointers
+ * in shared libraries, we must resolve data references
+ * in the libraries to point to PLT slots in the
+ * executable, if they exist.
+ */
+ else if (!(flags & SYMLOOK_IN_PLT) &&
+ symp->st_value != 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC)
+ /* Nothing to do */;
#endif
- else
- return NULL;
+ else
+ continue;
+
+ if (ventry == NULL) {
+ if (obj->versyms != NULL) {
+ verndx = VER_NDX(obj->versyms[symnum].vs_vers);
+ if (verndx > obj->vertabnum) {
+ _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) &&
+ verndx == VER_NDX_GIVEN) {
+ return symp;
+ } else if (verndx >= VER_NDX_GIVEN) {
+ if (!(obj->versyms[symnum].vs_vers & VER_NDX_HIDDEN)) {
+ if (vsymp == NULL)
+ vsymp = symp;
+ vcount++;
+ }
+ continue;
+ }
+ }
+ return symp;
+ } else {
+ if (obj->versyms == NULL) {
+ if (_rtld_object_match_name(obj, ventry->name)){
+ _rtld_error("%s: object %s should "
+ "provide version %s for symbol %s",
+ _rtld_objself.path, obj->path,
+ ventry->name, &obj->strtab[symnum]);
+ continue;
+ }
+ } else {
+ verndx = VER_NDX(obj->versyms[symnum].vs_vers);
+ if (verndx > obj->vertabnum) {
+ _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].vs_vers & VER_NDX_HIDDEN) ||
+ (verndx >= VER_NDX_GIVEN))
+ continue;
+ }
+ }
+ return symp;
}
}
-
+ if (vcount == 1)
+ return vsymp;
return NULL;
}
@@ -299,7 +381,7 @@ _rtld_combreloc_reset(const Obj_Entry *obj)
*/
const Elf_Sym *
_rtld_find_symdef(unsigned long symnum, const Obj_Entry *refobj,
- const Obj_Entry **defobj_out, bool in_plt)
+ const Obj_Entry **defobj_out, u_int flags)
{
const Elf_Sym *ref;
const Elf_Sym *def;
@@ -320,7 +402,7 @@ _rtld_find_symdef(unsigned long symnum, const Obj_Entry *refobj,
static const Elf_Sym *last_def;
if (symnum == last_symnum && refobj == _rtld_last_refobj
- && in_plt == false) {
+ && !(flags & SYMLOOK_IN_PLT)) {
*defobj_out = last_defobj;
return last_def;
}
@@ -342,7 +424,8 @@ _rtld_find_symdef(unsigned long symnum, const Obj_Entry *refobj,
hash = _rtld_elf_hash(name);
defobj = NULL;
- def = _rtld_symlook_default(name, hash, refobj, &defobj, in_plt);
+ def = _rtld_symlook_default(name, hash, refobj, &defobj, flags,
+ _rtld_fetch_ventry(refobj, symnum));
} else {
rdbg(("STB_LOCAL symbol %s in %s", name, refobj->path));
def = ref;
@@ -362,7 +445,7 @@ _rtld_find_symdef(unsigned long symnum, const Obj_Entry *refobj,
if (def != NULL) {
*defobj_out = defobj;
#ifdef COMBRELOC
- if (in_plt == false) {
+ if (!(flags & SYMLOOK_IN_PLT)) {
/*
* Cache the lookup arguments and results if this was
* non-PLT lookup.
@@ -376,7 +459,8 @@ _rtld_find_symdef(unsigned long symnum, const Obj_Entry *refobj,
} else {
rdbg(("lookup failed"));
_rtld_error("%s: Undefined %ssymbol \"%s\" (symnum = %ld)",
- refobj->path, in_plt ? "PLT " : "", name, symnum);
+ refobj->path, (flags & SYMLOOK_IN_PLT) ? "PLT " : "",
+ name, symnum);
}
return def;
}
@@ -385,7 +469,8 @@ const Elf_Sym *
_rtld_find_plt_symdef(unsigned long symnum, const Obj_Entry *obj,
const Obj_Entry **defobj, bool imm)
{
- const Elf_Sym *def = _rtld_find_symdef(symnum, obj, defobj, true);
+ const Elf_Sym *def = _rtld_find_symdef(symnum, obj, defobj,
+ SYMLOOK_IN_PLT);
if (__predict_false(def == NULL))
return NULL;
@@ -412,7 +497,8 @@ _rtld_find_plt_symdef(unsigned long symnum, const Obj_Entry *obj,
*/
const Elf_Sym *
_rtld_symlook_default(const char *name, unsigned long hash,
- const Obj_Entry *refobj, const Obj_Entry **defobj_out, bool in_plt)
+ const Obj_Entry *refobj, const Obj_Entry **defobj_out, u_int flags,
+ const Ver_Entry *ventry)
{
const Elf_Sym *def;
const Elf_Sym *symp;
@@ -428,7 +514,7 @@ _rtld_symlook_default(const char *name, unsigned long hash,
/* Look first in the referencing object if linked symbolically. */
if (refobj->symbolic && !_rtld_donelist_check(&donelist, refobj)) {
rdbg(("search referencing object for %s", name));
- symp = _rtld_symlook_obj(name, hash, refobj, in_plt);
+ symp = _rtld_symlook_obj(name, hash, refobj, flags, ventry);
if (symp != NULL) {
def = symp;
defobj = refobj;
@@ -439,7 +525,7 @@ _rtld_symlook_default(const char *name, unsigned long hash,
if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
rdbg(("search _rtld_list_main for %s", name));
symp = _rtld_symlook_list(name, hash, &_rtld_list_main, &obj,
- in_plt, &donelist);
+ flags, ventry, &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -451,7 +537,7 @@ _rtld_symlook_default(const char *name, unsigned long hash,
if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
rdbg(("search _rtld_list_global for %s", name));
symp = _rtld_symlook_list(name, hash, &_rtld_list_global,
- &obj, in_plt, &donelist);
+ &obj, flags, ventry, &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -466,7 +552,7 @@ _rtld_symlook_default(const char *name, unsigned long hash,
rdbg(("search DAG with root %p (%s) for %s", elm->obj,
elm->obj->path, name));
symp = _rtld_symlook_list(name, hash, &elm->obj->dagmembers,
- &obj, in_plt, &donelist);
+ &obj, flags, ventry, &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -483,7 +569,8 @@ _rtld_symlook_default(const char *name, unsigned long hash,
*/
if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
rdbg(("Search the dynamic linker itself."));
- symp = _rtld_symlook_obj(name, hash, &_rtld_objself, in_plt);
+ symp = _rtld_symlook_obj(name, hash, &_rtld_objself, flags,
+ ventry);
if (symp != NULL && _rtld_is_exported(symp)) {
def = symp;
defobj = &_rtld_objself;
diff --git a/libexec/ld.elf_so/symver.c b/libexec/ld.elf_so/symver.c
new file mode 100644
index 00000000000..4d4e463647a
--- /dev/null
+++ b/libexec/ld.elf_so/symver.c
@@ -0,0 +1,317 @@
+/* $NetBSD: symver.c,v 1.1 2011/06/25 05:45:12 nonaka Exp $ */
+
+/*-
+ * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
+ * Copyright 2003 Alexander Kabaev <kan@FreeBSD.ORG>.
+ * Copyright 2009, 2010, 2011 Konstantin Belousov <kib@FreeBSD.ORG>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $FreeBSD: head/libexec/rtld-elf/rtld.c 220004 2011-03-25 18:23:10Z avg $
+ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by NONAKA Kimihiro.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: symver.c,v 1.1 2011/06/25 05:45:12 nonaka Exp $");
+
+#include <sys/param.h>
+#include <sys/exec_elf.h>
+#include <string.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+
+int
+_rtld_object_match_name(const Obj_Entry *obj, const char *name)
+{
+ Name_Entry *entry;
+
+ STAILQ_FOREACH(entry, &obj->names, link) {
+ dbg(("name: %s, entry->name: %s", name, entry->name));
+ 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;
+
+ SIMPLEQ_FOREACH(entry, &_rtld_list_main, link) {
+ if (_rtld_object_match_name(entry->obj, name))
+ return entry->obj;
+ }
+
+ for (needed = obj->needed; needed != NULL; needed = needed->next) {
+ dbg(("needed: name: %s, str: %s", name,
+ &obj->strtab[needed->name]));
+ if (strcmp(name, &obj->strtab[needed->name]) == 0 ||
+ (needed->obj != NULL && _rtld_object_match_name(needed->obj, name))) {
+ /*
+ * If there is DT_NEEDED for the name we are looking
+ * for, we are all set. Note that object might not be
+ * found if dependency was not loaded yet, so the
+ * function can return NULL here. This is expected
+ * and handled properly by the caller.
+ */
+ return needed->obj;
+ }
+ }
+
+ _rtld_error("%s: Unexpected inconsistency: dependency %s not found",
+ obj->path, name);
+ return NULL;
+}
+
+static int
+check_object_provided_version(Obj_Entry *refobj, const Obj_Entry *depobj,
+ const Elf_Vernaux *vna)
+{
+ const char *vername = &refobj->strtab[vna->vna_name];
+ const char *depstrtab = depobj->strtab;
+ const Elf_Verdef *vd = depobj->verdef;
+ const Elf_Word hash = vna->vna_hash;
+
+ if (vd == NULL) {
+ _rtld_error("%s: version %s required by %s not defined",
+ depobj->path, vername, refobj->path);
+ return -1;
+ }
+
+ for (;; vd = (const Elf_Verdef *)((const char *)vd + vd->vd_next)) {
+ if (vd->vd_version != VER_DEF_CURRENT) {
+ _rtld_error(
+ "%s: Unsupported version %d of Elf_Verdef entry",
+ depobj->path, vd->vd_version);
+ return -1;
+ }
+ dbg(("hash: 0x%x, vd_hash: 0x%x", hash, vd->vd_hash));
+ if (hash == vd->vd_hash) {
+ const Elf_Verdaux *vda = (const Elf_Verdaux *)
+ ((const char *)vd + vd->vd_aux);
+ dbg(("vername: %s, str: %s", vername,
+ &depstrtab[vda->vda_name]));
+ if (strcmp(vername, &depstrtab[vda->vda_name]) == 0)
+ return 0;
+ }
+ if (vd->vd_next == 0)
+ break;
+ }
+ 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;
+}
+
+int
+_rtld_verify_object_versions(Obj_Entry *obj)
+{
+ const char *strtab = obj->strtab;
+ const Elf_Verneed *vn;
+ const Elf_Vernaux *vna;
+ const Elf_Verdef *vd;
+ const Elf_Verdaux *vda;
+ const Obj_Entry *depobj;
+ int maxvertab, vernum;
+
+ dbg(("obj->path: %s", obj->path));
+
+ /*
+ * If we don't have string table or objects that have their version
+ * requirements already checked, we must be ok.
+ */
+ if (strtab == NULL || obj->vertab != NULL)
+ return 0;
+
+ maxvertab = 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.
+ */
+ for (vn = obj->verneed;
+ vn != NULL;
+ vn = (const Elf_Verneed *)((const char *)vn + vn->vn_next)) {
+
+ if (vn->vn_version != VER_NEED_CURRENT) {
+ _rtld_error(
+ "%s: Unsupported version %d of Elf_Verneed entry",
+ obj->path, vn->vn_version);
+ return -1;
+ }
+
+ dbg(("verneed: vn_file: %d, str: %s",
+ vn->vn_file, &strtab[vn->vn_file]));
+ depobj = locate_dependency(obj, &strtab[vn->vn_file]);
+ assert(depobj != NULL);
+
+ for (vna = (const Elf_Vernaux *)((const char *)vn + vn->vn_aux);
+ /*CONSTCOND*/1;
+ vna = (const Elf_Vernaux *)((const char *)vna + vna->vna_next)) {
+
+ if (check_object_provided_version(obj, depobj, vna) == -1)
+ return -1;
+
+ vernum = VER_NEED_IDX(vna->vna_other);
+ if (vernum > maxvertab)
+ maxvertab = vernum;
+
+ if (vna->vna_next == 0) {
+ /* No more symbols. */
+ break;
+ }
+ }
+
+ if (vn->vn_next == 0) {
+ /* No more dependencies. */
+ break;
+ }
+ }
+
+ for (vd = obj->verdef;
+ vd != NULL;
+ vd = (const Elf_Verdef *)((const char *)vd + vd->vd_next)) {
+
+ if (vd->vd_version != VER_DEF_CURRENT) {
+ _rtld_error(
+ "%s: Unsupported version %d of Elf_Verdef entry",
+ obj->path, vd->vd_version);
+ return -1;
+ }
+
+ dbg(("verdef: vn_ndx: 0x%x", vd->vd_ndx));
+ vernum = VER_DEF_IDX(vd->vd_ndx);
+ if (vernum > maxvertab)
+ maxvertab = vernum;
+
+ if (vd->vd_next == 0) {
+ /* No more definitions. */
+ break;
+ }
+ }
+
+ dbg(("maxvertab: %d", maxvertab));
+ if (maxvertab == 0)
+ return 0;
+
+ /*
+ * Store version information in array indexable by version index.
+ * Verify that object version requirements are satisfied along the
+ * way.
+ */
+ obj->vertabnum = maxvertab + 1;
+ obj->vertab = (Ver_Entry *)xcalloc(obj->vertabnum * sizeof(Ver_Entry));
+
+ for (vn = obj->verneed;
+ vn != NULL;
+ vn = (const Elf_Verneed *)((const char *)vn + vn->vn_next)) {
+
+ for (vna = (const Elf_Vernaux *)((const char *)vn + vn->vn_aux);
+ /*CONSTCOND*/1;
+ vna = (const Elf_Vernaux *)((const char *)vna + vna->vna_next)) {
+
+ vernum = VER_NEED_IDX(vna->vna_other);
+ assert(vernum <= maxvertab);
+ obj->vertab[vernum].hash = vna->vna_hash;
+ obj->vertab[vernum].name = &strtab[vna->vna_name];
+ obj->vertab[vernum].file = &strtab[vn->vn_file];
+ obj->vertab[vernum].flags =
+ (vna->vna_other & VER_NEED_HIDDEN)
+ ? VER_INFO_HIDDEN : 0;
+ dbg(("verneed: vernum: %d, hash: 0x%x, name: %s, "
+ "file: %s, flags: 0x%x", vernum,
+ obj->vertab[vernum].hash, obj->vertab[vernum].name,
+ obj->vertab[vernum].file,
+ obj->vertab[vernum].flags));
+
+ if (vna->vna_next == 0) {
+ /* No more symbols. */
+ break;
+ }
+ }
+ if (vn->vn_next == 0) {
+ /* No more dependencies. */
+ break;
+ }
+ }
+
+ for (vd = obj->verdef;
+ vd != NULL;
+ vd = (const Elf_Verdef *)((const char *)vd + vd->vd_next)) {
+
+ if ((vd->vd_flags & VER_FLG_BASE) == 0) {
+ vernum = VER_DEF_IDX(vd->vd_ndx);
+ assert(vernum <= maxvertab);
+ vda = (const Elf_Verdaux *)
+ ((const char *)vd + vd->vd_aux);
+ obj->vertab[vernum].hash = vd->vd_hash;
+ obj->vertab[vernum].name = &strtab[vda->vda_name];
+ obj->vertab[vernum].file = NULL;
+ obj->vertab[vernum].flags = 0;
+ dbg(("verdef: vernum: %d, hash: 0x%x, name: %s",
+ vernum, obj->vertab[vernum].hash,
+ obj->vertab[vernum].name));
+ }
+
+ if (vd->vd_next == 0) {
+ /* No more definitions. */
+ break;
+ }
+ }
+
+ return 0;
+}