COMMIT: addrmap changes

Jim Blandy jimb@codesourcery.com
Tue Dec 4 23:44:00 GMT 2007


I've committed the following, approved in another thread.

gdb/ChangeLog:
2007-12-04  Jim Blandy  <jimb@codesourcery.com>

	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.

Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.963
diff -u -r1.963 Makefile.in
--- gdb/Makefile.in	30 Nov 2007 21:50:18 -0000	1.963
+++ gdb/Makefile.in	4 Dec 2007 23:41:20 -0000
@@ -586,6 +586,7 @@
 # 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 @@
 
 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 @@
 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 @@
 	$(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 @@
 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 @@
 	$(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_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)
Index: gdb/addrmap.c
===================================================================
RCS file: gdb/addrmap.c
diff -N gdb/addrmap.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/addrmap.c	4 Dec 2007 23:41:20 -0000
@@ -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 <stdlib.h>
+
+#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 *));
+}
Index: gdb/addrmap.h
===================================================================
RCS file: gdb/addrmap.h
diff -N gdb/addrmap.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/addrmap.h	4 Dec 2007 23:41:20 -0000
@@ -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 */
Index: gdb/block.c
===================================================================
RCS file: /cvs/src/src/gdb/block.c,v
retrieving revision 1.13
diff -u -r1.13 block.c
--- gdb/block.c	12 Oct 2007 15:34:45 -0000	1.13
+++ gdb/block.c	4 Dec 2007 23:41:20 -0000
@@ -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 @@
   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 @@
     }
 
   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 @@
       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 @@
    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 @@
 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;
 }
 
Index: gdb/block.h
===================================================================
RCS file: /cvs/src/src/gdb/block.h,v
retrieving revision 1.14
diff -u -r1.14 block.h
--- gdb/block.h	12 Oct 2007 15:34:45 -0000	1.14
+++ gdb/block.h	4 Dec 2007 23:41:20 -0000
@@ -28,6 +28,7 @@
 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 @@
 {
   /* 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 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);
 
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.287
diff -u -r1.287 breakpoint.c
--- gdb/breakpoint.c	30 Nov 2007 10:00:26 -0000	1.287
+++ gdb/breakpoint.c	4 Dec 2007 23:41:22 -0000
@@ -5747,12 +5747,10 @@
       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)
 	    {
Index: gdb/buildsym.c
===================================================================
RCS file: /cvs/src/src/gdb/buildsym.c,v
retrieving revision 1.57
diff -u -r1.57 buildsym.c
--- gdb/buildsym.c	15 Nov 2007 22:54:22 -0000	1.57
+++ gdb/buildsym.c	4 Dec 2007 23:41:22 -0000
@@ -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 @@
    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 @@
 
   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 @@
    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 @@
     }
 
   record_pending_block (objfile, block, opblock);
+
+  return block;
 }
 
 
@@ -454,6 +480,38 @@
     }
 }
 
+
+/* 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 @@
 
   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 @@
     }
   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 @@
   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 @@
   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
Index: gdb/buildsym.h
===================================================================
RCS file: /cvs/src/src/gdb/buildsym.h,v
retrieving revision 1.18
diff -u -r1.18 buildsym.h
--- gdb/buildsym.h	15 Nov 2007 22:54:22 -0000	1.18
+++ gdb/buildsym.h	4 Dec 2007 23:41:22 -0000
@@ -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 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);
 
Index: gdb/defs.h
===================================================================
RCS file: /cvs/src/src/gdb/defs.h,v
retrieving revision 1.212
diff -u -r1.212 defs.h
--- gdb/defs.h	17 Nov 2007 00:41:17 -0000	1.212
+++ gdb/defs.h	4 Dec 2007 23:41:22 -0000
@@ -84,6 +84,9 @@
 /* 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
Index: gdb/dwarf2read.c
===================================================================
RCS file: /cvs/src/src/gdb/dwarf2read.c,v
retrieving revision 1.237
diff -u -r1.237 dwarf2read.c
--- gdb/dwarf2read.c	25 Nov 2007 21:40:39 -0000	1.237
+++ gdb/dwarf2read.c	4 Dec 2007 23:41:22 -0000
@@ -906,6 +906,9 @@
 				 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 *);
 
@@ -2930,6 +2933,7 @@
   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));
 
@@ -3013,8 +3017,11 @@
 
   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
@@ -3071,8 +3078,21 @@
 
   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;
 }
@@ -3316,6 +3336,100 @@
   *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
Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.68
diff -u -r1.68 objfiles.c
--- gdb/objfiles.c	23 Aug 2007 18:08:36 -0000	1.68
+++ gdb/objfiles.c	4 Dec 2007 23:41:23 -0000
@@ -47,6 +47,7 @@
 #include "block.h"
 #include "dictionary.h"
 #include "source.h"
+#include "addrmap.h"
 
 /* Prototypes for local functions */
 
@@ -564,6 +565,9 @@
 	  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)
 	    {
Index: gdb/stack.c
===================================================================
RCS file: /cvs/src/src/gdb/stack.c,v
retrieving revision 1.159
diff -u -r1.159 stack.c
--- gdb/stack.c	21 Nov 2007 06:02:53 -0000	1.159
+++ gdb/stack.c	4 Dec 2007 23:41:23 -0000
@@ -1471,6 +1471,9 @@
 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 @@
 
   if (!values_printed && !this_level_only)
     fprintf_filtered (stream, _("No catches.\n"));
+#endif
 }
 
 void



More information about the Gdb-patches mailing list