From 566c4f31f122544715745d4e127eaf47249d194b Mon Sep 17 00:00:00 2001 From: kib Date: Mon, 30 Mar 2009 08:47:28 +0000 Subject: [PATCH] Implement support for RTLD_NODELETE flag for dlopen() and -z nodelete static linker option. Do it by incrementing reference count on the loaded object and its dependencies. Reviewed by: davidxu, kan --- include/dlfcn.h | 1 + libexec/rtld-elf/rtld.c | 22 ++++++++++++++++++---- libexec/rtld-elf/rtld.h | 2 ++ sys/sys/elf_common.h | 1 + 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/include/dlfcn.h b/include/dlfcn.h index 38f21b246812..9fac6204224b 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -47,6 +47,7 @@ #define RTLD_GLOBAL 0x100 /* Make symbols globally available. */ #define RTLD_LOCAL 0 /* Opposite of RTLD_GLOBAL, and the default. */ #define RTLD_TRACE 0x200 /* Trace loaded objects and exit. */ +#define RTLD_NODELETE 0x01000 /* Do not remove members. */ /* * Request arguments for dlinfo(). diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index d98ade7ce25a..07529b3091cf 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -937,6 +937,8 @@ digest_dynamic(Obj_Entry *obj, int early) /* XXX */; if (dynp->d_un.d_val & DF_1_BIND_NOW) obj->bind_now = true; + if (dynp->d_un.d_val & DF_1_NODELETE) + obj->z_nodelete = true; break; default: @@ -1422,15 +1424,21 @@ is_exported(const Elf_Sym *def) static int load_needed_objects(Obj_Entry *first) { - Obj_Entry *obj; + Obj_Entry *obj, *obj1; for (obj = first; obj != NULL; obj = obj->next) { Needed_Entry *needed; for (needed = obj->needed; needed != NULL; needed = needed->next) { - needed->obj = load_object(obj->strtab + needed->name, obj); - if (needed->obj == NULL && !ld_tracing) + obj1 = needed->obj = load_object(obj->strtab + needed->name, obj); + if (obj1 == NULL && !ld_tracing) return -1; + if (obj1 != NULL && obj1->z_nodelete && !obj1->ref_nodel) { + dbg("obj %s nodelete", obj1->path); + init_dag(obj1); + ref_dag(obj1); + obj1->ref_nodel = true; + } } } @@ -1976,12 +1984,13 @@ dlopen(const char *name, int mode) Obj_Entry **old_obj_tail; Obj_Entry *obj; Objlist initlist; - int result, lockstate; + int result, lockstate, nodelete; LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name); ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1"; if (ld_tracing != NULL) environ = (char **)*get_program_var_addr("environ"); + nodelete = mode & RTLD_NODELETE; objlist_init(&initlist); @@ -2029,6 +2038,11 @@ dlopen(const char *name, int mode) if (ld_tracing) goto trace; } + if (obj != NULL && (nodelete || obj->z_nodelete) && !obj->ref_nodel) { + dbg("obj %s nodelete", obj->path); + ref_dag(obj); + obj->z_nodelete = obj->ref_nodel = true; + } } LD_UTRACE(UTRACE_DLOPEN_STOP, obj, NULL, 0, obj ? obj->dl_refcount : 0, diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index f47c21515973..1d7ffb267f10 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -217,6 +217,8 @@ typedef struct Struct_Obj_Entry { bool tls_done : 1; /* Already allocated offset for static TLS */ bool phdr_alloc : 1; /* Phdr is allocated and needs to be freed. */ bool z_origin : 1; /* Process rpath and soname tokens */ + bool z_nodelete : 1; /* Do not unload the object and dependencies */ + bool ref_nodel : 1; /* refcount increased to prevent dlclose */ struct link_map linkmap; /* for GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h index bf64a3b2c768..42b5cac40e38 100644 --- a/sys/sys/elf_common.h +++ b/sys/sys/elf_common.h @@ -469,6 +469,7 @@ typedef struct { /* Values for DT_FLAGS_1 */ #define DF_1_BIND_NOW 0x00000001 /* Same as DF_BIND_NOW */ #define DF_1_GLOBAL 0x00000002 /* Set the RTLD_GLOBAL for object */ +#define DF_1_NODELETE 0x00000008 /* Set the RTLD_NODELETE for object */ #define DF_1_ORIGIN 0x00000080 /* Process $ORIGIN */ /* Values for n_type. Used in core files. */