+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.
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
../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 $@ \
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))
{
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)
{
/* 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);
#include <libintl.h>
#include <stdbool.h>
+#include <pthread.h>
#include <libdw.h>
#include <dwarf.h>
+#include "atomics.h"
/* gettext helper macros. */
#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
{
/* 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;
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; })
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;
}