diff options
| author | nonaka <nonaka@NetBSD.org> | 2011-06-25 05:45:10 +0000 |
|---|---|---|
| committer | nonaka <nonaka@NetBSD.org> | 2011-06-25 05:45:10 +0000 |
| commit | 07fac769f5abdca4765e54307805a16e2e3ffb14 (patch) | |
| tree | 1c67ea3e5b6fd5d8757cfc21270ac23fc8066c0d /libexec | |
| parent | 5f70c711cdde438a2dabe184f7ef9fa760cc9b6e (diff) | |
PR/45015: ld.elf_so: support ELF symbol versioning
Applied latest patch.
Diffstat (limited to 'libexec')
| -rw-r--r-- | libexec/ld.elf_so/Makefile | 4 | ||||
| -rw-r--r-- | libexec/ld.elf_so/headers.c | 27 | ||||
| -rw-r--r-- | libexec/ld.elf_so/reloc.c | 12 | ||||
| -rw-r--r-- | libexec/ld.elf_so/rtld.c | 80 | ||||
| -rw-r--r-- | libexec/ld.elf_so/rtld.h | 60 | ||||
| -rw-r--r-- | libexec/ld.elf_so/symbol.c | 169 | ||||
| -rw-r--r-- | libexec/ld.elf_so/symver.c | 317 |
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; +} |
