mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-30 22:43:12 +03:00
Remove all loaded objects if dlopen fails, ignoring NODELETE [BZ #20839]
This introduces a “pending NODELETE” state in the link map, which is flipped to the persistent NODELETE state late in dlopen, via activate_nodelete. During initial relocation, symbol binding records pending NODELETE state only. dlclose ignores pending NODELETE state. Taken together, this results that a partially completed dlopen is rolled back completely because new NODELETE mappings are unloaded. Tested on x86_64-linux-gnu and i386-linux-gnu. Change-Id: Ib2a3d86af6f92d75baca65431d74783ee0dbc292
This commit is contained in:
15
elf/Makefile
15
elf/Makefile
@ -199,7 +199,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
|
|||||||
tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
|
tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
|
||||||
tst-unwind-ctor tst-unwind-main tst-audit13 \
|
tst-unwind-ctor tst-unwind-main tst-audit13 \
|
||||||
tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-tlsmodid \
|
tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-tlsmodid \
|
||||||
tst-dlopen-self tst-auditmany tst-initfinilazyfail
|
tst-dlopen-self tst-auditmany tst-initfinilazyfail tst-dlopenfail
|
||||||
# reldep9
|
# reldep9
|
||||||
tests-internal += loadtest unload unload2 circleload1 \
|
tests-internal += loadtest unload unload2 circleload1 \
|
||||||
neededtest neededtest2 neededtest3 neededtest4 \
|
neededtest neededtest2 neededtest3 neededtest4 \
|
||||||
@ -291,7 +291,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
|
|||||||
tst-auditmanymod1 tst-auditmanymod2 tst-auditmanymod3 \
|
tst-auditmanymod1 tst-auditmanymod2 tst-auditmanymod3 \
|
||||||
tst-auditmanymod4 tst-auditmanymod5 tst-auditmanymod6 \
|
tst-auditmanymod4 tst-auditmanymod5 tst-auditmanymod6 \
|
||||||
tst-auditmanymod7 tst-auditmanymod8 tst-auditmanymod9 \
|
tst-auditmanymod7 tst-auditmanymod8 tst-auditmanymod9 \
|
||||||
tst-initlazyfailmod tst-finilazyfailmod
|
tst-initlazyfailmod tst-finilazyfailmod \
|
||||||
|
tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2
|
||||||
# Most modules build with _ISOMAC defined, but those filtered out
|
# Most modules build with _ISOMAC defined, but those filtered out
|
||||||
# depend on internal headers.
|
# depend on internal headers.
|
||||||
modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
|
modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
|
||||||
@ -1597,3 +1598,13 @@ LDFLAGS-tst-initlazyfailmod.so = \
|
|||||||
-Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
|
-Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
|
||||||
LDFLAGS-tst-finilazyfailmod.so = \
|
LDFLAGS-tst-finilazyfailmod.so = \
|
||||||
-Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
|
-Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
|
||||||
|
|
||||||
|
$(objpfx)tst-dlopenfail: $(libdl)
|
||||||
|
$(objpfx)tst-dlopenfail.out: \
|
||||||
|
$(objpfx)tst-dlopenfailmod1.so $(objpfx)tst-dlopenfailmod2.so
|
||||||
|
# Order matters here. tst-dlopenfaillinkmod.so's soname ensures
|
||||||
|
# a run-time loader failure.
|
||||||
|
$(objpfx)tst-dlopenfailmod1.so: \
|
||||||
|
$(shared-thread-library) $(objpfx)tst-dlopenfaillinkmod.so
|
||||||
|
LDFLAGS-tst-dlopenfaillinkmod.so = -Wl,-soname,tst-dlopenfail-missingmod.so
|
||||||
|
$(objpfx)tst-dlopenfailmod2.so: $(shared-thread-library)
|
||||||
|
@ -168,14 +168,6 @@ _dl_close_worker (struct link_map *map, bool force)
|
|||||||
char done[nloaded];
|
char done[nloaded];
|
||||||
struct link_map *maps[nloaded];
|
struct link_map *maps[nloaded];
|
||||||
|
|
||||||
/* Clear DF_1_NODELETE to force object deletion. We don't need to touch
|
|
||||||
l_tls_dtor_count because forced object deletion only happens when an
|
|
||||||
error occurs during object load. Destructor registration for TLS
|
|
||||||
non-POD objects should not have happened till then for this
|
|
||||||
object. */
|
|
||||||
if (force)
|
|
||||||
map->l_flags_1 &= ~DF_1_NODELETE;
|
|
||||||
|
|
||||||
/* Run over the list and assign indexes to the link maps and enter
|
/* Run over the list and assign indexes to the link maps and enter
|
||||||
them into the MAPS array. */
|
them into the MAPS array. */
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
@ -205,7 +197,7 @@ _dl_close_worker (struct link_map *map, bool force)
|
|||||||
/* Check whether this object is still used. */
|
/* Check whether this object is still used. */
|
||||||
if (l->l_type == lt_loaded
|
if (l->l_type == lt_loaded
|
||||||
&& l->l_direct_opencount == 0
|
&& l->l_direct_opencount == 0
|
||||||
&& (l->l_flags_1 & DF_1_NODELETE) == 0
|
&& l->l_nodelete != link_map_nodelete_active
|
||||||
/* See CONCURRENCY NOTES in cxa_thread_atexit_impl.c to know why
|
/* See CONCURRENCY NOTES in cxa_thread_atexit_impl.c to know why
|
||||||
acquire is sufficient and correct. */
|
acquire is sufficient and correct. */
|
||||||
&& atomic_load_acquire (&l->l_tls_dtor_count) == 0
|
&& atomic_load_acquire (&l->l_tls_dtor_count) == 0
|
||||||
@ -288,7 +280,7 @@ _dl_close_worker (struct link_map *map, bool force)
|
|||||||
if (!used[i])
|
if (!used[i])
|
||||||
{
|
{
|
||||||
assert (imap->l_type == lt_loaded
|
assert (imap->l_type == lt_loaded
|
||||||
&& (imap->l_flags_1 & DF_1_NODELETE) == 0);
|
&& imap->l_nodelete != link_map_nodelete_active);
|
||||||
|
|
||||||
/* Call its termination function. Do not do it for
|
/* Call its termination function. Do not do it for
|
||||||
half-cooked objects. Temporarily disable exception
|
half-cooked objects. Temporarily disable exception
|
||||||
@ -838,7 +830,7 @@ _dl_close (void *_map)
|
|||||||
before we took the lock. There is no way to detect this (see below)
|
before we took the lock. There is no way to detect this (see below)
|
||||||
so we proceed assuming this isn't the case. First see whether we
|
so we proceed assuming this isn't the case. First see whether we
|
||||||
can remove the object at all. */
|
can remove the object at all. */
|
||||||
if (__glibc_unlikely (map->l_flags_1 & DF_1_NODELETE))
|
if (__glibc_unlikely (map->l_nodelete == link_map_nodelete_active))
|
||||||
{
|
{
|
||||||
/* Nope. Do nothing. */
|
/* Nope. Do nothing. */
|
||||||
__rtld_lock_unlock_recursive (GL(dl_load_lock));
|
__rtld_lock_unlock_recursive (GL(dl_load_lock));
|
||||||
|
@ -192,9 +192,10 @@ enter_unique_sym (struct unique_sym *table, size_t size,
|
|||||||
Return the matching symbol in RESULT. */
|
Return the matching symbol in RESULT. */
|
||||||
static void
|
static void
|
||||||
do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
|
do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
|
||||||
const struct link_map *map, struct sym_val *result,
|
struct link_map *map, struct sym_val *result,
|
||||||
int type_class, const ElfW(Sym) *sym, const char *strtab,
|
int type_class, const ElfW(Sym) *sym, const char *strtab,
|
||||||
const ElfW(Sym) *ref, const struct link_map *undef_map)
|
const ElfW(Sym) *ref, const struct link_map *undef_map,
|
||||||
|
int flags)
|
||||||
{
|
{
|
||||||
/* We have to determine whether we already found a symbol with this
|
/* We have to determine whether we already found a symbol with this
|
||||||
name before. If not then we have to add it to the search table.
|
name before. If not then we have to add it to the search table.
|
||||||
@ -222,7 +223,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
|
|||||||
copy from the copy addressed through the
|
copy from the copy addressed through the
|
||||||
relocation. */
|
relocation. */
|
||||||
result->s = sym;
|
result->s = sym;
|
||||||
result->m = (struct link_map *) map;
|
result->m = map;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -311,9 +312,19 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
|
|||||||
new_hash, strtab + sym->st_name, sym, map);
|
new_hash, strtab + sym->st_name, sym, map);
|
||||||
|
|
||||||
if (map->l_type == lt_loaded)
|
if (map->l_type == lt_loaded)
|
||||||
/* Make sure we don't unload this object by
|
{
|
||||||
setting the appropriate flag. */
|
/* Make sure we don't unload this object by
|
||||||
((struct link_map *) map)->l_flags_1 |= DF_1_NODELETE;
|
setting the appropriate flag. */
|
||||||
|
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
|
||||||
|
&& map->l_nodelete == link_map_nodelete_inactive)
|
||||||
|
_dl_debug_printf ("\
|
||||||
|
marking %s [%lu] as NODELETE due to unique symbol\n",
|
||||||
|
map->l_name, map->l_ns);
|
||||||
|
if (flags & DL_LOOKUP_FOR_RELOCATE)
|
||||||
|
map->l_nodelete = link_map_nodelete_pending;
|
||||||
|
else
|
||||||
|
map->l_nodelete = link_map_nodelete_active;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++tab->n_elements;
|
++tab->n_elements;
|
||||||
|
|
||||||
@ -525,8 +536,9 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
case STB_GNU_UNIQUE:;
|
case STB_GNU_UNIQUE:;
|
||||||
do_lookup_unique (undef_name, new_hash, map, result, type_class,
|
do_lookup_unique (undef_name, new_hash, (struct link_map *) map,
|
||||||
sym, strtab, ref, undef_map);
|
result, type_class, sym, strtab, ref,
|
||||||
|
undef_map, flags);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -568,9 +580,13 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
|
|||||||
if (undef_map == map)
|
if (undef_map == map)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Avoid references to objects which cannot be unloaded anyway. */
|
/* Avoid references to objects which cannot be unloaded anyway. We
|
||||||
|
do not need to record dependencies if this object goes away
|
||||||
|
during dlopen failure, either. IFUNC resolvers with relocation
|
||||||
|
dependencies may pick an dependency which can be dlclose'd, but
|
||||||
|
such IFUNC resolvers are undefined anyway. */
|
||||||
assert (map->l_type == lt_loaded);
|
assert (map->l_type == lt_loaded);
|
||||||
if ((map->l_flags_1 & DF_1_NODELETE) != 0)
|
if (map->l_nodelete != link_map_nodelete_inactive)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
struct link_map_reldeps *l_reldeps
|
struct link_map_reldeps *l_reldeps
|
||||||
@ -678,16 +694,33 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
|
|||||||
|
|
||||||
/* Redo the NODELETE check, as when dl_load_lock wasn't held
|
/* Redo the NODELETE check, as when dl_load_lock wasn't held
|
||||||
yet this could have changed. */
|
yet this could have changed. */
|
||||||
if ((map->l_flags_1 & DF_1_NODELETE) != 0)
|
if (map->l_nodelete != link_map_nodelete_inactive)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* If the object with the undefined reference cannot be removed ever
|
/* If the object with the undefined reference cannot be removed ever
|
||||||
just make sure the same is true for the object which contains the
|
just make sure the same is true for the object which contains the
|
||||||
definition. */
|
definition. */
|
||||||
if (undef_map->l_type != lt_loaded
|
if (undef_map->l_type != lt_loaded
|
||||||
|| (undef_map->l_flags_1 & DF_1_NODELETE) != 0)
|
|| (undef_map->l_nodelete != link_map_nodelete_inactive))
|
||||||
{
|
{
|
||||||
map->l_flags_1 |= DF_1_NODELETE;
|
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
|
||||||
|
&& map->l_nodelete == link_map_nodelete_inactive)
|
||||||
|
{
|
||||||
|
if (undef_map->l_name[0] == '\0')
|
||||||
|
_dl_debug_printf ("\
|
||||||
|
marking %s [%lu] as NODELETE due to reference to main program\n",
|
||||||
|
map->l_name, map->l_ns);
|
||||||
|
else
|
||||||
|
_dl_debug_printf ("\
|
||||||
|
marking %s [%lu] as NODELETE due to reference to %s [%lu]\n",
|
||||||
|
map->l_name, map->l_ns,
|
||||||
|
undef_map->l_name, undef_map->l_ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & DL_LOOKUP_FOR_RELOCATE)
|
||||||
|
map->l_nodelete = link_map_nodelete_pending;
|
||||||
|
else
|
||||||
|
map->l_nodelete = link_map_nodelete_active;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,7 +745,18 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
|
|||||||
no fatal problem. We simply make sure the referenced object
|
no fatal problem. We simply make sure the referenced object
|
||||||
cannot be unloaded. This is semantically the correct
|
cannot be unloaded. This is semantically the correct
|
||||||
behavior. */
|
behavior. */
|
||||||
map->l_flags_1 |= DF_1_NODELETE;
|
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
|
||||||
|
&& map->l_nodelete == link_map_nodelete_inactive)
|
||||||
|
_dl_debug_printf ("\
|
||||||
|
marking %s [%lu] as NODELETE due to memory allocation failure\n",
|
||||||
|
map->l_name, map->l_ns);
|
||||||
|
if (flags & DL_LOOKUP_FOR_RELOCATE)
|
||||||
|
/* In case of non-lazy binding, we could actually
|
||||||
|
report the memory allocation error, but for now, we
|
||||||
|
use the conservative approximation as well. */
|
||||||
|
map->l_nodelete = link_map_nodelete_pending;
|
||||||
|
else
|
||||||
|
map->l_nodelete = link_map_nodelete_active;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -424,6 +424,40 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* Only new objects should have set
|
||||||
|
link_map_nodelete_pending. Existing objects should not
|
||||||
|
have gained any new dependencies and therefore cannot
|
||||||
|
reach NODELETE status. */
|
||||||
|
assert (!imap->l_init_called || imap->l_type != lt_loaded);
|
||||||
|
|
||||||
|
imap->l_nodelete = link_map_nodelete_active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* struct dl_init_args and call_dl_init are used to call _dl_init with
|
/* struct dl_init_args and call_dl_init are used to call _dl_init with
|
||||||
exception handling disabled. */
|
exception handling disabled. */
|
||||||
struct dl_init_args
|
struct dl_init_args
|
||||||
@ -493,12 +527,6 @@ dl_open_worker (void *a)
|
|||||||
return;
|
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))
|
if (__glibc_unlikely (mode & __RTLD_SPROF))
|
||||||
/* This happens only if we load a DSO for 'sprof'. */
|
/* This happens only if we load a DSO for 'sprof'. */
|
||||||
return;
|
return;
|
||||||
@ -514,19 +542,37 @@ dl_open_worker (void *a)
|
|||||||
_dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
|
_dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
|
||||||
new->l_name, new->l_ns, new->l_direct_opencount);
|
new->l_name, new->l_ns, new->l_direct_opencount);
|
||||||
|
|
||||||
/* If the user requested the object to be in the global namespace
|
/* If the user requested the object to be in the global
|
||||||
but it is not so far, add it now. */
|
namespace but it is not so far, prepare to add it now. This
|
||||||
|
can raise an exception to do a malloc failure. */
|
||||||
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
||||||
|
add_to_global_resize (new);
|
||||||
|
|
||||||
|
/* Mark the object as not deletable if the RTLD_NODELETE flags
|
||||||
|
was passed. */
|
||||||
|
if (__glibc_unlikely (mode & RTLD_NODELETE))
|
||||||
{
|
{
|
||||||
add_to_global_resize (new);
|
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)
|
||||||
add_to_global_update (new);
|
&& 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Finalize the addition to the global scope. */
|
||||||
|
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
||||||
|
add_to_global_update (new);
|
||||||
|
|
||||||
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
|
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
|
||||||
|
|
||||||
return;
|
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. */
|
/* Load that object's dependencies. */
|
||||||
_dl_map_object_deps (new, NULL, 0, 0,
|
_dl_map_object_deps (new, NULL, 0, 0,
|
||||||
mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
|
mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
|
||||||
@ -601,6 +647,14 @@ dl_open_worker (void *a)
|
|||||||
|
|
||||||
int relocation_in_progress = 0;
|
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; )
|
for (unsigned int i = nmaps; i-- > 0; )
|
||||||
{
|
{
|
||||||
l = maps[i];
|
l = maps[i];
|
||||||
@ -630,7 +684,7 @@ dl_open_worker (void *a)
|
|||||||
_dl_start_profile ();
|
_dl_start_profile ();
|
||||||
|
|
||||||
/* Prevent unloading the object. */
|
/* 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
|
else
|
||||||
@ -661,6 +715,8 @@ dl_open_worker (void *a)
|
|||||||
All memory allocations for new objects must have happened
|
All memory allocations for new objects must have happened
|
||||||
before. */
|
before. */
|
||||||
|
|
||||||
|
activate_nodelete (new, mode);
|
||||||
|
|
||||||
/* Second stage after resize_scopes: Actually perform the scope
|
/* Second stage after resize_scopes: Actually perform the scope
|
||||||
update. After this, dlsym and lazy binding can bind to new
|
update. After this, dlsym and lazy binding can bind to new
|
||||||
objects. */
|
objects. */
|
||||||
@ -820,6 +876,10 @@ no more namespaces available for dlmopen()"));
|
|||||||
GL(dl_tls_dtv_gaps) = true;
|
GL(dl_tls_dtv_gaps) = true;
|
||||||
|
|
||||||
_dl_close_worker (args.map, true);
|
_dl_close_worker (args.map, true);
|
||||||
|
|
||||||
|
/* All link_map_nodelete_pending objects should have been
|
||||||
|
deleted at this point, which is why it is not necessary
|
||||||
|
to reset the flag here. */
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
|
assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
|
||||||
|
@ -163,6 +163,8 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
|
|||||||
if (info[VERSYMIDX (DT_FLAGS_1)] != NULL)
|
if (info[VERSYMIDX (DT_FLAGS_1)] != NULL)
|
||||||
{
|
{
|
||||||
l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val;
|
l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val;
|
||||||
|
if (l->l_flags_1 & DF_1_NODELETE)
|
||||||
|
l->l_nodelete = link_map_nodelete_pending;
|
||||||
|
|
||||||
/* Only DT_1_SUPPORTED_MASK bits are supported, and we would like
|
/* Only DT_1_SUPPORTED_MASK bits are supported, and we would like
|
||||||
to assert this, but we can't. Users have been setting
|
to assert this, but we can't. Users have been setting
|
||||||
|
79
elf/tst-dlopenfail.c
Normal file
79
elf/tst-dlopenfail.c
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/* Test dlopen rollback after failures involving NODELETE objects (bug 20839).
|
||||||
|
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <gnu/lib-names.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <support/check.h>
|
||||||
|
#include <support/xdlfcn.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_test (void)
|
||||||
|
{
|
||||||
|
/* This test uses libpthread as the canonical NODELETE module. If
|
||||||
|
libpthread is no longer NODELETE because it has been merged into
|
||||||
|
libc, the test needs to be updated. */
|
||||||
|
TEST_VERIFY (dlsym (NULL, "pthread_create") == NULL);
|
||||||
|
|
||||||
|
/* This is expected to fail because of the missing dependency. */
|
||||||
|
puts ("info: attempting to load tst-dlopenfailmod1.so");
|
||||||
|
TEST_VERIFY (dlopen ("tst-dlopenfailmod1.so", RTLD_LAZY) == NULL);
|
||||||
|
const char *message = dlerror ();
|
||||||
|
TEST_COMPARE_STRING (message,
|
||||||
|
"tst-dlopenfail-missingmod.so:"
|
||||||
|
" cannot open shared object file:"
|
||||||
|
" No such file or directory");
|
||||||
|
|
||||||
|
/* Do not probe for the presence of libpthread at this point because
|
||||||
|
that might trigger relocation if bug 20839 is present, obscuring
|
||||||
|
a subsequent crash. */
|
||||||
|
|
||||||
|
/* This is expected to succeed. */
|
||||||
|
puts ("info: loading tst-dlopenfailmod2.so");
|
||||||
|
void *handle = xdlopen ("tst-dlopenfailmod2.so", RTLD_NOW);
|
||||||
|
xdlclose (handle);
|
||||||
|
|
||||||
|
/* libpthread should remain loaded. */
|
||||||
|
TEST_VERIFY (dlopen (LIBPTHREAD_SO, RTLD_LAZY | RTLD_NOLOAD) != NULL);
|
||||||
|
TEST_VERIFY (dlsym (NULL, "pthread_create") == NULL);
|
||||||
|
|
||||||
|
/* We can make libpthread global, and then the symbol should become
|
||||||
|
available. */
|
||||||
|
TEST_VERIFY (dlopen (LIBPTHREAD_SO, RTLD_LAZY | RTLD_GLOBAL) != NULL);
|
||||||
|
TEST_VERIFY (dlsym (NULL, "pthread_create") != NULL);
|
||||||
|
|
||||||
|
/* sem_open is sufficiently complex to depend on relocations. */
|
||||||
|
void *(*sem_open_ptr) (const char *, int flag, ...)
|
||||||
|
= dlsym (NULL, "sem_open");
|
||||||
|
if (sem_open_ptr == NULL)
|
||||||
|
/* Hurd does not implement sem_open. */
|
||||||
|
puts ("warning: sem_open not found, further testing not possible");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
TEST_VERIFY (sem_open_ptr ("/", 0) == NULL);
|
||||||
|
TEST_COMPARE (errno, EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <support/test-driver.c>
|
17
elf/tst-dlopenfaillinkmod.c
Normal file
17
elf/tst-dlopenfaillinkmod.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/* Empty module with a soname which is not available at run time.
|
||||||
|
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
<https://www.gnu.org/licenses/>. */
|
36
elf/tst-dlopenfailmod1.c
Normal file
36
elf/tst-dlopenfailmod1.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* Module which depends on two modules: one NODELETE, one missing.
|
||||||
|
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* Note: Due to the missing second module, this object cannot be
|
||||||
|
loaded at run time. */
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/* Force linking against libpthread. */
|
||||||
|
void *pthread_create_reference = pthread_create;
|
||||||
|
|
||||||
|
/* The constructor will never be executed because the module cannot be
|
||||||
|
loaded. */
|
||||||
|
static void __attribute__ ((constructor))
|
||||||
|
init (void)
|
||||||
|
{
|
||||||
|
puts ("tst-dlopenfailmod1 constructor executed");
|
||||||
|
_exit (1);
|
||||||
|
}
|
29
elf/tst-dlopenfailmod2.c
Normal file
29
elf/tst-dlopenfailmod2.c
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/* Module which depends on on a NODELETE module, and can be loaded.
|
||||||
|
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* Force linking against libpthread. */
|
||||||
|
void *pthread_create_reference = pthread_create;
|
||||||
|
|
||||||
|
static void __attribute__ ((constructor))
|
||||||
|
init (void)
|
||||||
|
{
|
||||||
|
puts ("info: tst-dlopenfailmod2.so constructor invoked");
|
||||||
|
}
|
@ -79,6 +79,21 @@ struct r_search_path_struct
|
|||||||
int malloced;
|
int malloced;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Type used by the l_nodelete member. */
|
||||||
|
enum link_map_nodelete
|
||||||
|
{
|
||||||
|
/* This link map can be deallocated. */
|
||||||
|
link_map_nodelete_inactive = 0, /* Zero-initialized in _dl_new_object. */
|
||||||
|
|
||||||
|
/* This link map cannot be deallocated. */
|
||||||
|
link_map_nodelete_active,
|
||||||
|
|
||||||
|
/* This link map cannot be deallocated after dlopen has succeded.
|
||||||
|
dlopen turns this into link_map_nodelete_active. dlclose treats
|
||||||
|
this intermediate state as link_map_nodelete_active. */
|
||||||
|
link_map_nodelete_pending,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Structure describing a loaded shared object. The `l_next' and `l_prev'
|
/* Structure describing a loaded shared object. The `l_next' and `l_prev'
|
||||||
members form a chain of all the shared objects loaded at startup.
|
members form a chain of all the shared objects loaded at startup.
|
||||||
@ -203,6 +218,11 @@ struct link_map
|
|||||||
freed, ie. not allocated with
|
freed, ie. not allocated with
|
||||||
the dummy malloc in ld.so. */
|
the dummy malloc in ld.so. */
|
||||||
|
|
||||||
|
/* Actually of type enum link_map_nodelete. Separate byte due to
|
||||||
|
a read in add_dependency in elf/dl-lookup.c outside the loader
|
||||||
|
lock. Only valid for l_type == lt_loaded. */
|
||||||
|
unsigned char l_nodelete;
|
||||||
|
|
||||||
#include <link_map.h>
|
#include <link_map.h>
|
||||||
|
|
||||||
/* Collected information about own RPATH directories. */
|
/* Collected information about own RPATH directories. */
|
||||||
|
Reference in New Issue
Block a user