Browse Source

ldso: support RTLD_NODELETE and DF_1_NODELETE

Honor the nodelete flags so we don't delete shared library if it's
sticky. This is useful for libpthread if it gets pulled in by a
dlopen'ed library.

Signed-off-by: Timo Teräs <timo.teras@iki.fi>
Signed-off-by: Austin Foxley <austinf@cetoncorp.com>
Timo Teräs 15 years ago
parent
commit
b93b98daf0
3 changed files with 22 additions and 13 deletions
  1. 13 7
      ldso/include/dl-elf.h
  2. 6 4
      ldso/ldso/dl-elf.c
  3. 3 2
      ldso/libdl/libdl.c

+ 13 - 7
ldso/include/dl-elf.h

@@ -104,13 +104,15 @@ extern void _dl_protect_relro (struct elf_resolve *l);
 # define DT_GNU_HASH_IDX (DT_RELCONT_IDX + 1)
 # define DT_GNU_HASH_IDX (DT_RELCONT_IDX + 1)
 #endif
 #endif
 
 
-extern void _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
-                                   void *debug_addr, DL_LOADADDR_TYPE load_off);
+extern unsigned int _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
+                                           void *debug_addr, DL_LOADADDR_TYPE load_off);
 
 
 static __always_inline
 static __always_inline
-void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
-                             void *debug_addr, DL_LOADADDR_TYPE load_off)
+unsigned int __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
+                                     void *debug_addr, DL_LOADADDR_TYPE load_off)
 {
 {
+	unsigned int rtld_flags = 0;
+
 	for (; dpnt->d_tag; dpnt++) {
 	for (; dpnt->d_tag; dpnt++) {
 		if (dpnt->d_tag < DT_NUM) {
 		if (dpnt->d_tag < DT_NUM) {
 			dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val;
 			dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val;
@@ -138,9 +140,12 @@ void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
 		} else if (dpnt->d_tag < DT_LOPROC) {
 		} else if (dpnt->d_tag < DT_LOPROC) {
 			if (dpnt->d_tag == DT_RELOCCOUNT)
 			if (dpnt->d_tag == DT_RELOCCOUNT)
 				dynamic_info[DT_RELCONT_IDX] = dpnt->d_un.d_val;
 				dynamic_info[DT_RELCONT_IDX] = dpnt->d_un.d_val;
-			if (dpnt->d_tag == DT_FLAGS_1 &&
-			    (dpnt->d_un.d_val & DF_1_NOW))
-				dynamic_info[DT_BIND_NOW] = 1;
+			if (dpnt->d_tag == DT_FLAGS_1) {
+				if (dpnt->d_un.d_val & DF_1_NOW)
+					dynamic_info[DT_BIND_NOW] = 1;
+				if (dpnt->d_un.d_val & DF_1_NODELETE)
+					rtld_flags |= RTLD_NODELETE;
+			}
 #ifdef __LDSO_GNU_HASH_SUPPORT__
 #ifdef __LDSO_GNU_HASH_SUPPORT__
 			if (dpnt->d_tag == DT_GNU_HASH)
 			if (dpnt->d_tag == DT_GNU_HASH)
 				dynamic_info[DT_GNU_HASH_IDX] = dpnt->d_un.d_ptr;
 				dynamic_info[DT_GNU_HASH_IDX] = dpnt->d_un.d_ptr;
@@ -167,6 +172,7 @@ void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
 	ADJUST_DYN_INFO(DT_GNU_HASH_IDX, load_off);
 	ADJUST_DYN_INFO(DT_GNU_HASH_IDX, load_off);
 #endif
 #endif
 #undef ADJUST_DYN_INFO
 #undef ADJUST_DYN_INFO
+	return rtld_flags;
 }
 }
 
 
 /* Reloc type classes as returned by elf_machine_type_class().
 /* Reloc type classes as returned by elf_machine_type_class().

+ 6 - 4
ldso/ldso/dl-elf.c

@@ -337,6 +337,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure,
 	unsigned long *lpnt;
 	unsigned long *lpnt;
 	unsigned long libaddr;
 	unsigned long libaddr;
 	unsigned long minvma = 0xffffffff, maxvma = 0;
 	unsigned long minvma = 0xffffffff, maxvma = 0;
+	unsigned int rtld_flags;
 	int i, flags, piclib, infile;
 	int i, flags, piclib, infile;
 	ElfW(Addr) relro_addr = 0;
 	ElfW(Addr) relro_addr = 0;
 	size_t relro_size = 0;
 	size_t relro_size = 0;
@@ -700,7 +701,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure,
 
 
 	dpnt = (ElfW(Dyn) *) dynamic_addr;
 	dpnt = (ElfW(Dyn) *) dynamic_addr;
 	_dl_memset(dynamic_info, 0, sizeof(dynamic_info));
 	_dl_memset(dynamic_info, 0, sizeof(dynamic_info));
-	_dl_parse_dynamic_info(dpnt, dynamic_info, NULL, lib_loadaddr);
+	rtld_flags = _dl_parse_dynamic_info(dpnt, dynamic_info, NULL, lib_loadaddr);
 	/* If the TEXTREL is set, this means that we need to make the pages
 	/* If the TEXTREL is set, this means that we need to make the pages
 	   writable before we perform relocations.  Do this now. They get set
 	   writable before we perform relocations.  Do this now. They get set
 	   back again later. */
 	   back again later. */
@@ -732,6 +733,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure,
 	tpnt->st_ino = st.st_ino;
 	tpnt->st_ino = st.st_ino;
 	tpnt->ppnt = (ElfW(Phdr) *) DL_RELOC_ADDR(tpnt->loadaddr, epnt->e_phoff);
 	tpnt->ppnt = (ElfW(Phdr) *) DL_RELOC_ADDR(tpnt->loadaddr, epnt->e_phoff);
 	tpnt->n_phent = epnt->e_phnum;
 	tpnt->n_phent = epnt->e_phnum;
+	tpnt->rtld_flags |= rtld_flags;
 
 
 #if defined(USE_TLS) && USE_TLS
 #if defined(USE_TLS) && USE_TLS
 	if (tlsppnt) {
 	if (tlsppnt) {
@@ -991,8 +993,8 @@ char *_dl_strdup(const char *string)
 	return retval;
 	return retval;
 }
 }
 
 
-void _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
-                            void *debug_addr, DL_LOADADDR_TYPE load_off)
+unsigned int _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
+                                    void *debug_addr, DL_LOADADDR_TYPE load_off)
 {
 {
-	__dl_parse_dynamic_info(dpnt, dynamic_info, debug_addr, load_off);
+	return __dl_parse_dynamic_info(dpnt, dynamic_info, debug_addr, load_off);
 }
 }

+ 3 - 2
ldso/libdl/libdl.c

@@ -736,7 +736,7 @@ static int do_dlclose(void *vhandle, int need_fini)
 		_dl_handles = rpnt->next_handle;
 		_dl_handles = rpnt->next_handle;
 	_dl_if_debug_print("%s: usage count: %d\n",
 	_dl_if_debug_print("%s: usage count: %d\n",
 			handle->dyn->libname, handle->dyn->usage_count);
 			handle->dyn->libname, handle->dyn->usage_count);
-	if (handle->dyn->usage_count != 1) {
+	if (handle->dyn->usage_count != 1 || (handle->dyn->rtld_flags & RTLD_NODELETE)) {
 		handle->dyn->usage_count--;
 		handle->dyn->usage_count--;
 		free(handle);
 		free(handle);
 		return 0;
 		return 0;
@@ -744,7 +744,8 @@ static int do_dlclose(void *vhandle, int need_fini)
 	/* OK, this is a valid handle - now close out the file */
 	/* OK, this is a valid handle - now close out the file */
 	for (j = 0; j < handle->init_fini.nlist; ++j) {
 	for (j = 0; j < handle->init_fini.nlist; ++j) {
 		tpnt = handle->init_fini.init_fini[j];
 		tpnt = handle->init_fini.init_fini[j];
-		if (--tpnt->usage_count == 0) {
+		tpnt->usage_count--;
+		if (tpnt->usage_count == 0 && !(tpnt->rtld_flags & RTLD_NODELETE)) {
 			if ((tpnt->dynamic_info[DT_FINI]
 			if ((tpnt->dynamic_info[DT_FINI]
 			     || tpnt->dynamic_info[DT_FINI_ARRAY])
 			     || tpnt->dynamic_info[DT_FINI_ARRAY])
 			 && need_fini
 			 && need_fini