From: Jonathon Anderson <jm...@rice.edu> Signed-off-by: Jonathon Anderson <jm...@rice.edu> --- libdw/ChangeLog | 8 ++++++ libdw/Makefile.am | 4 +-- libdw/dwarf_begin_elf.c | 12 ++++----- libdw/dwarf_end.c | 7 ++--- libdw/libdwP.h | 59 ++++++++++++++++++++++++++--------------- libdw/libdw_alloc.c | 5 ++-- 6 files changed, 59 insertions(+), 36 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 1d3586f0..ec809070 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,11 @@ +2019-08-26 Jonathon Anderson <jm...@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-08-25 Jonathon Anderson <jm...@rice.edu> * dwarf_getcfi.c (dwarf_getcfi): Set default_same_value to false. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 7a3d5322..ba5745f3 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 @@ -108,7 +108,7 @@ am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os) libdw_so_LIBS = libdw_pic.a ../libdwelf/libdwelf_pic.a \ ../libdwfl/libdwfl_pic.a ../libebl/libebl.a libdw_so_DEPS = ../lib/libeu.a ../libelf/libelf.so -libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(zip_LIBS) +libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(zip_LIBS) -pthread libdw_so_SOURCES = libdw.so$(EXEEXT): $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS) # The rpath is necessary for libebl because its $ORIGIN use will diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 38c8f5c6..f865f69c 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,12 @@ 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; + pthread_key_create (&result->mem_key, 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..fc573cb3 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -94,14 +94,15 @@ 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 = (struct libdw_memblock *)dwarf->mem_tail; + 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..78977e54 100644 --- a/libdw/libdw_alloc.c +++ b/libdw/libdw_alloc.c @@ -52,8 +52,9 @@ __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); + pthread_setspecific(dbg->mem_key, newp); return (void *) result; } -- 2.18.1