diff options
Diffstat (limited to 'elf/dl-open.c')
-rw-r--r-- | elf/dl-open.c | 68 |
1 files changed, 59 insertions, 9 deletions
diff --git a/elf/dl-open.c b/elf/dl-open.c index 94d29d181c..7f8fe80bd7 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -419,6 +419,35 @@ TLS generation counter wrapped! Please report this.")); } } +/* Mark the objects as NODELETE if required. This is delayed until + after dlopen failure is not possible, so that _dl_close can clean + up objects if necessary. */ +static void +activate_nodelete (struct link_map *new, int mode) +{ + if (mode & RTLD_NODELETE || new->l_nodelete == link_map_nodelete_pending) + { + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("activating NODELETE for %s [%lu]\n", + new->l_name, new->l_ns); + new->l_nodelete = link_map_nodelete_active; + } + + /* Update all objects in our scope. Any of them could have been + promoted to NODELETE status due to new relocations. */ + for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) + { + struct link_map *imap = new->l_searchlist.r_list[i]; + if (imap->l_nodelete == link_map_nodelete_pending) + { + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("activating NODELETE for %s [%lu]\n", + imap->l_name, imap->l_ns); + imap->l_nodelete = link_map_nodelete_active; + } + } +} + /* struct dl_init_args and call_dl_init are used to call _dl_init with exception handling disabled. */ struct dl_init_args @@ -488,12 +517,6 @@ dl_open_worker (void *a) return; } - /* Mark the object as not deletable if the RTLD_NODELETE flags was passed. - Do this early so that we don't skip marking the object if it was - already loaded. */ - if (__glibc_unlikely (mode & RTLD_NODELETE)) - new->l_flags_1 |= DF_1_NODELETE; - if (__glibc_unlikely (mode & __RTLD_SPROF)) /* This happens only if we load a DSO for 'sprof'. */ return; @@ -509,16 +532,33 @@ dl_open_worker (void *a) _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", new->l_name, new->l_ns, new->l_direct_opencount); - /* If the user requested the object to be in the global namespace - but it is not so far, add it now. */ + /* If the user requested the object to be in the global + namespace but it is not so far, add it now. This can raise + an exception to do a malloc failure. */ if ((mode & RTLD_GLOBAL) && new->l_global == 0) add_to_global (new); + /* Mark the object as not deletable if the RTLD_NODELETE flags + was passed. */ + if (__glibc_unlikely (mode & RTLD_NODELETE)) + { + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES) + && new->l_nodelete == link_map_nodelete_inactive) + _dl_debug_printf ("marking %s [%lu] as NODELETE\n", + new->l_name, new->l_ns); + new->l_nodelete = link_map_nodelete_active; + } + assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); return; } + /* Schedule NODELETE marking for the directly loaded object if + requested. */ + if (__glibc_unlikely (mode & RTLD_NODELETE)) + new->l_nodelete = link_map_nodelete_pending; + /* Load that object's dependencies. */ _dl_map_object_deps (new, NULL, 0, 0, mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT)); @@ -590,6 +630,14 @@ dl_open_worker (void *a) int relocation_in_progress = 0; + /* Perform relocation. This can trigger lazy binding in IFUNC + resolvers. For NODELETE mappings, these dependencies are not + recorded because the flag has not been applied to the newly + loaded objects. This means that upon dlopen failure, these + NODELETE objects can be unloaded despite existing references to + them. However, such relocation dependencies in IFUNC resolvers + are undefined anyway, so this is not a problem. */ + for (unsigned int i = nmaps; i-- > 0; ) { l = maps[i]; @@ -619,7 +667,7 @@ dl_open_worker (void *a) _dl_start_profile (); /* Prevent unloading the object. */ - GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE; + GL(dl_profile_map)->l_nodelete = link_map_nodelete_active; } } else @@ -650,6 +698,8 @@ dl_open_worker (void *a) All memory allocations for new objects must have happened before. */ + activate_nodelete (new, mode); + /* Second stage after resize_scopes: Actually perform the scope update. After this, dlsym and lazy binding can bind to new objects. */ |