diff options
author | kib <kib@FreeBSD.org> | 2012-04-30 13:29:21 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2012-04-30 13:29:21 +0000 |
commit | 8de997e0528cf3b3d79049e6a2e6901bd746a076 (patch) | |
tree | 681fef8aca874b75de3886c0099dcde0d9ea3a45 | |
parent | ad64077869a527becbbbfbd77ec33f4246f66797 (diff) | |
download | FreeBSD-src-8de997e0528cf3b3d79049e6a2e6901bd746a076.zip FreeBSD-src-8de997e0528cf3b3d79049e6a2e6901bd746a076.tar.gz |
Split the symlook_obj1 into a loop iterating over the ELF object symbol
hash elements, and a helper matched_symbol() which match the given hash
entry and request, performing needed type and version checks.
Based on dragonflybsd support for GNU hash by John Marino <draco marino st>
Reviewed by: kan
Tested by: bapt
MFC after: 2 weeks
-rw-r--r-- | libexec/rtld-elf/rtld.c | 205 | ||||
-rw-r--r-- | libexec/rtld-elf/rtld.h | 6 |
2 files changed, 116 insertions, 95 deletions
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 1def854..b92d404 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1,7 +1,8 @@ /*- * 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>. + * Copyright 2009-2012 Konstantin Belousov <kib@FreeBSD.ORG>. + * Copyright 2012 John Marino <draco@marino.st>. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -149,6 +150,8 @@ static int object_match_name(const Obj_Entry *, const char *); static void ld_utrace_log(int, void *, void *, size_t, int, const char *); static void rtld_fill_dl_phdr_info(const Obj_Entry *obj, struct dl_phdr_info *phdr_info); +static bool matched_symbol(SymLook *, const Obj_Entry *, Sym_Match_Result *, + const unsigned long); void r_debug_state(struct r_debug *, struct link_map *) __noinline; @@ -3439,28 +3442,15 @@ symlook_obj(SymLook *req, const Obj_Entry *obj) return (mres); } -static int -symlook_obj1(SymLook *req, const Obj_Entry *obj) +/* Symbol match routine common to both hash functions */ +static bool +matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result, + const unsigned long symnum) { - unsigned long symnum; - const Elf_Sym *vsymp; - Elf_Versym verndx; - int vcount; - - if (obj->buckets == NULL) - return (ESRCH); - - vsymp = NULL; - vcount = 0; - symnum = obj->buckets[req->hash % obj->nbuckets]; - - for (; symnum != STN_UNDEF; symnum = obj->chains[symnum]) { + Elf_Versym verndx; const Elf_Sym *symp; const char *strp; - if (symnum >= obj->nchains) - return (ESRCH); /* Bad object */ - symp = obj->symtab + symnum; strp = obj->strtab + symp->st_name; @@ -3468,103 +3458,128 @@ symlook_obj1(SymLook *req, const Obj_Entry *obj) case STT_FUNC: case STT_NOTYPE: case STT_OBJECT: + case STT_COMMON: case STT_GNU_IFUNC: - if (symp->st_value == 0) - continue; + if (symp->st_value == 0) + return (false); /* fallthrough */ case STT_TLS: - if (symp->st_shndx != SHN_UNDEF) - break; + if (symp->st_shndx != SHN_UNDEF) + break; #ifndef __mips__ - else if (((req->flags & SYMLOOK_IN_PLT) == 0) && - (ELF_ST_TYPE(symp->st_info) == STT_FUNC)) - break; + else if (((req->flags & SYMLOOK_IN_PLT) == 0) && + (ELF_ST_TYPE(symp->st_info) == STT_FUNC)) + break; /* fallthrough */ #endif default: - continue; + return (false); } if (req->name[0] != strp[0] || strcmp(req->name, strp) != 0) - continue; + return (false); if (req->ventry == NULL) { - if (obj->versyms != NULL) { - verndx = VER_NDX(obj->versyms[symnum]); - if (verndx > obj->vernum) { - _rtld_error("%s: symbol %s references wrong version %d", - obj->path, obj->strtab + symnum, verndx); - continue; - } - /* - * If we are not called from dlsym (i.e. this is a normal - * relocation from unversioned binary), accept the symbol - * immediately if it happens to have first version after - * this shared object became versioned. Otherwise, if - * symbol is versioned and not hidden, remember it. If it - * is the only symbol with this name exported by the - * shared object, it will be returned as a match at the - * end of the function. If symbol is global (verndx < 2) - * accept it unconditionally. - */ - if ((req->flags & SYMLOOK_DLSYM) == 0 && - verndx == VER_NDX_GIVEN) { - req->sym_out = symp; - req->defobj_out = obj; - return (0); - } - else if (verndx >= VER_NDX_GIVEN) { - if ((obj->versyms[symnum] & VER_NDX_HIDDEN) == 0) { - if (vsymp == NULL) - vsymp = symp; - vcount ++; - } - continue; + 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); + return (false); + } + /* + * 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 by the calling + * function. If symbol is global (verndx < 2) + * accept it unconditionally. + */ + if ((req->flags & SYMLOOK_DLSYM) == 0 && + verndx == VER_NDX_GIVEN) { + result->sym_out = symp; + return (true); + } + else if (verndx >= VER_NDX_GIVEN) { + if ((obj->versyms[symnum] & VER_NDX_HIDDEN) + == 0) { + if (result->vsymp == NULL) + result->vsymp = symp; + result->vcount++; + } + return (false); + } } - } - req->sym_out = symp; - req->defobj_out = obj; - return (0); - } else { - if (obj->versyms == NULL) { + result->sym_out = symp; + return (true); + } + if (obj->versyms == NULL) { if (object_match_name(obj, req->ventry->name)) { - _rtld_error("%s: object %s should provide version %s for " - "symbol %s", obj_rtld.path, obj->path, - req->ventry->name, obj->strtab + symnum); - continue; + _rtld_error("%s: object %s should provide version %s " + "for symbol %s", obj_rtld.path, obj->path, + req->ventry->name, obj->strtab + symnum); + return (false); } - } else { + } 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; + _rtld_error("%s: symbol %s references wrong version %d", + obj->path, obj->strtab + symnum, verndx); + return (false); } if (obj->vertab[verndx].hash != req->ventry->hash || strcmp(obj->vertab[verndx].name, req->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 ((req->flags & SYMLOOK_DLSYM) || - (obj->versyms[symnum] & VER_NDX_HIDDEN) || - (verndx >= VER_NDX_GIVEN)) - continue; + /* + * 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 ((req->flags & SYMLOOK_DLSYM) || + (verndx >= VER_NDX_GIVEN) || + (obj->versyms[symnum] & VER_NDX_HIDDEN)) + return (false); } - } - req->sym_out = symp; - req->defobj_out = obj; - return (0); } - } - if (vcount == 1) { - req->sym_out = vsymp; - req->defobj_out = obj; - return (0); - } - return (ESRCH); + result->sym_out = symp; + return (true); +} + +static int +symlook_obj1(SymLook *req, const Obj_Entry *obj) +{ + unsigned long symnum; + Sym_Match_Result matchres; + + matchres.sym_out = NULL; + matchres.vsymp = NULL; + matchres.vcount = 0; + + for (symnum = obj->buckets[req->hash % obj->nbuckets]; + symnum != STN_UNDEF; symnum = obj->chains[symnum]) { + if (symnum >= obj->nchains) + return (ESRCH); /* Bad object */ + + if (matched_symbol(req, obj, &matchres, symnum)) { + req->sym_out = matchres.sym_out; + req->defobj_out = obj; + return (0); + } + } + if (matchres.vcount == 1) { + req->sym_out = matchres.vsymp; + req->defobj_out = obj; + return (0); + } + return (ESRCH); } static void diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 6017957..314e522 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -126,6 +126,12 @@ typedef struct Struct_Ver_Entry { const char *file; } Ver_Entry; +typedef struct Struct_Sym_Match_Result { + const Elf_Sym *sym_out; + const Elf_Sym *vsymp; + int vcount; +} Sym_Match_Result; + #define VER_INFO_HIDDEN 0x01 /* |