summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2010-04-22 08:28:10 +0000
committerAustin Foxley <austinf@cetoncorp.com>2010-04-22 08:52:17 -0700
commitb93b98daf0dd45ac52f99fc4d906e5926cdd5239 (patch)
tree66c617599c0d459a479adc70c33d93c0631fbd8c
parentdfedf78cc696bb51069ca591c3be8f05018d5be1 (diff)
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>
-rw-r--r--ldso/include/dl-elf.h20
-rw-r--r--ldso/ldso/dl-elf.c10
-rw-r--r--ldso/libdl/libdl.c5
3 files changed, 22 insertions, 13 deletions
diff --git a/ldso/include/dl-elf.h b/ldso/include/dl-elf.h
index 076678cfc..9c2888f5b 100644
--- a/ldso/include/dl-elf.h
+++ b/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)
#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
-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++) {
if (dpnt->d_tag < DT_NUM) {
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) {
if (dpnt->d_tag == DT_RELOCCOUNT)
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__
if (dpnt->d_tag == DT_GNU_HASH)
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);
#endif
#undef ADJUST_DYN_INFO
+ return rtld_flags;
}
/* Reloc type classes as returned by elf_machine_type_class().
diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c
index 8fb8ffa2d..5562e0784 100644
--- a/ldso/ldso/dl-elf.c
+++ b/ldso/ldso/dl-elf.c
@@ -337,6 +337,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure,
unsigned long *lpnt;
unsigned long libaddr;
unsigned long minvma = 0xffffffff, maxvma = 0;
+ unsigned int rtld_flags;
int i, flags, piclib, infile;
ElfW(Addr) relro_addr = 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;
_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
writable before we perform relocations. Do this now. They get set
back again later. */
@@ -732,6 +733,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure,
tpnt->st_ino = st.st_ino;
tpnt->ppnt = (ElfW(Phdr) *) DL_RELOC_ADDR(tpnt->loadaddr, epnt->e_phoff);
tpnt->n_phent = epnt->e_phnum;
+ tpnt->rtld_flags |= rtld_flags;
#if defined(USE_TLS) && USE_TLS
if (tlsppnt) {
@@ -991,8 +993,8 @@ char *_dl_strdup(const char *string)
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);
}
diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c
index 05a68ddcc..f19a0151e 100644
--- a/ldso/libdl/libdl.c
+++ b/ldso/libdl/libdl.c
@@ -736,7 +736,7 @@ static int do_dlclose(void *vhandle, int need_fini)
_dl_handles = rpnt->next_handle;
_dl_if_debug_print("%s: usage count: %d\n",
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--;
free(handle);
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 */
for (j = 0; j < handle->init_fini.nlist; ++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]
|| tpnt->dynamic_info[DT_FINI_ARRAY])
&& need_fini