Index: map_object.c =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/map_object.c,v retrieving revision 1.16 diff -u -r1.16 map_object.c --- map_object.c 27 Feb 2005 12:55:40 -0000 1.16 +++ map_object.c 8 Dec 2005 03:10:26 -0000 @@ -305,15 +305,19 @@ { Objlist_Entry *elm; - if (obj->tls_done) { + if (obj->tls_done) free_tls_offset(obj); - } free(obj->path); while (obj->needed != NULL) { Needed_Entry *needed = obj->needed; obj->needed = needed->next; free(needed); } + while (!STAILQ_EMPTY(&obj->names)) { + Name_Entry *entry = STAILQ_FIRST(&obj->names); + STAILQ_REMOVE_HEAD(&obj->names, link); + free(entry); + } while (!STAILQ_EMPTY(&obj->dldags)) { elm = STAILQ_FIRST(&obj->dldags); STAILQ_REMOVE_HEAD(&obj->dldags, link); @@ -324,6 +328,7 @@ STAILQ_REMOVE_HEAD(&obj->dagmembers, link); free(elm); } + free(obj->vertab); free(obj->origin_path); free(obj->priv); free(obj); @@ -337,6 +342,7 @@ obj = CNEW(Obj_Entry); STAILQ_INIT(&obj->dldags); STAILQ_INIT(&obj->dagmembers); + STAILQ_INIT(&obj->names); return obj; } Index: rtld.c =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/rtld.c,v retrieving revision 1.107 diff -u -r1.107 rtld.c --- rtld.c 11 Nov 2005 19:57:41 -0000 1.107 +++ rtld.c 9 Dec 2005 17:01:53 -0000 @@ -80,10 +80,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 *); @@ -102,7 +103,7 @@ 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,17 +119,21 @@ 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 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, Objlist *, + const Obj_Entry **, const Ver_Entry *, int flags, DoneList *); static void trace_loaded_objects(Obj_Entry *obj); 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_check_versions(); +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 *); void r_debug_state(struct r_debug*, struct link_map*); @@ -388,6 +393,10 @@ obj->refcount++; } + dbg("checking for required version"); + if (rtld_check_versions() == -1) + die(); + if (ld_tracing) { /* We're done */ trace_loaded_objects(obj_main); exit(0); @@ -616,6 +625,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 *) @@ -902,11 +934,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 +971,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 +1198,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,14 +1219,11 @@ 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; @@ -1211,24 +1233,26 @@ } /* - * 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 +1262,77 @@ * 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++; + 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); return obj; } @@ -1709,9 +1747,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) { @@ -1723,6 +1759,8 @@ assert(*old_obj_tail == obj); result = load_needed_objects(obj); + if (result != -1) + result = rtld_verify_object_versions(obj); if (result != -1 && ld_tracing) goto trace; @@ -1771,10 +1809,12 @@ const Elf_Sym *def; const Obj_Entry *defobj; int lockstate; + int flags; hash = elf_hash(name); def = NULL; defobj = NULL; + flags = SYMLOOK_IN_PLT; lockstate = rlock_acquire(rtld_bind_lock); if (handle == NULL || handle == RTLD_NEXT || @@ -1788,21 +1828,21 @@ return NULL; } if (handle == NULL) { /* Just the caller's shared object. */ - def = symlook_obj(name, hash, obj, true); + def = symlook_obj(name, hash, obj, NULL, 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, NULL, 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, NULL, flags); } } else { if ((obj = dlcheck(handle)) == NULL) { @@ -1815,14 +1855,14 @@ /* Search main program and all libraries loaded by it. */ donelist_init(&donelist); - def = symlook_list(name, hash, &list_main, &defobj, true, + def = symlook_list(name, hash, &list_main, &defobj, NULL, 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); + def = symlook_obj(name, hash, obj, NULL, flags); defobj = obj; } } @@ -2163,7 +2203,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 +2236,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 +2251,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 +2260,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 +2273,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 +2286,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 +2302,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; @@ -2275,7 +2316,8 @@ static const Elf_Sym * symlook_list(const char *name, unsigned long hash, Objlist *objlist, - const Obj_Entry **defobj_out, bool in_plt, DoneList *dlp) + const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags, + DoneList *dlp) { const Elf_Sym *symp; const Elf_Sym *def; @@ -2287,7 +2329,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; @@ -2303,15 +2345,118 @@ /* * 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 - * definition was found. + * 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) +{ + unsigned long symnum; + const Elf_Sym *vsymp; + Elf_Versym verndx; + int vcount; + + if (obj->buckets == NULL) + return NULL; + + 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) + return NULL; /* Bad object */ + + 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; + default: + continue; + } + if (name[0] != strp[0] || strcmp(name, strp) != 0) + continue; + + 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 (verndx >= 2) { + 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->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 default symbol + * and if it is not hidden. If default symbol (num < 2) + * is available, use it. + */ + if ((ventry->flags & VER_INFO_HIDDEN) || + (obj->versyms[symnum] & VER_NDX_HIDDEN) || + (verndx >= 2)) + continue; + } + } + return symp; + } + } + return (vcount == 1) ? vsymp : NULL; +} + +#if 0 +/* + * 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, + int flags) { if (obj->buckets != NULL) { unsigned long symnum = obj->buckets[hash % obj->nbuckets]; @@ -2327,7 +2472,7 @@ if (name[0] == strp[0] && strcmp(name, strp) == 0) return symp->st_shndx != SHN_UNDEF || - (!in_plt && symp->st_value != 0 && + ((flags & SYMLOOK_IN_PLT) == 0 && symp->st_value != 0 && ELF_ST_TYPE(symp->st_info) == STT_FUNC) ? symp : NULL; symnum = obj->chains[symnum]; @@ -2335,6 +2480,7 @@ } return NULL; } +#endif static void trace_loaded_objects(Obj_Entry *obj) @@ -2840,3 +2986,226 @@ 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("Unexpected inconsistency"); + 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 does not have version information, but %s requires it", + depobj->path, refobj->path); + *(char *)0 = 0; + return (-1); + } + for (;;) { + if (vd->vd_version != VER_DEF_CURRENT) { + _rtld_error("Unsupported version of Elf_Verdef entry in %s : %d", + 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("Version %s required by %s can not be found in %s", vername, + refobj->path, depobj->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("Unsupported version of Elf_Verneed entry in %s: %d", + 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("Unsupported version of Elf_Verneed entry: %d", + 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_check_versions() +{ + Objlist_Entry *entry; + + STAILQ_FOREACH(entry, &list_main, link) { + Obj_Entry *obj = entry->obj; + + if (obj->strtab == NULL) + continue; + if (rtld_verify_object_versions(obj)) + return (-1); + } + return 0; +} + +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; +} Index: rtld.h =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/rtld.h,v retrieving revision 1.35 diff -u -r1.35 rtld.h --- rtld.h 27 Feb 2005 12:55:40 -0000 1.35 +++ rtld.h 8 Dec 2005 06:23:52 -0000 @@ -36,6 +36,7 @@ #include #include +#include "rtld_elf.h" #include "rtld_lock.h" #include "rtld_machdep.h" @@ -90,6 +91,11 @@ unsigned long name; /* Offset of name in string table */ } Needed_Entry; +typedef struct Struct_Name_Entry { + STAILQ_ENTRY(Struct_Name_Entry) link; + char name[1]; +} Name_Entry; + /* Lock object */ typedef struct Struct_LockInfo { void *context; /* Client context for creating locks */ @@ -107,6 +113,15 @@ void (*context_destroy)(void *context); } LockInfo; +typedef struct Struct_Ver_Entry { + Elf_Word hash; + unsigned int flags; + const char *name; + const char *file; +} Ver_Entry; + +#define VER_INFO_HIDDEN 0x01 + /* * Shared object descriptor. * @@ -165,6 +180,12 @@ const char *strtab; /* String table */ unsigned long strsize; /* Size in bytes of string table */ + 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 */ + const Elf_Hashelt *buckets; /* Hash table buckets array */ unsigned long nbuckets; /* Number of buckets */ const Elf_Hashelt *chains; /* Hash table chain array */ @@ -173,6 +194,11 @@ const char *rpath; /* Search path specified in object */ Needed_Entry *needed; /* Shared objects needed by this one (%) */ + STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we + know about. */ + Ver_Entry *vertab; /* Versions required /defined by this object */ + int vernum; /* Number of entries in vertab */ + Elf_Addr init; /* Initialization function to call */ Elf_Addr fini; /* Termination function to call */ @@ -199,6 +225,12 @@ #define RTLD_STATIC_TLS_EXTRA 64 +/* Flags to be passed into symlook_ family of functions. */ +#define SYMLOOK_IN_PLT 0x01 /* Lookup for PLT symbol */ +#define SYMLOOK_COMPAT 0x02 /* Return older versioned symbol for + compatibility with older unversioned + binaries. */ + /* * Symbol cache entry used during relocation to avoid multiple lookups * of the same symbol. @@ -225,20 +257,21 @@ */ unsigned long elf_hash(const char *); const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *, - const Obj_Entry **, bool, SymCache *); + const Obj_Entry **, int, SymCache *); void init_pltgot(Obj_Entry *); void lockdflt_init(void); void obj_free(Obj_Entry *); Obj_Entry *obj_new(void); void _rtld_bind_start(void); -const Elf_Sym *symlook_obj(const char *, unsigned long, - const Obj_Entry *, bool); +const Elf_Sym *symlook_obj(const char *, unsigned long, const Obj_Entry *, + const Ver_Entry *, int); void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset); void *allocate_tls(Obj_Entry *, void *, size_t, size_t); void free_tls(void *, size_t, size_t); void *allocate_module_tls(int index); bool allocate_tls_offset(Obj_Entry *obj); void free_tls_offset(Obj_Entry *obj); +const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long); /* * MD function declarations. Index: rtld_elf.h =================================================================== RCS file: rtld_elf.h diff -N rtld_elf.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ rtld_elf.h 10 Dec 2005 03:48:57 -0000 @@ -0,0 +1,110 @@ +#ifndef _RTLD_ELF_H_ +#define _RTLD_ELF_H_ + +#include + +#define DT_VERSYM 0x6ffffff0 +#define DT_VERDEF 0x6ffffffc +#define DT_VERDEFNUM 0x6ffffffd +#define DT_VERNEED 0x6ffffffe +#define DT_VERNEEDNUM 0x6fffffff + +typedef struct +{ + Elf32_Half vd_version; + Elf32_Half vd_flags; + Elf32_Half vd_ndx; + Elf32_Half vd_cnt; + Elf32_Word vd_hash; + Elf32_Word vd_aux; + Elf32_Word vd_next; +} Elf32_Verdef; + +typedef struct +{ + Elf32_Word vda_name; + Elf32_Word vda_next; +} Elf32_Verdaux; + +typedef struct +{ + Elf32_Half vn_version; + Elf32_Half vn_cnt; + Elf32_Word vn_file; + Elf32_Word vn_aux; + Elf32_Word vn_next; +} Elf32_Verneed; + +typedef struct +{ + Elf32_Word vna_hash; + Elf32_Half vna_flags; + Elf32_Half vna_other; + Elf32_Word vna_name; + Elf32_Word vna_next; +} Elf32_Vernaux; + +/* 64 bit versions */ +typedef struct +{ + Elf64_Quarter vd_version; + Elf64_Quarter vd_flags; + Elf64_Quarter vd_ndx; + Elf64_Quarter vd_cnt; + Elf64_Half vd_hash; + Elf64_Half vd_aux; + Elf64_Half vd_next; +} Elf64_Verdef; + +typedef struct +{ + Elf64_Half vda_name; + Elf64_Half vda_next; +} Elf64_Verdaux; + +typedef struct +{ + Elf64_Quarter vn_version; + Elf64_Quarter vn_cnt; + Elf64_Half vn_file; + Elf64_Half vn_aux; + Elf64_Half vn_next; +} Elf64_Verneed; + +typedef struct +{ + Elf64_Half vna_hash; + Elf64_Quarter vna_flags; + Elf64_Quarter vna_other; + Elf64_Half vna_name; + Elf64_Half vna_next; +} Elf64_Vernaux; + +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Quarter Elf64_Versym; + +__ElfType(Verdef); +__ElfType(Verdaux); +__ElfType(Verneed); +__ElfType(Vernaux); +__ElfType(Versym); + +#define VER_DEF_CURRENT 1 +#define VER_DEF_IDX(x) VER_NDX(x) + +#define VER_FLG_BASE 0x01 +#define VER_FLG_WEAK 0x02 + +#define VER_NEED_CURRENT 1 +#define VER_NEED_WEAK (1u << 15) +#define VER_NEED_HIDDEN VER_NDX_HIDDEN +#define VER_NEED_IDX(x) VER_NDX(x) + +#define VER_NDX_LOCAL 0 +#define VER_NDX_GLOBAL 1 +#define VER_NDX_GIVEN 2 + +#define VER_NDX_HIDDEN (1u << 15) +#define VER_NDX(x) ((x) & ~(1u << 15)) + +#endif /* _RTLD_ELF_H_ */ Index: alpha/reloc.c =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/alpha/reloc.c,v retrieving revision 1.20 diff -u -r1.20 reloc.c --- alpha/reloc.c 2 Jun 2005 05:34:08 -0000 1.20 +++ alpha/reloc.c 8 Dec 2005 03:19:35 -0000 @@ -417,6 +417,7 @@ size_t size; const void *srcaddr; const Elf_Sym *srcsym; + const Ver_Entry *ve; Obj_Entry *srcobj; dstaddr = (void *) (dstobj->relocbase + rela->r_offset); @@ -424,9 +425,10 @@ name = dstobj->strtab + dstsym->st_name; hash = elf_hash(name); size = dstsym->st_size; + ve = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) - if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL) + if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL) break; if (srcobj == NULL) { Index: amd64/reloc.c =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/amd64/reloc.c,v retrieving revision 1.15 diff -u -r1.15 reloc.c --- amd64/reloc.c 3 Aug 2004 08:50:59 -0000 1.15 +++ amd64/reloc.c 8 Dec 2005 03:19:01 -0000 @@ -74,15 +74,17 @@ const void *srcaddr; const Elf_Sym *srcsym; Obj_Entry *srcobj; + const Ver_Entry *ve; dstaddr = (void *) (dstobj->relocbase + rela->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); name = dstobj->strtab + dstsym->st_name; hash = elf_hash(name); size = dstsym->st_size; + ve = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) - if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL) + if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL) break; if (srcobj == NULL) { Index: arm/reloc.c =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/arm/reloc.c,v retrieving revision 1.3 diff -u -r1.3 reloc.c --- arm/reloc.c 28 Sep 2004 14:43:12 -0000 1.3 +++ arm/reloc.c 8 Dec 2005 03:16:29 -0000 @@ -41,15 +41,17 @@ const void *srcaddr; const Elf_Sym *srcsym; Obj_Entry *srcobj; + const Ver_Entry *ve; dstaddr = (void *) (dstobj->relocbase + rel->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); name = dstobj->strtab + dstsym->st_name; hash = elf_hash(name); size = dstsym->st_size; + ve = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info)); for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) - if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL) + if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL) break; if (srcobj == NULL) { Index: i386/reloc.c =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/i386/reloc.c,v retrieving revision 1.18 diff -u -r1.18 reloc.c --- i386/reloc.c 29 Jun 2005 23:15:36 -0000 1.18 +++ i386/reloc.c 8 Dec 2005 03:10:26 -0000 @@ -74,6 +74,7 @@ size_t size; const void *srcaddr; const Elf_Sym *srcsym; + const Ver_Entry *ve; Obj_Entry *srcobj; dstaddr = (void *) (dstobj->relocbase + rel->r_offset); @@ -81,9 +82,10 @@ name = dstobj->strtab + dstsym->st_name; hash = elf_hash(name); size = dstsym->st_size; + ve = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info)); for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) - if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL) + if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL) break; if (srcobj == NULL) { Index: powerpc/reloc.c =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/powerpc/reloc.c,v retrieving revision 1.5 diff -u -r1.5 reloc.c --- powerpc/reloc.c 2 Nov 2004 09:47:01 -0000 1.5 +++ powerpc/reloc.c 8 Dec 2005 03:20:12 -0000 @@ -71,6 +71,7 @@ const void *srcaddr; const Elf_Sym *srcsym = NULL; Obj_Entry *srcobj; + const Ver_Entry *ve; if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) { continue; @@ -81,10 +82,11 @@ name = dstobj->strtab + dstsym->st_name; hash = elf_hash(name); size = dstsym->st_size; + ve = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) { - if ((srcsym = symlook_obj(name, hash, srcobj, false)) + if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL) { break; } Index: sparc64/reloc.c =================================================================== RCS file: /home/ncvs/src/libexec/rtld-elf/sparc64/reloc.c,v retrieving revision 1.10 diff -u -r1.10 reloc.c --- sparc64/reloc.c 4 Jun 2005 20:49:15 -0000 1.10 +++ sparc64/reloc.c 8 Dec 2005 03:20:23 -0000 @@ -205,6 +205,7 @@ const Elf_Rela *rela; const Elf_Sym *dstsym; const Elf_Sym *srcsym; + const Ver_Entry *ve; void *dstaddr; const void *srcaddr; Obj_Entry *srcobj; @@ -222,11 +223,12 @@ name = dstobj->strtab + dstsym->st_name; hash = elf_hash(name); size = dstsym->st_size; + ve = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) if ((srcsym = symlook_obj(name, hash, srcobj, - false)) != NULL) + ve, 0)) != NULL) break; if (srcobj == NULL) {