diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 0f5aaba..b3c469c 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -553,9 +553,11 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) Elf_Addr _rtld_bind(Obj_Entry *obj, Elf_Size reloff) { + const Objlist_Entry *dag, *elm; const Elf_Rel *rel; const Elf_Sym *def; const Obj_Entry *defobj; + Obj_Entry *global_obj; Elf_Addr *where; Elf_Addr target; RtldLockState lockstate; @@ -569,11 +571,44 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) rel = (const Elf_Rel *) ((caddr_t) obj->pltrela + reloff); where = (Elf_Addr *) (obj->relocbase + rel->r_offset); - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL, - &lockstate); + def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT, + NULL, &lockstate); if (def == NULL) die(); + /* + * Heavy weight checking if defobj shares any DAGs with refobj. + * There's gotta be a way to do this in better way. + */ + elm = NULL; + global_obj = NULL; + STAILQ_FOREACH(dag, &defobj->dldags, link) { + /* Remember first global DAG defobj belongs to. */ + if (global_obj == NULL && dag->obj->global) + global_obj = dag->obj; + STAILQ_FOREACH(elm, &dag->obj->dagmembers, link) { + if (elm->obj == obj) + break; + } + } + + /* + * Symbol referencing and defining object are not part + * of common dependency chain. We need to establish a + * dynamic dependency between them to prevent defining + * object from going away from under object that has + * grown a dependency on it. + */ + if (elm == NULL && global_obj != NULL) { + lock_restart_for_upgrade(&lockstate); + /* + * We are running with an exclusive lock at this time, so it + * safe to massage defobj into a new dependency. For giggles, + * just prevent defobj from being unloaded for now. + */ + ref_dag(global_obj); + } + target = (Elf_Addr)(defobj->relocbase + def->st_value); dbg("\"%s\" in \"%s\" ==> %p in \"%s\"", @@ -2183,8 +2218,10 @@ dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode) if (obj) { obj->dl_refcount++; - if (mode & RTLD_GLOBAL && objlist_find(&list_global, obj) == NULL) + if (mode & RTLD_GLOBAL && !obj->global) { objlist_push_tail(&list_global, obj); + obj->global = true; + } if (*old_obj_tail != NULL) { /* We loaded something new. */ assert(*old_obj_tail == obj); result = load_needed_objects(obj, lo_flags & RTLD_LO_DLOPEN); @@ -3307,7 +3344,10 @@ unlink_object(Obj_Entry *root) if (root->refcount == 0) { /* Remove the object from the RTLD_GLOBAL list. */ - objlist_remove(&list_global, root); + if (root->global) { + objlist_remove(&list_global, root); + root->global = false; + } /* Remove the object from all objects' DAG lists. */ STAILQ_FOREACH(elm, &root->dagmembers, link) { diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 011dcc3..e020acd 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -230,6 +230,7 @@ typedef struct Struct_Obj_Entry { bool on_fini_list: 1; /* Object is already on fini list. */ bool dag_inited : 1; /* Object has its DAG initialized. */ bool filtees_loaded : 1; /* Filtees loaded */ + bool global : 1; /* Object is a root of a global DAG. */ struct link_map linkmap; /* For GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */