about summary refs log tree commit diff
path: root/elf/tst-dl_find_object-threads.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2021-12-28 22:52:56 +0100
committerFlorian Weimer <fweimer@redhat.com>2021-12-28 22:52:56 +0100
commit5d28a8962dcb6ec056b81d730e3c6fb57185a210 (patch)
tree3d714aaef575deba322fa5a1e29c76c6f96dc850 /elf/tst-dl_find_object-threads.c
parent83b8d5027d2f80c4603cd706da95d6c9a09a4e16 (diff)
downloadglibc-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.c275
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>