aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathon Anderson <jma14@rice.edu>2019-08-25 10:07:00 -0500
committerMark Wielaard <mark@klomp.org>2019-10-24 18:46:40 +0200
commite9d4067a15eb100c0598a9895567aac6e9ed6a10 (patch)
treeafd678dcc509aa197cbfa0d2fc2201dc93568fea
parent77b66a71adaff5b445c3a5bb43b6d5742265df14 (diff)
downloadelfutils-e9d4067a15eb100c0598a9895567aac6e9ed6a10.tar.gz
libdw: Rewrite the memory handler to be thread-safe.
Signed-off-by: Jonathon Anderson <jma14@rice.edu>
-rw-r--r--libdw/ChangeLog8
-rw-r--r--libdw/Makefile.am4
-rw-r--r--libdw/dwarf_begin_elf.c17
-rw-r--r--libdw/dwarf_end.c10
-rw-r--r--libdw/libdwP.h59
-rw-r--r--libdw/libdw_alloc.c6
6 files changed, 68 insertions, 36 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 498cf0b7..394c0df2 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -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.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 274571c3..ce793e90 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -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 $@ \
diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
index 38c8f5c6..8d137414 100644
--- a/libdw/dwarf_begin_elf.c
+++ b/libdw/dwarf_begin_elf.c
@@ -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)
{
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 29795c10..a2e94436 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -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);
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index eebb7d12..ad2599eb 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -31,9 +31,11 @@
#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; })
diff --git a/libdw/libdw_alloc.c b/libdw/libdw_alloc.c
index f1e08714..f2e74d18 100644
--- a/libdw/libdw_alloc.c
+++ b/libdw/libdw_alloc.c
@@ -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;
}