]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw: Rewrite the memory handler to be thread-safe.
authorJonathon Anderson <jma14@rice.edu>
Sun, 25 Aug 2019 15:07:00 +0000 (10:07 -0500)
committerMark Wielaard <mark@klomp.org>
Thu, 24 Oct 2019 16:46:40 +0000 (18:46 +0200)
Signed-off-by: Jonathon Anderson <jma14@rice.edu>
libdw/ChangeLog
libdw/Makefile.am
libdw/dwarf_begin_elf.c
libdw/dwarf_end.c
libdw/libdwP.h
libdw/libdw_alloc.c

index 498cf0b79de0d2bc22a0c386f3f2f25e34373777..394c0df293f0d7dbfb38232f745c5725532364a5 100644 (file)
@@ -1,3 +1,11 @@
+2019-08-26  Jonathon Anderson  <jma14@rice.edu>
+
+       * libdw_alloc.c (__libdw_allocate): Added thread-safe stack allocator.
+       * libdwP.h (Dwarf): Likewise.
+       * dwarf_begin_elf.c (dwarf_begin_elf): Support for above.
+       * dwarf_end.c (dwarf_end): Likewise.
+       * Makefile.am: Use -pthread to provide rwlocks.
+
 2019-07-05  Omar Sandoval  <osandov@fb.com>
 
        * Makefile.am (libdw_so_LIBS): Replace libebl.a with libebl_pic.a.
index 274571c35d96fe943e35ef9e879f8ea8666fd307..ce793e903b881517f629fef2daa4498194e45a85 100644 (file)
@@ -31,7 +31,7 @@ include $(top_srcdir)/config/eu.am
 if BUILD_STATIC
 AM_CFLAGS += $(fpic_CFLAGS)
 endif
-AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdwelf
+AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdwelf -pthread
 VERSION = 1
 
 lib_LIBRARIES = libdw.a
@@ -109,7 +109,7 @@ libdw_so_LIBS = ../libebl/libebl_pic.a ../backends/libebl_backends_pic.a \
                ../libcpu/libcpu_pic.a libdw_pic.a ../libdwelf/libdwelf_pic.a \
                ../libdwfl/libdwfl_pic.a
 libdw_so_DEPS = ../lib/libeu.a ../libelf/libelf.so
-libdw_so_LDLIBS = $(libdw_so_DEPS) -lz $(argp_LDADD) $(zip_LIBS)
+libdw_so_LDLIBS = $(libdw_so_DEPS) -lz $(argp_LDADD) $(zip_LIBS) -pthread
 libdw_so_SOURCES =
 libdw.so$(EXEEXT): $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS)
        $(AM_V_CCLD)$(LINK) $(dso_LDFLAGS) -o $@ \
index 38c8f5c605041720fd2d295c0103a72d30730a49..8d137414bc76cd05e455c02177593c276b40f659 100644 (file)
@@ -397,7 +397,7 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
   assert (sizeof (struct Dwarf) < mem_default_size);
 
   /* Allocate the data structure.  */
-  Dwarf *result = (Dwarf *) calloc (1, sizeof (Dwarf) + mem_default_size);
+  Dwarf *result = (Dwarf *) calloc (1, sizeof (Dwarf));
   if (unlikely (result == NULL)
       || unlikely (Dwarf_Sig8_Hash_init (&result->sig8_hash, 11) < 0))
     {
@@ -414,14 +414,17 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
   result->elf = elf;
   result->alt_fd = -1;
 
-  /* Initialize the memory handling.  */
+  /* Initialize the memory handling.  Initial blocks are allocated on first
+     actual allocation.  */
   result->mem_default_size = mem_default_size;
   result->oom_handler = __libdw_oom;
-  result->mem_tail = (struct libdw_memblock *) (result + 1);
-  result->mem_tail->size = (result->mem_default_size
-                           - offsetof (struct libdw_memblock, mem));
-  result->mem_tail->remaining = result->mem_tail->size;
-  result->mem_tail->prev = NULL;
+  if (pthread_key_create (&result->mem_key, NULL) != 0)
+    {
+      free (result);
+      __libdw_seterrno (DWARF_E_NOMEM); /* no memory or max pthread keys.  */
+      return NULL;
+    }
+  atomic_init (&result->mem_tail, (uintptr_t)NULL);
 
   if (cmd == DWARF_C_READ || cmd == DWARF_C_RDWR)
     {
index 29795c10a18ce38f7bf6fad7d525b7abc5d8a287..a2e944360c3803710f8217d9ea16c3b3a1a5126f 100644 (file)
@@ -94,14 +94,18 @@ dwarf_end (Dwarf *dwarf)
       /* And the split Dwarf.  */
       tdestroy (dwarf->split_tree, noop_free);
 
-      struct libdw_memblock *memp = dwarf->mem_tail;
-      /* The first block is allocated together with the Dwarf object.  */
-      while (memp->prev != NULL)
+      /* Free the internally allocated memory.  */
+      struct libdw_memblock *memp;
+      memp = (struct libdw_memblock *) (atomic_load_explicit
+                                       (&dwarf->mem_tail,
+                                        memory_order_relaxed));
+      while (memp != NULL)
        {
          struct libdw_memblock *prevp = memp->prev;
          free (memp);
          memp = prevp;
        }
+      pthread_key_delete (dwarf->mem_key);
 
       /* Free the pubnames helper structure.  */
       free (dwarf->pubnames_sets);
index eebb7d1269911b40f55aed41db03097e2d9377b0..ad2599ebd1bc3b9782968f29073c5a568125b994 100644 (file)
 
 #include <libintl.h>
 #include <stdbool.h>
+#include <pthread.h>
 
 #include <libdw.h>
 #include <dwarf.h>
+#include "atomics.h"
 
 
 /* gettext helper macros.  */
@@ -147,6 +149,17 @@ enum
 
 #include "dwarf_sig8_hash.h"
 
+/* Structure for internal memory handling.  This is basically a simplified
+   reimplementation of obstacks.  Unfortunately the standard obstack
+   implementation is not usable in libraries.  */
+struct libdw_memblock
+{
+  size_t size;
+  size_t remaining;
+  struct libdw_memblock *prev;
+  char mem[0];
+};
+
 /* This is the structure representing the debugging state.  */
 struct Dwarf
 {
@@ -218,16 +231,11 @@ struct Dwarf
   /* Similar for addrx/constx, which will come from .debug_addr section.  */
   struct Dwarf_CU *fake_addr_cu;
 
-  /* Internal memory handling.  This is basically a simplified
-     reimplementation of obstacks.  Unfortunately the standard obstack
-     implementation is not usable in libraries.  */
-  struct libdw_memblock
-  {
-    size_t size;
-    size_t remaining;
-    struct libdw_memblock *prev;
-    char mem[0];
-  } *mem_tail;
+  /* Internal memory handling.  Each thread allocates separately and only
+     allocates from its own blocks, while all the blocks are pushed atomically
+     onto a unified stack for easy deallocation.  */
+  pthread_key_t mem_key;
+  atomic_uintptr_t mem_tail;
 
   /* Default size of allocated memory blocks.  */
   size_t mem_default_size;
@@ -570,21 +578,28 @@ libdw_valid_user_form (int form)
 extern void __libdw_seterrno (int value) internal_function;
 
 
-/* Memory handling, the easy parts.  This macro does not do any locking.  */
+/* Memory handling, the easy parts.  This macro does not do nor need to do any
+   locking for proper concurrent operation.  */
 #define libdw_alloc(dbg, type, tsize, cnt) \
-  ({ struct libdw_memblock *_tail = (dbg)->mem_tail;                         \
-     size_t _required = (tsize) * (cnt);                                     \
-     type *_result = (type *) (_tail->mem + (_tail->size - _tail->remaining));\
-     size_t _padding = ((__alignof (type)                                    \
-                        - ((uintptr_t) _result & (__alignof (type) - 1)))    \
-                       & (__alignof (type) - 1));                            \
-     if (unlikely (_tail->remaining < _required + _padding))                 \
-       _result = (type *) __libdw_allocate (dbg, _required, __alignof (type));\
+  ({ struct libdw_memblock *_tail = pthread_getspecific (dbg->mem_key);       \
+     size_t _req = (tsize) * (cnt);                                          \
+     type *_result;                                                          \
+     if (unlikely (_tail == NULL))                                           \
+       _result = (type *) __libdw_allocate (dbg, _req, __alignof (type));     \
      else                                                                    \
        {                                                                     \
-        _required += _padding;                                               \
-        _result = (type *) ((char *) _result + _padding);                    \
-        _tail->remaining -= _required;                                       \
+        _result = (type *) (_tail->mem + (_tail->size - _tail->remaining));  \
+        size_t _padding = ((__alignof (type)                                 \
+                           - ((uintptr_t) _result & (__alignof (type) - 1))) \
+                              & (__alignof (type) - 1));                     \
+        if (unlikely (_tail->remaining < _req + _padding))                   \
+          _result = (type *) __libdw_allocate (dbg, _req, __alignof (type)); \
+        else                                                                 \
+          {                                                                  \
+            _req += _padding;                                                \
+            _result = (type *) ((char *) _result + _padding);                \
+            _tail->remaining -= _req;                                        \
+          }                                                                  \
        }                                                                     \
      _result; })
 
index f1e087144e504f074b80b37d1fc7bf44796030dc..f2e74d18e5c36b3cefe1a5654d968bf7b84c50b8 100644 (file)
@@ -52,8 +52,10 @@ __libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
   newp->size = size - offsetof (struct libdw_memblock, mem);
   newp->remaining = (uintptr_t) newp + size - (result + minsize);
 
-  newp->prev = dbg->mem_tail;
-  dbg->mem_tail = newp;
+  newp->prev = (struct libdw_memblock*)atomic_exchange_explicit(
+      &dbg->mem_tail, (uintptr_t)newp, memory_order_relaxed);
+  if (pthread_setspecific (dbg->mem_key, newp) != 0)
+    dbg->oom_handler ();
 
   return (void *) result;
 }