mirror of git://sourceware.org/git/glibc.git
Remove all loaded objects if dlopen fails [BZ #20839]
This commit is contained in:
parent
93e07b2f9e
commit
8c59ed1f74
15
elf/Makefile
15
elf/Makefile
|
|
@ -193,7 +193,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
|
|||
tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
|
||||
tst-unwind-ctor tst-unwind-main tst-audit13 \
|
||||
tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-tlsmodid \
|
||||
tst-dlopen-self tst-initfinilazyfail
|
||||
tst-dlopen-self tst-initfinilazyfail tst-dlopenfail
|
||||
# reldep9
|
||||
tests-internal += loadtest unload unload2 circleload1 \
|
||||
neededtest neededtest2 neededtest3 neededtest4 \
|
||||
|
|
@ -282,7 +282,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
|
|||
tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib \
|
||||
tst-audit13mod1 tst-sonamemove-linkmod1 \
|
||||
tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
|
||||
tst-initlazyfailmod tst-finilazyfailmod
|
||||
tst-initlazyfailmod tst-finilazyfailmod \
|
||||
tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2
|
||||
# Most modules build with _ISOMAC defined, but those filtered out
|
||||
# depend on internal headers.
|
||||
modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
|
||||
|
|
@ -1580,3 +1581,13 @@ LDFLAGS-tst-initlazyfailmod.so = \
|
|||
-Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
|
||||
LDFLAGS-tst-finilazyfailmod.so = \
|
||||
-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];
|
||||
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
|
||||
them into the MAPS array. */
|
||||
int idx = 0;
|
||||
|
|
@ -205,7 +197,7 @@ _dl_close_worker (struct link_map *map, bool force)
|
|||
/* Check whether this object is still used. */
|
||||
if (l->l_type == lt_loaded
|
||||
&& 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
|
||||
acquire is sufficient and correct. */
|
||||
&& 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])
|
||||
{
|
||||
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
|
||||
half-cooked objects. Temporarily disable exception
|
||||
|
|
@ -828,7 +820,7 @@ _dl_close (void *_map)
|
|||
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
|
||||
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. */
|
||||
__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. */
|
||||
static void
|
||||
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,
|
||||
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
|
||||
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
|
||||
relocation. */
|
||||
result->s = sym;
|
||||
result->m = (struct link_map *) map;
|
||||
result->m = map;
|
||||
}
|
||||
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);
|
||||
|
||||
if (map->l_type == lt_loaded)
|
||||
/* Make sure we don't unload this object by
|
||||
setting the appropriate flag. */
|
||||
((struct link_map *) map)->l_flags_1 |= DF_1_NODELETE;
|
||||
{
|
||||
/* Make sure we don't unload this object by
|
||||
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_INSIDE_DLOPEN)
|
||||
map->l_nodelete = link_map_nodelete_pending;
|
||||
else
|
||||
map->l_nodelete = link_map_nodelete_active;
|
||||
}
|
||||
}
|
||||
++tab->n_elements;
|
||||
|
||||
|
|
@ -525,8 +536,9 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
|
|||
return 1;
|
||||
|
||||
case STB_GNU_UNIQUE:;
|
||||
do_lookup_unique (undef_name, new_hash, map, result, type_class,
|
||||
sym, strtab, ref, undef_map);
|
||||
do_lookup_unique (undef_name, new_hash, (struct link_map *) map,
|
||||
result, type_class, sym, strtab, ref,
|
||||
undef_map, flags);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
|
|
@ -568,9 +580,13 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
|
|||
if (undef_map == map)
|
||||
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);
|
||||
if ((map->l_flags_1 & DF_1_NODELETE) != 0)
|
||||
if (map->l_nodelete != link_map_nodelete_inactive)
|
||||
return 0;
|
||||
|
||||
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
|
||||
yet this could have changed. */
|
||||
if ((map->l_flags_1 & DF_1_NODELETE) != 0)
|
||||
if (map->l_nodelete != link_map_nodelete_inactive)
|
||||
goto out;
|
||||
|
||||
/* If the object with the undefined reference cannot be removed ever
|
||||
just make sure the same is true for the object which contains the
|
||||
definition. */
|
||||
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_INSIDE_DLOPEN)
|
||||
map->l_nodelete = link_map_nodelete_pending;
|
||||
else
|
||||
map->l_nodelete = link_map_nodelete_active;
|
||||
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
|
||||
cannot be unloaded. This is semantically the correct
|
||||
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_INSIDE_DLOPEN)
|
||||
/* 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;
|
||||
}
|
||||
else
|
||||
|
|
@ -792,11 +836,9 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
|
|||
|
||||
bump_num_relocations ();
|
||||
|
||||
/* No other flag than DL_LOOKUP_ADD_DEPENDENCY or DL_LOOKUP_GSCOPE_LOCK
|
||||
is allowed if we look up a versioned symbol. */
|
||||
assert (version == NULL
|
||||
|| (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK))
|
||||
== 0);
|
||||
/* DL_LOOKUP_RETURN_NEWEST does not make sense for versioned
|
||||
lookups. */
|
||||
assert (version == NULL || !(flags & DL_LOOKUP_RETURN_NEWEST));
|
||||
|
||||
size_t i = 0;
|
||||
if (__glibc_unlikely (skip_map != NULL))
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -244,7 +244,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
|
|||
v = (version); \
|
||||
_lr = _dl_lookup_symbol_x (strtab + (*ref)->st_name, l, (ref), \
|
||||
scope, v, _tc, \
|
||||
DL_LOOKUP_ADD_DEPENDENCY, NULL); \
|
||||
DL_LOOKUP_ADD_DEPENDENCY \
|
||||
| DL_LOOKUP_INSIDE_DLOPEN, NULL); \
|
||||
l->l_lookup_cache.ret = (*ref); \
|
||||
l->l_lookup_cache.value = _lr; })) \
|
||||
: l)
|
||||
|
|
|
|||
|
|
@ -163,6 +163,8 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
|
|||
if (info[VERSYMIDX (DT_FLAGS_1)] != NULL)
|
||||
{
|
||||
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
|
||||
to assert this, but we can't. Users have been setting
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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/>. */
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/* Type used by the l_nodelete member. */
|
||||
enum link_map_nodelete
|
||||
{
|
||||
/* This link map can be deallocated. */
|
||||
link_map_nodelete_inactive,
|
||||
|
||||
/* 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'
|
||||
members form a chain of all the shared objects loaded at startup.
|
||||
|
|
@ -203,6 +218,10 @@ struct link_map
|
|||
freed, ie. not allocated with
|
||||
the dummy malloc in ld.so. */
|
||||
|
||||
/* Actually of type enum link_map_nodelete. Separate byte due to
|
||||
concurrent access. Only valid for l_type == lt_loaded. */
|
||||
unsigned char l_nodelete;
|
||||
|
||||
#include <link_map.h>
|
||||
|
||||
/* Collected information about own RPATH directories. */
|
||||
|
|
|
|||
|
|
@ -923,6 +923,10 @@ enum
|
|||
DL_LOOKUP_RETURN_NEWEST = 2,
|
||||
/* Set if dl_lookup* called with GSCOPE lock held. */
|
||||
DL_LOOKUP_GSCOPE_LOCK = 4,
|
||||
|
||||
/* Set if dl_lookup* is called from within dlopen, for non-lazy
|
||||
binding. */
|
||||
DL_LOOKUP_INSIDE_DLOPEN = 8,
|
||||
};
|
||||
|
||||
/* Lookup versioned symbol. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue