diff options
Diffstat (limited to 'elf/dl-fini.c')
-rw-r--r-- | elf/dl-fini.c | 152 |
1 files changed, 52 insertions, 100 deletions
diff --git a/elf/dl-fini.c b/elf/dl-fini.c index 50ff94db16..50087a1bfc 100644 --- a/elf/dl-fini.c +++ b/elf/dl-fini.c @@ -24,116 +24,68 @@ void _dl_fini (void) { - /* Lots of fun ahead. We have to call the destructors for all still - loaded objects, in all namespaces. The problem is that the ELF - specification now demands that dependencies between the modules - are taken into account. I.e., the destructor for a module is - called before the ones for any of its dependencies. - - To make things more complicated, we cannot simply use the reverse - order of the constructors. Since the user might have loaded objects - using `dlopen' there are possibly several other modules with its - dependencies to be taken into account. Therefore we have to start - determining the order of the modules once again from the beginning. */ - - /* We run the destructors of the main namespaces last. As for the - other namespaces, we pick run the destructors in them in reverse - order of the namespace ID. */ -#ifdef SHARED - int do_audit = 0; - again: -#endif - for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns) - { - /* Protect against concurrent loads and unloads. */ - __rtld_lock_lock_recursive (GL(dl_load_lock)); - - unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded; - /* No need to do anything for empty namespaces or those used for - auditing DSOs. */ - if (nloaded == 0 -#ifdef SHARED - || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit -#endif - ) - __rtld_lock_unlock_recursive (GL(dl_load_lock)); - else - { + /* Call destructors strictly in the reverse order of constructors. + This causes fewer surprises than some arbitrary reordering based + on new (relocation) dependencies. None of the objects are + unmapped, so applications can deal with this if their DSOs remain + in a consistent state after destructors have run. */ + + /* Protect against concurrent loads and unloads. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + /* Ignore objects which are opened during shutdown. */ + struct link_map *local_init_called_list = _dl_init_called_list; + + for (struct link_map *l = local_init_called_list; l != NULL; + l = l->l_init_called_next) + /* Bump l_direct_opencount of all objects so that they + are not dlclose()ed from underneath us. */ + ++l->l_direct_opencount; + + /* After this point, everything linked from local_init_called_list + cannot be unloaded because of the reference counter update. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + /* Perform two passes: One for non-audit modules, one for audit + modules. This way, audit modules receive unload notifications + for non-audit objects, and the destructors for audit modules + still run. */ #ifdef SHARED - _dl_audit_activity_nsid (ns, LA_ACT_DELETE); + int last_pass = GLRO(dl_naudit) > 0; + Lmid_t last_ns = -1; + for (int do_audit = 0; do_audit <= last_pass; ++do_audit) #endif - - /* Now we can allocate an array to hold all the pointers and - copy the pointers in. */ - struct link_map *maps[nloaded]; - - unsigned int i; - struct link_map *l; - assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL); - for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next) - /* Do not handle ld.so in secondary namespaces. */ - if (l == l->l_real) - { - assert (i < nloaded); - - maps[i] = l; - l->l_idx = i; - ++i; - - /* Bump l_direct_opencount of all objects so that they - are not dlclose()ed from underneath us. */ - ++l->l_direct_opencount; - } - assert (ns != LM_ID_BASE || i == nloaded); - assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1); - unsigned int nmaps = i; - - /* Now we have to do the sorting. We can skip looking for the - binary itself which is at the front of the search list for - the main namespace. */ - _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE), true); - - /* We do not rely on the linked list of loaded object anymore - from this point on. We have our own list here (maps). The - various members of this list cannot vanish since the open - count is too high and will be decremented in this loop. So - we release the lock so that some code which might be called - from a destructor can directly or indirectly access the - lock. */ - __rtld_lock_unlock_recursive (GL(dl_load_lock)); - - /* 'maps' now contains the objects in the right order. Now - call the destructors. We have to process this array from - the front. */ - for (i = 0; i < nmaps; ++i) - { - struct link_map *l = maps[i]; - - if (l->l_init_called) - { - _dl_call_fini (l); + for (struct link_map *l = local_init_called_list; l != NULL; + l = l->l_init_called_next) + { #ifdef SHARED - /* Auditing checkpoint: another object closed. */ - _dl_audit_objclose (l); + if (GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing != do_audit) + continue; + + /* Avoid back-to-back calls of _dl_audit_activity_nsid for the + same namespace. */ + if (last_ns != l->l_ns) + { + if (last_ns >= 0) + _dl_audit_activity_nsid (last_ns, LA_ACT_CONSISTENT); + _dl_audit_activity_nsid (l->l_ns, LA_ACT_DELETE); + last_ns = l->l_ns; + } #endif - } - /* Correct the previous increment. */ - --l->l_direct_opencount; - } + /* There is no need to re-enable exceptions because _dl_fini + is not called from a context where exceptions are caught. */ + _dl_call_fini (l); #ifdef SHARED - _dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT); + /* Auditing checkpoint: another object closed. */ + _dl_audit_objclose (l); #endif - } - } + } #ifdef SHARED - if (! do_audit && GLRO(dl_naudit) > 0) - { - do_audit = 1; - goto again; - } + if (last_ns >= 0) + _dl_audit_activity_nsid (last_ns, LA_ACT_CONSISTENT); if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS)) _dl_debug_printf ("\nruntime linker statistics:\n" |