diff options
author | Florian Weimer <fweimer@redhat.com> | 2021-12-28 22:52:56 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2021-12-28 22:52:56 +0100 |
commit | 5d28a8962dcb6ec056b81d730e3c6fb57185a210 (patch) | |
tree | 3d714aaef575deba322fa5a1e29c76c6f96dc850 /elf/tst-dl_find_object-threads.c | |
parent | 83b8d5027d2f80c4603cd706da95d6c9a09a4e16 (diff) | |
download | glibc-5d28a8962dcb6ec056b81d730e3c6fb57185a210.tar.gz glibc-5d28a8962dcb6ec056b81d730e3c6fb57185a210.tar.xz glibc-5d28a8962dcb6ec056b81d730e3c6fb57185a210.zip |
elf: Add _dl_find_object function
It can be used to speed up the libgcc unwinder, and the internal _dl_find_dso_for_object function (which is used for caller identification in dlopen and related functions, and in dladdr). _dl_find_object is in the internal namespace due to bug 28503. If libgcc switches to _dl_find_object, this namespace issue will be fixed. It is located in libc for two reasons: it is necessary to forward the call to the static libc after static dlopen, and there is a link ordering issue with -static-libgcc and libgcc_eh.a because libc.so is not a linker script that includes ld.so in the glibc build tree (so that GCC's internal -lc after libgcc_eh.a does not pick up ld.so). It is necessary to do the i386 customization in the sysdeps/x86/bits/dl_find_object.h header shared with x86-64 because otherwise, multilib installations are broken. The implementation uses software transactional memory, as suggested by Torvald Riegel. Two copies of the supporting data structures are used, also achieving full async-signal-safety. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Diffstat (limited to 'elf/tst-dl_find_object-threads.c')
-rw-r--r-- | elf/tst-dl_find_object-threads.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/elf/tst-dl_find_object-threads.c b/elf/tst-dl_find_object-threads.c new file mode 100644 index 0000000000..472deeec57 --- /dev/null +++ b/elf/tst-dl_find_object-threads.c @@ -0,0 +1,275 @@ +/* _dl_find_object test with parallelism. + Copyright (C) 2021 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 <array_length.h> +#include <dlfcn.h> +#include <elf/dl-find_object.h> +#include <stdio.h> +#include <stdlib.h> +#include <support/check.h> +#include <support/support.h> +#include <support/xdlfcn.h> +#include <support/xthread.h> +#include <support/xunistd.h> + +/* Computes the expected _dl_find_object result directly from the + map. */ +static void +from_map (struct link_map *l, struct dl_find_object *expected) +{ + struct dl_find_object_internal internal; + _dl_find_object_from_map (l, &internal); + _dl_find_object_to_external (&internal, expected); +} + +/* Returns the soname for the test object NUMBER. */ +static char * +soname (int number) +{ + return xasprintf ("tst-dl_find_object-mod%d.so", number); +} + +/* Returns the data symbol name for the test object NUMBER. */ +static char * +symbol (int number) +{ + return xasprintf ("mod%d_data", number); +} + +struct verify_data +{ + char *soname; + void *address; /* Address in the shared object. */ + struct dl_find_object dlfo; + pthread_t thr; +}; + +/* Compare _dl_find_object result at ADDRESS with *EXPECTED. */ +static void +check (void *address, struct dl_find_object *expected, int line) +{ + struct dl_find_object actual; + int ret = _dl_find_object (address, &actual); + if (expected == NULL) + { + if (ret != -1) + { + support_record_failure (); + printf ("%s:%d: unexpected success for %p\n", + __FILE__, line, address); + } + return; + } + if (ret != 0) + { + support_record_failure (); + printf ("%s:%d: unexpected failure for %p\n", + __FILE__, line, address); + return; + } + + if (actual.dlfo_flags != expected->dlfo_flags) + { + support_record_failure (); + printf ("%s:%d: error: %p: flags is %llu, expected %llu\n", + __FILE__, line, address, + actual.dlfo_flags, expected->dlfo_flags); + } + if (actual.dlfo_flags != expected->dlfo_flags) + { + support_record_failure (); + printf ("%s:%d: error: %p: map start is %p, expected %p\n", + __FILE__, line, + address, actual.dlfo_map_start, expected->dlfo_map_start); + } + if (actual.dlfo_map_end != expected->dlfo_map_end) + { + support_record_failure (); + printf ("%s:%d: error: %p: map end is %p, expected %p\n", + __FILE__, line, + address, actual.dlfo_map_end, expected->dlfo_map_end); + } + if (actual.dlfo_link_map != expected->dlfo_link_map) + { + support_record_failure (); + printf ("%s:%d: error: %p: link map is %p, expected %p\n", + __FILE__, line, + address, actual.dlfo_link_map, expected->dlfo_link_map); + } + if (actual.dlfo_eh_frame != expected->dlfo_eh_frame) + { + support_record_failure (); + printf ("%s:%d: error: %p: EH frame is %p, expected %p\n", + __FILE__, line, + address, actual.dlfo_eh_frame, expected->dlfo_eh_frame); + } +#if DLFO_STRUCT_HAS_EH_DBASE + if (actual.dlfo_eh_dbase != expected->dlfo_eh_dbase) + { + support_record_failure (); + printf ("%s:%d: error: %p: data base is %p, expected %p\n", + __FILE__, line, + address, actual.dlfo_eh_dbase, expected->dlfo_eh_dbase); + } +#endif +#if DLFO_STRUCT_HAS_EH_COUNT + if (actual.dlfo_eh_count != expected->dlfo_eh_count) + { + support_record_failure (); + printf ("%s:%d: error: %p: count is %d, expected %d\n", + __FILE__, line, + address, actual.dlfo_eh_count, expected->dlfo_eh_count); + } +#endif +} + +/* Request process termination after 3 seconds. */ +static bool exit_requested; +static void * +exit_thread (void *ignored) +{ + usleep (3 * 100 * 1000); + __atomic_store_n (&exit_requested, true, __ATOMIC_RELAXED); + return NULL; +} + +static void * +verify_thread (void *closure) +{ + struct verify_data *data = closure; + + while (!__atomic_load_n (&exit_requested, __ATOMIC_RELAXED)) + { + check (data->address, &data->dlfo, __LINE__); + check (data->dlfo.dlfo_map_start, &data->dlfo, __LINE__); + check (data->dlfo.dlfo_map_end - 1, &data->dlfo, __LINE__); + } + + return NULL; +} + +/* Sets up the verification data, dlopen'ing shared object NUMBER, and + launches a verification thread. */ +static void +start_verify (int number, struct verify_data *data) +{ + data->soname = soname (number); + struct link_map *l = xdlopen (data->soname, RTLD_NOW); + from_map (l, &data->dlfo); + TEST_VERIFY_EXIT (data->dlfo.dlfo_link_map == l); + char *sym = symbol (number); + data->address = xdlsym (data->dlfo.dlfo_link_map, sym); + free (sym); + data->thr = xpthread_create (NULL, verify_thread, data); +} + + +static int +do_test (void) +{ + struct verify_data data_mod2; + struct verify_data data_mod4; + struct verify_data data_mod7; + + /* Load the modules with gaps. */ + { + void *mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW); + start_verify (2, &data_mod2); + void *mod3 = xdlopen ("tst-dl_find_object-mod3.so", RTLD_NOW); + start_verify (4, &data_mod4); + void *mod5 = xdlopen ("tst-dl_find_object-mod5.so", RTLD_NOW); + void *mod6 = xdlopen ("tst-dl_find_object-mod6.so", RTLD_NOW); + start_verify (7, &data_mod7); + xdlclose (mod6); + xdlclose (mod5); + xdlclose (mod3); + xdlclose (mod1); + } + + /* Objects that continuously opened and closed. */ + struct temp_object + { + char *soname; + char *symbol; + struct link_map *link_map; + void *address; + } temp_objects[] = + { + { soname (1), symbol (1), }, + { soname (3), symbol (3), }, + { soname (5), symbol (5), }, + { soname (6), symbol (6), }, + { soname (8), symbol (8), }, + { soname (9), symbol (9), }, + }; + + pthread_t exit_thr = xpthread_create (NULL, exit_thread, NULL); + + struct drand48_data state; + srand48_r (1, &state); + while (!__atomic_load_n (&exit_requested, __ATOMIC_RELAXED)) + { + long int idx; + lrand48_r (&state, &idx); + idx %= array_length (temp_objects); + if (temp_objects[idx].link_map == NULL) + { + temp_objects[idx].link_map = xdlopen (temp_objects[idx].soname, + RTLD_NOW); + temp_objects[idx].address = xdlsym (temp_objects[idx].link_map, + temp_objects[idx].symbol); + } + else + { + xdlclose (temp_objects[idx].link_map); + temp_objects[idx].link_map = NULL; + struct dl_find_object dlfo; + int ret = _dl_find_object (temp_objects[idx].address, &dlfo); + if (ret != -1) + { + TEST_VERIFY_EXIT (ret == 0); + support_record_failure (); + printf ("%s: error: %s EH found after dlclose, link map %p\n", + __FILE__, temp_objects[idx].soname, dlfo.dlfo_link_map); + } + } + } + + xpthread_join (data_mod2.thr); + xpthread_join (data_mod4.thr); + xpthread_join (data_mod7.thr); + xpthread_join (exit_thr); + + for (size_t i = 0; i < array_length (temp_objects); ++i) + { + free (temp_objects[i].soname); + free (temp_objects[i].symbol); + if (temp_objects[i].link_map != NULL) + xdlclose (temp_objects[i].link_map); + } + + free (data_mod2.soname); + free (data_mod4.soname); + xdlclose (data_mod4.dlfo.dlfo_link_map); + free (data_mod7.soname); + xdlclose (data_mod7.dlfo.dlfo_link_map); + + return 0; +} + +#include <support/test-driver.c> |