summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ldso/include/dl-hash.h9
-rw-r--r--ldso/include/ldso.h3
-rw-r--r--ldso/ldso/ldso.c70
-rw-r--r--ldso/libdl/libdl.c85
4 files changed, 129 insertions, 38 deletions
diff --git a/ldso/include/dl-hash.h b/ldso/include/dl-hash.h
index a30c78afb..38b50bc65 100644
--- a/ldso/include/dl-hash.h
+++ b/ldso/include/dl-hash.h
@@ -5,10 +5,15 @@
#define RTLD_NEXT ((void*)-1)
#endif
+struct init_fini {
+ struct elf_resolve **init_fini;
+ unsigned long nlist; /* Number of entries in init_fini */
+};
+
struct dyn_elf{
struct elf_resolve * dyn;
struct dyn_elf * next_handle; /* Used by dlopen et al. */
- struct init_fini_list *init_fini;
+ struct init_fini init_fini;
struct dyn_elf * next;
struct dyn_elf * prev;
};
@@ -29,6 +34,8 @@ struct elf_resolve{
unsigned long rtld_flags; /* RTLD_GLOBAL, RTLD_NOW etc. */
unsigned int nbucket;
unsigned long * elf_buckets;
+ struct init_fini_list *init_fini;
+
/*
* These are only used with ELF style shared libraries
*/
diff --git a/ldso/include/ldso.h b/ldso/include/ldso.h
index 37ec439cb..2b803f2fe 100644
--- a/ldso/include/ldso.h
+++ b/ldso/include/ldso.h
@@ -31,10 +31,9 @@
#include <dl-elf.h>
#include <dl-hash.h>
-/* For INIT/FINI handling */
+/* For INIT/FINI dependency sorting. */
struct init_fini_list {
struct init_fini_list *next;
- struct init_fini_list *prev;
struct elf_resolve *tpnt;
};
diff --git a/ldso/ldso/ldso.c b/ldso/ldso/ldso.c
index 6feebe624..bf9cd0213 100644
--- a/ldso/ldso/ldso.c
+++ b/ldso/ldso/ldso.c
@@ -91,7 +91,8 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr,
ElfW(Phdr) *ppnt;
Elf32_Dyn *dpnt;
char *lpntstr;
- int i, goof = 0, unlazy = 0, trace_loaded_objects = 0;
+ int i, nlist, goof = 0, unlazy = 0, trace_loaded_objects = 0;
+ struct elf_resolve **init_fini_list;
struct dyn_elf *rpnt;
struct elf_resolve *tcurr;
struct elf_resolve *tpnt1;
@@ -104,7 +105,6 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr,
#if defined (__SUPPORT_LD_DEBUG__)
int (*_dl_on_exit) (void (*FUNCTION)(int STATUS, void *ARG),void*);
#endif
- struct init_fini_list *init_list;
#ifdef __SUPPORT_LD_DEBUG_EARLY__
/* Wahoo!!! */
@@ -567,9 +567,11 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr,
}
}
#endif
- init_list = NULL;
+ nlist = 0;
for (tcurr = _dl_loaded_modules; tcurr; tcurr = tcurr->next) {
Elf32_Dyn *dpnt;
+
+ nlist++;
for (dpnt = (Elf32_Dyn *) tcurr->dynamic_addr; dpnt->d_tag; dpnt++) {
if (dpnt->d_tag == DT_NEEDED) {
char *name;
@@ -599,10 +601,10 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr,
}
}
tmp = alloca(sizeof(struct init_fini_list)); /* Allocates on stack, no need to free this memory */
- /* Don't set the tmp->next ptr, it is not used */
tmp->tpnt = tpnt1;
- tmp->prev = init_list;
- init_list = tmp;
+ tmp->next = tcurr->init_fini;
+ tcurr->init_fini = tmp;
+
tpnt1->rtld_flags = unlazy | RTLD_GLOBAL;
#ifdef __SUPPORT_LD_DEBUG_EARLY__
_dl_dprintf(_dl_debug_file, "Loading:\t(%x) %s\n", tpnt1->loadaddr, tpnt1->libname);
@@ -615,9 +617,58 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr,
}
}
}
-
_dl_unmap_cache();
+ --nlist; /* Exclude the application. */
+
+ /* As long as atexit() is used to run the FINI functions, we can
+ use alloca here. The use of atexit() should go away at some time as that
+ will make Valgring happy. */
+ init_fini_list = alloca(nlist * sizeof(struct elf_resolve *));
+ i = 0;
+ for (tcurr = _dl_loaded_modules->next; tcurr; tcurr = tcurr->next) {
+ init_fini_list[i++] = tcurr;
+ }
+ /* Sort the INIT/FINI list in dependency order. */
+ for (tcurr = _dl_loaded_modules->next; tcurr; tcurr = tcurr->next) {
+ int j, k;
+
+ for (j = 0; init_fini_list[j] != tcurr; ++j)
+ /* Empty */;
+ for (k = j + 1; k < nlist; ++k) {
+ struct init_fini_list *runp = init_fini_list[k]->init_fini;
+
+ for (; runp; runp = runp->next) {
+ if (runp->tpnt == tcurr) {
+ struct elf_resolve *here = init_fini_list[k];
+#ifdef __SUPPORT_LD_DEBUG__
+ if(_dl_debug)
+ _dl_dprintf(2, "Move %s from pos %d to %d in INIT/FINI list.\n", here->libname, k, j);
+#endif
+ for (i = (k - j); i; --i)
+ init_fini_list[i+j] = init_fini_list[i+j-1];
+ init_fini_list[j] = here;
+ ++j;
+ break;
+ }
+ }
+ }
+ }
+#ifdef __SUPPORT_LD_DEBUG__
+ if(_dl_debug) {
+ _dl_dprintf(2, "\nINIT/FINI order and dependencies:\n");
+ for (i=0;i < nlist;i++) {
+ struct init_fini_list *tmp;
+
+ _dl_dprintf(2, "lib: %s has deps:\n", init_fini_list[i]->libname);
+ tmp = init_fini_list[i]->init_fini;
+ for ( ;tmp; tmp = tmp->next)
+ _dl_dprintf(2, " %s ", tmp->tpnt->libname);
+ _dl_dprintf(2, "\n");
+ }
+ }
+#endif
+
/*
* If the program interpreter is not in the module chain, add it. This will
* be required for dlopen to be able to access the internal functions in the
@@ -722,8 +773,9 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr,
/* Notify the debugger we have added some objects. */
_dl_debug_addr->r_state = RT_ADD;
_dl_debug_state();
- for (; init_list; init_list = init_list->prev) {
- tpnt = init_list->tpnt;
+ for (i = nlist; i; --i) {
+ tpnt = init_fini_list[i-1];
+ tpnt->init_fini = NULL; /* Clear, since alloca was used */
if (tpnt->init_flag & INIT_FUNCS_CALLED)
continue;
tpnt->init_flag |= INIT_FUNCS_CALLED;
diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c
index dee95b6fd..3992163ae 100644
--- a/ldso/libdl/libdl.c
+++ b/ldso/libdl/libdl.c
@@ -134,8 +134,9 @@ void *dlopen(const char *libname, int flag)
struct elf_resolve *tpnt1;
void (*dl_brk) (void);
int now_flag;
- struct init_fini_list *init_list;
struct init_fini_list *tmp;
+ int nlist, i;
+ struct elf_resolve **init_fini_list;
/* A bit of sanity checking... */
if (!(flag & (RTLD_LAZY|RTLD_NOW))) {
@@ -194,19 +195,14 @@ void *dlopen(const char *libname, int flag)
if(_dl_debug)
fprintf(stderr, "Looking for needed libraries\n");
#endif
- init_list = NULL;
-
- tmp = malloc(sizeof(struct init_fini_list));
- tmp->tpnt = tpnt;
- tmp->next = NULL;
- tmp->prev = init_list;
- init_list = tmp;
-
- dyn_chain->init_fini = init_list;
+ nlist = 0;
for (tcurr = tpnt; tcurr; tcurr = tcurr->next)
{
Elf32_Dyn *dpnt;
char *lpntstr;
+
+ nlist++;
+ tcurr->init_fini = NULL; /* clear any previous dependcies */
for (dpnt = (Elf32_Dyn *) tcurr->dynamic_addr; dpnt->d_tag; dpnt++) {
if (dpnt->d_tag == DT_NEEDED) {
char *name;
@@ -234,15 +230,58 @@ void *dlopen(const char *libname, int flag)
tpnt1->rtld_flags |= RTLD_GLOBAL;
tpnt1->usage_count++;
}
- tmp = malloc(sizeof(struct init_fini_list));
+ tmp = alloca(sizeof(struct init_fini_list)); /* Allocates on stack, no need to free this memory */
tmp->tpnt = tpnt1;
- tmp->next = NULL;
- tmp->prev = init_list;
- init_list->next = tmp;
- init_list = init_list->next;;
+ tmp->next = tcurr->init_fini;
+ tcurr->init_fini = tmp;
+ }
+ }
+ }
+ init_fini_list = malloc(nlist * sizeof(struct elf_resolve *));
+ dyn_chain->init_fini.init_fini = init_fini_list;
+ dyn_chain->init_fini.nlist = nlist;
+ i = 0;
+ for (tcurr = tpnt; tcurr; tcurr = tcurr->next) {
+ init_fini_list[i++] = tcurr;
+ }
+ /* Sort the INIT/FINI list in dependency order. */
+ for (tcurr = tpnt; tcurr; tcurr = tcurr->next) {
+ int j, k;
+ for (j = 0; init_fini_list[j] != tcurr; ++j)
+ /* Empty */;
+ for (k = j + 1; k < nlist; ++k) {
+ struct init_fini_list *runp = init_fini_list[k]->init_fini;
+
+ for (; runp; runp = runp->next) {
+ if (runp->tpnt == tcurr) {
+ struct elf_resolve *here = init_fini_list[k];
+#ifdef __SUPPORT_LD_DEBUG__
+ if(_dl_debug)
+ fprintf(stderr, "Move %s from pos %d to %d in INIT/FINI list.\n", here->libname, k, j);
+#endif
+ for (i = (k - j); i; --i)
+ init_fini_list[i+j] = init_fini_list[i+j-1];
+ init_fini_list[j] = here;
+ ++j;
+ break;
+ }
}
}
}
+#ifdef __SUPPORT_LD_DEBUG__
+ if(_dl_debug) {
+ fprintf(stderr, "\nINIT/FINI order and dependencies:\n");
+ for (i=0;i < nlist;i++) {
+ struct init_fini_list *tmp;
+
+ fprintf(stderr, "lib: %s has deps:\n", init_fini_list[i]->libname);
+ tmp = init_fini_list[i]->init_fini;
+ for ( ;tmp; tmp = tmp->next)
+ printf(" %s ", tmp->tpnt->libname);
+ printf("\n");
+ }
+ }
+#endif
if (dyn_chain->dyn->init_flag & INIT_FUNCS_CALLED) {
/* If the init and fini stuff has already been run, that means
@@ -290,10 +329,8 @@ void *dlopen(const char *libname, int flag)
#if defined (__LIBDL_SHARED__)
/* Run the ctors and setup the dtors */
- for (; init_list; init_list = init_list->prev) {
- /* Apparently crt1 for the application is responsible for handling this.
- * We only need to run the init/fini for shared libraries. */
- tpnt = init_list->tpnt;
+ for (i = nlist; i; --i) {
+ tpnt = init_fini_list[i-1];
if (tpnt->init_flag & INIT_FUNCS_CALLED)
continue;
tpnt->init_flag |= INIT_FUNCS_CALLED;
@@ -384,7 +421,6 @@ static int do_dlclose(void *vhandle, int need_fini)
struct dyn_elf *handle;
unsigned int end;
int i = 0;
- struct init_fini_list *fini_list, *tmp;
handle = (struct dyn_elf *) vhandle;
rpnt1 = NULL;
@@ -403,9 +439,8 @@ static int do_dlclose(void *vhandle, int need_fini)
else
_dl_handles = rpnt->next_handle;
if (need_fini) {
- for (fini_list = handle->init_fini; fini_list; ) {
- tpnt = fini_list->tpnt;
- tmp = NULL;
+ for (i = 0; i < handle->init_fini.nlist; ++i) {
+ tpnt = handle->init_fini.init_fini[i];
if (tpnt->dynamic_info[DT_FINI] && tpnt->usage_count == 1 &&
!(tpnt->init_flag & FINI_FUNCS_CALLED)) {
tpnt->init_flag |= FINI_FUNCS_CALLED;
@@ -415,12 +450,10 @@ static int do_dlclose(void *vhandle, int need_fini)
fprintf(stderr, "running dtors for library %s at '%x'\n", tpnt->libname, dl_elf_fini);
#endif
(*dl_elf_fini) ();
- tmp = fini_list;
}
- fini_list = fini_list->next;
- free(tmp);
}
}
+ free(handle->init_fini.init_fini);
/* OK, this is a valid handle - now close out the file */
for (rpnt = handle; rpnt; rpnt = rpnt->next) {
tpnt = rpnt->dyn;