From 90c50876acd1339310fd7002a872655819ced3e4 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 8 Sep 2004 09:43:25 +0000 Subject: Second attempt to fix the INIT/FINI order. This time I think I got it right :) This needs testing with apps that have complex dependencies. --- ldso/include/dl-hash.h | 9 +++++- ldso/include/ldso.h | 3 +- ldso/ldso/ldso.c | 70 +++++++++++++++++++++++++++++++++++------ ldso/libdl/libdl.c | 85 +++++++++++++++++++++++++++++++++++--------------- 4 files changed, 129 insertions(+), 38 deletions(-) (limited to 'ldso') 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 #include -/* 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; -- cgit v1.2.3