diff options
Diffstat (limited to 'libdw')
-rw-r--r-- | libdw/ChangeLog | 60 | ||||
-rw-r--r-- | libdw/Makefile.am | 51 | ||||
-rw-r--r-- | libdw/dwarf_abbrev_hash.c | 2 | ||||
-rw-r--r-- | libdw/dwarf_abbrev_hash.h | 3 | ||||
-rw-r--r-- | libdw/dwarf_begin_elf.c | 36 | ||||
-rw-r--r-- | libdw/dwarf_end.c | 45 | ||||
-rw-r--r-- | libdw/dwarf_formref_die.c | 2 | ||||
-rw-r--r-- | libdw/dwarf_getabbrev.c | 12 | ||||
-rw-r--r-- | libdw/dwarf_getcfi.c | 1 | ||||
-rw-r--r-- | libdw/dwarf_sig8_hash.c | 2 | ||||
-rw-r--r-- | libdw/dwarf_sig8_hash.h | 9 | ||||
-rw-r--r-- | libdw/dwarf_tag.c | 2 | ||||
-rw-r--r-- | libdw/libdwP.h | 33 | ||||
-rw-r--r-- | libdw/libdw_alloc.c | 78 |
14 files changed, 260 insertions, 76 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index bf1f4857..59f33f9e 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,63 @@ +2019-10-28 Aaron Merey <[email protected]> + + * Makefile.am (libdw_so_LDLIBS): Add -ldl for libdebuginfod.so dlopen. + +2019-11-10 Mark Wielaard <[email protected]> + + * libdwP.h (libdw_unalloc): New define. + (libdw_typed_unalloc): Likewise. + (__libdw_thread_tail): New function declaration. + * libdw_alloc.c (__libdw_thread_tail): New function. + * dwarf_getabbrev.c (__libdw_getabbrev): Call libdw_typed_unalloc + when reading invalid data or when hash collission detected. + +2019-10-28 Jonathon Anderson <[email protected]> + + * libdw_alloc.c: Added __libdw_alloc_tail. + (__libdw_allocate): Switch to use the mem_tails array. + * libdwP.h (Dwarf): Likewise. + * dwarf_begin_elf.c (dwarf_begin_elf): Support for above. + * dwarf_end.c (dwarf_end): Likewise. + * atomics.h: Add support for thread_local. + +2019-10-28 Mark Wielaard <[email protected]> + + * dwarf_sig8_hash.h: Include libdw.h. Remove COMPARE. Include + dynamicsizehash_concurrent.h. + * dwarf_sig8_hash.c: Include dynamicsizehash_concurrent.c. + * dwarf_formref_die.c (dwarf_formref_die): Drop NULL argument to + Dwarf_Sig8_Hash_find. + +2019-08-26 Srđan Milaković <[email protected]@rice.edu> + + * dwarf_abbrev_hash.{c,h}: Use the *_concurrent hash table. + +2019-11-01 Jonathon Anderson <[email protected]> + + * dwarf_begin_elf.c (valid_p): Switch calloc for malloc for fake CUs. + Add explicit initialization of some fields. + * dwarf_end.c (cu_free): Add clause to limit freeing of CU internals. + +2019-08-26 Jonathon Anderson <[email protected]> + + * 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 <[email protected]> + + * Makefile.am (libdw_so_LIBS): Replace libebl.a with libebl_pic.a. + Move libebl_pic.a to the beginning so that libdw symbols are resolved. + (libdw_so_LDLIBS): Remove -ldl. + (libdw.so): Remove -rpath. + (libdw_a_LIBADD): Add libebl, libebl_backends, and libcpu objects. + +2019-08-25 Jonathon Anderson <[email protected]> + + * dwarf_getcfi.c (dwarf_getcfi): Set default_same_value to false. + 2019-08-12 Mark Wielaard <[email protected]> * libdw.map (ELFUTILS_0.177): Add new version of dwelf_elf_begin. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 166e37cf..ef566399 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -31,17 +31,17 @@ 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 -noinst_LIBRARIES = libdw_pic.a libdw_static_pic.a -noinst_PROGRAMS = libdw.so +noinst_LIBRARIES = libdw_pic.a +noinst_PROGRAMS = $(noinst_LIBRARIES:_pic.a=.so) include_HEADERS = dwarf.h pkginclude_HEADERS = libdw.h known-dwarf.h -libdw_a_CFLAGS = -fPIC -fvisibility=hidden $(AM_CFLAGS) +libdw_a_CFLAGS = -fPIC $(AM_CFLAGS) libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_getpubnames.c dwarf_getabbrev.c dwarf_tag.c \ dwarf_error.c dwarf_nextcu.c dwarf_diename.c dwarf_offdie.c \ @@ -95,25 +95,6 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ libdw_find_split_unit.c dwarf_cu_info.c \ dwarf_next_lines.c -# Minimal library with symbols needed by those libebl backends that we -# ship statically. This is so that e.g. strip doesn't end up bringing -# in the whole of libdw, and depending on libbz2, liblzma, etc. -libdw_static_pic_a_SOURCES = libdw_form.c dwarf_child.c dwarf_attr.c \ - dwarf_attr_integrate.c dwarf_formref_die.c dwarf_error.c \ - dwarf_tag.c dwarf_formref.c \ - dwarf_abbrev_hash.c dwarf_sig8_hash.c \ - dwarf_formudata.c libdw_findcu.c dwarf_offdie.c \ - dwarf_getabbrev.c dwarf_nextcu.c libdw_alloc.c \ - dwarf_hasattr_integrate.c dwarf_hasattr.c \ - dwarf_aggregate_size.c dwarf_siblingof.c dwarf_formsdata.c \ - dwarf_srclang.c dwarf_formflag.c dwarf_diecu.c \ - dwarf_bytesize.c dwarf_bitsize.c dwarf_peel_type.c \ - dwarf_default_lower_bound.c libdw_find_split_unit.c \ - dwarf_getalt.c dwarf_haschildren.c \ - dwarf_begin.c dwarf_begin_elf.c dwarf_end.c frame-cache.c \ - dwarf_get_units.c dwarf_formstring.c \ - ../libdwelf/dwelf_dwarf_gnu_debugaltlink.c - if MAINTAINER_MODE BUILT_SOURCES = $(srcdir)/known-dwarf.h MAINTAINERCLEANFILES = $(srcdir)/known-dwarf.h @@ -124,21 +105,16 @@ endif libdw_pic_a_SOURCES = am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os) -am_libdw_static_pic_a_OBJECTS = $(libdw_static_pic_a_SOURCES:.c=.os) -libdw_so_LIBS = libdw_pic.a ../libdwelf/libdwelf_pic.a \ - ../libdwfl/libdwfl_pic.a ../libebl/libebl.a \ - ../backends/libebl_static_pic.a \ - ../libcpu/libcpu_static_pic.a +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) -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 -# not fly in a setuid executable that links in libdw. $(AM_V_CCLD)$(LINK) $(dso_LDFLAGS) -o $@ \ - -Wl,--soname,$@.$(VERSION) \ - -Wl,--enable-new-dtags,-rpath,$(pkglibdir) \ + -Wl,--soname,$@.$(VERSION),--enable-new-dtags \ -Wl,--version-script,$<,--no-undefined \ -Wl,--whole-archive $(libdw_so_LIBS) -Wl,--no-whole-archive \ $(libdw_so_LDLIBS) @@ -163,6 +139,15 @@ libdw_a_LIBADD = $(addprefix ../libdwfl/,$(libdwfl_objects)) libdwelf_objects = $(shell $(AR) t ../libdwelf/libdwelf.a) libdw_a_LIBADD += $(addprefix ../libdwelf/,$(libdwelf_objects)) +libebl_objects = $(shell $(AR) t ../libebl/libebl.a) +libdw_a_LIBADD += $(addprefix ../libebl/,$(libebl_objects)) + +backends_objects = $(shell $(AR) t ../backends/libebl_backends.a) +libdw_a_LIBADD += $(addprefix ../backends/,$(backends_objects)) + +libcpu_objects = $(shell $(AR) t ../libcpu/libcpu.a) +libdw_a_LIBADD += $(addprefix ../libcpu/,$(libcpu_objects)) + noinst_HEADERS = libdwP.h memory-access.h dwarf_abbrev_hash.h \ dwarf_sig8_hash.h cfi.h encoded-value.h diff --git a/libdw/dwarf_abbrev_hash.c b/libdw/dwarf_abbrev_hash.c index f52f5ad5..c2548140 100644 --- a/libdw/dwarf_abbrev_hash.c +++ b/libdw/dwarf_abbrev_hash.c @@ -38,7 +38,7 @@ #define next_prime __libdwarf_next_prime extern size_t next_prime (size_t) attribute_hidden; -#include <dynamicsizehash.c> +#include <dynamicsizehash_concurrent.c> #undef next_prime #define next_prime attribute_hidden __libdwarf_next_prime diff --git a/libdw/dwarf_abbrev_hash.h b/libdw/dwarf_abbrev_hash.h index d2f02ccc..a368c598 100644 --- a/libdw/dwarf_abbrev_hash.h +++ b/libdw/dwarf_abbrev_hash.h @@ -32,8 +32,7 @@ #define NAME Dwarf_Abbrev_Hash #define TYPE Dwarf_Abbrev * -#define COMPARE(a, b) (0) -#include <dynamicsizehash.h> +#include <dynamicsizehash_concurrent.h> #endif /* dwarf_abbrev_hash.h */ diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 38c8f5c6..85343088 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -223,7 +223,7 @@ valid_p (Dwarf *result) inside the .debug_loc or .debug_loclists section. */ if (result != NULL && result->sectiondata[IDX_debug_loc] != NULL) { - result->fake_loc_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); + result->fake_loc_cu = (Dwarf_CU *) malloc (sizeof (Dwarf_CU)); if (unlikely (result->fake_loc_cu == NULL)) { Dwarf_Sig8_Hash_free (&result->sig8_hash); @@ -240,12 +240,16 @@ valid_p (Dwarf *result) result->fake_loc_cu->endp = (result->sectiondata[IDX_debug_loc]->d_buf + result->sectiondata[IDX_debug_loc]->d_size); + result->fake_loc_cu->locs = NULL; + result->fake_loc_cu->address_size = 0; + result->fake_loc_cu->version = 0; + result->fake_loc_cu->split = NULL; } } if (result != NULL && result->sectiondata[IDX_debug_loclists] != NULL) { - result->fake_loclists_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); + result->fake_loclists_cu = (Dwarf_CU *) malloc (sizeof (Dwarf_CU)); if (unlikely (result->fake_loclists_cu == NULL)) { Dwarf_Sig8_Hash_free (&result->sig8_hash); @@ -263,6 +267,10 @@ valid_p (Dwarf *result) result->fake_loclists_cu->endp = (result->sectiondata[IDX_debug_loclists]->d_buf + result->sectiondata[IDX_debug_loclists]->d_size); + result->fake_loclists_cu->locs = NULL; + result->fake_loclists_cu->address_size = 0; + result->fake_loclists_cu->version = 0; + result->fake_loclists_cu->split = NULL; } } @@ -272,7 +280,7 @@ valid_p (Dwarf *result) inside the .debug_addr section, if it exists. */ if (result != NULL && result->sectiondata[IDX_debug_addr] != NULL) { - result->fake_addr_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); + result->fake_addr_cu = (Dwarf_CU *) malloc (sizeof (Dwarf_CU)); if (unlikely (result->fake_addr_cu == NULL)) { Dwarf_Sig8_Hash_free (&result->sig8_hash); @@ -291,6 +299,10 @@ valid_p (Dwarf *result) result->fake_addr_cu->endp = (result->sectiondata[IDX_debug_addr]->d_buf + result->sectiondata[IDX_debug_addr]->d_size); + result->fake_addr_cu->locs = NULL; + result->fake_addr_cu->address_size = 0; + result->fake_addr_cu->version = 0; + result->fake_addr_cu->split = NULL; } } @@ -397,7 +409,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 +426,18 @@ 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_rwlock_init(&result->mem_rwl, NULL) != 0) + { + free (result); + __libdw_seterrno (DWARF_E_NOMEM); /* no memory. */ + return NULL; + } + result->mem_stacks = 0; + result->mem_tails = NULL; if (cmd == DWARF_C_READ || cmd == DWARF_C_RDWR) { diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index 29795c10..77f537a7 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -52,18 +52,23 @@ cu_free (void *arg) { struct Dwarf_CU *p = (struct Dwarf_CU *) arg; - Dwarf_Abbrev_Hash_free (&p->abbrev_hash); - tdestroy (p->locs, noop_free); - /* Free split dwarf one way (from skeleton to split). */ - if (p->unit_type == DW_UT_skeleton - && p->split != NULL && p->split != (void *)-1) + /* Only free the CU internals if its not a fake CU. */ + if(p != p->dbg->fake_loc_cu && p != p->dbg->fake_loclists_cu + && p != p->dbg->fake_addr_cu) { - /* The fake_addr_cu might be shared, only release one. */ - if (p->dbg->fake_addr_cu == p->split->dbg->fake_addr_cu) - p->split->dbg->fake_addr_cu = NULL; - INTUSE(dwarf_end) (p->split->dbg); + Dwarf_Abbrev_Hash_free (&p->abbrev_hash); + + /* Free split dwarf one way (from skeleton to split). */ + if (p->unit_type == DW_UT_skeleton + && p->split != NULL && p->split != (void *)-1) + { + /* The fake_addr_cu might be shared, only release one. */ + if (p->dbg->fake_addr_cu == p->split->dbg->fake_addr_cu) + p->split->dbg->fake_addr_cu = NULL; + INTUSE(dwarf_end) (p->split->dbg); + } } } @@ -94,14 +99,20 @@ 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) - { - struct libdw_memblock *prevp = memp->prev; - free (memp); - memp = prevp; - } + /* Free the internally allocated memory. */ + for (size_t i = 0; i < dwarf->mem_stacks; i++) + { + struct libdw_memblock *memp = dwarf->mem_tails[i]; + while (memp != NULL) + { + struct libdw_memblock *prevp = memp->prev; + free (memp); + memp = prevp; + } + } + if (dwarf->mem_tails != NULL) + free (dwarf->mem_tails); + pthread_rwlock_destroy (&dwarf->mem_rwl); /* Free the pubnames helper structure. */ free (dwarf->pubnames_sets); diff --git a/libdw/dwarf_formref_die.c b/libdw/dwarf_formref_die.c index f196331a..48ba8194 100644 --- a/libdw/dwarf_formref_die.c +++ b/libdw/dwarf_formref_die.c @@ -83,7 +83,7 @@ dwarf_formref_die (Dwarf_Attribute *attr, Dwarf_Die *result) have to match in the type unit headers. */ uint64_t sig = read_8ubyte_unaligned (cu->dbg, attr->valp); - cu = Dwarf_Sig8_Hash_find (&cu->dbg->sig8_hash, sig, NULL); + cu = Dwarf_Sig8_Hash_find (&cu->dbg->sig8_hash, sig); if (cu == NULL) { /* Not seen before. We have to scan through the type units. diff --git a/libdw/dwarf_getabbrev.c b/libdw/dwarf_getabbrev.c index 6a7e981b..13bee493 100644 --- a/libdw/dwarf_getabbrev.c +++ b/libdw/dwarf_getabbrev.c @@ -83,7 +83,7 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset, bool foundit = false; Dwarf_Abbrev *abb = NULL; if (cu == NULL - || (abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code, NULL)) == NULL) + || (abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code)) == NULL) { if (result == NULL) abb = libdw_typed_alloc (dbg, Dwarf_Abbrev); @@ -99,6 +99,8 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset, /* A duplicate abbrev code at a different offset, that should never happen. */ invalid: + if (! foundit) + libdw_typed_unalloc (dbg, Dwarf_Abbrev); __libdw_seterrno (DWARF_E_INVALID_DWARF); return NULL; } @@ -148,7 +150,13 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset, /* Add the entry to the hash table. */ if (cu != NULL && ! foundit) - (void) Dwarf_Abbrev_Hash_insert (&cu->abbrev_hash, abb->code, abb); + if (Dwarf_Abbrev_Hash_insert (&cu->abbrev_hash, abb->code, abb) == -1) + { + /* The entry was already in the table, remove the one we just + created and get the one already inserted. */ + libdw_typed_unalloc (dbg, Dwarf_Abbrev); + abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code); + } out: return abb; diff --git a/libdw/dwarf_getcfi.c b/libdw/dwarf_getcfi.c index 9aed403e..51932cd9 100644 --- a/libdw/dwarf_getcfi.c +++ b/libdw/dwarf_getcfi.c @@ -58,6 +58,7 @@ dwarf_getcfi (Dwarf *dbg) cfi->e_ident = (unsigned char *) elf_getident (dbg->elf, NULL); cfi->other_byte_order = dbg->other_byte_order; + cfi->default_same_value = false; cfi->next_offset = 0; cfi->cie_tree = cfi->fde_tree = cfi->expr_tree = NULL; diff --git a/libdw/dwarf_sig8_hash.c b/libdw/dwarf_sig8_hash.c index 043cac78..777f9ebc 100644 --- a/libdw/dwarf_sig8_hash.c +++ b/libdw/dwarf_sig8_hash.c @@ -38,4 +38,4 @@ #define next_prime __libdwarf_next_prime extern size_t next_prime (size_t) attribute_hidden; -#include <dynamicsizehash.c> +#include <dynamicsizehash_concurrent.c> diff --git a/libdw/dwarf_sig8_hash.h b/libdw/dwarf_sig8_hash.h index 705ffbcd..c399919a 100644 --- a/libdw/dwarf_sig8_hash.h +++ b/libdw/dwarf_sig8_hash.h @@ -29,10 +29,15 @@ #ifndef _DWARF_SIG8_HASH_H #define _DWARF_SIG8_HASH_H 1 +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "libdw.h" + #define NAME Dwarf_Sig8_Hash #define TYPE struct Dwarf_CU * -#define COMPARE(a, b) (0) -#include <dynamicsizehash.h> +#include <dynamicsizehash_concurrent.h> #endif /* dwarf_sig8_hash.h */ diff --git a/libdw/dwarf_tag.c b/libdw/dwarf_tag.c index 331eaa0d..d784970c 100644 --- a/libdw/dwarf_tag.c +++ b/libdw/dwarf_tag.c @@ -45,7 +45,7 @@ __libdw_findabbrev (struct Dwarf_CU *cu, unsigned int code) return DWARF_END_ABBREV; /* See whether the entry is already in the hash table. */ - abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code, NULL); + abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code); if (abb == NULL) while (cu->last_abbrev_offset != (size_t) -1l) { diff --git a/libdw/libdwP.h b/libdw/libdwP.h index eebb7d12..36c2acd9 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. */ @@ -218,16 +220,22 @@ 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 + /* Supporting lock for internal memory handling. Ensures threads that have + an entry in the mem_tails array are not disturbed by new threads doing + allocations for this Dwarf. */ + pthread_rwlock_t mem_rwl; + + /* Internal memory handling. This is basically a simplified thread-local reimplementation of obstacks. Unfortunately the standard obstack implementation is not usable in libraries. */ + size_t mem_stacks; struct libdw_memblock { size_t size; size_t remaining; struct libdw_memblock *prev; char mem[0]; - } *mem_tail; + } **mem_tails; /* Default size of allocated memory blocks. */ size_t mem_default_size; @@ -570,9 +578,9 @@ 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. */ #define libdw_alloc(dbg, type, tsize, cnt) \ - ({ struct libdw_memblock *_tail = (dbg)->mem_tail; \ + ({ struct libdw_memblock *_tail = __libdw_alloc_tail(dbg); \ size_t _required = (tsize) * (cnt); \ type *_result = (type *) (_tail->mem + (_tail->size - _tail->remaining));\ size_t _padding = ((__alignof (type) \ @@ -591,6 +599,23 @@ extern void __libdw_seterrno (int value) internal_function; #define libdw_typed_alloc(dbg, type) \ libdw_alloc (dbg, type, sizeof (type), 1) +/* Can only be used to undo the last libdw_alloc. */ +#define libdw_unalloc(dbg, type, tsize, cnt) \ + ({ struct libdw_memblock *_tail = __libdw_thread_tail (dbg); \ + size_t _required = (tsize) * (cnt); \ + /* We cannot know the padding, it is lost. */ \ + _tail->remaining += _required; }) \ + +#define libdw_typed_unalloc(dbg, type) \ + libdw_unalloc (dbg, type, sizeof (type), 1) + +/* Callback to choose a thread-local memory allocation stack. */ +extern struct libdw_memblock *__libdw_alloc_tail (Dwarf* dbg) + __nonnull_attribute__ (1); + +extern struct libdw_memblock *__libdw_thread_tail (Dwarf* dbg) + __nonnull_attribute__ (1); + /* Callback to allocate more. */ extern void *__libdw_allocate (Dwarf *dbg, size_t minsize, size_t align) __attribute__ ((__malloc__)) __nonnull_attribute__ (1); diff --git a/libdw/libdw_alloc.c b/libdw/libdw_alloc.c index f1e08714..e0281a3d 100644 --- a/libdw/libdw_alloc.c +++ b/libdw/libdw_alloc.c @@ -35,7 +35,79 @@ #include <stdlib.h> #include "libdwP.h" #include "system.h" +#include "atomics.h" +#if USE_VG_ANNOTATIONS == 1 +#include <helgrind.h> +#else +#define ANNOTATE_HAPPENS_BEFORE(X) +#define ANNOTATE_HAPPENS_AFTER(X) +#endif + +#define THREAD_ID_UNSET ((size_t) -1) +static __thread size_t thread_id = THREAD_ID_UNSET; +static atomic_size_t next_id = ATOMIC_VAR_INIT(0); +struct libdw_memblock * +__libdw_alloc_tail (Dwarf *dbg) +{ + if (thread_id == THREAD_ID_UNSET) + thread_id = atomic_fetch_add (&next_id, 1); + + pthread_rwlock_rdlock (&dbg->mem_rwl); + if (thread_id >= dbg->mem_stacks) + { + pthread_rwlock_unlock (&dbg->mem_rwl); + pthread_rwlock_wrlock (&dbg->mem_rwl); + + /* Another thread may have already reallocated. In theory using an + atomic would be faster, but given that this only happens once per + thread per Dwarf, some minor slowdown should be fine. */ + if (thread_id >= dbg->mem_stacks) + { + dbg->mem_tails = realloc (dbg->mem_tails, (thread_id+1) + * sizeof (struct libdw_memblock *)); + if (dbg->mem_tails == NULL) + { + pthread_rwlock_unlock (&dbg->mem_rwl); + dbg->oom_handler(); + } + for (size_t i = dbg->mem_stacks; i <= thread_id; i++) + dbg->mem_tails[i] = NULL; + dbg->mem_stacks = thread_id + 1; + ANNOTATE_HAPPENS_BEFORE (&dbg->mem_tails); + } + + pthread_rwlock_unlock (&dbg->mem_rwl); + pthread_rwlock_rdlock (&dbg->mem_rwl); + } + + /* At this point, we have an entry in the tail array. */ + ANNOTATE_HAPPENS_AFTER (&dbg->mem_tails); + struct libdw_memblock *result = dbg->mem_tails[thread_id]; + if (result == NULL) + { + result = malloc (dbg->mem_default_size); + result->size = dbg->mem_default_size + - offsetof (struct libdw_memblock, mem); + result->remaining = result->size; + result->prev = NULL; + dbg->mem_tails[thread_id] = result; + } + pthread_rwlock_unlock (&dbg->mem_rwl); + return result; +} + +/* Can only be called after a allocation for this thread has already + been done, to possibly undo it. */ +struct libdw_memblock * +__libdw_thread_tail (Dwarf *dbg) +{ + struct libdw_memblock *result; + pthread_rwlock_rdlock (&dbg->mem_rwl); + result = dbg->mem_tails[thread_id]; + pthread_rwlock_unlock (&dbg->mem_rwl); + return result; +} void * __libdw_allocate (Dwarf *dbg, size_t minsize, size_t align) @@ -52,8 +124,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; + pthread_rwlock_rdlock (&dbg->mem_rwl); + newp->prev = dbg->mem_tails[thread_id]; + dbg->mem_tails[thread_id] = newp; + pthread_rwlock_unlock (&dbg->mem_rwl); return (void *) result; } |