Implement ELF symbol versioning using GNU semantics. This code aims

to be compatible with symbol versioning support as implemented by
GNU libc and documented by http://people.redhat.com/~drepper/symbol-versioning
and LSB 3.0.

Implement dlvsym() function to allow lookups for a specific version of
a given symbol.
This commit is contained in:
Alexander Kabaev 2005-12-18 19:43:33 +00:00
parent 9d51867579
commit 0eb88f2029
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=153515
15 changed files with 663 additions and 135 deletions

View File

@ -131,6 +131,8 @@ void dllockinit(void *_context,
void (*_lock_release)(void *_lock),
void (*_lock_destroy)(void *_lock),
void (*_context_destroy)(void *_context));
void *dlvsym(void * __restrict, const char * __restrict,
const char * __restrict);
#endif /* __BSD_VISIBLE */
__END_DECLS

View File

@ -104,6 +104,15 @@ dlsym(void * __restrict handle, const char * __restrict name)
return NULL;
}
#pragma weak dlvsym
void *
dlvsym(void * __restrict handle, const char * __restrict name,
const char * __restrict version)
{
_rtld_error(sorry);
return NULL;
}
#pragma weak dlinfo
int
dlinfo(void * __restrict handle, int request, void * __restrict p)

View File

@ -417,6 +417,7 @@ do_copy_relocation(Obj_Entry *dstobj, const Elf_Rela *rela)
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 @@ do_copy_relocation(Obj_Entry *dstobj, const Elf_Rela *rela)
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) {

View File

@ -74,15 +74,17 @@ do_copy_relocations(Obj_Entry *dstobj)
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) {

View File

@ -41,15 +41,17 @@ do_copy_relocations(Obj_Entry *dstobj)
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) {

View File

@ -74,6 +74,7 @@ do_copy_relocations(Obj_Entry *dstobj)
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 @@ do_copy_relocations(Obj_Entry *dstobj)
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) {

View File

@ -305,15 +305,19 @@ obj_free(Obj_Entry *obj)
{
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 @@ obj_free(Obj_Entry *obj)
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_new(void)
obj = CNEW(Obj_Entry);
STAILQ_INIT(&obj->dldags);
STAILQ_INIT(&obj->dagmembers);
STAILQ_INIT(&obj->names);
return obj;
}

View File

@ -71,6 +71,7 @@ do_copy_relocations(Obj_Entry *dstobj)
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 @@ do_copy_relocations(Obj_Entry *dstobj)
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;
}

View File

@ -80,10 +80,11 @@ typedef struct Struct_DoneList {
* 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_add(Obj_Entry *);
static void linkmap_delete(Obj_Entry *);
static int load_needed_objects(Obj_Entry *);
static int load_preload_objects(void);
static Obj_Entry *load_object(char *);
static Obj_Entry *load_object(const char *, const Obj_Entry *);
static Obj_Entry *obj_from_addr(const void *);
static void objlist_call_fini(Objlist *);
static void objlist_call_init(Objlist *);
@ -118,17 +119,21 @@ static int relocate_objects(Obj_Entry *, bool, Obj_Entry *);
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_verify_versions(const Objlist *);
static int rtld_verify_object_versions(Obj_Entry *);
static void object_add_name(Obj_Entry *, const char *);
static int object_match_name(const Obj_Entry *, const char *);
void r_debug_state(struct r_debug*, struct link_map*);
@ -182,6 +187,7 @@ static func_ptr_type exports[] = {
(func_ptr_type) &dlerror,
(func_ptr_type) &dlopen,
(func_ptr_type) &dlsym,
(func_ptr_type) &dlvsym,
(func_ptr_type) &dladdr,
(func_ptr_type) &dllockinit,
(func_ptr_type) &dlinfo,
@ -388,6 +394,10 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
obj->refcount++;
}
dbg("checking for required versions");
if (rtld_verify_versions(&list_main) == -1 && !ld_tracing)
die();
if (ld_tracing) { /* We're done */
trace_loaded_objects(obj_main);
exit(0);
@ -555,6 +565,7 @@ digest_dynamic(Obj_Entry *obj, int early)
const Elf_Dyn *dynp;
Needed_Entry **needed_tail = &obj->needed;
const Elf_Dyn *dyn_rpath = NULL;
const Elf_Dyn *dyn_soname = NULL;
int plttype = DT_REL;
obj->bind_now = false;
@ -616,6 +627,29 @@ digest_dynamic(Obj_Entry *obj, int early)
obj->strsize = dynp->d_un.d_val;
break;
case DT_VERNEED:
obj->verneed = (const Elf_Verneed *) (obj->relocbase +
dynp->d_un.d_val);
break;
case DT_VERNEEDNUM:
obj->verneednum = dynp->d_un.d_val;
break;
case DT_VERDEF:
obj->verdef = (const Elf_Verdef *) (obj->relocbase +
dynp->d_un.d_val);
break;
case DT_VERDEFNUM:
obj->verdefnum = dynp->d_un.d_val;
break;
case DT_VERSYM:
obj->versyms = (const Elf_Versym *)(obj->relocbase +
dynp->d_un.d_val);
break;
case DT_HASH:
{
const Elf_Hashelt *hashtab = (const Elf_Hashelt *)
@ -661,7 +695,7 @@ digest_dynamic(Obj_Entry *obj, int early)
break;
case DT_SONAME:
/* Not used by the dynamic linker. */
dyn_soname = dynp;
break;
case DT_INIT:
@ -715,6 +749,9 @@ digest_dynamic(Obj_Entry *obj, int early)
if (dyn_rpath != NULL)
obj->rpath = obj->strtab + dyn_rpath->d_un.d_val;
if (dyn_soname != NULL)
object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val);
}
/*
@ -902,11 +939,12 @@ find_library(const char *xname, const Obj_Entry *refobj)
*/
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 +976,9 @@ find_symdef(unsigned long symnum, const Obj_Entry *refobj,
_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 +1203,9 @@ load_needed_objects(Obj_Entry *first)
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 +1224,11 @@ load_preload_objects(void)
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 +1238,26 @@ load_preload_objects(void)
}
/*
* 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 +1267,77 @@ load_object(char *path)
* 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) { /* 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);
return NULL;
}
}
dbg("loading \"%s\"", path);
obj = map_object(fd, path, &sb);
if (obj != NULL) {
object_add_name(obj, name);
free(path);
close(fd);
if (obj == NULL) {
free(path);
return obj;
}
/* 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;
}
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;
obj->path = path;
digest_dynamic(obj, 0);
object_add_name(obj, name);
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 +1752,7 @@ dlopen(const char *name, int mode)
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 +1764,8 @@ dlopen(const char *name, int mode)
assert(*old_obj_tail == obj);
result = load_needed_objects(obj);
if (result != -1)
result = rtld_verify_versions(&obj->dagmembers);
if (result != -1 && ld_tracing)
goto trace;
@ -1763,8 +1806,9 @@ dlopen(const char *name, int mode)
exit(0);
}
void *
dlsym(void *handle, const char *name)
static void *
do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
int flags)
{
const Obj_Entry *obj;
unsigned long hash;
@ -1775,34 +1819,33 @@ dlsym(void *handle, const char *name)
hash = elf_hash(name);
def = NULL;
defobj = NULL;
flags |= SYMLOOK_IN_PLT;
lockstate = rlock_acquire(rtld_bind_lock);
if (handle == NULL || handle == RTLD_NEXT ||
handle == RTLD_DEFAULT || handle == RTLD_SELF) {
void *retaddr;
retaddr = __builtin_return_address(0); /* __GNUC__ only */
if ((obj = obj_from_addr(retaddr)) == NULL) {
_rtld_error("Cannot determine caller's shared object");
rlock_release(rtld_bind_lock, lockstate);
return NULL;
}
if (handle == NULL) { /* Just the caller's shared object. */
def = symlook_obj(name, hash, obj, true);
def = symlook_obj(name, hash, obj, ve, flags);
defobj = obj;
} else if (handle == RTLD_NEXT || /* Objects after caller's */
handle == RTLD_SELF) { /* ... caller included */
if (handle == RTLD_NEXT)
obj = obj->next;
for (; obj != NULL; obj = obj->next) {
if ((def = symlook_obj(name, hash, obj, true)) != NULL) {
if ((def = symlook_obj(name, hash, obj, ve, flags)) != NULL) {
defobj = obj;
break;
}
}
} else {
assert(handle == RTLD_DEFAULT);
def = symlook_default(name, hash, obj, &defobj, true);
def = symlook_default(name, hash, obj, &defobj, ve, flags);
}
} else {
if ((obj = dlcheck(handle)) == NULL) {
@ -1815,14 +1858,14 @@ dlsym(void *handle, const char *name)
/* 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, ve, flags,
&donelist);
} else {
/*
* XXX - This isn't correct. The search should include the whole
* DAG rooted at the given object.
*/
def = symlook_obj(name, hash, obj, true);
def = symlook_obj(name, hash, obj, ve, flags);
defobj = obj;
}
}
@ -1849,6 +1892,26 @@ dlsym(void *handle, const char *name)
return NULL;
}
void *
dlsym(void *handle, const char *name)
{
return do_dlsym(handle, name, __builtin_return_address(0), NULL,
SYMLOOK_DLSYM);
}
void *
dlvsym(void *handle, const char *name, const char *version)
{
Ver_Entry ventry;
ventry.name = version;
ventry.file = NULL;
ventry.hash = elf_hash(version);
ventry.flags= 0;
return do_dlsym(handle, name, __builtin_return_address(0), &ventry,
SYMLOOK_DLSYM);
}
int
dladdr(const void *addr, Dl_info *info)
{
@ -2163,7 +2226,7 @@ get_program_var_addr(const char *name)
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 +2259,8 @@ set_program_var(const char *name, const void *value)
* 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 +2274,7 @@ symlook_default(const char *name, unsigned long hash,
/* 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 +2283,8 @@ symlook_default(const char *name, unsigned long hash,
/* 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 +2296,8 @@ symlook_default(const char *name, unsigned long hash,
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 +2309,8 @@ symlook_default(const char *name, unsigned long hash,
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 +2325,7 @@ symlook_default(const char *name, unsigned long hash,
* 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 +2339,8 @@ symlook_default(const char *name, unsigned long hash,
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 +2352,7 @@ symlook_list(const char *name, unsigned long hash, Objlist *objlist,
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,37 +2368,107 @@ symlook_list(const char *name, unsigned long hash, Objlist *objlist,
/*
* 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)
{
if (obj->buckets != NULL) {
unsigned long symnum = obj->buckets[hash % obj->nbuckets];
unsigned long symnum;
const Elf_Sym *vsymp;
Elf_Versym verndx;
int vcount;
while (symnum != STN_UNDEF) {
const Elf_Sym *symp;
const char *strp;
if (obj->buckets == NULL)
return NULL;
if (symnum >= obj->nchains)
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;
if (name[0] == strp[0] && strcmp(name, strp) == 0)
return symp->st_shndx != SHN_UNDEF ||
(!in_plt && symp->st_value != 0 &&
ELF_ST_TYPE(symp->st_info) == STT_FUNC) ? symp : NULL;
symp = obj->symtab + symnum;
strp = obj->strtab + symp->st_name;
symnum = obj->chains[symnum];
switch (ELF_ST_TYPE(symp->st_info)) {
case STT_FUNC:
case STT_NOTYPE:
case STT_OBJECT:
if (symp->st_value == 0)
continue;
/* fallthrough */
case STT_TLS:
if (symp->st_shndx != SHN_UNDEF ||
((flags & SYMLOOK_IN_PLT) == 0 &&
ELF_ST_TYPE(symp->st_info) == STT_FUNC))
break;
/* fallthrough */
default:
continue;
}
if (name[0] != strp[0] || strcmp(name, strp) != 0)
continue;
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 >= VER_NDX_GIVEN) {
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. Do not return symbol if we are
* called by dlvsym, because dlvsym looks for a specific
* version and default one is not what dlvsym wants.
*/
if ((flags & SYMLOOK_DLSYM) ||
(obj->versyms[symnum] & VER_NDX_HIDDEN) ||
(verndx >= VER_NDX_GIVEN))
continue;
}
}
return symp;
}
}
return NULL;
return (vcount == 1) ? vsymp : NULL;
}
static void
@ -2476,7 +2611,7 @@ unlink_object(Obj_Entry *root)
objlist_remove(&list_global, root);
/* Remove the object from all objects' DAG lists. */
STAILQ_FOREACH(elm, &root->dagmembers , link) {
STAILQ_FOREACH(elm, &root->dagmembers, link) {
objlist_remove(&elm->obj->dldags, root);
if (elm->obj != root)
unlink_object(elm->obj);
@ -2489,7 +2624,7 @@ ref_dag(Obj_Entry *root)
{
Objlist_Entry *elm;
STAILQ_FOREACH(elm, &root->dagmembers , link)
STAILQ_FOREACH(elm, &root->dagmembers, link)
elm->obj->refcount++;
}
@ -2498,7 +2633,7 @@ unref_dag(Obj_Entry *root)
{
Objlist_Entry *elm;
STAILQ_FOREACH(elm, &root->dagmembers , link)
STAILQ_FOREACH(elm, &root->dagmembers, link)
elm->obj->refcount--;
}
@ -2840,3 +2975,234 @@ _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
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: %s not found in dependency list",
name);
die();
}
static int
check_object_provided_version(Obj_Entry *refobj, const Obj_Entry *depobj,
const Elf_Vernaux *vna)
{
const Elf_Verdef *vd;
const char *vername;
vername = refobj->strtab + vna->vna_name;
vd = depobj->verdef;
if (vd == NULL) {
_rtld_error("%s 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_verify_versions(const Objlist *objlist)
{
Objlist_Entry *entry;
int rc;
rc = 0;
STAILQ_FOREACH(entry, objlist, link) {
/*
* Skip dummy objects or objects that have their version requirements
* already checked.
*/
if (entry->obj->strtab == NULL || entry->obj->vertab != NULL)
continue;
if (rtld_verify_object_versions(entry->obj) == -1) {
rc = -1;
if (ld_tracing == NULL)
break;
}
}
return rc;
}
const Ver_Entry *
fetch_ventry(const Obj_Entry *obj, unsigned long symnum)
{
Elf_Versym vernum;
if (obj->vertab) {
vernum = VER_NDX(obj->versyms[symnum]);
if (vernum >= obj->vernum) {
_rtld_error("%s: symbol %s has wrong verneed value %d",
obj->path, obj->strtab + symnum, vernum);
} else if (obj->vertab[vernum].hash != 0) {
return &obj->vertab[vernum];
}
}
return NULL;
}

View File

@ -90,6 +90,11 @@ typedef struct Struct_Needed_Entry {
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 +112,15 @@ typedef struct Struct_LockInfo {
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 +179,12 @@ typedef struct Struct_Obj_Entry {
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 +193,11 @@ typedef struct Struct_Obj_Entry {
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 +224,11 @@ typedef struct Struct_Obj_Entry {
#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_DLSYM 0x02 /* Return newes versioned symbol. Used by
dlsym. */
/*
* Symbol cache entry used during relocation to avoid multiple lookups
* of the same symbol.
@ -225,20 +255,21 @@ extern void dump_Elf_Rela (Obj_Entry *, const Elf_Rela *, u_long);
*/
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.

View File

@ -205,6 +205,7 @@ do_copy_relocations(Obj_Entry *dstobj)
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 @@ do_copy_relocations(Obj_Entry *dstobj)
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) {

View File

@ -160,4 +160,42 @@ typedef struct {
/* Macro for accessing the fields of st_other. */
#define ELF32_ST_VISIBILITY(oth) ((oth) & 0x3)
/* Structures used by Sun & GNU symbol versioning. */
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;
typedef Elf32_Half Elf32_Versym;
#endif /* !_SYS_ELF32_H_ */

View File

@ -173,4 +173,38 @@ typedef struct {
/* Macro for accessing the fields of st_other. */
#define ELF64_ST_VISIBILITY(oth) ((oth) & 0x3)
/* Structures used by Sun & GNU-style symbol versioning. */
typedef struct {
Elf64_Half vd_version;
Elf64_Half vd_flags;
Elf64_Half vd_ndx;
Elf64_Half vd_cnt;
Elf64_Word vd_hash;
Elf64_Word vd_aux;
Elf64_Word vd_next;
} Elf64_Verdef;
typedef struct {
Elf64_Word vda_name;
Elf64_Word vda_next;
} Elf64_Verdaux;
typedef struct {
Elf64_Half vn_version;
Elf64_Half vn_cnt;
Elf64_Word vn_file;
Elf64_Word vn_aux;
Elf64_Word vn_next;
} Elf64_Verneed;
typedef struct {
Elf64_Word vna_hash;
Elf64_Half vna_flags;
Elf64_Half vna_other;
Elf64_Word vna_name;
Elf64_Word vna_next;
} Elf64_Vernaux;
typedef Elf64_Half Elf64_Versym;
#endif /* !_SYS_ELF64_H_ */

View File

@ -301,6 +301,12 @@ typedef struct {
#define DT_LOPROC 0x70000000 /* First processor-specific type. */
#define DT_HIPROC 0x7fffffff /* Last processor-specific type. */
#define DT_VERSYM 0x6ffffff0 /* Address of versym section. */
#define DT_VERDEF 0x6ffffffc /* Address of verdef section. */
#define DT_VERDEFNUM 0x6ffffffd /* Number of elems in verdef section */
#define DT_VERNEED 0x6ffffffe /* Address of verneed section. */
#define DT_VERNEEDNUM 0x6fffffff /* Number of elems in verneed section */
/* Values for DT_FLAGS */
#define DF_ORIGIN 0x0001 /* Indicates that the object being loaded may
make reference to the $ORIGIN substitution
@ -352,4 +358,23 @@ typedef struct {
/* Special symbol table indexes. */
#define STN_UNDEF 0 /* Undefined symbol index. */
/* Symbol versioning flags. */
#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 /* !_SYS_ELF_COMMON_H_ */

View File

@ -67,6 +67,11 @@ __ElfType(Dyn);
__ElfType(Rel);
__ElfType(Rela);
__ElfType(Sym);
__ElfType(Verdef);
__ElfType(Verdaux);
__ElfType(Verneed);
__ElfType(Vernaux);
__ElfType(Versym);
/* Non-standard ELF types. */
__ElfType(Hashelt);