From: Jim Blandy Date: Tue, 4 Dec 2007 23:43:57 +0000 (+0000) Subject: Support lexical blocks and function bodies that occupy X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=801e3a5b5681c44b2fec1b392dddec386870647c;p=binutils-gdb.git Support lexical blocks and function bodies that occupy non-contiguous address ranges. * addrmap.c, addrmap.h: New files. * block.h (struct addrmap): New forward declaration. (struct blockvector): New member, 'map'. (BLOCKVECTOR_MAP): New accessor macro. * block.c: #include "addrmap.h" (blockvector_for_pc_sect): If the blockvector we've found has an address map, use it instead of searching the blocks. * buildsym.c: #include "addrmap.h" (pending_addrmap_obstack, pending_addrmap_interesting): New static variables. (really_free_pendings): If we have a pending addrmap, free it too. (record_block_range): New function. (make_blockvector): If we have an interesting pending addrmap, record it in the new blockvector. (start_symtab, buildsym_init): Assert that there is no pending addrmap now; we should have cleaned up any addrmaps we'd built previously. (end_symtab): If there is a pending addrmap left over that didn't get included in the blockvector, free it. * buildsym.h (struct addrmap): New forward declaration. (record_block_range): New prototype. * objfiles.c: #include "addrmap.h". (objfile_relocate): Relocate the blockvector's address map, if present. * dwarf2read.c (dwarf2_record_block_ranges): New function. (read_func_scope, read_lexical_block_scope): Call it. * Makefile.in (SFILES): Add addrmap.c. (addrmap_h): New header dependency variable. (COMMON_OBS): Add addrmap.o. (addrmap.o): New rule.l (block.o, objfiles.o, buildsym.o): Depend on $(addrmap_h). * block.c (blockvector_for_pc, blockvector_for_pc_sect): Return a pointer to the block, not its index in the blockvector. (block_for_pc_sect): Use the returned block, instead of looking it up ourselves. * block.h (blockvector_for_pc, blockvector_for_pc_sect): Update declarations. * breakpoint.c (resolve_sal_pc): Use returned block, instead of looking it up ourselves. * stack.c (print_frame_label_vars): Disable function, which depends on the block's index. * buildsym.c (finish_block): Return the block we've built. * buildsym.h (finish_block): Update prototype. * defs.h (CORE_ADDR_MAX): New constant. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1709fcb1ecd..cdba0ff8dd7 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,55 @@ +2007-12-04 Jim Blandy + + Support lexical blocks and function bodies that occupy + non-contiguous address ranges. + * addrmap.c, addrmap.h: New files. + * block.h (struct addrmap): New forward declaration. + (struct blockvector): New member, 'map'. + (BLOCKVECTOR_MAP): New accessor macro. + * block.c: #include "addrmap.h" + (blockvector_for_pc_sect): If the blockvector we've found has + an address map, use it instead of searching the blocks. + * buildsym.c: #include "addrmap.h" + (pending_addrmap_obstack, pending_addrmap_interesting): New static + variables. + (really_free_pendings): If we have a pending addrmap, free it too. + (record_block_range): New function. + (make_blockvector): If we have an interesting pending addrmap, + record it in the new blockvector. + (start_symtab, buildsym_init): Assert that there is no pending + addrmap now; we should have cleaned up any addrmaps we'd built + previously. + (end_symtab): If there is a pending addrmap left over that didn't + get included in the blockvector, free it. + * buildsym.h (struct addrmap): New forward declaration. + (record_block_range): New prototype. + * objfiles.c: #include "addrmap.h". + (objfile_relocate): Relocate the blockvector's address map, if + present. + * dwarf2read.c (dwarf2_record_block_ranges): New function. + (read_func_scope, read_lexical_block_scope): Call it. + * Makefile.in (SFILES): Add addrmap.c. + (addrmap_h): New header dependency variable. + (COMMON_OBS): Add addrmap.o. + (addrmap.o): New rule.l + (block.o, objfiles.o, buildsym.o): Depend on $(addrmap_h). + + * block.c (blockvector_for_pc, blockvector_for_pc_sect): Return a + pointer to the block, not its index in the blockvector. + (block_for_pc_sect): Use the returned block, instead of looking it + up ourselves. + * block.h (blockvector_for_pc, blockvector_for_pc_sect): Update + declarations. + * breakpoint.c (resolve_sal_pc): Use returned block, instead of + looking it up ourselves. + * stack.c (print_frame_label_vars): Disable function, which + depends on the block's index. + + * buildsym.c (finish_block): Return the block we've built. + * buildsym.h (finish_block): Update prototype. + + * defs.h (CORE_ADDR_MAX): New constant. + 2007-12-04 Ulrich Weigand * coffread.c (decode_type): Use builtin_type_int32 instead diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 309b2e91bc5..6c5117258f9 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -586,6 +586,7 @@ TARGET_FLAGS_TO_PASS = \ # SFILES is used in building the distribution archive. SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \ + addrmap.c \ auxv.c ax-general.c ax-gdb.c \ bcache.c \ bfd-target.c \ @@ -717,6 +718,7 @@ nm_h = @nm_h@ ada_lang_h = ada-lang.h $(value_h) $(gdbtypes_h) $(breakpoint_h) ada_lex_c = ada-lex.c $(gdb_string_h) +addrmap_h = addrmap.h alphabsd_tdep_h = alphabsd-tdep.h alpha_tdep_h = alpha-tdep.h amd64_linux_tdep_h = amd64-linux-tdep.h @@ -1022,6 +1024,7 @@ TAGFILES_WITH_SRCDIR = $(HFILES_WITH_SRCDIR) COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ version.o \ annotate.o \ + addrmap.o \ auxv.o \ bfd-target.o \ blockframe.o breakpoint.o findvar.o regcache.o \ @@ -1803,6 +1806,8 @@ ada-valprint.o: ada-valprint.c $(defs_h) $(gdb_string_h) $(symtab_h) \ $(gdbtypes_h) $(expression_h) $(value_h) $(demangle_h) $(valprint_h) \ $(language_h) $(annotate_h) $(ada_lang_h) $(c_lang_h) $(infcall_h) \ $(exceptions_h) +addrmap.o: addrmap.c $(defs_h) $(splay_tree_h) $(gdb_obstack_h) $(addrmap_h) \ + $(gdb_assert_h) aix-thread.o: aix-thread.c $(defs_h) $(gdb_assert_h) $(gdbthread_h) \ $(target_h) $(inferior_h) $(regcache_h) $(gdbcmd_h) $(ppc_tdep_h) \ $(gdb_string_h) $(observer_h) @@ -1930,7 +1935,7 @@ bcache.o: bcache.c $(defs_h) $(gdb_obstack_h) $(bcache_h) $(gdb_string_h) \ bfd-target.o: bfd-target.c $(defs_h) $(target_h) $(bfd_target_h) \ $(gdb_assert_h) $(gdb_string_h) block.o: block.c $(defs_h) $(block_h) $(symtab_h) $(symfile_h) \ - $(gdb_obstack_h) $(cp_support_h) + $(gdb_obstack_h) $(cp_support_h) $(addrmap_h) blockframe.o: blockframe.c $(defs_h) $(symtab_h) $(bfd_h) $(objfiles_h) \ $(frame_h) $(gdbcore_h) $(value_h) $(target_h) $(inferior_h) \ $(annotate_h) $(regcache_h) $(gdb_assert_h) $(dummy_frame_h) \ @@ -1954,7 +1959,8 @@ buildsym.o: buildsym.c $(defs_h) $(bfd_h) $(gdb_obstack_h) $(symtab_h) \ $(symfile_h) $(objfiles_h) $(gdbtypes_h) $(gdb_assert_h) \ $(complaints_h) $(gdb_string_h) $(expression_h) $(bcache_h) \ $(filenames_h) $(macrotab_h) $(demangle_h) $(block_h) \ - $(cp_support_h) $(dictionary_h) $(buildsym_h) $(stabsread_h) + $(cp_support_h) $(dictionary_h) $(buildsym_h) $(stabsread_h) \ + $(addrmap_h) c-exp.o: c-exp.c $(defs_h) $(gdb_string_h) $(expression_h) $(value_h) \ $(parser_defs_h) $(language_h) $(c_lang_h) $(bfd_h) $(symfile_h) \ $(objfiles_h) $(charset_h) $(block_h) $(cp_support_h) $(dfp_h) @@ -2520,7 +2526,7 @@ objfiles.o: objfiles.c $(defs_h) $(bfd_h) $(symtab_h) $(symfile_h) \ $(objfiles_h) $(gdb_stabs_h) $(target_h) $(bcache_h) $(mdebugread_h) \ $(gdb_assert_h) $(gdb_stat_h) $(gdb_obstack_h) $(gdb_string_h) \ $(hashtab_h) $(breakpoint_h) $(block_h) $(dictionary_h) $(source_h) \ - $(parser_defs_h) $(expression_h) + $(parser_defs_h) $(expression_h) $(addrmap_h) observer.o: observer.c $(defs_h) $(observer_h) $(command_h) $(gdbcmd_h) \ $(observer_inc) obsd-tdep.o: obsd-tdep.c $(defs_h) $(frame_h) $(symtab_h) $(obsd_tdep_h) diff --git a/gdb/addrmap.c b/gdb/addrmap.c new file mode 100644 index 00000000000..fd800cfddba --- /dev/null +++ b/gdb/addrmap.c @@ -0,0 +1,532 @@ +/* addrmap.c --- implementation of address map data structure. + + Copyright (C) 2007 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" + +#include + +#include "splay-tree.h" +#include "gdb_obstack.h" +#include "addrmap.h" +#include "gdb_assert.h" + + + +/* The "abstract class". */ + +/* Functions implementing the addrmap functions for a particular + implementation. */ +struct addrmap_funcs +{ + void (*set_empty) (struct addrmap *this, + CORE_ADDR start, CORE_ADDR end_inclusive, + void *obj); + void *(*find) (struct addrmap *this, CORE_ADDR addr); + struct addrmap *(*create_fixed) (struct addrmap *this, + struct obstack *obstack); + void (*relocate) (struct addrmap *this, CORE_ADDR offset); +}; + + +struct addrmap +{ + struct addrmap_funcs *funcs; +}; + + +void +addrmap_set_empty (struct addrmap *map, + CORE_ADDR start, CORE_ADDR end_inclusive, + void *obj) +{ + map->funcs->set_empty (map, start, end_inclusive, obj); +} + + +void * +addrmap_find (struct addrmap *map, CORE_ADDR addr) +{ + return map->funcs->find (map, addr); +} + + +struct addrmap * +addrmap_create_fixed (struct addrmap *original, struct obstack *obstack) +{ + return original->funcs->create_fixed (original, obstack); +} + + +/* Relocate all the addresses in MAP by OFFSET. (This can be applied + to either mutable or immutable maps.) */ +void +addrmap_relocate (struct addrmap *map, CORE_ADDR offset) +{ + map->funcs->relocate (map, offset); +} + + + +/* Fixed address maps. */ + +/* A transition: a point in an address map where the value changes. + The map maps ADDR to VALUE, but if ADDR > 0, it maps ADDR-1 to + something else. */ +struct addrmap_transition +{ + CORE_ADDR addr; + void *value; +}; + + +struct addrmap_fixed +{ + struct addrmap addrmap; + + /* The number of transitions in TRANSITIONS. */ + size_t num_transitions; + + /* An array of transitions, sorted by address. For every point in + the map where either ADDR == 0 or ADDR is mapped to one value and + ADDR - 1 is mapped to something different, we have an entry here + containing ADDR and VALUE. (Note that this means we always have + an entry for address 0). */ + struct addrmap_transition transitions[1]; +}; + + +static void +addrmap_fixed_set_empty (struct addrmap *this, + CORE_ADDR start, CORE_ADDR end_inclusive, + void *obj) +{ + internal_error (__FILE__, __LINE__, + "addrmap_fixed_set_empty: " + "fixed addrmaps can't be changed\n"); +} + + +static void * +addrmap_fixed_find (struct addrmap *this, CORE_ADDR addr) +{ + struct addrmap_fixed *map = (struct addrmap_fixed *) this; + struct addrmap_transition *bottom = &map->transitions[0]; + struct addrmap_transition *top = &map->transitions[map->num_transitions - 1]; + + while (bottom < top) + { + /* This needs to round towards top, or else when top = bottom + + 1 (i.e., two entries are under consideration), then mid == + bottom, and then we may not narrow the range when (mid->addr + < addr). */ + struct addrmap_transition *mid = top - (top - bottom) / 2; + + if (mid->addr == addr) + { + bottom = mid; + break; + } + else if (mid->addr < addr) + /* We don't eliminate mid itself here, since each transition + covers all subsequent addresses until the next. This is why + we must round up in computing the midpoint. */ + bottom = mid; + else + top = mid - 1; + } + + return bottom->value; +} + + +static struct addrmap * +addrmap_fixed_create_fixed (struct addrmap *this, struct obstack *obstack) +{ + abort (); +} + + +static void +addrmap_fixed_relocate (struct addrmap *this, CORE_ADDR offset) +{ + struct addrmap_fixed *map = (struct addrmap_fixed *) this; + size_t i; + + for (i = 0; i < map->num_transitions; i++) + map->transitions[i].addr += offset; +} + + +static struct addrmap_funcs addrmap_fixed_funcs = +{ + .set_empty = addrmap_fixed_set_empty, + .find = addrmap_fixed_find, + .create_fixed = addrmap_fixed_create_fixed, + .relocate = addrmap_fixed_relocate +}; + + + +/* Mutable address maps. */ + +struct addrmap_mutable +{ + struct addrmap addrmap; + + /* The obstack to use for allocations for this map. */ + struct obstack *obstack; + + /* A splay tree, with a node for each transition; there is a + transition at address T if T-1 and T map to different objects. + + Any addresses below the first node map to NULL. (Unlike + fixed maps, we have no entry at (CORE_ADDR) 0; it doesn't + simplify enough.) + + The last region is assumed to end at CORE_ADDR_MAX. + + Since we can't know whether CORE_ADDR is larger or smaller than + splay_tree_key (unsigned long) --- I think both are possible, + given all combinations of 32- and 64-bit hosts and targets --- + our keys are pointers to CORE_ADDR values. Since the splay tree + library doesn't pass any closure pointer to the key free + function, we can't keep a freelist for keys. Since mutable + addrmaps are only used temporarily right now, we just leak keys + from deleted nodes; they'll be freed when the obstack is freed. */ + splay_tree tree; + + /* A freelist for splay tree nodes, allocated on obstack, and + chained together by their 'right' pointers. */ + splay_tree_node free_nodes; +}; + + +/* Allocate a copy of CORE_ADDR in MAP's obstack. */ +static splay_tree_key +allocate_key (struct addrmap_mutable *map, CORE_ADDR addr) +{ + CORE_ADDR *key = obstack_alloc (map->obstack, sizeof (*key)); + *key = addr; + + return (splay_tree_key) key; +} + + +/* Type-correct wrappers for splay tree access. */ +static splay_tree_node +addrmap_splay_tree_lookup (struct addrmap_mutable *map, CORE_ADDR addr) +{ + return splay_tree_lookup (map->tree, (splay_tree_key) &addr); +} + + +static splay_tree_node +addrmap_splay_tree_predecessor (struct addrmap_mutable *map, CORE_ADDR addr) +{ + return splay_tree_predecessor (map->tree, (splay_tree_key) &addr); +} + + +static splay_tree_node +addrmap_splay_tree_successor (struct addrmap_mutable *map, CORE_ADDR addr) +{ + return splay_tree_successor (map->tree, (splay_tree_key) &addr); +} + + +static CORE_ADDR +addrmap_node_key (splay_tree_node node) +{ + return * (CORE_ADDR *) node->key; +} + + +static void * +addrmap_node_value (splay_tree_node node) +{ + return (void *) node->value; +} + + +static void +addrmap_node_set_value (splay_tree_node node, void *value) +{ + node->value = (splay_tree_value) value; +} + + +static void +addrmap_splay_tree_insert (struct addrmap_mutable *map, CORE_ADDR key, void *value) +{ + splay_tree_insert (map->tree, + allocate_key (map, key), + (splay_tree_value) value); +} + + +/* Without changing the mapping of any address, ensure that there is a + tree node at ADDR, even if it would represent a "transition" from + one value to the same value. */ +static void +force_transition (struct addrmap_mutable *this, CORE_ADDR addr) +{ + splay_tree_node n + = addrmap_splay_tree_lookup (this, addr); + + if (! n) + { + n = addrmap_splay_tree_predecessor (this, addr); + addrmap_splay_tree_insert (this, addr, + n ? addrmap_node_value (n) : NULL); + } +} + + +static void +addrmap_mutable_set_empty (struct addrmap *this, + CORE_ADDR start, CORE_ADDR end_inclusive, + void *obj) +{ + struct addrmap_mutable *map = (struct addrmap_mutable *) this; + splay_tree_node n, next; + void *prior_value; + + /* If we're being asked to set all empty portions of the given + address range to empty, then probably the caller is confused. + (If that turns out to be useful in some cases, then we can change + this to simply return, since overriding NULL with NULL is a + no-op.) */ + gdb_assert (obj); + + /* We take a two-pass approach, for simplicity. + - Establish transitions where we think we might need them. + - First pass: change all NULL regions to OBJ. + - Second pass: remove any unnecessary transitions. */ + + /* Establish transitions at the start and end. */ + force_transition (map, start); + if (end_inclusive < CORE_ADDR_MAX) + force_transition (map, end_inclusive + 1); + + /* Walk the area, changing all NULL regions to OBJ. */ + for (n = addrmap_splay_tree_lookup (map, start), gdb_assert (n); + n && addrmap_node_key (n) <= end_inclusive; + n = addrmap_splay_tree_successor (map, addrmap_node_key (n))) + { + if (! addrmap_node_value (n)) + addrmap_node_set_value (n, obj); + } + + /* Walk the area again, removing transitions from any value to + itself. Be sure to visit both the transitions we forced + above. */ + n = addrmap_splay_tree_predecessor (map, start); + prior_value = n ? addrmap_node_value (n) : NULL; + for (n = addrmap_splay_tree_lookup (map, start), gdb_assert (n); + n && (end_inclusive == CORE_ADDR_MAX + || addrmap_node_key (n) <= end_inclusive + 1); + n = next) + { + next = addrmap_splay_tree_successor (map, addrmap_node_key (n)); + if (addrmap_node_value (n) == prior_value) + splay_tree_remove (map->tree, addrmap_node_key (n)); + else + prior_value = addrmap_node_value (n); + } +} + + +static void * +addrmap_mutable_find (struct addrmap *this, CORE_ADDR addr) +{ + /* Not needed yet. */ + abort (); +} + + +/* A function to pass to splay_tree_foreach to count the number of nodes + in the tree. */ +static int +splay_foreach_count (splay_tree_node n, void *closure) +{ + size_t *count = (size_t *) closure; + + (*count)++; + return 0; +} + + +/* A function to pass to splay_tree_foreach to copy entries into a + fixed address map. */ +static int +splay_foreach_copy (splay_tree_node n, void *closure) +{ + struct addrmap_fixed *fixed = (struct addrmap_fixed *) closure; + struct addrmap_transition *t = &fixed->transitions[fixed->num_transitions]; + + t->addr = addrmap_node_key (n); + t->value = addrmap_node_value (n); + fixed->num_transitions++; + + return 0; +} + + +static struct addrmap * +addrmap_mutable_create_fixed (struct addrmap *this, struct obstack *obstack) +{ + struct addrmap_mutable *mutable = (struct addrmap_mutable *) this; + struct addrmap_fixed *fixed; + size_t num_transitions; + + /* Count the number of transitions in the tree. */ + num_transitions = 0; + splay_tree_foreach (mutable->tree, splay_foreach_count, &num_transitions); + + /* Include an extra entry for the transition at zero (which fixed + maps have, but mutable maps do not.) */ + num_transitions++; + + fixed = obstack_alloc (obstack, + (sizeof (*fixed) + + (num_transitions + * sizeof (fixed->transitions[0])))); + fixed->addrmap.funcs = &addrmap_fixed_funcs; + fixed->num_transitions = 1; + fixed->transitions[0].addr = 0; + fixed->transitions[0].value = NULL; + + /* Copy all entries from the splay tree to the array, in order + of increasing address. */ + splay_tree_foreach (mutable->tree, splay_foreach_copy, fixed); + + /* We should have filled the array. */ + gdb_assert (fixed->num_transitions == num_transitions); + + return (struct addrmap *) fixed; +} + + +static void +addrmap_mutable_relocate (struct addrmap *this, CORE_ADDR offset) +{ + /* Not needed yet. */ + abort (); +} + + +static struct addrmap_funcs addrmap_mutable_funcs = +{ + .set_empty = addrmap_mutable_set_empty, + .find = addrmap_mutable_find, + .create_fixed = addrmap_mutable_create_fixed, + .relocate = addrmap_mutable_relocate +}; + + +static void * +splay_obstack_alloc (int size, void *closure) +{ + struct addrmap_mutable *map = closure; + splay_tree_node n; + + /* We should only be asked to allocate nodes and larger things. + (If, at some point in the future, this is no longer true, we can + just round up the size to sizeof (*n).) */ + gdb_assert (size >= sizeof (*n)); + + if (map->free_nodes) + { + n = map->free_nodes; + map->free_nodes = n->right; + return n; + } + else + return obstack_alloc (map->obstack, size); +} + + +static void +splay_obstack_free (void *obj, void *closure) +{ + struct addrmap_mutable *map = closure; + splay_tree_node n = obj; + + /* We've asserted in the allocation function that we only allocate + nodes or larger things, so it should be safe to put whatever + we get passed back on the free list. */ + n->right = map->free_nodes; + map->free_nodes = n; +} + + +/* Compare keys as CORE_ADDR * values. */ +static int +splay_compare_CORE_ADDR_ptr (splay_tree_key ak, splay_tree_key bk) +{ + CORE_ADDR a = * (CORE_ADDR *) ak; + CORE_ADDR b = * (CORE_ADDR *) bk; + + /* We can't just return a-b here, because of over/underflow. */ + if (a < b) + return -1; + else if (a == b) + return 0; + else + return 1; +} + + +struct addrmap * +addrmap_create_mutable (struct obstack *obstack) +{ + struct addrmap_mutable *map = obstack_alloc (obstack, sizeof (*map)); + + map->addrmap.funcs = &addrmap_mutable_funcs; + map->obstack = obstack; + + /* splay_tree_new_with_allocator uses the provided allocation + function to allocate the main splay_tree structure itself, so our + free list has to be initialized before we create the tree. */ + map->free_nodes = NULL; + + map->tree = splay_tree_new_with_allocator (splay_compare_CORE_ADDR_ptr, + NULL, /* no delete key */ + NULL, /* no delete value */ + splay_obstack_alloc, + splay_obstack_free, + map); + + return (struct addrmap *) map; +} + + + +/* Initialization. */ + +void +_initialize_addrmap (void) +{ + /* Make sure splay trees can actually hold the values we want to + store in them. */ + gdb_assert (sizeof (splay_tree_key) >= sizeof (CORE_ADDR *)); + gdb_assert (sizeof (splay_tree_value) >= sizeof (void *)); +} diff --git a/gdb/addrmap.h b/gdb/addrmap.h new file mode 100644 index 00000000000..8fa18732bb5 --- /dev/null +++ b/gdb/addrmap.h @@ -0,0 +1,96 @@ +/* addrmap.h --- interface to address map data structure. + + Copyright (C) 2007 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef ADDRMAP_H +#define ADDRMAP_H + +/* An address map is essentially a table mapping CORE_ADDRs onto GDB + data structures, like blocks, symtabs, partial symtabs, and so on. + An address map uses memory proportional to the number of + transitions in the map, where a CORE_ADDR N is mapped to one + object, and N+1 is mapped to a different object. + + Address maps come in two flavors: fixed, and mutable. Mutable + address maps consume more memory, but can be changed and extended. + A fixed address map, once constructed (from a mutable address map), + can't be edited. Both kinds of map are allocated in obstacks. */ + +/* The opaque type representing address maps. */ +struct addrmap; + +/* Create a mutable address map which maps every address to NULL. + Allocate entries in OBSTACK. */ +struct addrmap *addrmap_create_mutable (struct obstack *obstack); + +/* In the mutable address map MAP, associate the addresses from START + to END_INCLUSIVE that are currently associated with NULL with OBJ + instead. Addresses mapped to an object other than NULL are left + unchanged. + + As the name suggests, END_INCLUSIVE is also mapped to OBJ. This + convention is unusual, but it allows callers to accurately specify + ranges that abut the top of the address space, and ranges that + cover the entire address space. + + This operation seems a bit complicated for a primitive: if it's + needed, why not just have a simpler primitive operation that sets a + range to a value, wiping out whatever was there before, and then + let the caller construct more complicated operations from that, + along with some others for traversal? + + It turns out this is the mutation operation we want to use all the + time, at least for now. Our immediate use for address maps is to + represent lexical blocks whose address ranges are not contiguous. + We walk the tree of lexical blocks present in the debug info, and + only create 'struct block' objects after we've traversed all a + block's children. If a lexical block declares no local variables + (and isn't the lexical block for a function's body), we omit it + from GDB's data structures entirely. + + However, this menas that we don't decide to create a block (and + thus record it in the address map) until after we've traversed its + children. If we do decide to create the block, we do so at a time + when all its children have already been recorded in the map. So + this operation --- change only those addresses left unset --- is + actually the operation we want to use every time. + + It seems simpler to let the code which operates on the + representation directly deal with the hair of implementing these + semantics than to provide an interface which allows it to be + implemented efficiently, but doesn't reveal too much of the + representation. */ +void addrmap_set_empty (struct addrmap *map, + CORE_ADDR start, CORE_ADDR end_inclusive, + void *obj); + +/* Return the object associated with ADDR in MAP. */ +void *addrmap_find (struct addrmap *map, CORE_ADDR addr); + +/* Create a fixed address map which is a copy of the mutable address + map ORIGINAL. Allocate entries in OBSTACK. */ +struct addrmap *addrmap_create_fixed (struct addrmap *original, + struct obstack *obstack); + +/* Relocate all the addresses in MAP by OFFSET. (This can be applied + to either mutable or immutable maps.) */ +void addrmap_relocate (struct addrmap *map, CORE_ADDR offset); + +#endif /* ADDRMAP_H */ diff --git a/gdb/block.c b/gdb/block.c index 858d42d1863..d5c458e6b21 100644 --- a/gdb/block.c +++ b/gdb/block.c @@ -23,6 +23,7 @@ #include "symfile.h" #include "gdb_obstack.h" #include "cp-support.h" +#include "addrmap.h" /* This is used by struct block to store namespace-related info for C++ files, namely using declarations and the current namespace in @@ -63,14 +64,14 @@ block_function (const struct block *bl) return BLOCK_FUNCTION (bl); } -/* Return the blockvector immediately containing the innermost lexical block - containing the specified pc value and section, or 0 if there is none. - PINDEX is a pointer to the index value of the block. If PINDEX - is NULL, we don't pass this information back to the caller. */ +/* Return the blockvector immediately containing the innermost lexical + block containing the specified pc value and section, or 0 if there + is none. PBLOCK is a pointer to the block. If PBLOCK is NULL, we + don't pass this information back to the caller. */ struct blockvector * blockvector_for_pc_sect (CORE_ADDR pc, struct bfd_section *section, - int *pindex, struct symtab *symtab) + struct block **pblock, struct symtab *symtab) { struct block *b; int bot, top, half; @@ -85,11 +86,27 @@ blockvector_for_pc_sect (CORE_ADDR pc, struct bfd_section *section, } bl = BLOCKVECTOR (symtab); - b = BLOCKVECTOR_BLOCK (bl, 0); /* Then search that symtab for the smallest block that wins. */ - /* Use binary search to find the last block that starts before PC. */ + /* If we have an addrmap mapping code addresses to blocks, then use + that. */ + if (BLOCKVECTOR_MAP (bl)) + { + b = addrmap_find (BLOCKVECTOR_MAP (bl), pc); + if (b) + { + if (pblock) + *pblock = b; + return bl; + } + else + return 0; + } + + + /* Otherwise, use binary search to find the last block that starts + before PC. */ bot = 0; top = BLOCKVECTOR_NBLOCKS (bl); @@ -110,8 +127,8 @@ blockvector_for_pc_sect (CORE_ADDR pc, struct bfd_section *section, b = BLOCKVECTOR_BLOCK (bl, bot); if (BLOCK_END (b) > pc) { - if (pindex) - *pindex = bot; + if (pblock) + *pblock = b; return bl; } bot--; @@ -124,10 +141,10 @@ blockvector_for_pc_sect (CORE_ADDR pc, struct bfd_section *section, Backward compatibility, no section. */ struct blockvector * -blockvector_for_pc (CORE_ADDR pc, int *pindex) +blockvector_for_pc (CORE_ADDR pc, struct block **pblock) { return blockvector_for_pc_sect (pc, find_pc_mapped_section (pc), - pindex, NULL); + pblock, NULL); } /* Return the innermost lexical block containing the specified pc value @@ -137,11 +154,11 @@ struct block * block_for_pc_sect (CORE_ADDR pc, struct bfd_section *section) { struct blockvector *bl; - int index; + struct block *b; - bl = blockvector_for_pc_sect (pc, section, &index, NULL); + bl = blockvector_for_pc_sect (pc, section, &b, NULL); if (bl) - return BLOCKVECTOR_BLOCK (bl, index); + return b; return 0; } diff --git a/gdb/block.h b/gdb/block.h index 0b69f877721..6ed1cbe94a9 100644 --- a/gdb/block.h +++ b/gdb/block.h @@ -28,6 +28,7 @@ struct block_namespace_info; struct using_direct; struct obstack; struct dictionary; +struct addrmap; /* All of the name-scope contours of the program are represented by `struct block' objects. @@ -115,12 +116,17 @@ struct blockvector { /* Number of blocks in the list. */ int nblocks; + /* An address map mapping addresses to blocks in this blockvector. + This pointer is zero if the blocks' start and end addresses are + enough. */ + struct addrmap *map; /* The blocks themselves. */ struct block *block[1]; }; #define BLOCKVECTOR_NBLOCKS(blocklist) (blocklist)->nblocks #define BLOCKVECTOR_BLOCK(blocklist,n) (blocklist)->block[n] +#define BLOCKVECTOR_MAP(blocklist) ((blocklist)->map) /* Special block numbers */ @@ -130,10 +136,11 @@ extern struct symbol *block_function (const struct block *); extern int contained_in (const struct block *, const struct block *); -extern struct blockvector *blockvector_for_pc (CORE_ADDR, int *); +extern struct blockvector *blockvector_for_pc (CORE_ADDR, struct block **); extern struct blockvector *blockvector_for_pc_sect (CORE_ADDR, asection *, - int *, struct symtab *); + struct block **, + struct symtab *); extern struct block *block_for_pc (CORE_ADDR); diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 0f96997ec18..26864d7e400 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -5747,12 +5747,10 @@ resolve_sal_pc (struct symtab_and_line *sal) struct blockvector *bv; struct block *b; struct symbol *sym; - int index; - bv = blockvector_for_pc_sect (sal->pc, 0, &index, sal->symtab); + bv = blockvector_for_pc_sect (sal->pc, 0, &b, sal->symtab); if (bv != NULL) { - b = BLOCKVECTOR_BLOCK (bv, index); sym = block_function (b); if (sym != NULL) { diff --git a/gdb/buildsym.c b/gdb/buildsym.c index 0bba94ab529..42fc78e8295 100644 --- a/gdb/buildsym.c +++ b/gdb/buildsym.c @@ -43,6 +43,7 @@ #include "block.h" #include "cp-support.h" #include "dictionary.h" +#include "addrmap.h" /* Ask buildsym.h to define the vars it normally declares `extern'. */ #define EXTERN @@ -67,6 +68,23 @@ static struct pending *free_pendings; otherwise empty symtab from being tossed. */ static int have_line_numbers; + +/* The mutable address map for the compilation unit whose symbols + we're currently reading. The symtabs' shared blockvector will + point to a fixed copy of this. */ +static struct addrmap *pending_addrmap; + +/* The obstack on which we allocate pending_addrmap. + If pending_addrmap is NULL, this is uninitialized; otherwise, it is + initialized (and holds pending_addrmap). */ +static struct obstack pending_addrmap_obstack; + +/* Non-zero if we recorded any ranges in the addrmap that are + different from those in the blockvector already. We set this to + zero when we start processing a symfile, and if it's still zero at + the end, then we just toss the addrmap. */ +static int pending_addrmap_interesting; + static int compare_line_numbers (const void *ln1p, const void *ln2p); @@ -195,6 +213,12 @@ really_free_pendings (void *dummy) if (pending_macros) free_macro_table (pending_macros); + + if (pending_addrmap) + { + obstack_free (&pending_addrmap_obstack, NULL); + pending_addrmap = NULL; + } } /* This function is called to discard any pending blocks. */ @@ -211,7 +235,7 @@ free_pending_blocks (void) the order the symbols have in the list (reversed from the input file). Put the block on the list of pending blocks. */ -void +struct block * finish_block (struct symbol *symbol, struct pending **listhead, struct pending_block *old_blocks, CORE_ADDR start, CORE_ADDR end, @@ -423,6 +447,8 @@ finish_block (struct symbol *symbol, struct pending **listhead, } record_pending_block (objfile, block, opblock); + + return block; } @@ -454,6 +480,38 @@ record_pending_block (struct objfile *objfile, struct block *block, } } + +/* Record that the range of addresses from START to END_INCLUSIVE + (inclusive, like it says) belongs to BLOCK. BLOCK's start and end + addresses must be set already. You must apply this function to all + BLOCK's children before applying it to BLOCK. + + If a call to this function complicates the picture beyond that + already provided by BLOCK_START and BLOCK_END, then we create an + address map for the block. */ +void +record_block_range (struct block *block, + CORE_ADDR start, CORE_ADDR end_inclusive) +{ + /* If this is any different from the range recorded in the block's + own BLOCK_START and BLOCK_END, then note that the address map has + become interesting. Note that even if this block doesn't have + any "interesting" ranges, some later block might, so we still + need to record this block in the addrmap. */ + if (start != BLOCK_START (block) + || end_inclusive + 1 != BLOCK_END (block)) + pending_addrmap_interesting = 1; + + if (! pending_addrmap) + { + obstack_init (&pending_addrmap_obstack); + pending_addrmap = addrmap_create_mutable (&pending_addrmap_obstack); + } + + addrmap_set_empty (pending_addrmap, start, end_inclusive, block); +} + + static struct blockvector * make_blockvector (struct objfile *objfile) { @@ -486,6 +544,14 @@ make_blockvector (struct objfile *objfile) free_pending_blocks (); + /* If we needed an address map for this symtab, record it in the + blockvector. */ + if (pending_addrmap && pending_addrmap_interesting) + BLOCKVECTOR_MAP (blockvector) + = addrmap_create_fixed (pending_addrmap, &objfile->objfile_obstack); + else + BLOCKVECTOR_MAP (blockvector) = 0; + /* Some compilers output blocks in the wrong order, but we depend on their being in the right order so we can binary search. Check the order and moan about it. */ @@ -808,6 +874,9 @@ start_symtab (char *name, char *dirname, CORE_ADDR start_addr) } context_stack_depth = 0; + /* We shouldn't have any address map at this point. */ + gdb_assert (! pending_addrmap); + /* Set up support for C++ namespace support, in case we need it. */ cp_initialize_namespace (); @@ -1083,6 +1152,11 @@ end_symtab (CORE_ADDR end_addr, struct objfile *objfile, int section) last_source_file = NULL; current_subfile = NULL; pending_macros = NULL; + if (pending_addrmap) + { + obstack_free (&pending_addrmap_obstack, NULL); + pending_addrmap = NULL; + } return symtab; } @@ -1196,6 +1270,10 @@ buildsym_init (void) global_symbols = NULL; pending_blocks = NULL; pending_macros = NULL; + + /* We shouldn't have any address map at this point. */ + gdb_assert (! pending_addrmap); + pending_addrmap_interesting = 0; } /* Initialize anything that needs initializing when a completely new diff --git a/gdb/buildsym.h b/gdb/buildsym.h index 16affd9528a..ce85a99115c 100644 --- a/gdb/buildsym.h +++ b/gdb/buildsym.h @@ -22,6 +22,7 @@ struct objfile; struct symbol; +struct addrmap; /* This module provides definitions used for creating and adding to the symbol table. These routines are called from various symbol- @@ -230,11 +231,14 @@ extern void add_symbol_to_list (struct symbol *symbol, extern struct symbol *find_symbol_in_list (struct pending *list, char *name, int length); -extern void finish_block (struct symbol *symbol, - struct pending **listhead, - struct pending_block *old_blocks, - CORE_ADDR start, CORE_ADDR end, - struct objfile *objfile); +extern struct block *finish_block (struct symbol *symbol, + struct pending **listhead, + struct pending_block *old_blocks, + CORE_ADDR start, CORE_ADDR end, + struct objfile *objfile); + +extern void record_block_range (struct block *, + CORE_ADDR start, CORE_ADDR end_inclusive); extern void really_free_pendings (void *dummy); diff --git a/gdb/defs.h b/gdb/defs.h index 1b8cd2a1e0b..f37df1bdfd1 100644 --- a/gdb/defs.h +++ b/gdb/defs.h @@ -84,6 +84,9 @@ typedef bfd_byte gdb_byte; /* An address in the program being debugged. Host byte order. */ typedef bfd_vma CORE_ADDR; +/* The largest CORE_ADDR value. */ +#define CORE_ADDR_MAX (~ (CORE_ADDR) 0) + /* This is to make sure that LONGEST is at least as big as CORE_ADDR. */ #ifndef LONGEST diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index fa5d3c8ee40..eb2564efff3 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -895,6 +895,9 @@ static void get_scope_pc_bounds (struct die_info *, CORE_ADDR *, CORE_ADDR *, struct dwarf2_cu *); +static void dwarf2_record_block_ranges (struct die_info *, struct block *, + CORE_ADDR, struct dwarf2_cu *); + static void dwarf2_add_field (struct field_info *, struct die_info *, struct dwarf2_cu *); @@ -2910,6 +2913,7 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) const char *previous_prefix = processing_current_prefix; struct cleanup *back_to = NULL; CORE_ADDR baseaddr; + struct block *block; baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); @@ -2993,8 +2997,11 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) new = pop_context (); /* Make a block for the local symbols within. */ - finish_block (new->name, &local_symbols, new->old_blocks, - lowpc, highpc, objfile); + block = finish_block (new->name, &local_symbols, new->old_blocks, + lowpc, highpc, objfile); + + /* If we have address ranges, record them. */ + dwarf2_record_block_ranges (die, block, baseaddr, cu); /* In C++, we can have functions nested inside functions (e.g., when a function declares a class that has methods). This means that @@ -3051,8 +3058,21 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu) if (local_symbols != NULL) { - finish_block (0, &local_symbols, new->old_blocks, new->start_addr, - highpc, objfile); + struct block *block + = finish_block (0, &local_symbols, new->old_blocks, new->start_addr, + highpc, objfile); + + /* Note that recording ranges after traversing children, as we + do here, means that recording a parent's ranges entails + walking across all its children's ranges as they appear in + the address map, which is quadratic behavior. + + It would be nicer to record the parent's ranges before + traversing its children, simply overriding whatever you find + there. But since we don't even decide whether to create a + block until after we've traversed its children, that's hard + to do. */ + dwarf2_record_block_ranges (die, block, baseaddr, cu); } local_symbols = new->locals; } @@ -3296,6 +3316,100 @@ get_scope_pc_bounds (struct die_info *die, *highpc = best_high; } +/* Record the address ranges for BLOCK, offset by BASEADDR, as given + in DIE. */ +static void +dwarf2_record_block_ranges (struct die_info *die, struct block *block, + CORE_ADDR baseaddr, struct dwarf2_cu *cu) +{ + struct attribute *attr; + + attr = dwarf2_attr (die, DW_AT_high_pc, cu); + if (attr) + { + CORE_ADDR high = DW_ADDR (attr); + attr = dwarf2_attr (die, DW_AT_low_pc, cu); + if (attr) + { + CORE_ADDR low = DW_ADDR (attr); + record_block_range (block, baseaddr + low, baseaddr + high - 1); + } + } + + attr = dwarf2_attr (die, DW_AT_ranges, cu); + if (attr) + { + bfd *obfd = cu->objfile->obfd; + + /* The value of the DW_AT_ranges attribute is the offset of the + address range list in the .debug_ranges section. */ + unsigned long offset = DW_UNSND (attr); + gdb_byte *buffer = dwarf2_per_objfile->ranges_buffer + offset; + + /* For some target architectures, but not others, the + read_address function sign-extends the addresses it returns. + To recognize base address selection entries, we need a + mask. */ + unsigned int addr_size = cu->header.addr_size; + CORE_ADDR base_select_mask = ~(~(CORE_ADDR)1 << (addr_size * 8 - 1)); + + /* The base address, to which the next pair is relative. Note + that this 'base' is a DWARF concept: most entries in a range + list are relative, to reduce the number of relocs against the + debugging information. This is separate from this function's + 'baseaddr' argument, which GDB uses to relocate debugging + information from a shared library based on the address at + which the library was loaded. */ + CORE_ADDR base = cu->header.base_address; + int base_known = cu->header.base_known; + + if (offset >= dwarf2_per_objfile->ranges_size) + { + complaint (&symfile_complaints, + _("Offset %lu out of bounds for DW_AT_ranges attribute"), + offset); + return; + } + + for (;;) + { + unsigned int bytes_read; + CORE_ADDR start, end; + + start = read_address (obfd, buffer, cu, &bytes_read); + buffer += bytes_read; + end = read_address (obfd, buffer, cu, &bytes_read); + buffer += bytes_read; + + /* Did we find the end of the range list? */ + if (start == 0 && end == 0) + break; + + /* Did we find a base address selection entry? */ + else if ((start & base_select_mask) == base_select_mask) + { + base = end; + base_known = 1; + } + + /* We found an ordinary address range. */ + else + { + if (!base_known) + { + complaint (&symfile_complaints, + _("Invalid .debug_ranges data (no base address)")); + return; + } + + record_block_range (block, + baseaddr + base + start, + baseaddr + base + end - 1); + } + } + } +} + /* Add an aggregate field to the field list. */ static void diff --git a/gdb/objfiles.c b/gdb/objfiles.c index 5b7b12fa224..2147328f28b 100644 --- a/gdb/objfiles.c +++ b/gdb/objfiles.c @@ -47,6 +47,7 @@ #include "block.h" #include "dictionary.h" #include "source.h" +#include "addrmap.h" /* Prototypes for local functions */ @@ -564,6 +565,9 @@ objfile_relocate (struct objfile *objfile, struct section_offsets *new_offsets) b = BLOCKVECTOR_BLOCK (bv, i); BLOCK_START (b) += ANOFFSET (delta, s->block_line_section); BLOCK_END (b) += ANOFFSET (delta, s->block_line_section); + if (BLOCKVECTOR_MAP (bv)) + addrmap_relocate (BLOCKVECTOR_MAP (bv), + ANOFFSET (delta, s->block_line_section)); ALL_BLOCK_SYMBOLS (b, iter, sym) { diff --git a/gdb/stack.c b/gdb/stack.c index caf23a4c031..c23529f3630 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -1471,6 +1471,9 @@ static void print_frame_label_vars (struct frame_info *frame, int this_level_only, struct ui_file *stream) { +#if 1 + fprintf_filtered (stream, "print_frame_label_vars disabled.\n"); +#else struct blockvector *bl; struct block *block = get_frame_block (frame, 0); int values_printed = 0; @@ -1531,6 +1534,7 @@ print_frame_label_vars (struct frame_info *frame, int this_level_only, if (!values_printed && !this_level_only) fprintf_filtered (stream, _("No catches.\n")); +#endif } void