summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf/rtld.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/rtld-elf/rtld.c')
-rw-r--r--libexec/rtld-elf/rtld.c250
1 files changed, 135 insertions, 115 deletions
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 146b9b2..6a1ccf3 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -58,16 +58,15 @@
/* Types. */
typedef void (*func_ptr_type)();
-typedef struct Struct_LockInfo {
- void *context; /* Client context for creating locks */
- void *thelock; /* The one big lock */
- /* Methods */
- void (*rlock_acquire)(void *lock);
- void (*wlock_acquire)(void *lock);
- void (*lock_release)(void *lock);
- void (*lock_destroy)(void *lock);
- void (*context_destroy)(void *context);
-} LockInfo;
+/*
+ * This structure provides a reentrant way to keep a list of objects and
+ * check which ones have already been processed in some way.
+ */
+typedef struct Struct_DoneList {
+ Obj_Entry **objs; /* Array of object pointers */
+ unsigned int num_alloc; /* Allocated size of the array */
+ unsigned int num_used; /* Number of array slots used */
+} DoneList;
/*
* Function declarations.
@@ -77,6 +76,7 @@ static void die(void);
static void digest_dynamic(Obj_Entry *);
static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
static Obj_Entry *dlcheck(void *);
+static bool donelist_check(DoneList *, Obj_Entry *);
static char *find_library(const char *, const Obj_Entry *);
static void funclist_call(Funclist *);
static void funclist_clear(Funclist *);
@@ -85,7 +85,7 @@ static void funclist_push_head(Funclist *, InitFunc);
static void funclist_push_tail(Funclist *, InitFunc);
static const char *gethints(void);
static void init_dag(Obj_Entry *);
-static void init_dag1(Obj_Entry *root, Obj_Entry *obj);
+static void init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *);
static void init_rtld(caddr_t);
static bool is_exported(const Elf_Sym *);
static void linkmap_add(Obj_Entry *);
@@ -93,18 +93,17 @@ 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 void lock_nop(void *);
+static void lock_check(void);
static Obj_Entry *obj_from_addr(const void *);
static void objlist_add(Objlist *, Obj_Entry *);
static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *);
static void objlist_remove(Objlist *, Obj_Entry *);
-static void prebind(void *);
static int relocate_objects(Obj_Entry *, bool);
static void rtld_exit(void);
static char *search_library_path(const char *, const char *);
static void set_program_var(const char *, const void *);
static const Elf_Sym *symlook_list(const char *, unsigned long,
- Objlist *, const Obj_Entry **, bool in_plt);
+ Objlist *, const Obj_Entry **, bool in_plt, DoneList *);
static void trace_loaded_objects(Obj_Entry *obj);
static void unload_object(Obj_Entry *);
static void unref_dag(Obj_Entry *);
@@ -128,7 +127,7 @@ static Obj_Entry *obj_list; /* Head of linked list of shared objects */
static Obj_Entry **obj_tail; /* Link field of last object in list */
static Obj_Entry *obj_main; /* The main program shared object */
static Obj_Entry obj_rtld; /* The dynamic linker shared object */
-static unsigned long curmark; /* Current mark value */
+static unsigned int obj_count; /* Number of objects in obj_list */
static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */
STAILQ_HEAD_INITIALIZER(list_global);
@@ -167,22 +166,45 @@ static func_ptr_type exports[] = {
char *__progname;
char **environ;
+/*
+ * Fill in a DoneList with an allocation large enough to hold all of
+ * the currently-loaded objects. Keep this as a macro since it calls
+ * alloca and we want that to occur within the scope of the caller.
+ */
+#define donelist_init(dlp) \
+ ((dlp)->objs = alloca(obj_count * sizeof (dlp)->objs[0]), \
+ assert((dlp)->objs != NULL), \
+ (dlp)->num_alloc = obj_count, \
+ (dlp)->num_used = 0)
+
static __inline void
rlock_acquire(void)
{
lockinfo.rlock_acquire(lockinfo.thelock);
+ atomic_incr_int(&lockinfo.rcount);
+ lock_check();
}
static __inline void
wlock_acquire(void)
{
lockinfo.wlock_acquire(lockinfo.thelock);
+ atomic_incr_int(&lockinfo.wcount);
+ lock_check();
}
static __inline void
-lock_release(void)
+rlock_release(void)
{
- lockinfo.lock_release(lockinfo.thelock);
+ atomic_decr_int(&lockinfo.rcount);
+ lockinfo.rlock_release(lockinfo.thelock);
+}
+
+static __inline void
+wlock_release(void)
+{
+ atomic_decr_int(&lockinfo.wcount);
+ lockinfo.wlock_release(lockinfo.thelock);
}
/*
@@ -316,6 +338,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
/* Link the main program into the list of objects. */
*obj_tail = obj_main;
obj_tail = &obj_main->next;
+ obj_count++;
obj_main->refcount++;
/* Initialize a fake symbol for resolving undefined weak references. */
@@ -358,15 +381,16 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
set_program_var("__progname", argv[0] != NULL ? basename(argv[0]) : "");
set_program_var("environ", env);
- dbg("initializing default locks");
- dllockinit(NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ dbg("initializing thread locks");
+ lockdflt_init(&lockinfo);
+ lockinfo.thelock = lockinfo.lock_create(lockinfo.context);
r_debug_state(); /* say hello to gdb! */
funclist_call(&initlist);
wlock_acquire();
funclist_clear(&initlist);
- lock_release();
+ wlock_release();
dbg("transferring control to program entry point = %p", obj_main->entry);
@@ -385,7 +409,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Word reloff)
Elf_Addr *where;
Elf_Addr target;
- wlock_acquire();
+ rlock_acquire();
if (obj->pltrel)
rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff);
else
@@ -403,7 +427,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Word reloff)
(void *)target, basename(defobj->path));
reloc_jmpslot(where, target);
- lock_release();
+ rlock_release();
return target;
}
@@ -671,6 +695,29 @@ dlcheck(void *handle)
}
/*
+ * If the given object is already in the donelist, return true. Otherwise
+ * add the object to the list and return false.
+ */
+static bool
+donelist_check(DoneList *dlp, Obj_Entry *obj)
+{
+ unsigned int i;
+
+ for (i = 0; i < dlp->num_used; i++)
+ if (dlp->objs[i] == obj)
+ return true;
+ /*
+ * Our donelist allocation should always be sufficient. But if
+ * our threads locking isn't working properly, more shared objects
+ * could have been loaded since we allocated the list. That should
+ * never happen, but we'll handle it properly just in case it does.
+ */
+ if (dlp->num_used < dlp->num_alloc)
+ dlp->objs[dlp->num_used++] = obj;
+ return false;
+}
+
+/*
* Hash function for symbol table lookup. Don't even think about changing
* this. It is specified by the System V ABI.
*/
@@ -741,6 +788,7 @@ const Elf_Sym *
find_symdef(unsigned long symnum, Obj_Entry *refobj,
const Obj_Entry **defobj_out, bool in_plt)
{
+ DoneList donelist;
const Elf_Sym *ref;
const Elf_Sym *def;
const Elf_Sym *symp;
@@ -755,11 +803,11 @@ find_symdef(unsigned long symnum, Obj_Entry *refobj,
hash = elf_hash(name);
def = NULL;
defobj = NULL;
- curmark++;
+ donelist_init(&donelist);
- if (refobj->symbolic) { /* Look first in the referencing object */
+ /* Look first in the referencing object if linked symbolically. */
+ if (refobj->symbolic && !donelist_check(&donelist, refobj)) {
symp = symlook_obj(name, hash, refobj, in_plt);
- refobj->mark = curmark;
if (symp != NULL) {
def = symp;
defobj = refobj;
@@ -768,7 +816,7 @@ find_symdef(unsigned long symnum, Obj_Entry *refobj,
/* 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);
+ symp = symlook_list(name, hash, &list_main, &obj, in_plt, &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -780,7 +828,8 @@ find_symdef(unsigned long symnum, Obj_Entry *refobj,
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);
+ symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt,
+ &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -790,7 +839,7 @@ find_symdef(unsigned long symnum, Obj_Entry *refobj,
/* Search all RTLD_GLOBAL objects. */
if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
- symp = symlook_list(name, hash, &list_global, &obj, in_plt);
+ symp = symlook_list(name, hash, &list_global, &obj, in_plt, &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -919,23 +968,24 @@ gethints(void)
static void
init_dag(Obj_Entry *root)
{
- curmark++;
- init_dag1(root, root);
+ DoneList donelist;
+
+ donelist_init(&donelist);
+ init_dag1(root, root, &donelist);
}
static void
-init_dag1(Obj_Entry *root, Obj_Entry *obj)
+init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *dlp)
{
const Needed_Entry *needed;
- if (obj->mark == curmark)
+ if (donelist_check(dlp, obj))
return;
- obj->mark = curmark;
objlist_add(&obj->dldags, root);
objlist_add(&root->dagmembers, obj);
for (needed = obj->needed; needed != NULL; needed = needed->next)
if (needed->obj != NULL)
- init_dag1(root, needed->obj);
+ init_dag1(root, needed->obj, dlp);
}
/*
@@ -971,6 +1021,7 @@ init_rtld(caddr_t mapbase)
*/
obj_list = &obj_rtld;
obj_tail = &obj_rtld.next;
+ obj_count = 1;
relocate_objects(&obj_rtld, true);
}
@@ -978,6 +1029,7 @@ init_rtld(caddr_t mapbase)
/* Make the object list empty again. */
obj_list = NULL;
obj_tail = &obj_list;
+ obj_count = 0;
/* Replace the path with a dynamically allocated copy. */
obj_rtld.path = xstrdup(obj_rtld.path);
@@ -1118,6 +1170,7 @@ load_object(char *path)
*obj_tail = obj;
obj_tail = &obj->next;
+ obj_count++;
linkmap_add(obj); /* for GDB */
dbg(" %p .. %p: %s", obj->mapbase,
@@ -1131,9 +1184,24 @@ load_object(char *path)
return obj;
}
+/*
+ * Check for locking violations and die if one is found.
+ */
static void
-lock_nop(void *lock)
+lock_check(void)
{
+ int rcount, wcount;
+
+ rcount = lockinfo.rcount;
+ wcount = lockinfo.wcount;
+ assert(rcount >= 0);
+ assert(wcount >= 0);
+ if (wcount > 1 || (wcount != 0 && rcount != 0)) {
+ _rtld_error("Application locking error: %d readers and %d writers"
+ " in dynamic linker. See DLLOCKINIT(3) in manual pages.",
+ rcount, wcount);
+ die();
+ }
}
static Obj_Entry *
@@ -1317,7 +1385,7 @@ dlclose(void *handle)
wlock_acquire();
root = dlcheck(handle);
if (root == NULL) {
- lock_release();
+ wlock_release();
return -1;
}
@@ -1336,7 +1404,7 @@ dlclose(void *handle)
if (obj->refcount == 0 && obj->fini != NULL)
funclist_push_tail(&finilist, obj->fini);
- lock_release();
+ wlock_release();
funclist_call(&finilist);
wlock_acquire();
funclist_clear(&finilist);
@@ -1346,7 +1414,7 @@ dlclose(void *handle)
unload_object(root);
GDB_STATE(RT_CONSISTENT);
}
- lock_release();
+ wlock_release();
return 0;
}
@@ -1358,6 +1426,9 @@ dlerror(void)
return msg;
}
+/*
+ * This function is deprecated and has no effect.
+ */
void
dllockinit(void *context,
void *(*lock_create)(void *context),
@@ -1367,68 +1438,14 @@ dllockinit(void *context,
void (*lock_destroy)(void *lock),
void (*context_destroy)(void *context))
{
- bool is_dflt = false;
-
- /* NULL arguments mean reset to the built-in locks. */
- if (lock_create == NULL) {
- is_dflt = true;
- context = NULL;
- lock_create = lockdflt_create;
- rlock_acquire = wlock_acquire = lockdflt_acquire;
- lock_release = lockdflt_release;
- lock_destroy = lockdflt_destroy;
- context_destroy = NULL;
- }
-
- /* Temporarily set locking methods to no-ops. */
- lockinfo.rlock_acquire = lock_nop;
- lockinfo.wlock_acquire = lock_nop;
- lockinfo.lock_release = lock_nop;
-
- /* Release any existing locks and context. */
- if (lockinfo.lock_destroy != NULL)
- lockinfo.lock_destroy(lockinfo.thelock);
- if (lockinfo.context_destroy != NULL)
- lockinfo.context_destroy(lockinfo.context);
-
- /*
- * Make sure the shared objects containing the locking methods are
- * fully bound, to avoid infinite recursion when they are called
- * from the lazy binding code.
- */
- if (!is_dflt) {
- prebind((void *)rlock_acquire);
- prebind((void *)wlock_acquire);
- prebind((void *)lock_release);
- }
-
- /* Allocate our lock. */
- lockinfo.thelock = lock_create(lockinfo.context);
-
- /* Record the new method information. */
- lockinfo.context = context;
- lockinfo.rlock_acquire = rlock_acquire;
- lockinfo.wlock_acquire = wlock_acquire;
- lockinfo.lock_release = lock_release;
- lockinfo.lock_destroy = lock_destroy;
- lockinfo.context_destroy = context_destroy;
-}
-
-static void
-prebind(void *addr)
-{
- Obj_Entry *obj;
-
- if ((obj = obj_from_addr(addr)) == NULL) {
- _rtld_error("Cannot determine shared object of locking method at %p",
- addr);
- die();
- }
- if (!obj->rtld && !obj->jmpslots_done) {
- dbg("Pre-binding %s for locking", obj->path);
- if (reloc_jmpslots(obj) == -1)
- die();
- }
+ static void *cur_context;
+ static void (*cur_context_destroy)(void *);
+
+ /* Just destroy the context from the previous call, if necessary. */
+ if (cur_context_destroy != NULL)
+ cur_context_destroy(cur_context);
+ cur_context = context;
+ cur_context_destroy = context_destroy;
}
void *
@@ -1482,11 +1499,11 @@ dlopen(const char *name, int mode)
GDB_STATE(RT_CONSISTENT);
/* Call the init functions with no locks held. */
- lock_release();
+ wlock_release();
funclist_call(&initlist);
wlock_acquire();
funclist_clear(&initlist);
- lock_release();
+ wlock_release();
return obj;
}
@@ -1502,14 +1519,14 @@ dlsym(void *handle, const char *name)
def = NULL;
defobj = NULL;
- wlock_acquire();
+ rlock_acquire();
if (handle == NULL || handle == RTLD_NEXT) {
void *retaddr;
retaddr = __builtin_return_address(0); /* __GNUC__ only */
if ((obj = obj_from_addr(retaddr)) == NULL) {
_rtld_error("Cannot determine caller's shared object");
- lock_release();
+ rlock_release();
return NULL;
}
if (handle == NULL) { /* Just the caller's shared object. */
@@ -1525,14 +1542,17 @@ dlsym(void *handle, const char *name)
}
} else {
if ((obj = dlcheck(handle)) == NULL) {
- lock_release();
+ rlock_release();
return NULL;
}
if (obj->mainprog) {
+ DoneList donelist;
+
/* Search main program and all libraries loaded by it. */
- curmark++;
- def = symlook_list(name, hash, &list_main, &defobj, true);
+ donelist_init(&donelist);
+ def = symlook_list(name, hash, &list_main, &defobj, true,
+ &donelist);
} else {
/*
* XXX - This isn't correct. The search should include the whole
@@ -1544,12 +1564,12 @@ dlsym(void *handle, const char *name)
}
if (def != NULL) {
- lock_release();
+ rlock_release();
return defobj->relocbase + def->st_value;
}
_rtld_error("Undefined symbol \"%s\"", name);
- lock_release();
+ rlock_release();
return NULL;
}
@@ -1561,11 +1581,11 @@ dladdr(const void *addr, Dl_info *info)
void *symbol_addr;
unsigned long symoffset;
- wlock_acquire();
+ rlock_acquire();
obj = obj_from_addr(addr);
if (obj == NULL) {
_rtld_error("No shared object contains address");
- lock_release();
+ rlock_release();
return 0;
}
info->dli_fname = obj->path;
@@ -1604,7 +1624,7 @@ dladdr(const void *addr, Dl_info *info)
if (info->dli_saddr == addr)
break;
}
- lock_release();
+ rlock_release();
return 1;
}
@@ -1695,7 +1715,7 @@ set_program_var(const char *name, const void *value)
static const Elf_Sym *
symlook_list(const char *name, unsigned long hash, Objlist *objlist,
- const Obj_Entry **defobj_out, bool in_plt)
+ const Obj_Entry **defobj_out, bool in_plt, DoneList *dlp)
{
const Elf_Sym *symp;
const Elf_Sym *def;
@@ -1705,9 +1725,8 @@ symlook_list(const char *name, unsigned long hash, Objlist *objlist,
def = NULL;
defobj = NULL;
STAILQ_FOREACH(elm, objlist, link) {
- if (elm->obj->mark == curmark)
+ if (donelist_check(dlp, elm->obj))
continue;
- elm->obj->mark = curmark;
if ((symp = symlook_obj(name, hash, elm->obj, in_plt)) != NULL) {
if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) {
def = symp;
@@ -1877,6 +1896,7 @@ unload_object(Obj_Entry *root)
munmap(obj->mapbase, obj->mapsize);
linkmap_delete(obj);
*linkp = obj->next;
+ obj_count--;
obj_free(obj);
} else
linkp = &obj->next;
OpenPOWER on IntegriCloud