Merge in the DATASYMS branch.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7540 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/callgrind/bb.c b/callgrind/bb.c
index 9ab1f98..74ffee7 100644
--- a/callgrind/bb.c
+++ b/callgrind/bb.c
@@ -141,7 +141,7 @@
    new->jmp         = (CJmpInfo*) &(new->instr[instr_count]);
    new->instr_len   = 0;
    new->cost_count  = 0;
-   new->sect_kind   = VG_(seginfo_sect_kind)(offset + obj->offset);
+   new->sect_kind   = VG_(seginfo_sect_kind)(NULL, 0, offset + obj->offset);
    new->fn          = 0;
    new->line        = 0;
    new->is_entry    = 0;
@@ -196,22 +196,24 @@
 obj_node* obj_of_address(Addr addr)
 {
   obj_node* obj;
-  SegInfo* si;
+  DebugInfo* di;
   OffT offset;
 
-  si = VG_(find_seginfo)(addr);
-  obj = CLG_(get_obj_node)( si );
+  di = VG_(find_seginfo)(addr);
+  obj = CLG_(get_obj_node)( di );
 
   /* Update symbol offset in object if remapped */
-  offset = si ? VG_(seginfo_sym_offset)(si):0;
+  /* FIXME (or at least check this) 2008 Feb 19: 'offset' is
+     only correct for text symbols, not for data symbols */
+  offset = di ? VG_(seginfo_get_text_bias)(di):0;
   if (obj->offset != offset) {
-      Addr start = si ? VG_(seginfo_start)(si) : 0;
+      Addr start = di ? VG_(seginfo_get_text_avma)(di) : 0;
 
       CLG_DEBUG(0, "Mapping changed for '%s': %p -> %p\n",
 		obj->name, obj->start, start);
 
       /* Size should be the same, and offset diff == start diff */
-      CLG_ASSERT( obj->size == (si ? VG_(seginfo_size)(si) : 0) );
+      CLG_ASSERT( obj->size == (di ? VG_(seginfo_get_text_size)(di) : 0) );
       CLG_ASSERT( obj->start - start == obj->offset - offset );
       obj->offset = offset;
       obj->start = start;
diff --git a/callgrind/debug.c b/callgrind/debug.c
index 6bb973f..99ec7dc 100644
--- a/callgrind/debug.c
+++ b/callgrind/debug.c
@@ -370,7 +370,7 @@
     Char fl_buf[FILENAME_LEN];
     Char fn_buf[FN_NAME_LEN];
     const UChar* obj_name;
-    SegInfo* si;
+    DebugInfo* di;
     int ln, i=0, opos=0;
 	
     if (addr == 0) {
@@ -378,15 +378,15 @@
 	return;
     }
 
-    CLG_(get_debug_info)(addr, fl_buf, fn_buf, &ln, &si);
+    CLG_(get_debug_info)(addr, fl_buf, fn_buf, &ln, &di);
 
     if (VG_(strcmp)(fn_buf,"???")==0)
 	VG_(printf)("%p", addr);
     else
 	VG_(printf)("%p %s", addr, fn_buf);
 
-    if (si) {
-      obj_name = VG_(seginfo_filename)(si);
+    if (di) {
+      obj_name = VG_(seginfo_filename)(di);
       if (obj_name) {
 	while(obj_name[i]) {
 	  if (obj_name[i]=='/') opos = i+1;
diff --git a/callgrind/fn.c b/callgrind/fn.c
index 66a9508..7fd14f3 100644
--- a/callgrind/fn.c
+++ b/callgrind/fn.c
@@ -181,22 +181,25 @@
 static Char* anonymous_obj = "???";
 
 static __inline__ 
-obj_node* new_obj_node(SegInfo* si, obj_node* next)
+obj_node* new_obj_node(DebugInfo* di, obj_node* next)
 {
    Int i;
    obj_node* new;
 
    new = (obj_node*) CLG_MALLOC(sizeof(obj_node));
-   new->name  = si ? VG_(strdup)( VG_(seginfo_filename)(si) )
+   new->name  = di ? VG_(strdup)( VG_(seginfo_filename)(di) )
                      : anonymous_obj;
    for (i = 0; i < N_FILE_ENTRIES; i++) {
       new->files[i] = NULL;
    }
    CLG_(stat).distinct_objs ++;
    new->number  = CLG_(stat).distinct_objs;
-   new->start   = si ? VG_(seginfo_start)(si) : 0;
-   new->size    = si ? VG_(seginfo_size)(si) : 0;
-   new->offset  = si ? VG_(seginfo_sym_offset)(si) : 0;
+   /* JRS 2008 Feb 19: maybe rename .start/.size/.offset to
+      .text_avma/.text_size/.test_bias to make it clearer what these
+      fields really mean */
+   new->start   = di ? VG_(seginfo_get_text_avma)(di) : 0;
+   new->size    = di ? VG_(seginfo_get_text_size)(di) : 0;
+   new->offset  = di ? VG_(seginfo_get_text_bias)(di) : 0;
    new->next    = next;
 
    // not only used for debug output (see static.c)
@@ -212,13 +215,13 @@
    return new;
 }
 
-obj_node* CLG_(get_obj_node)(SegInfo* si)
+obj_node* CLG_(get_obj_node)(DebugInfo* di)
 {
     obj_node*    curr_obj_node;
     UInt         objname_hash;
     const UChar* obj_name;
     
-    obj_name = si ? (Char*) VG_(seginfo_filename)(si) : anonymous_obj;
+    obj_name = di ? (Char*) VG_(seginfo_filename)(di) : anonymous_obj;
 
     /* lookup in obj hash */
     objname_hash = str_hash(obj_name, N_OBJ_ENTRIES);
@@ -229,7 +232,7 @@
     }
     if (NULL == curr_obj_node) {
 	obj_table[objname_hash] = curr_obj_node = 
-	    new_obj_node(si, obj_table[objname_hash]);
+	    new_obj_node(di, obj_table[objname_hash]);
     }
 
     return curr_obj_node;
@@ -351,11 +354,11 @@
  * Hash nodes are created if needed.
  */
 static __inline__
-fn_node* get_fn_node_inseg(SegInfo* si,
+fn_node* get_fn_node_inseg(DebugInfo* di,
 			   Char filename[FILENAME_LEN],
 			   Char fnname[FN_NAME_LEN])
 {
-  obj_node  *obj  = CLG_(get_obj_node)(si);
+  obj_node  *obj  = CLG_(get_obj_node)(di);
   file_node *file = CLG_(get_file_node)(obj, filename);
   fn_node   *fn   = get_fn_node_infile(file, fnname);
 
@@ -366,7 +369,7 @@
 Bool CLG_(get_debug_info)(Addr instr_addr,
 			 Char file[FILENAME_LEN],
 			 Char fn_name[FN_NAME_LEN], UInt* line_num,
-			 SegInfo** pSegInfo)
+			 DebugInfo** pDebugInfo)
 {
   Bool found_file_line, found_fn, found_dirname, result = True;
   Char dir[FILENAME_LEN];
@@ -374,8 +377,8 @@
   
   CLG_DEBUG(6, "  + get_debug_info(%p)\n", instr_addr);
 
-  if (pSegInfo) {
-      *pSegInfo = VG_(find_seginfo)(instr_addr);
+  if (pDebugInfo) {
+      *pDebugInfo = VG_(find_seginfo)(instr_addr);
 
       // for generated code in anonymous space, pSegInfo is 0
    }
@@ -420,8 +423,8 @@
 
    CLG_DEBUG(6, "  - get_debug_info(%p): seg '%s', fn %s\n",
 	    instr_addr,
-	    !pSegInfo   ? (const UChar*)"-" :
-	    (*pSegInfo) ? VG_(seginfo_filename)(*pSegInfo) :
+	    !pDebugInfo   ? (const UChar*)"-" :
+	    (*pDebugInfo) ? VG_(seginfo_filename)(*pDebugInfo) :
 	    (const UChar*)"(None)",
 	    fn_name);
 
@@ -438,7 +441,7 @@
 fn_node* CLG_(get_fn_node)(BB* bb)
 {
     Char       filename[FILENAME_LEN], fnname[FN_NAME_LEN];
-    SegInfo*   si;
+    DebugInfo* di;
     UInt       line_num;
     fn_node*   fn;
 
@@ -451,7 +454,7 @@
      * the BB according to debug information
      */
     CLG_(get_debug_info)(bb_addr(bb),
-			filename, fnname, &line_num, &si);
+			filename, fnname, &line_num, &di);
 
     if (0 == VG_(strcmp)(fnname, "???")) {
 	int p;
@@ -481,7 +484,7 @@
     if (0 == VG_(strcmp)(fnname, "vgPlain___libc_freeres_wrapper")
 	&& exit_bb) {
       CLG_(get_debug_info)(bb_addr(exit_bb),
-			  filename, fnname, &line_num, &si);
+			  filename, fnname, &line_num, &di);
 	
 	CLG_DEBUG(1, "__libc_freeres_wrapper renamed to _exit\n");
     }
@@ -496,7 +499,7 @@
     }
 
     /* get fn_node struct for this function */
-    fn = get_fn_node_inseg( si, filename, fnname);
+    fn = get_fn_node_inseg( di, filename, fnname);
 
     /* if this is the 1st time the function is seen,
      * some attributes are set */
diff --git a/callgrind/global.h b/callgrind/global.h
index de510a3..90f151e 100644
--- a/callgrind/global.h
+++ b/callgrind/global.h
@@ -705,7 +705,7 @@
 
 /* from main.c */
 Bool CLG_(get_debug_info)(Addr, Char filename[FILENAME_LEN],
-			 Char fn_name[FN_NAME_LEN], UInt*, SegInfo**);
+			 Char fn_name[FN_NAME_LEN], UInt*, DebugInfo**);
 void CLG_(collectBlockInfo)(IRSB* bbIn, UInt*, UInt*, Bool*);
 void CLG_(set_instrument_state)(Char*,Bool);
 void CLG_(dump_profile)(Char* trigger,Bool only_current_thread);
@@ -738,7 +738,7 @@
 UInt* CLG_(get_fn_entry)(Int n);
 
 void      CLG_(init_obj_table)(void);
-obj_node* CLG_(get_obj_node)(SegInfo* si);
+obj_node* CLG_(get_obj_node)(DebugInfo* si);
 file_node* CLG_(get_file_node)(obj_node*, Char* filename);
 fn_node*  CLG_(get_fn_node)(BB* bb);
 
diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am
index 8994904..90d92e1 100644
--- a/coregrind/Makefile.am
+++ b/coregrind/Makefile.am
@@ -138,9 +138,13 @@
 	pub_core_vkiscnums.h	\
 	pub_core_xarray.h	\
 	m_coredump/priv_elf.h	\
+	m_debuginfo/priv_misc.h	\
 	m_debuginfo/priv_storage.h	\
+	m_debuginfo/priv_tytypes.h      \
 	m_debuginfo/priv_readstabs.h	\
+	m_debuginfo/priv_d3basics.h	\
 	m_debuginfo/priv_readdwarf.h	\
+	m_debuginfo/priv_readdwarf3.h	\
 	m_debuginfo/priv_readelf.h	\
 	m_debuginfo/priv_readxcoff.h	\
 	m_demangle/ansidecl.h	\
@@ -197,8 +201,11 @@
 	m_vkiscnums.c \
 	m_xarray.c \
 	m_aspacemgr/aspacemgr-common.c \
+	m_debuginfo/misc.c \
+	m_debuginfo/d3basics.c \
 	m_debuginfo/storage.c \
 	m_debuginfo/debuginfo.c \
+	m_debuginfo/tytypes.c \
 	m_demangle/cp-demangle.c \
 	m_demangle/cplus-dem.c \
 	m_demangle/demangle.c \
@@ -217,6 +224,7 @@
 	m_initimg/initimg-linux.c \
 	m_debuginfo/readelf.c \
 	m_debuginfo/readdwarf.c \
+	m_debuginfo/readdwarf3.c \
 	m_debuginfo/readstabs.c \
 	m_syswrap/syswrap-generic.c
 
diff --git a/coregrind/m_debuginfo/d3basics.c b/coregrind/m_debuginfo/d3basics.c
new file mode 100644
index 0000000..8832189
--- /dev/null
+++ b/coregrind/m_debuginfo/d3basics.c
@@ -0,0 +1,678 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Basic definitions and helper functions for DWARF3.           ---*/
+/*---                                                   d3basics.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 OpenWorks LLP
+      info@open-works.co.uk
+
+   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., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+
+   Neither the names of the U.S. Department of Energy nor the
+   University of California nor the names of its contributors may be
+   used to endorse or promote products derived from this software
+   without prior written permission.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_options.h"
+
+#include "priv_d3basics.h"      /* self */
+
+HChar* ML_(pp_DW_children) ( DW_children hashch )
+{
+   switch (hashch) {
+      case DW_children_no:  return "no children";
+      case DW_children_yes: return "has children";
+      default:              return "DW_children_???";
+   }
+}
+
+HChar* ML_(pp_DW_TAG) ( DW_TAG tag )
+{
+   switch (tag) {
+      case DW_TAG_padding:            return "DW_TAG_padding";
+      case DW_TAG_array_type:         return "DW_TAG_array_type";
+      case DW_TAG_class_type:         return "DW_TAG_class_type";
+      case DW_TAG_entry_point:        return "DW_TAG_entry_point";
+      case DW_TAG_enumeration_type:   return "DW_TAG_enumeration_type";
+      case DW_TAG_formal_parameter:   return "DW_TAG_formal_parameter";
+      case DW_TAG_imported_declaration: 
+         return "DW_TAG_imported_declaration";
+      case DW_TAG_label:              return "DW_TAG_label";
+      case DW_TAG_lexical_block:      return "DW_TAG_lexical_block";
+      case DW_TAG_member:             return "DW_TAG_member";
+      case DW_TAG_pointer_type:       return "DW_TAG_pointer_type";
+      case DW_TAG_reference_type:     return "DW_TAG_reference_type";
+      case DW_TAG_compile_unit:       return "DW_TAG_compile_unit";
+      case DW_TAG_string_type:        return "DW_TAG_string_type";
+      case DW_TAG_structure_type:     return "DW_TAG_structure_type";
+      case DW_TAG_subroutine_type:    return "DW_TAG_subroutine_type";
+      case DW_TAG_typedef:            return "DW_TAG_typedef";
+      case DW_TAG_union_type:         return "DW_TAG_union_type";
+      case DW_TAG_unspecified_parameters: 
+         return "DW_TAG_unspecified_parameters";
+      case DW_TAG_variant:            return "DW_TAG_variant";
+      case DW_TAG_common_block:       return "DW_TAG_common_block";
+      case DW_TAG_common_inclusion:   return "DW_TAG_common_inclusion";
+      case DW_TAG_inheritance:        return "DW_TAG_inheritance";
+      case DW_TAG_inlined_subroutine:
+         return "DW_TAG_inlined_subroutine";
+      case DW_TAG_module:             return "DW_TAG_module";
+      case DW_TAG_ptr_to_member_type: return "DW_TAG_ptr_to_member_type";
+      case DW_TAG_set_type:           return "DW_TAG_set_type";
+      case DW_TAG_subrange_type:      return "DW_TAG_subrange_type";
+      case DW_TAG_with_stmt:          return "DW_TAG_with_stmt";
+      case DW_TAG_access_declaration: return "DW_TAG_access_declaration";
+      case DW_TAG_base_type:          return "DW_TAG_base_type";
+      case DW_TAG_catch_block:        return "DW_TAG_catch_block";
+      case DW_TAG_const_type:         return "DW_TAG_const_type";
+      case DW_TAG_constant:           return "DW_TAG_constant";
+      case DW_TAG_enumerator:         return "DW_TAG_enumerator";
+      case DW_TAG_file_type:          return "DW_TAG_file_type";
+      case DW_TAG_friend:             return "DW_TAG_friend";
+      case DW_TAG_namelist:           return "DW_TAG_namelist";
+      case DW_TAG_namelist_item:      return "DW_TAG_namelist_item";
+      case DW_TAG_packed_type:        return "DW_TAG_packed_type";
+      case DW_TAG_subprogram:         return "DW_TAG_subprogram";
+      case DW_TAG_template_type_param:
+         return "DW_TAG_template_type_param";
+      case DW_TAG_template_value_param:
+         return "DW_TAG_template_value_param";
+      case DW_TAG_thrown_type:        return "DW_TAG_thrown_type";
+      case DW_TAG_try_block:          return "DW_TAG_try_block";
+      case DW_TAG_variant_part:       return "DW_TAG_variant_part";
+      case DW_TAG_variable:           return "DW_TAG_variable";
+      case DW_TAG_volatile_type:      return "DW_TAG_volatile_type";
+      /* DWARF 3.  */
+      case DW_TAG_dwarf_procedure:    return "DW_TAG_dwarf_procedure";
+      case DW_TAG_restrict_type:      return "DW_TAG_restrict_type";
+      case DW_TAG_interface_type:     return "DW_TAG_interface_type";
+      case DW_TAG_namespace:          return "DW_TAG_namespace";
+      case DW_TAG_imported_module:    return "DW_TAG_imported_module";
+      case DW_TAG_unspecified_type:   return "DW_TAG_unspecified_type";
+      case DW_TAG_partial_unit:       return "DW_TAG_partial_unit";
+      case DW_TAG_imported_unit:      return "DW_TAG_imported_unit";
+      case DW_TAG_condition:          return "DW_TAG_condition";
+      case DW_TAG_shared_type:        return "DW_TAG_shared_type";
+      /* SGI/MIPS Extensions.  */
+      case DW_TAG_MIPS_loop:          return "DW_TAG_MIPS_loop";
+      /* HP extensions.  See:
+         ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .  */
+      case DW_TAG_HP_array_descriptor:
+         return "DW_TAG_HP_array_descriptor";
+      /* GNU extensions.  */
+      case DW_TAG_format_label:       return "DW_TAG_format_label";
+      case DW_TAG_function_template:  return "DW_TAG_function_template";
+      case DW_TAG_class_template:     return "DW_TAG_class_template";
+      case DW_TAG_GNU_BINCL:          return "DW_TAG_GNU_BINCL";
+      case DW_TAG_GNU_EINCL:          return "DW_TAG_GNU_EINCL";
+      /* Extensions for UPC.  See: http://upc.gwu.edu/~upc.  */
+      case DW_TAG_upc_shared_type:    return "DW_TAG_upc_shared_type";
+      case DW_TAG_upc_strict_type:    return "DW_TAG_upc_strict_type";
+      case DW_TAG_upc_relaxed_type:   return "DW_TAG_upc_relaxed_type";
+      /* PGI (STMicroelectronics) extensions.  No documentation available.  */
+      case DW_TAG_PGI_kanji_type:     return "DW_TAG_PGI_kanji_type";
+      case DW_TAG_PGI_interface_block:
+         return "DW_TAG_PGI_interface_block";
+      default:                        return "DW_TAG_???";
+   }
+}
+
+HChar* ML_(pp_DW_FORM) ( DW_FORM form )
+{
+   switch (form) {
+      case DW_FORM_addr:      return "DW_FORM_addr";
+      case DW_FORM_block2:    return "DW_FORM_block2";
+      case DW_FORM_block4:    return "DW_FORM_block4";
+      case DW_FORM_data2:     return "DW_FORM_data2";
+      case DW_FORM_data4:     return "DW_FORM_data4";
+      case DW_FORM_data8:     return "DW_FORM_data8";
+      case DW_FORM_string:    return "DW_FORM_string";
+      case DW_FORM_block:     return "DW_FORM_block";
+      case DW_FORM_block1:    return "DW_FORM_block1";
+      case DW_FORM_data1:     return "DW_FORM_data1";
+      case DW_FORM_flag:      return "DW_FORM_flag";
+      case DW_FORM_sdata:     return "DW_FORM_sdata";
+      case DW_FORM_strp:      return "DW_FORM_strp";
+      case DW_FORM_udata:     return "DW_FORM_udata";
+      case DW_FORM_ref_addr:  return "DW_FORM_ref_addr";
+      case DW_FORM_ref1:      return "DW_FORM_ref1";
+      case DW_FORM_ref2:      return "DW_FORM_ref2";
+      case DW_FORM_ref4:      return "DW_FORM_ref4";
+      case DW_FORM_ref8:      return "DW_FORM_ref8";
+      case DW_FORM_ref_udata: return "DW_FORM_ref_udata";
+      case DW_FORM_indirect:  return "DW_FORM_indirect";
+      default:                return "DW_FORM_???";
+   }
+}
+
+HChar* ML_(pp_DW_AT) ( DW_AT attr )
+{
+   switch (attr) {
+      case DW_AT_sibling:             return "DW_AT_sibling";
+      case DW_AT_location:            return "DW_AT_location";
+      case DW_AT_name: return "DW_AT_name";
+      case DW_AT_ordering: return "DW_AT_ordering";
+      case DW_AT_subscr_data: return "DW_AT_subscr_data";
+      case DW_AT_byte_size: return "DW_AT_byte_size";
+      case DW_AT_bit_offset: return "DW_AT_bit_offset";
+      case DW_AT_bit_size: return "DW_AT_bit_size";
+      case DW_AT_element_list: return "DW_AT_element_list";
+      case DW_AT_stmt_list: return "DW_AT_stmt_list";
+      case DW_AT_low_pc: return "DW_AT_low_pc";
+      case DW_AT_high_pc: return "DW_AT_high_pc";
+      case DW_AT_language: return "DW_AT_language";
+      case DW_AT_member: return "DW_AT_member";
+      case DW_AT_discr: return "DW_AT_discr";
+      case DW_AT_discr_value: return "DW_AT_discr_value";
+      case DW_AT_visibility: return "DW_AT_visibility";
+      case DW_AT_import: return "DW_AT_import";
+      case DW_AT_string_length: return "DW_AT_string_length";
+      case DW_AT_common_reference: return "DW_AT_common_reference";
+      case DW_AT_comp_dir: return "DW_AT_comp_dir";
+      case DW_AT_const_value: return "DW_AT_const_value";
+      case DW_AT_containing_type: return "DW_AT_containing_type";
+      case DW_AT_default_value: return "DW_AT_default_value";
+      case DW_AT_inline: return "DW_AT_inline";
+      case DW_AT_is_optional: return "DW_AT_is_optional";
+      case DW_AT_lower_bound: return "DW_AT_lower_bound";
+      case DW_AT_producer: return "DW_AT_producer";
+      case DW_AT_prototyped: return "DW_AT_prototyped";
+      case DW_AT_return_addr: return "DW_AT_return_addr";
+      case DW_AT_start_scope: return "DW_AT_start_scope";
+      case DW_AT_stride_size: return "DW_AT_stride_size";
+      case DW_AT_upper_bound: return "DW_AT_upper_bound";
+      case DW_AT_abstract_origin: return "DW_AT_abstract_origin";
+      case DW_AT_accessibility: return "DW_AT_accessibility";
+      case DW_AT_address_class: return "DW_AT_address_class";
+      case DW_AT_artificial: return "DW_AT_artificial";
+      case DW_AT_base_types: return "DW_AT_base_types";
+      case DW_AT_calling_convention: return "DW_AT_calling_convention";
+      case DW_AT_count: return "DW_AT_count";
+      case DW_AT_data_member_location: return "DW_AT_data_member_location";
+      case DW_AT_decl_column: return "DW_AT_decl_column";
+      case DW_AT_decl_file: return "DW_AT_decl_file";
+      case DW_AT_decl_line: return "DW_AT_decl_line";
+      case DW_AT_declaration: return "DW_AT_declaration";
+      case DW_AT_discr_list: return "DW_AT_discr_list";
+      case DW_AT_encoding: return "DW_AT_encoding";
+      case DW_AT_external: return "DW_AT_external";
+      case DW_AT_frame_base: return "DW_AT_frame_base";
+      case DW_AT_friend: return "DW_AT_friend";
+      case DW_AT_identifier_case: return "DW_AT_identifier_case";
+      case DW_AT_macro_info: return "DW_AT_macro_info";
+      case DW_AT_namelist_items: return "DW_AT_namelist_items";
+      case DW_AT_priority: return "DW_AT_priority";
+      case DW_AT_segment: return "DW_AT_segment";
+      case DW_AT_specification: return "DW_AT_specification";
+      case DW_AT_static_link: return "DW_AT_static_link";
+      case DW_AT_type: return "DW_AT_type";
+      case DW_AT_use_location: return "DW_AT_use_location";
+      case DW_AT_variable_parameter: return "DW_AT_variable_parameter";
+      case DW_AT_virtuality: return "DW_AT_virtuality";
+      case DW_AT_vtable_elem_location: return "DW_AT_vtable_elem_location";
+      /* DWARF 3 values.  */
+      case DW_AT_allocated: return "DW_AT_allocated";
+      case DW_AT_associated: return "DW_AT_associated";
+      case DW_AT_data_location: return "DW_AT_data_location";
+      case DW_AT_stride: return "DW_AT_stride";
+      case DW_AT_entry_pc: return "DW_AT_entry_pc";
+      case DW_AT_use_UTF8: return "DW_AT_use_UTF8";
+      case DW_AT_extension: return "DW_AT_extension";
+      case DW_AT_ranges: return "DW_AT_ranges";
+      case DW_AT_trampoline: return "DW_AT_trampoline";
+      case DW_AT_call_column: return "DW_AT_call_column";
+      case DW_AT_call_file: return "DW_AT_call_file";
+      case DW_AT_call_line: return "DW_AT_call_line";
+      case DW_AT_description: return "DW_AT_description";
+      case DW_AT_binary_scale: return "DW_AT_binary_scale";
+      case DW_AT_decimal_scale: return "DW_AT_decimal_scale";
+      case DW_AT_small: return "DW_AT_small";
+      case DW_AT_decimal_sign: return "DW_AT_decimal_sign";
+      case DW_AT_digit_count: return "DW_AT_digit_count";
+      case DW_AT_picture_string: return "DW_AT_picture_string";
+      case DW_AT_mutable: return "DW_AT_mutable";
+      case DW_AT_threads_scaled: return "DW_AT_threads_scaled";
+      case DW_AT_explicit: return "DW_AT_explicit";
+      case DW_AT_object_pointer: return "DW_AT_object_pointer";
+      case DW_AT_endianity: return "DW_AT_endianity";
+      case DW_AT_elemental: return "DW_AT_elemental";
+      case DW_AT_pure: return "DW_AT_pure";
+      case DW_AT_recursive: return "DW_AT_recursive";
+      /* SGI/MIPS extensions.  */
+      /* case DW_AT_MIPS_fde: return "DW_AT_MIPS_fde"; */
+      /* DW_AT_MIPS_fde == DW_AT_HP_unmodifiable */
+      case DW_AT_MIPS_loop_begin: return "DW_AT_MIPS_loop_begin";
+      case DW_AT_MIPS_tail_loop_begin: return "DW_AT_MIPS_tail_loop_begin";
+      case DW_AT_MIPS_epilog_begin: return "DW_AT_MIPS_epilog_begin";
+      case DW_AT_MIPS_loop_unroll_factor: return "DW_AT_MIPS_loop_unroll_factor";
+      case DW_AT_MIPS_software_pipeline_depth: return "DW_AT_MIPS_software_pipeline_depth";
+      case DW_AT_MIPS_linkage_name: return "DW_AT_MIPS_linkage_name";
+      case DW_AT_MIPS_stride: return "DW_AT_MIPS_stride";
+      case DW_AT_MIPS_abstract_name: return "DW_AT_MIPS_abstract_name";
+      case DW_AT_MIPS_clone_origin: return "DW_AT_MIPS_clone_origin";
+      case DW_AT_MIPS_has_inlines: return "DW_AT_MIPS_has_inlines";
+      /* HP extensions.  */
+      case DW_AT_HP_block_index: return "DW_AT_HP_block_index";
+      case DW_AT_HP_unmodifiable: return "DW_AT_HP_unmodifiable";
+      case DW_AT_HP_actuals_stmt_list: return "DW_AT_HP_actuals_stmt_list";
+      case DW_AT_HP_proc_per_section: return "DW_AT_HP_proc_per_section";
+      case DW_AT_HP_raw_data_ptr: return "DW_AT_HP_raw_data_ptr";
+      case DW_AT_HP_pass_by_reference: return "DW_AT_HP_pass_by_reference";
+      case DW_AT_HP_opt_level: return "DW_AT_HP_opt_level";
+      case DW_AT_HP_prof_version_id: return "DW_AT_HP_prof_version_id";
+      case DW_AT_HP_opt_flags: return "DW_AT_HP_opt_flags";
+      case DW_AT_HP_cold_region_low_pc: return "DW_AT_HP_cold_region_low_pc";
+      case DW_AT_HP_cold_region_high_pc: return "DW_AT_HP_cold_region_high_pc";
+      case DW_AT_HP_all_variables_modifiable: return "DW_AT_HP_all_variables_modifiable";
+      case DW_AT_HP_linkage_name: return "DW_AT_HP_linkage_name";
+      case DW_AT_HP_prof_flags: return "DW_AT_HP_prof_flags";
+      /* GNU extensions.  */
+      case DW_AT_sf_names: return "DW_AT_sf_names";
+      case DW_AT_src_info: return "DW_AT_src_info";
+      case DW_AT_mac_info: return "DW_AT_mac_info";
+      case DW_AT_src_coords: return "DW_AT_src_coords";
+      case DW_AT_body_begin: return "DW_AT_body_begin";
+      case DW_AT_body_end: return "DW_AT_body_end";
+      case DW_AT_GNU_vector: return "DW_AT_GNU_vector";
+      /* VMS extensions.  */
+      case DW_AT_VMS_rtnbeg_pd_address: return "DW_AT_VMS_rtnbeg_pd_address";
+      /* UPC extension.  */
+      case DW_AT_upc_threads_scaled: return "DW_AT_upc_threads_scaled";
+      /* PGI (STMicroelectronics) extensions.  */
+      case DW_AT_PGI_lbase: return "DW_AT_PGI_lbase";
+      case DW_AT_PGI_soffset: return "DW_AT_PGI_soffset";
+      case DW_AT_PGI_lstride: return "DW_AT_PGI_lstride";
+      default: return "DW_AT_???";
+   }
+}
+
+
+/* ------ To do with evaluation of Dwarf expressions ------ */
+
+/* FIXME: duplicated in readdwarf.c */
+static 
+ULong read_leb128 ( UChar* data, Int* length_return, Int sign )
+{
+  ULong  result = 0;
+  UInt   num_read = 0;
+  Int    shift = 0;
+  UChar  byte;
+
+  vg_assert(sign == 0 || sign == 1);
+
+  do
+    {
+      byte = * data ++;
+      num_read ++;
+
+      result |= ((ULong)(byte & 0x7f)) << shift;
+
+      shift += 7;
+
+    }
+  while (byte & 0x80);
+
+  if (length_return != NULL)
+    * length_return = num_read;
+
+  if (sign && (shift < 64) && (byte & 0x40))
+    result |= -(1ULL << shift);
+
+  return result;
+}
+
+/* Small helper functions easier to use
+ * value is returned and the given pointer is
+ * moved past end of leb128 data */
+/* FIXME: duplicated in readdwarf.c */
+static ULong read_leb128U( UChar **data )
+{
+  Int len;
+  ULong val = read_leb128( *data, &len, 0 );
+  *data += len;
+  return val;
+}
+
+/* Same for signed data */
+/* FIXME: duplicated in readdwarf.c */
+static Long read_leb128S( UChar **data )
+{
+   Int len;
+   ULong val = read_leb128( *data, &len, 1 );
+   *data += len;
+   return (Long)val;
+}
+
+
+/* FIXME: duplicates logic in readdwarf.c: copy_convert_CfiExpr_tree
+   and {FP,SP}_REG decls */
+static Bool get_Dwarf_Reg( /*OUT*/Addr* a, Word regno, RegSummary* regs )
+{
+   vg_assert(regs);
+#  if defined(VGP_amd64_linux)
+   if (regno == 6/*RBP*/) { *a = regs->fp; return True; }
+   if (regno == 7/*RSP*/) { *a = regs->sp; return True; }
+#  elif defined(VGP_x86_linux)
+   if (regno == 5/*EBP*/) { *a = regs->fp; return True; }
+   if (regno == 4/*ESP*/) { *a = regs->sp; return True; }
+#  elif defined(VGP_ppc32_linux)
+   if (regno == 1/*SP*/) { *a = regs->sp; return True; }
+   VG_(printf)("get_Dwarf_Reg(ppc32-linux)(%ld)\n", regno);
+   if (regno == 31) return False;
+   vg_assert(0);
+#  elif defined(VGP_ppc64_linux)
+   if (regno == 1/*SP*/) { *a = regs->sp; return True; }
+   VG_(printf)("get_Dwarf_Reg(ppc64-linux)(%ld)\n", regno);
+   if (regno == 31) return False;
+   vg_assert(0);
+#  elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
+   vg_assert(0); /* this function should never be called */
+#  else
+#    error "Unknown platform"
+#  endif
+   return False;
+}
+
+
+/* Evaluate a standard DWARF3 expression.  See detailed description in
+   priv_d3basics.h. */
+GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB, 
+                                     GExpr* fbGX, RegSummary* regs,
+                                     Addr data_bias,
+                                     Bool push_initial_zero )
+{
+#  define N_EXPR_STACK 20
+
+#  define FAIL(_str)                                          \
+      do {                                                    \
+         res.kind = GXR_Failure;                              \
+         res.word = (UWord)(_str);                            \
+         return res;                                          \
+      } while (0)
+
+#  define PUSH(_arg)                                          \
+      do {                                                    \
+         vg_assert(sp >= -1 && sp < N_EXPR_STACK);            \
+         if (sp == N_EXPR_STACK-1)                            \
+            FAIL("evaluate_Dwarf3_Expr: stack overflow(1)");  \
+         sp++;                                                \
+         stack[sp] = (_arg);                                  \
+      } while (0)
+
+#  define POP(_lval)                                          \
+      do {                                                    \
+         vg_assert(sp >= -1 && sp < N_EXPR_STACK);            \
+         if (sp == -1)                                        \
+            FAIL("evaluate_Dwarf3_Expr: stack underflow(1)"); \
+         _lval = stack[sp];                                   \
+         sp--;                                                \
+      } while (0)
+
+   UChar    opcode;
+   UChar*   limit;
+   Int      sp; /* # of top element: valid is -1 .. N_EXPR_STACK-1 */
+   Addr     stack[N_EXPR_STACK]; /* stack of addresses, as per D3 spec */
+   GXResult fbval, res;
+   Addr     a1;
+   Word     sw1;
+   UWord    uw1;
+   Bool     ok;
+
+   sp = -1;
+   vg_assert(expr);
+   vg_assert(exprszB >= 0);
+   limit = expr + exprszB;
+
+   /* Deal with the case where the entire expression is a single
+      Register Name Operation (D3 spec sec 2.6.1).  Then the
+      denotation of the expression as a whole is a register name. */
+   if (exprszB == 1
+       && expr[0] >= DW_OP_reg0 && expr[0] <= DW_OP_reg31) {
+      res.kind = GXR_RegNo;
+      res.word = (UWord)(expr[0] - DW_OP_reg0);
+      return res;
+   }
+   if (exprszB > 1
+       && expr[0] == DW_OP_regx) {
+      /* JRS: 2008Feb20: I believe the following is correct, but would
+         like to see a test case show up before enabling it. */
+      vg_assert(0);
+      expr++;
+      res.kind = GXR_RegNo;
+      res.word = (UWord)read_leb128U( &expr );
+      if (expr != limit)
+         FAIL("evaluate_Dwarf3_Expr: DW_OP_regx*: invalid expr size");
+      else
+         return res;
+      /*NOTREACHED*/
+   }
+
+   /* Evidently this expresion denotes a value, not a register name.
+      So evaluate it accordingly. */
+
+   if (push_initial_zero)
+      PUSH(0);
+
+   while (True) {
+
+      vg_assert(sp >= -1 && sp < N_EXPR_STACK);
+
+      if (expr > limit) 
+         /* overrun - something's wrong */
+         FAIL("evaluate_Dwarf3_Expr: ran off end of expr");
+
+      if (expr == limit) {
+         /* end of expr - return expr on the top of stack. */
+         if (sp == -1)
+            /* stack empty.  Bad. */
+            FAIL("evaluate_Dwarf3_Expr: stack empty at end of expr");
+         else
+            break;
+      }
+
+      opcode = *expr++;
+      switch (opcode) {
+         case DW_OP_addr:
+            /* Presumably what is given in the Dwarf3 is a SVMA (how
+               could it be otherwise?)  So we add the data bias on
+               before pushing the result.  FIXME: how can we be sure
+               the data bias is intended, not the text bias?  I don't
+               know. */
+            PUSH( *(Addr*)expr + data_bias ); 
+            expr += sizeof(Addr);
+            break;
+         case DW_OP_fbreg:
+            if (!fbGX)
+               FAIL("evaluate_Dwarf3_Expr: DW_OP_fbreg with "
+                    "no expr for fbreg present");
+            fbval = ML_(evaluate_GX)(fbGX, NULL, regs, data_bias);
+            /* Convert fbval into something we can use.  If we got a
+               Value, no problem.  However, as per D3 spec sec 3.3.5
+               (Low Level Information) sec 2, we could also get a
+               RegNo, and that is taken to mean the value in the
+               indicated register.  So we have to manually
+               "dereference" it. */
+            a1 = 0;
+            switch (fbval.kind) {
+               case GXR_Failure:
+                  return fbval; /* propagate failure */
+               case GXR_Value:
+                  a1 = fbval.word; break; /* use as-is */
+               case GXR_RegNo:
+                  ok = get_Dwarf_Reg( &a1, fbval.word, regs );
+                  if (!ok) return fbval; /* propagate failure */
+                  break;
+               default:
+                  vg_assert(0);
+            }
+            sw1 = (Word)read_leb128S( &expr );
+            PUSH( a1 + sw1 );
+            break;
+         /* DW_OP_breg* denotes 'contents of specified register, plus
+            constant offset'.  So provided we know what the register's
+            value is, we can evaluate this.  Contrast DW_OP_reg*,
+            which indicates that denoted location is in a register
+            itself.  If DW_OP_reg* shows up here the expression is
+            malformed, since we are evaluating for value now, and
+            DW_OP_reg* denotes a register location, not a value.  See
+            D3 Spec sec 2.6.1 ("Register Name Operations") for
+            details. */
+         case DW_OP_breg0 ... DW_OP_breg31:
+            if (!regs)
+               FAIL("evaluate_Dwarf3_Expr: DW_OP_breg* but no reg info");
+            a1 = 0;
+            if (!get_Dwarf_Reg( &a1, opcode - DW_OP_breg0, regs ))
+               FAIL("evaluate_Dwarf3_Expr: unhandled DW_OP_breg*");
+            sw1 = (Word)read_leb128S( &expr );
+            a1 += sw1;
+            PUSH( a1 );
+            break;
+         /* As per comment on DW_OP_breg*, the following denote that
+            the value in question is in a register, not in memory.  So
+            we simply return failure. (iow, the expression is
+            malformed). */
+         case DW_OP_reg0 ... DW_OP_reg31:
+            FAIL("evaluate_Dwarf3_Expr: DW_OP_reg* "
+                 "whilst evaluating for a value");
+            break;
+         case DW_OP_plus_uconst:
+            POP(uw1);
+            uw1 += (UWord)read_leb128U( &expr );
+            PUSH(uw1);
+            break;
+         case DW_OP_GNU_push_tls_address:
+            /* GDB contains the following cryptic comment: */
+            /* Variable is at a constant offset in the thread-local
+            storage block into the objfile for the current thread and
+            the dynamic linker module containing this expression. Here
+            we return returns the offset from that base.  The top of the
+            stack has the offset from the beginning of the thread
+            control block at which the variable is located.  Nothing
+            should follow this operator, so the top of stack would be
+            returned.  */
+            /* But no spec resulting from Googling.  Punt for now. */
+            FAIL("warning: evaluate_Dwarf3_Expr: unhandled "         
+                 "DW_OP_GNU_push_tls_address");
+            /*NOTREACHED*/
+         default:
+            if (!VG_(clo_xml))
+               VG_(message)(Vg_DebugMsg, 
+                            "warning: evaluate_Dwarf3_Expr: unhandled "
+                            "DW_OP_ 0x%x", (Int)opcode); 
+            FAIL("evaluate_Dwarf3_Expr: unhandled DW_OP_");
+            /*NOTREACHED*/
+      }
+
+   }
+
+   vg_assert(sp >= 0 && sp < N_EXPR_STACK);
+   res.word = stack[sp];
+   res.kind = GXR_Value;
+   return res;
+ 
+#  undef POP
+#  undef PUSH
+#  undef FAIL
+#  undef N_EXPR_STACK
+}
+
+
+/* Evaluate a so-called Guarded (DWARF3) expression.  See detailed
+   description in priv_d3basics.h. */
+GXResult ML_(evaluate_GX)( GExpr* gx, GExpr* fbGX,
+                           RegSummary* regs, Addr data_bias )
+{
+   GXResult res;
+   Addr     aMin, aMax;
+   UChar    uc;
+   UShort   nbytes;
+   UWord    nGuards = 0;
+   UChar* p = &gx->payload[0];
+   uc = *p++; /*biasMe*/
+   vg_assert(uc == 0 || uc == 1);
+   /* in fact it's senseless to evaluate if the guards need biasing.
+      So don't. */
+   vg_assert(uc == 0);
+   while (True) {
+      uc = *p++;
+      if (uc == 1) { /*isEnd*/
+         /* didn't find any matching range. */
+         res.kind = GXR_Failure;
+         res.word = (UWord)"no matching range";
+         return res;
+      }
+      vg_assert(uc == 0);
+      aMin   = * (Addr*)p;   p += sizeof(Addr);
+      aMax   = * (Addr*)p;   p += sizeof(Addr);
+      nbytes = * (UShort*)p; p += sizeof(UShort);
+      nGuards++;
+      if (0) VG_(printf)("           guard %d: %p %p\n", 
+                         (Int)nGuards, aMin,aMax);
+      if (regs == NULL) {
+         vg_assert(aMin == (Addr)0);
+         vg_assert(aMax == ~(Addr)0);
+         /* Assert this is the first guard. */
+         vg_assert(nGuards == 1);
+         res = ML_(evaluate_Dwarf3_Expr)(
+                  p, (UWord)nbytes, fbGX, regs, data_bias,
+                  False/*push_initial_zero*/ );
+         /* Now check there are no more guards. */
+         p += (UWord)nbytes;
+         vg_assert(*p == 1); /*isEnd*/
+         return res;
+      } else {
+         if (aMin <= regs->ip && regs->ip <= aMax) {
+            /* found a matching range.  Evaluate the expression. */
+            return ML_(evaluate_Dwarf3_Expr)(
+                      p, (UWord)nbytes, fbGX, regs, data_bias,
+                      False/*push_initial_zero*/ );
+         }
+      }
+      /* else keep searching */
+      p += (UWord)nbytes;
+   }
+}
+
+
+void ML_(pp_GXResult) ( GXResult res )
+{
+   switch (res.kind) {
+      case GXR_Failure:
+         VG_(printf)("GXR_Failure(%s)", (HChar*)res.word); break;
+      case GXR_Value:
+         VG_(printf)("GXR_Value(0x%lx)", res.word); break;
+      case GXR_RegNo:
+         VG_(printf)("GXR_RegNo(%lu)", res.word); break;
+      default:
+         VG_(printf)("GXR_???"); break;
+   }
+}
+
+
+/*--------------------------------------------------------------------*/
+/*--- end                                               d3basics.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c
index 0aa8b1c..7216fc3 100644
--- a/coregrind/m_debuginfo/debuginfo.c
+++ b/coregrind/m_debuginfo/debuginfo.c
@@ -37,22 +37,29 @@
 #include "pub_core_basics.h"
 #include "pub_core_vki.h"
 #include "pub_core_threadstate.h"
-#include "pub_core_debuginfo.h"   /* self */
+#include "pub_core_debuginfo.h"  /* self */
 #include "pub_core_demangle.h"
 #include "pub_core_libcbase.h"
 #include "pub_core_libcassert.h"
 #include "pub_core_libcprint.h"
-#include "pub_core_mallocfree.h"
+#include "pub_core_libcfile.h"
 #include "pub_core_options.h"
-#include "pub_core_redir.h"       // VG_(redir_notify_{new,delete}_SegInfo)
+#include "pub_core_redir.h"      // VG_(redir_notify_{new,delete}_SegInfo)
 #include "pub_core_aspacemgr.h"
-#include "pub_core_machine.h"     // VG_PLAT_USES_PPCTOC
+#include "pub_core_machine.h"    // VG_PLAT_USES_PPCTOC
 #include "pub_core_xarray.h"
+#include "pub_core_oset.h"
+#include "pub_core_stacktrace.h" // VG_(get_StackTrace)
+
+#include "priv_misc.h"           /* dinfo_zalloc/free */
+#include "priv_d3basics.h"       /* ML_(pp_GX) */
+#include "priv_tytypes.h"
 #include "priv_storage.h"
 #include "priv_readdwarf.h"
 #include "priv_readstabs.h"
 #if defined(VGO_linux)
 # include "priv_readelf.h"
+# include "priv_readdwarf3.h"
 #elif defined(VGO_aix5)
 # include "pub_core_debuglog.h"
 # include "pub_core_libcproc.h"
@@ -95,38 +102,74 @@
 /*--- Root structure                                       ---*/
 /*------------------------------------------------------------*/
 
-/* The root structure for the entire symbol table system.  It is a
-   linked list of SegInfos.  Note that this entire mechanism assumes
-   that what we read from /proc/self/maps doesn't contain overlapping
-   address ranges, and as a result the SegInfos in this list describe
-   disjoint address ranges.
-*/
-static SegInfo* segInfo_list = NULL;
+/* The root structure for the entire debug info system.  It is a
+   linked list of DebugInfos. */
+static DebugInfo* debugInfo_list = NULL;
+
+
+/* Find 'di' in the debugInfo_list and move it one step closer the the
+   front of the list, so as to make subsequent searches for it
+   cheaper.  When used in a controlled way, makes a major improvement
+   in some DebugInfo-search-intensive situations, most notably stack
+   unwinding on amd64-linux. */
+static void move_DebugInfo_one_step_forward ( DebugInfo* di )
+{
+   DebugInfo *di0, *di1, *di2;
+   if (di == debugInfo_list)
+      return; /* already at head of list */
+   vg_assert(di != NULL);
+   di0 = debugInfo_list;
+   di1 = NULL;
+   di2 = NULL;
+   while (True) {
+      if (di0 == NULL || di0 == di) break;
+      di2 = di1;
+      di1 = di0;
+      di0 = di0->next;
+   }
+   vg_assert(di0 == di);
+   if (di0 != NULL && di1 != NULL && di2 != NULL) {
+      DebugInfo* tmp;
+      /* di0 points to di, di1 to its predecessor, and di2 to di1's
+         predecessor.  Swap di0 and di1, that is, move di0 one step
+         closer to the start of the list. */
+      vg_assert(di2->next == di1);
+      vg_assert(di1->next == di0);
+      tmp = di0->next;
+      di2->next = di0;
+      di0->next = di1;
+      di1->next = tmp;
+   }
+   else
+   if (di0 != NULL && di1 != NULL && di2 == NULL) {
+      /* it's second in the list. */
+      vg_assert(debugInfo_list == di1);
+      vg_assert(di1->next == di0);
+      di1->next = di0->next;
+      di0->next = di1;
+      debugInfo_list = di0;
+   }
+}
 
 
 /*------------------------------------------------------------*/
 /*--- Notification (acquire/discard) helpers               ---*/
 /*------------------------------------------------------------*/
 
-/* Allocate and zero out a new SegInfo record. */
+/* Allocate and zero out a new DebugInfo record. */
 static 
-SegInfo* alloc_SegInfo(Addr start, SizeT size, OffT foffset, 
-                       const UChar* filename,
-                       const UChar* memname)
+DebugInfo* alloc_DebugInfo( const UChar* filename,
+                            const UChar* memname )
 {
-   Bool     traceme;
-   SegInfo* si;
+   Bool       traceme;
+   DebugInfo* di;
 
    vg_assert(filename);
 
-   si = VG_(arena_calloc)(VG_AR_SYMTAB, 1, sizeof(SegInfo));
-   si->text_start_avma = start;
-   si->text_size       = size;
-   si->foffset         = foffset;
-   si->filename        = VG_(arena_strdup)(VG_AR_SYMTAB, filename);
-   si->memname         = memname 
-                           ?  VG_(arena_strdup)(VG_AR_SYMTAB, memname)
-                           :  NULL;
+   di = ML_(dinfo_zalloc)(sizeof(DebugInfo));
+   di->filename  = ML_(dinfo_strdup)(filename);
+   di->memname   = memname ? ML_(dinfo_strdup)(memname)
+                           : NULL;
 
    /* Everything else -- pointers, sizes, arrays -- is zeroed by calloc.
       Now set up the debugging-output flags. */
@@ -135,41 +178,89 @@
         || (memname && VG_(string_match)( VG_(clo_trace_symtab_patt), 
                                           memname ));
    if (traceme) {
-      si->trace_symtab = VG_(clo_trace_symtab);
-      si->trace_cfi    = VG_(clo_trace_cfi);
-      si->ddump_syms   = VG_(clo_debug_dump_syms);
-      si->ddump_line   = VG_(clo_debug_dump_line);
-      si->ddump_frames = VG_(clo_debug_dump_frames);
+      di->trace_symtab = VG_(clo_trace_symtab);
+      di->trace_cfi    = VG_(clo_trace_cfi);
+      di->ddump_syms   = VG_(clo_debug_dump_syms);
+      di->ddump_line   = VG_(clo_debug_dump_line);
+      di->ddump_frames = VG_(clo_debug_dump_frames);
    }
 
-   return si;
+   return di;
 }
 
 
-/* Free a SegInfo, and also all the stuff hanging off it. */
-static void free_SegInfo ( SegInfo* si )
+/* Free a DebugInfo, and also all the stuff hanging off it. */
+static void free_DebugInfo ( DebugInfo* di )
 {
+   Word i, j;
    struct strchunk *chunk, *next;
-   vg_assert(si != NULL);
-   if (si->filename)   VG_(arena_free)(VG_AR_SYMTAB, si->filename);
-   if (si->symtab)     VG_(arena_free)(VG_AR_SYMTAB, si->symtab);
-   if (si->loctab)     VG_(arena_free)(VG_AR_SYMTAB, si->loctab);
-   if (si->cfsi)       VG_(arena_free)(VG_AR_SYMTAB, si->cfsi);
-   if (si->cfsi_exprs) VG_(deleteXA)(si->cfsi_exprs);
+   TyAdmin *admin1, *admin2;
+   GExpr *gexpr1, *gexpr2;
 
-   for (chunk = si->strchunks; chunk != NULL; chunk = next) {
+   vg_assert(di != NULL);
+   if (di->filename)   ML_(dinfo_free)(di->filename);
+   if (di->symtab)     ML_(dinfo_free)(di->symtab);
+   if (di->loctab)     ML_(dinfo_free)(di->loctab);
+   if (di->cfsi)       ML_(dinfo_free)(di->cfsi);
+   if (di->cfsi_exprs) VG_(deleteXA)(di->cfsi_exprs);
+
+   for (chunk = di->strchunks; chunk != NULL; chunk = next) {
       next = chunk->next;
-      VG_(arena_free)(VG_AR_SYMTAB, chunk);
+      ML_(dinfo_free)(chunk);
    }
-   VG_(arena_free)(VG_AR_SYMTAB, si);
+
+   /* Delete the two admin lists.  These lists exist purely so that we
+      can visit each object exactly once when we need to delete
+      them. */
+   for (admin1 = di->admin_tyadmins; admin1; admin1 = admin2) {
+      admin2 = admin1->next;
+      ML_(delete_TyAdmin_and_payload)(admin1);
+   }
+   for (gexpr1 = di->admin_gexprs; gexpr1; gexpr1 = gexpr2) {
+      gexpr2 = gexpr1->next;
+      ML_(dinfo_free)(gexpr1);
+   }
+
+   /* Dump the variable info.  This is kinda complex: we must take
+      care not to free items which reside in either the admin lists
+      (as we have just freed them) or which reside in the DebugInfo's
+      string table. */
+   if (di->varinfo) {
+      for (i = 0; i < VG_(sizeXA)(di->varinfo); i++) {
+         OSet* scope = *(OSet**)VG_(indexXA)(di->varinfo, i);
+         if (!scope) continue;
+         /* iterate over all entries in 'scope' */
+         VG_(OSetGen_ResetIter)(scope);
+         while (True) {
+            DiAddrRange* arange = VG_(OSetGen_Next)(scope);
+            if (!arange) break;
+            /* for each var in 'arange' */
+            vg_assert(arange->vars);
+            for (j = 0; j < VG_(sizeXA)( arange->vars ); j++) {
+               DiVariable* var = (DiVariable*)VG_(indexXA)(arange->vars,j);
+               vg_assert(var);
+               /* Nothing to free in var: all the pointer fields refer
+                  to stuff either on an admin list, or in
+                  .strchunks */
+            }
+            VG_(deleteXA)(arange->vars);
+            /* Don't free arange itself, as OSetGen_Destroy does
+               that */
+         }
+         VG_(OSetGen_Destroy)(scope);
+      }
+      VG_(deleteXA)(di->varinfo);
+   }
+
+   ML_(dinfo_free)(di);
 }
 
 
-/* 'si' is a member of segInfo_list.  Find it, remove it from the
+/* 'si' is a member of debugInfo_list.  Find it, remove it from the
    list, notify m_redir that this has happened, and free all storage
    reachable from it.
 */
-static void discard_SegInfo ( SegInfo* si )
+static void discard_DebugInfo ( DebugInfo* di )
 {
 #  if defined(VGP_ppc32_aix5)
    HChar* reason = "__unload";
@@ -179,51 +270,54 @@
    HChar* reason = "munmap";
 #  endif
 
-   SegInfo** prev_next_ptr = &segInfo_list;
-   SegInfo*  curr          =  segInfo_list;
+   DebugInfo** prev_next_ptr = &debugInfo_list;
+   DebugInfo*  curr          =  debugInfo_list;
 
    while (curr) {
-      if (curr == si) {
-         // Found it;  remove from list and free it.
+      if (curr == di) {
+         /* Found it;  remove from list and free it. */
          if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))
             VG_(message)(Vg_DebugMsg, 
                          "Discarding syms at %p-%p in %s due to %s()", 
-                         si->text_start_avma, 
-                         si->text_start_avma + si->text_size,
+                         di->text_avma, 
+                         di->text_avma + di->text_size,
                          curr->filename ? curr->filename : (UChar*)"???",
                          reason);
          vg_assert(*prev_next_ptr == curr);
          *prev_next_ptr = curr->next;
-         VG_(redir_notify_delete_SegInfo)( curr );
-         free_SegInfo(curr);
+         VG_(redir_notify_delete_DebugInfo)( curr );
+         free_DebugInfo(curr);
          return;
       }
       prev_next_ptr = &curr->next;
       curr          =  curr->next;
    }
 
-   // Not found.
+   /* Not found. */
 }
 
 
-/* Repeatedly scan segInfo_list, looking for segInfos intersecting
-   [start,start+length), and call discard_SegInfo to get rid of them.
-   This modifies the list, hence the multiple iterations.
-   JRS 20060401: I don't understand that last sentence. */
+/* Repeatedly scan debugInfo_list, looking for DebugInfos with text
+   AVMAs intersecting [start,start+length), and call discard_DebugInfo
+   to get rid of them.  This modifies the list, hence the multiple
+   iterations.
+*/
 static void discard_syms_in_range ( Addr start, SizeT length )
 {
-   Bool found;
-   SegInfo* curr;
+   Bool       found;
+   DebugInfo* curr;
 
    while (True) {
       found = False;
 
-      curr = segInfo_list;
+      curr = debugInfo_list;
       while (True) {
          if (curr == NULL)
             break;
-         if (start+length - 1 < curr->text_start_avma 
-             || curr->text_start_avma + curr->text_size - 1 < start) {
+         if (curr->text_present
+             && curr->text_size > 0
+             && (start+length - 1 < curr->text_avma 
+                 || curr->text_avma + curr->text_size - 1 < start)) {
             /* no overlap */
 	 } else {
 	    found = True;
@@ -233,53 +327,134 @@
       }
 
       if (!found) break;
-      discard_SegInfo( curr );
+      discard_DebugInfo( curr );
    }
 }
 
 
-/* Create a new SegInfo with the specific address/length/vma offset,
-   then snarf whatever info we can from the given filename into it. */
-static
-SegInfo* acquire_syms_for_range( 
-            /* ALL        */ Addr  seg_addr, 
-            /* ALL        */ SizeT seg_len,
-            /* ELF only   */ OffT  seg_offset, 
-            /* ALL        */ const UChar* seg_filename,
-            /* XCOFF only */ const UChar* seg_memname,
-	    /* XCOFF only */ Addr  data_addr,
-	    /* XCOFF only */ SizeT data_len,
-	    /* XCOFF only */ Bool  is_mainexe
-         )
+/* Does [s1,+len1) overlap [s2,+len2) ?  Note: does not handle
+   wraparound at the end of the address space -- just asserts in that
+   case. */
+static Bool ranges_overlap (Addr s1, SizeT len1, Addr s2, SizeT len2 )
 {
-   Bool     ok;
-   SegInfo* si = alloc_SegInfo(seg_addr, seg_len, seg_offset, 
-                               seg_filename, seg_memname);
-#  if defined(VGO_linux)
-   ok = ML_(read_elf_debug_info) ( si );
-#  elif defined(VGO_aix5)
-   ok = ML_(read_xcoff_debug_info) ( si, data_addr, data_len, is_mainexe );
-#  else
-#    error Unknown OS
-#  endif
+   Addr e1, e2;
+   if (len1 == 0 || len2 == 0) 
+      return False;
+   e1 = s1 + len1 - 1;
+   e2 = s2 + len2 - 1;
+   /* Assert that we don't have wraparound.  If we do it would imply
+      that file sections are getting mapped around the end of the
+      address space, which sounds unlikely. */
+   vg_assert(s1 <= e1);
+   vg_assert(s2 <= e2);
+   if (e1 < s2 || e2 < s1) return False;
+   return True;
+}
 
-   if (!ok) {
-      // Something went wrong (eg. bad ELF file).
-      free_SegInfo( si );
-      si = NULL;
 
-   } else {
-      // Prepend si to segInfo_list
-      si->next = segInfo_list;
-      segInfo_list = si;
+/* Do the basic rx_ and rw_ mappings of the two DebugInfos overlap in
+   any way? */
+static Bool do_DebugInfos_overlap ( DebugInfo* di1, DebugInfo* di2 )
+{
+   vg_assert(di1);
+   vg_assert(di2);
 
-      ML_(canonicaliseTables) ( si );
+   if (di1->have_rx_map && di2->have_rx_map
+       && ranges_overlap(di1->rx_map_avma, di1->rx_map_size,
+                         di2->rx_map_avma, di2->rx_map_size))
+      return True;
 
-      /* notify m_redir about it */
-      VG_(redir_notify_new_SegInfo)( si );
+   if (di1->have_rx_map && di2->have_rw_map
+       && ranges_overlap(di1->rx_map_avma, di1->rx_map_size,
+                         di2->rw_map_avma, di2->rw_map_size))
+      return True;
+
+   if (di1->have_rw_map && di2->have_rx_map
+       && ranges_overlap(di1->rw_map_avma, di1->rw_map_size,
+                         di2->rx_map_avma, di2->rx_map_size))
+      return True;
+
+   if (di1->have_rw_map && di2->have_rw_map
+       && ranges_overlap(di1->rw_map_avma, di1->rw_map_size,
+                         di2->rw_map_avma, di2->rw_map_size))
+      return True;
+
+   return False;
+}
+
+
+/* Discard all elements of debugInfo_list whose .mark bit is set.
+*/
+static void discard_marked_DebugInfos ( void )
+{
+   DebugInfo* curr;
+
+   while (True) {
+
+      curr = debugInfo_list;
+      while (True) {
+         if (!curr)
+            break;
+         if (curr->mark)
+            break;
+	 curr = curr->next;
+      }
+
+      if (!curr) break;
+      discard_DebugInfo( curr );
+
    }
+}
 
-   return si;
+
+/* Discard any elements of debugInfo_list which overlap with diRef.
+   Clearly diRef must have its rx_ and rw_ mapping information set to
+   something sane. */
+#if defined(VGO_aix5)
+__attribute__((unused))
+#endif
+static void discard_DebugInfos_which_overlap_with ( DebugInfo* diRef )
+{
+   DebugInfo* di;
+   /* Mark all the DebugInfos in debugInfo_list that need to be
+      deleted.  First, clear all the mark bits; then set them if they
+      overlap with siRef.  Since siRef itself is in this list we at
+      least expect its own mark bit to be set. */
+   for (di = debugInfo_list; di; di = di->next) {
+      di->mark = do_DebugInfos_overlap( di, diRef );
+      if (di == diRef) {
+         vg_assert(di->mark);
+         di->mark = False;
+      }
+   }
+   discard_marked_DebugInfos();
+}
+
+
+/* Find the existing DebugInfo for (memname,filename) or if not found,
+   create one.  In the latter case memname and filename are strdup'd
+   into VG_AR_DINFO, and the new DebugInfo is added to
+   debugInfo_list. */
+static
+DebugInfo* find_or_create_DebugInfo_for ( UChar* filename, UChar* memname )
+{
+   DebugInfo* di;
+   vg_assert(filename);
+   for (di = debugInfo_list; di; di = di->next) {
+      vg_assert(di->filename);
+      if (0==VG_(strcmp)(di->filename, filename)
+          && ( (memname && di->memname) 
+                  ? 0==VG_(strcmp)(memname, di->memname)
+                  : True ))
+         break;
+   }
+   if (!di) {
+      di = alloc_DebugInfo(filename, memname);
+      vg_assert(di);
+      di->next = debugInfo_list;
+      debugInfo_list = di;
+   }
+   return di;
 }
 
 
@@ -307,15 +482,76 @@
 void VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV )
 {
    NSegment const * seg;
-   HChar*    filename;
-   Bool      ok;
+   HChar*     filename;
+   Bool       ok, is_rx_map, is_rw_map;
+   DebugInfo* di;
+   SysRes     fd;
+   Int        nread;
+   HChar      buf1k[1024];
+   Bool       debug = False;
 
-   /* If this mapping is at the beginning of a file, isn't part of
-      Valgrind, is at least readable and seems to contain an object
-      file, then try reading symbols from it.
+   /* In short, figure out if this mapping is of interest to us, and
+      if so, try to guess what ld.so is doing and when/if we should
+      read debug info. */
+   seg = VG_(am_find_nsegment)(a);
+   vg_assert(seg);
 
-      Getting this heuristic right is critical.  On x86-linux, objects
-      are typically mapped twice:
+   if (debug)
+      VG_(printf)("di_notify_mmap-1: %p-%p %c%c%c\n",
+                  seg->start, seg->end, 
+                  seg->hasR ? 'r' : '-',
+                  seg->hasW ? 'w' : '-',seg->hasX ? 'x' : '-' );
+
+   /* guaranteed by aspacemgr-linux.c, sane_NSegment() */
+   vg_assert(seg->end > seg->start);
+
+   /* Ignore non-file mappings */
+   if ( ! (seg->kind == SkFileC
+           || (seg->kind == SkFileV && allow_SkFileV)) )
+      return;
+
+   /* If the file doesn't have a name, we're hosed.  Give up. */
+   filename = VG_(am_get_filename)( (NSegment*)seg );
+   if (!filename)
+      return;
+
+   if (debug)
+      VG_(printf)("di_notify_mmap-2: %s\n", filename);
+
+   /* Peer at the first few bytes of the file, to see if it is an ELF
+      object file. */
+   VG_(memset)(buf1k, 0, sizeof(buf1k));
+   fd = VG_(open)( filename, VKI_O_RDONLY, 0 );
+   if (fd.isError) {
+      DebugInfo fake_di;
+      VG_(memset)(&fake_di, 0, sizeof(fake_di));
+      fake_di.filename = filename;
+      ML_(symerr)(&fake_di, True, "can't open file to inspect ELF header");
+      return;
+   }
+   nread = VG_(read)( fd.res, buf1k, sizeof(buf1k) );
+   VG_(close)( fd.res );
+
+   if (nread <= 0) {
+      ML_(symerr)(NULL, True, "can't read file to inspect ELF header");
+      return;
+   }
+   vg_assert(nread > 0 && nread <= sizeof(buf1k) );
+
+   /* We're only interested in mappings of ELF object files. */
+   if (!ML_(is_elf_object_file)( buf1k, (SizeT)nread ))
+      return;
+
+   /* Now we have to guess if this is a text-like mapping, a data-like
+      mapping, neither or both.  The rules are:
+
+        text if:   x86-linux    r and x
+                   other-linux  r and x and not w
+
+        data if:   x86-linux    r and w
+                   other-linux  r and w and not x
+
+      Background: On x86-linux, objects are typically mapped twice:
 
       1b8fb000-1b8ff000 r-xp 00000000 08:02 4471477 vgpreload_memcheck.so
       1b8ff000-1b900000 rw-p 00004000 08:02 4471477 vgpreload_memcheck.so
@@ -333,53 +569,105 @@
       to redirect to addresses in that third segment, which is wrong
       and causes crashes.
 
-      ------ 
       JRS 28 Dec 05: unfortunately icc 8.1 on x86 has been seen to
       produce executables with a single rwx segment rather than a
       (r-x,rw-) pair. That means the rules have to be modified thusly:
 
       x86-linux:   consider if r and x
-      all others:  consider if r and x and NOT w
+      all others:  consider if r and x and not w
    */
+   is_rx_map = False;
+   is_rw_map = False;
 #  if defined(VGP_x86_linux)
-   Bool      require_no_W = False;
+   is_rx_map = seg->hasR && seg->hasX;
+   is_rw_map = seg->hasR && seg->hasW;
+#  elif defined(VGP_amd64_linux) \
+        || defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
+   is_rx_map = seg->hasR && seg->hasX && !seg->hasW;
+   is_rw_map = seg->hasR && seg->hasW && !seg->hasX;
 #  else
-   Bool      require_no_W = True;
+#    error "Unknown platform"
 #  endif
 
-   seg = VG_(am_find_nsegment)(a);
-   vg_assert(seg);
+   if (debug)
+      VG_(printf)("di_notify_mmap-3: is_rx_map %d, is_rw_map %d\n",
+                  (Int)is_rx_map, (Int)is_rw_map);
 
-   filename = VG_(am_get_filename)( (NSegment*)seg );
-   if (!filename)
+   /* If it is neither text-ish nor data-ish, we're not interested. */
+   if (!(is_rx_map || is_rw_map))
       return;
 
-   filename = VG_(arena_strdup)( VG_AR_SYMTAB, filename );
+   /* See if we have a DebugInfo for this filename.  If not,
+      create one. */
+   di = find_or_create_DebugInfo_for( filename, NULL/*membername*/ );
+   vg_assert(di);
 
-   ok = (seg->kind == SkFileC || (seg->kind == SkFileV && allow_SkFileV))
-        && seg->offset == 0
-        && seg->fnIdx != -1
-        && seg->hasR
-        && seg->hasX
-        && (require_no_W ? (!seg->hasW) : True)
-        && ML_(is_elf_object_file)( (const void*)seg->start );
-
-   if (!ok) {
-      VG_(arena_free)(VG_AR_SYMTAB, filename);
-      return;
+   if (is_rx_map) {
+      /* We have a text-like mapping.  Note the details. */
+      if (!di->have_rx_map) {
+         di->have_rx_map = True;
+         di->rx_map_avma = a;
+         di->rx_map_size = seg->end + 1 - seg->start;
+         di->rx_map_foff = seg->offset;
+      } else {
+         /* FIXME: complain about a second text-like mapping */
+      }
    }
 
-   /* Dump any info previously associated with the range. */
-   discard_syms_in_range( seg->start, seg->end + 1 - seg->start );
+   if (is_rw_map) {
+      /* We have a data-like mapping.  Note the details. */
+      if (!di->have_rw_map) {
+         di->have_rw_map = True;
+         di->rw_map_avma = a;
+         di->rw_map_size = seg->end + 1 - seg->start;
+         di->rw_map_foff = seg->offset;
+      } else {
+         /* FIXME: complain about a second data-like mapping */
+      }
+   }
 
-   /* .. and acquire new info. */
-   acquire_syms_for_range( seg->start, seg->end + 1 - seg->start, 
-                           seg->offset, filename,
-                           /* XCOFF only */ NULL, 0, 0, False );
+   if (di->have_rx_map && di->have_rw_map && !di->have_dinfo) {
 
-   /* acquire_syms_for_range makes its own copy of filename, so is
-      safe to free it. */
-   VG_(arena_free)(VG_AR_SYMTAB, filename);
+      vg_assert(di->filename);
+      TRACE_SYMTAB("\n");
+      TRACE_SYMTAB("------ start ELF OBJECT "
+                   "------------------------------\n");
+      TRACE_SYMTAB("------ name = %s\n", di->filename);
+      TRACE_SYMTAB("\n");
+
+      /* We're going to read symbols and debug info for the avma
+         ranges [rx_map_avma, +rx_map_size) and [rw_map_avma,
+         +rw_map_size).  First get rid of any other DebugInfos which
+         overlap either of those ranges (to avoid total confusion). */
+      discard_DebugInfos_which_overlap_with( di );
+
+      /* .. and acquire new info. */
+      ok = ML_(read_elf_debug_info)( di );
+
+      if (ok) {
+         TRACE_SYMTAB("\n------ Canonicalising the "
+                      "acquired info ------\n");
+         /* prepare read data for use */
+         ML_(canonicaliseTables)( di );
+         /* notify m_redir about it */
+         TRACE_SYMTAB("\n------ Notifying m_redir ------\n");
+         VG_(redir_notify_new_DebugInfo)( di );
+         /* Note that we succeeded */
+         di->have_dinfo = True;
+      } else {
+         TRACE_SYMTAB("\n------ ELF reading failed ------\n");
+         /* Something went wrong (eg. bad ELF file).  Should we delete
+            this DebugInfo?  No - it contains info on the rw/rx
+            mappings, at least. */
+      }
+
+      TRACE_SYMTAB("\n");
+      TRACE_SYMTAB("------ name = %s\n", di->filename);
+      TRACE_SYMTAB("------ end ELF OBJECT "
+                   "------------------------------\n");
+      TRACE_SYMTAB("\n");
+
+   }
 }
 
 
@@ -387,6 +675,7 @@
    [a, a+len).  */
 void VG_(di_notify_munmap)( Addr a, SizeT len )
 {
+   if (0) VG_(printf)("DISCARD %p %p\n", a, a+len);
    discard_syms_in_range(a, len);
 }
 
@@ -431,38 +720,59 @@
                Bool   is_mainexe,
                Bool   acquire )
 {
-   SegInfo* si;
-
    if (acquire) {
 
-      acquire_syms_for_range(
-         /* ALL        */ code_start, 
-         /* ALL        */ code_len,
-         /* ELF only   */ 0,
-         /* ALL        */ file_name,
-         /* XCOFF only */ mem_name,
-         /* XCOFF only */ data_start,
-         /* XCOFF only */ data_len,
-         /* XCOFF only */ is_mainexe 
-      );
+      Bool       ok;
+      DebugInfo* di;
+      di = find_or_create_DebugInfo_for( file_name, mem_name );
+      vg_assert(di);
+
+      if (code_len > 0) {
+         di->text_present = True;
+         di->text_svma = 0; /* don't know yet */
+         di->text_bias = 0; /* don't know yet */
+         di->text_avma = code_start;
+         di->text_size = code_len;
+      }
+      if (data_len > 0) {
+         di->data_present = True;
+         di->data_svma = 0; /* don't know yet */
+         di->data_bias = 0; /* don't know yet */
+         di->data_avma = data_start;
+         di->data_size = data_len;
+      }
+
+      /* These need to be filled in in order to keep various
+         assertions in storage.c happy.  In particular see
+         "Comment_Regarding_Text_Range_Checks" in that file. */
+      di->have_rx_map = True;
+      di->rx_map_avma = code_start;
+      di->rx_map_size = code_len;
+      di->have_rw_map = True;
+      di->rw_map_avma = data_start;
+      di->rw_map_size = data_len;
+
+      ok = ML_(read_xcoff_debug_info) ( di, is_mainexe );
+
+      if (ok) {
+         /* prepare read data for use */
+         ML_(canonicaliseTables)( di );
+         /* notify m_redir about it */
+         VG_(redir_notify_new_DebugInfo)( di );
+         /* Note that we succeeded */
+         di->have_dinfo = True;
+      } else {
+         /*  Something went wrong (eg. bad XCOFF file). */
+         discard_DebugInfo( di );
+         di = NULL;
+      }
 
    } else {
 
-      /* Dump all the segInfos whose text segments intersect
+      /* Dump all the debugInfos whose text segments intersect
          code_start/code_len. */
-      while (True) {
-         for (si = segInfo_list; si; si = si->next) {
-            if (code_start + code_len <= si->text_start_avma
-                || si->text_start_avma + si->text_size <= code_start)
-               continue; /* no overlap */
-            else 
-               break;
-         }
-         if (si == NULL)
-            break;
-         /* Need to delete 'si' */
-         discard_SegInfo(si);
-      }
+      if (code_len > 0)
+         discard_syms_in_range( code_start, code_len );
 
    }
 }
@@ -483,52 +793,80 @@
 /*------------------------------------------------------------*/
 
 /* Search all symtabs that we know about to locate ptr.  If found, set
-   *psi to the relevant SegInfo, and *symno to the symtab entry number
-   within that.  If not found, *psi is set to NULL.  */
-static void search_all_symtabs ( Addr ptr, /*OUT*/SegInfo** psi, 
+   *pdi to the relevant DebugInfo, and *symno to the symtab entry
+   *number within that.  If not found, *psi is set to NULL.
+   If findText==True,  only text symbols are searched for.
+   If findText==False, only data symbols are searched for.
+*/
+static void search_all_symtabs ( Addr ptr, /*OUT*/DebugInfo** pdi,
                                            /*OUT*/Int* symno,
-                                 Bool match_anywhere_in_fun )
+                                 Bool match_anywhere_in_sym,
+                                 Bool findText )
 {
-   Int      sno;
-   SegInfo* si;
+   Int        sno;
+   DebugInfo* di;
+   Bool       inRange;
 
-   for (si = segInfo_list; si != NULL; si = si->next) {
-      if (si->text_start_avma <= ptr 
-          && ptr < si->text_start_avma + si->text_size) {
-         sno = ML_(search_one_symtab) ( si, ptr, match_anywhere_in_fun );
-         if (sno == -1) goto not_found;
-         *symno = sno;
-         *psi = si;
-         return;
+   for (di = debugInfo_list; di != NULL; di = di->next) {
+
+      if (findText) {
+         inRange = di->text_present
+                   && di->text_size > 0
+                   && di->text_avma <= ptr 
+                   && ptr < di->text_avma + di->text_size;
+      } else {
+         inRange = (di->data_present
+                    && di->data_size > 0
+                    && di->data_avma <= ptr 
+                    && ptr < di->data_avma + di->data_size)
+                   ||
+                   (di->sdata_present
+                    && di->sdata_size > 0
+                    && di->sdata_avma <= ptr 
+                    && ptr < di->sdata_avma + di->sdata_size)
+                   ||
+                   (di->bss_present
+                    && di->bss_size > 0
+                    && di->bss_avma <= ptr 
+                    && ptr < di->bss_avma + di->bss_size);
       }
+
+      if (!inRange) continue;
+
+      sno = ML_(search_one_symtab) ( 
+               di, ptr, match_anywhere_in_sym, findText );
+      if (sno == -1) goto not_found;
+      *symno = sno;
+      *pdi = di;
+      return;
+
    }
   not_found:
-   *psi = NULL;
+   *pdi = NULL;
 }
 
 
 /* Search all loctabs that we know about to locate ptr.  If found, set
-   *psi to the relevant SegInfo, and *locno to the loctab entry number
-   within that.  If not found, *psi is set to NULL.
-*/
-static void search_all_loctabs ( Addr ptr, /*OUT*/SegInfo** psi,
+   *pdi to the relevant DebugInfo, and *locno to the loctab entry
+   *number within that.  If not found, *pdi is set to NULL. */
+static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi,
                                            /*OUT*/Int* locno )
 {
-   Int      lno;
-   SegInfo* si;
-
-   for (si = segInfo_list; si != NULL; si = si->next) {
-      if (si->text_start_avma <= ptr 
-          && ptr < si->text_start_avma + si->text_size) {
-         lno = ML_(search_one_loctab) ( si, ptr );
+   Int        lno;
+   DebugInfo* di;
+   for (di = debugInfo_list; di != NULL; di = di->next) {
+      if (di->text_present
+          && di->text_avma <= ptr 
+          && ptr < di->text_avma + di->text_size) {
+         lno = ML_(search_one_loctab) ( di, ptr );
          if (lno == -1) goto not_found;
          *locno = lno;
-         *psi = si;
+         *pdi = di;
          return;
       }
    }
   not_found:
-   *psi = NULL;
+   *pdi = NULL;
 }
 
 
@@ -536,26 +874,31 @@
    plausible symbol name.  Returns False if no idea; otherwise True.
    Caller supplies buf and nbuf.  If demangle is False, don't do
    demangling, regardless of VG_(clo_demangle) -- probably because the
-   call has come from VG_(get_fnname_nodemangle)(). */
+   call has come from VG_(get_fnname_nodemangle)().  findText
+   indicates whether we're looking for a text symbol or a data symbol
+   -- caller must choose one kind or the other. */
 static
-Bool get_fnname ( Bool demangle, Addr a, Char* buf, Int nbuf,
-                  Bool match_anywhere_in_fun, Bool show_offset)
+Bool get_sym_name ( Bool demangle, Addr a, Char* buf, Int nbuf,
+                    Bool match_anywhere_in_sym, Bool show_offset,
+                    Bool findText, /*OUT*/OffT* offsetP )
 {
-   SegInfo* si;
-   Int      sno;
-   Int      offset;
+   DebugInfo* di;
+   Int        sno;
+   Int        offset;
 
-   search_all_symtabs ( a, &si, &sno, match_anywhere_in_fun );
-   if (si == NULL) 
+   search_all_symtabs ( a, &di, &sno, match_anywhere_in_sym, findText );
+   if (di == NULL) 
       return False;
    if (demangle) {
       VG_(demangle) ( True/*do C++ demangle*/,
-                      si->symtab[sno].name, buf, nbuf );
+                      di->symtab[sno].name, buf, nbuf );
    } else {
-      VG_(strncpy_safely) ( buf, si->symtab[sno].name, nbuf );
+      VG_(strncpy_safely) ( buf, di->symtab[sno].name, nbuf );
    }
 
-   offset = a - si->symtab[sno].addr;
+   offset = a - di->symtab[sno].addr;
+   if (offsetP) *offsetP = (OffT)offset;
+
    if (show_offset && offset != 0) {
       Char     buf2[12];
       Char*    symend = buf + VG_(strlen)(buf);
@@ -581,10 +924,12 @@
    guest_code_addr.  Returns 0 if not known. */
 Addr VG_(get_tocptr) ( Addr guest_code_addr )
 {
-   SegInfo* si;
-   Int      sno;
+   DebugInfo* si;
+   Int        sno;
    search_all_symtabs ( guest_code_addr, 
-                        &si, &sno, True/*match_anywhere_in_fun*/ );
+                        &si, &sno,
+                        True/*match_anywhere_in_fun*/,
+                        True/*consider text symbols only*/ );
    if (si == NULL) 
       return 0;
    else
@@ -595,18 +940,22 @@
    match anywhere in function, but don't show offsets. */
 Bool VG_(get_fnname) ( Addr a, Char* buf, Int nbuf )
 {
-   return get_fnname ( /*demangle*/True, a, buf, nbuf,
-                       /*match_anywhere_in_fun*/True, 
-                       /*show offset?*/False );
+   return get_sym_name ( /*demangle*/True, a, buf, nbuf,
+                         /*match_anywhere_in_fun*/True, 
+                         /*show offset?*/False,
+                         /*text syms only*/True,
+                         /*offsetP*/NULL );
 }
 
 /* This is available to tools... always demangle C++ names,
    match anywhere in function, and show offset if nonzero. */
 Bool VG_(get_fnname_w_offset) ( Addr a, Char* buf, Int nbuf )
 {
-   return get_fnname ( /*demangle*/True, a, buf, nbuf,
-                       /*match_anywhere_in_fun*/True, 
-                       /*show offset?*/True );
+   return get_sym_name ( /*demangle*/True, a, buf, nbuf,
+                         /*match_anywhere_in_fun*/True, 
+                         /*show offset?*/True,
+                         /*text syms only*/True,
+                         /*offsetP*/NULL );
 }
 
 /* This is available to tools... always demangle C++ names,
@@ -614,18 +963,22 @@
    and don't show offsets. */
 Bool VG_(get_fnname_if_entry) ( Addr a, Char* buf, Int nbuf )
 {
-   return get_fnname ( /*demangle*/True, a, buf, nbuf,
-                       /*match_anywhere_in_fun*/False, 
-                       /*show offset?*/False );
+   return get_sym_name ( /*demangle*/True, a, buf, nbuf,
+                         /*match_anywhere_in_fun*/False, 
+                         /*show offset?*/False,
+                         /*text syms only*/True,
+                         /*offsetP*/NULL );
 }
 
 /* This is only available to core... don't demangle C++ names,
    match anywhere in function, and don't show offsets. */
 Bool VG_(get_fnname_nodemangle) ( Addr a, Char* buf, Int nbuf )
 {
-   return get_fnname ( /*demangle*/False, a, buf, nbuf,
-                       /*match_anywhere_in_fun*/True, 
-                       /*show offset?*/False );
+   return get_sym_name ( /*demangle*/False, a, buf, nbuf,
+                         /*match_anywhere_in_fun*/True, 
+                         /*show offset?*/False,
+                         /*text syms only*/True,
+                         /*offsetP*/NULL );
 }
 
 /* This is only available to core... don't demangle C++ names, but do
@@ -637,9 +990,11 @@
    Char tmpbuf[N_TMPBUF];
    Bool ok;
    vg_assert(nbuf > 0);
-   ok = get_fnname ( /*demangle*/False, a, tmpbuf, N_TMPBUF,
-                     /*match_anywhere_in_fun*/True, 
-                     /*show offset?*/False );
+   ok = get_sym_name ( /*demangle*/False, a, tmpbuf, N_TMPBUF,
+                       /*match_anywhere_in_fun*/True, 
+                       /*show offset?*/False,
+                       /*text syms only*/True,
+                       /*offsetP*/NULL );
    tmpbuf[N_TMPBUF-1] = 0; /* paranoia */
    if (!ok) 
       return False;
@@ -652,28 +1007,49 @@
 #  undef N_TMPBUF
 }
 
-/* Map a code address to the name of a shared object file or the executable.
-   Returns False if no idea; otherwise True.  Doesn't require debug info.
-   Caller supplies buf and nbuf. */
+/* Looks up data_addr in the collection of data symbols, and if found
+   puts its name (or as much as will fit) into dname[0 .. n_dname-1],
+   which is guaranteed to be zero terminated.  Also data_addr's offset
+   from the symbol start is put into *offset. */
+Bool VG_(get_datasym_and_offset)( Addr data_addr,
+                                  /*OUT*/Char* dname, Int n_dname,
+                                  /*OUT*/OffT* offset )
+{
+   Bool ok;
+   vg_assert(n_dname > 1);
+   ok = get_sym_name ( /*demangle*/False, data_addr, dname, n_dname,
+                       /*match_anywhere_in_sym*/True, 
+                       /*show offset?*/False,
+                       /*data syms only please*/False,
+                       offset );
+   if (!ok)
+      return False;
+   dname[n_dname-1] = 0;
+   return True;
+}
+
+/* Map a code address to the name of a shared object file or the
+   executable.  Returns False if no idea; otherwise True.  Doesn't
+   require debug info.  Caller supplies buf and nbuf. */
 Bool VG_(get_objname) ( Addr a, Char* buf, Int nbuf )
 {
    Int used;
-   SegInfo* si;
+   DebugInfo* di;
    const NSegment *seg;
    HChar* filename;
-
    vg_assert(nbuf > 0);
-   for (si = segInfo_list; si != NULL; si = si->next) {
-      if (si->text_start_avma <= a 
-          && a < si->text_start_avma + si->text_size) {
-         VG_(strncpy_safely)(buf, si->filename, nbuf);
-         if (si->memname) {
+   for (di = debugInfo_list; di != NULL; di = di->next) {
+      if (di->text_present
+          && di->text_avma <= a 
+          && a < di->text_avma + di->text_size) {
+         VG_(strncpy_safely)(buf, di->filename, nbuf);
+         if (di->memname) {
             used = VG_(strlen)(buf);
             if (used < nbuf) 
                VG_(strncpy_safely)(&buf[used], "(", nbuf-used);
             used = VG_(strlen)(buf);
             if (used < nbuf) 
-               VG_(strncpy_safely)(&buf[used], si->memname, nbuf-used);
+               VG_(strncpy_safely)(&buf[used], di->memname, nbuf-used);
             used = VG_(strlen)(buf);
             if (used < nbuf) 
                VG_(strncpy_safely)(&buf[used], ")", nbuf-used);
@@ -691,16 +1067,16 @@
    return False;
 }
 
-/* Map a code address to its SegInfo.  Returns NULL if not found.  Doesn't
+/* Map a code address to its DebugInfo.  Returns NULL if not found.  Doesn't
    require debug info. */
-SegInfo* VG_(find_seginfo) ( Addr a )
+DebugInfo* VG_(find_seginfo) ( Addr a )
 {
-   SegInfo* si;
-
-   for (si = segInfo_list; si != NULL; si = si->next) {
-      if (si->text_start_avma <= a 
-          && a < si->text_start_avma + si->text_size) {
-         return si;
+   DebugInfo* di;
+   for (di = debugInfo_list; di != NULL; di = di->next) {
+      if (di->text_present
+          && di->text_avma <= a 
+          && a < di->text_avma + di->text_size) {
+         return di;
       }
    }
    return NULL;
@@ -709,7 +1085,7 @@
 /* Map a code address to a filename.  Returns True if successful.  */
 Bool VG_(get_filename)( Addr a, Char* filename, Int n_filename )
 {
-   SegInfo* si;
+   DebugInfo* si;
    Int      locno;
    search_all_loctabs ( a, &si, &locno );
    if (si == NULL) 
@@ -721,7 +1097,7 @@
 /* Map a code address to a line number.  Returns True if successful. */
 Bool VG_(get_linenum)( Addr a, UInt* lineno )
 {
-   SegInfo* si;
+   DebugInfo* si;
    Int      locno;
    search_all_loctabs ( a, &si, &locno );
    if (si == NULL) 
@@ -740,7 +1116,7 @@
                                  /*OUT*/Bool* dirname_available,
                                  /*OUT*/UInt* lineno )
 {
-   SegInfo* si;
+   DebugInfo* si;
    Int      locno;
 
    vg_assert( (dirname == NULL && dirname_available == NULL)
@@ -785,16 +1161,17 @@
    Therefore specify "*" to search all the objects.  On TOC-afflicted
    platforms, a symbol is deemed to be found only if it has a nonzero
    TOC pointer.  */
-Bool VG_(lookup_symbol_SLOW)(UChar* sopatt, UChar* name, Addr* pEnt, Addr* pToc)
+Bool VG_(lookup_symbol_SLOW)(UChar* sopatt, UChar* name, 
+                             Addr* pEnt, Addr* pToc)
 {
    Bool     require_pToc = False;
    Int      i;
-   SegInfo* si;
+   DebugInfo* si;
    Bool     debug = False;
 #  if defined(VG_PLAT_USES_PPCTOC)
    require_pToc = True;
 #  endif
-   for (si = segInfo_list; si; si = si->next) {
+   for (si = debugInfo_list; si; si = si->next) {
       if (debug)
          VG_(printf)("lookup_symbol_SLOW: considering %s\n", si->soname);
       if (!VG_(string_match)(sopatt, si->soname)) {
@@ -987,10 +1364,12 @@
 }
 
 
-/*------------------------------------------------------------*/
-/*--- For unwinding the stack using                       --- */
-/*--- pre-summarised DWARF3 .eh_frame info                 ---*/
-/*------------------------------------------------------------*/
+/*--------------------------------------------------------------*/
+/*---                                                        ---*/
+/*--- TOP LEVEL: FOR UNWINDING THE STACK USING               ---*/
+/*---            DWARF3 .eh_frame INFO                       ---*/
+/*---                                                        ---*/
+/*--------------------------------------------------------------*/
 
 /* Gather up all the constant pieces of info needed to evaluate
    a CfiExpr into one convenient struct. */
@@ -1067,7 +1446,7 @@
    previous frame, if possible. */
 /* Returns True if OK.  If not OK, *{ip,sp,fp}P are not changed. */
 /* NOTE: this function may rearrange the order of entries in the
-   SegInfo list. */
+   DebugInfo list. */
 Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
                         /*MOD*/Addr* spP,
                         /*MOD*/Addr* fpP,
@@ -1076,7 +1455,7 @@
 {
    Bool     ok;
    Int      i;
-   SegInfo* si;
+   DebugInfo* si;
    DiCfSI*  cfsi = NULL;
    Addr     cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev;
 
@@ -1088,14 +1467,14 @@
 
    if (0) VG_(printf)("search for %p\n", *ipP);
 
-   for (si = segInfo_list; si != NULL; si = si->next) {
+   for (si = debugInfo_list; si != NULL; si = si->next) {
       n_steps++;
 
-      /* Use the per-SegInfo summary address ranges to skip
-	 inapplicable SegInfos quickly. */
+      /* Use the per-DebugInfo summary address ranges to skip
+         inapplicable DebugInfos quickly. */
       if (si->cfsi_used == 0)
          continue;
-      if (*ipP < si->cfsi_minaddr || *ipP > si->cfsi_maxaddr)
+      if (*ipP < si->cfsi_minavma || *ipP > si->cfsi_maxavma)
          continue;
 
       i = ML_(search_one_cfitab)( si, *ipP );
@@ -1109,37 +1488,20 @@
    if (cfsi == NULL)
       return False;
 
-   if (0 && ((n_search & 0xFFFFF) == 0))
-      VG_(printf)("%u %u\n", n_search, n_steps);
+   if (0 && ((n_search & 0x7FFFF) == 0))
+      VG_(printf)("VG_(use_CF_info): %u searches, "
+                  "%u DebugInfos looked at\n", 
+                  n_search, n_steps);
 
-   /* Start of performance-enhancing hack: once every 16 (chosen
+   /* Start of performance-enhancing hack: once every 64 (chosen
       hackily after profiling) successful searches, move the found
-      SegInfo one step closer to the start of the list.  This makes
+      DebugInfo one step closer to the start of the list.  This makes
       future searches cheaper.  For starting konqueror on amd64, this
       in fact reduces the total amount of searching done by the above
-      find-the-right-SegInfo loop by more than a factor of 20. */
-   if ((n_search & 0xF) == 0) {
+      find-the-right-DebugInfo loop by more than a factor of 20. */
+   if ((n_search & 0x3F) == 0) {
       /* Move si one step closer to the start of the list. */
-      SegInfo* si0 = segInfo_list;
-      SegInfo* si1 = NULL;
-      SegInfo* si2 = NULL;
-      SegInfo* tmp;
-      while (True) {
-         if (si0 == NULL) break;
-         if (si0 == si) break;
-         si2 = si1;
-         si1 = si0;
-         si0 = si0->next;
-      }
-      if (si0 == si && si0 != NULL && si1 != NULL && si2 != NULL) {
-         /* si0 points to si, si1 to its predecessor, and si2 to si1's
-            predecessor.  Swap si0 and si1, that is, move si0 one step
-            closer to the start of the list. */
-         tmp = si0->next;
-         si2->next = si0;
-         si0->next = si1;
-         si1->next = tmp;
-      }
+      move_DebugInfo_one_step_forward( si );
    }
    /* End of performance-enhancing hack. */
 
@@ -1233,123 +1595,728 @@
 }
 
 
-/*------------------------------------------------------------*/
-/*--- SegInfo accessor functions                           ---*/
-/*------------------------------------------------------------*/
+/*--------------------------------------------------------------*/
+/*---                                                        ---*/
+/*--- TOP LEVEL: GENERATE DESCRIPTION OF DATA ADDRESSES      ---*/
+/*---            FROM DWARF3 DEBUG INFO                      ---*/
+/*---                                                        ---*/
+/*--------------------------------------------------------------*/
 
-const SegInfo* VG_(next_seginfo)(const SegInfo* si)
+/* Evaluate the location expression/list for var, to see whether or
+   not data_addr falls within the variable.  If so also return the
+   offset of data_addr from the start of the variable.  Note that
+   regs, which supplies ip,sp,fp values, will be NULL for global
+   variables, and non-NULL for local variables. */
+static Bool data_address_is_in_var ( /*OUT*/UWord* offset,
+                                     DiVariable*   var,
+                                     RegSummary*   regs,
+                                     Addr          data_addr,
+                                     Addr          data_bias )
 {
-   if (si == NULL)
-      return segInfo_list;
-   return si->next;
+   MaybeUWord muw;
+   SizeT      var_szB;
+   GXResult   res;
+   Bool       show = False;
+   vg_assert(var->name);
+   vg_assert(var->type);
+   vg_assert(var->gexpr);
+
+   /* Figure out how big the variable is. */
+   muw = ML_(sizeOfType)(var->type);
+   /* if this var has a type whose size is unknown, it should never
+      have been added.  ML_(addVar) should have rejected it. */
+   vg_assert(muw.b == True);
+
+   var_szB = muw.w;
+
+   if (show) {
+      VG_(printf)("VVVV: data_address_%p_is_in_var: %s :: ",
+                  data_addr, var->name );
+      ML_(pp_Type_C_ishly)( var->type );
+      VG_(printf)("\n");
+   }
+
+   /* ignore zero-sized vars; they can never match anything. */
+   if (var_szB == 0) {
+      if (show)
+         VG_(printf)("VVVV: -> Fail (variable is zero sized)\n");
+      return False;
+   }
+
+   res = ML_(evaluate_GX)( var->gexpr, var->fbGX, regs, data_bias );
+
+   if (show) {
+      VG_(printf)("VVVV: -> ");
+      ML_(pp_GXResult)( res );
+      VG_(printf)("\n");
+   }
+
+   if (res.kind == GXR_Value 
+       && res.word <= data_addr
+       && data_addr < res.word + var_szB) {
+      *offset = data_addr - res.word;
+      return True;
+   } else {
+      return False;
+   }
 }
 
-Addr VG_(seginfo_start)(const SegInfo* si)
+
+/* Format the acquired information into dname1[0 .. n_dname-1] and
+   dname2[0 .. n_dname-1] in an understandable way.  Not so easy.
+   If frameNo is -1, this is assumed to be a global variable; else
+   a local variable. */
+static void format_message ( /*OUT*/Char* dname1,
+                             /*OUT*/Char* dname2,
+                             Int      n_dname,
+                             Addr     data_addr,
+                             DiVariable* var,
+                             OffT     var_offset,
+                             OffT     residual_offset,
+                             XArray* /*UChar*/ described,
+                             Int      frameNo, 
+                             ThreadId tid )
 {
-   return si->text_start_avma;
+   Bool have_descr, have_srcloc;
+   UChar* vo_plural = var_offset == 1 ? "" : "s";
+   UChar* ro_plural = residual_offset == 1 ? "" : "s";
+
+   vg_assert(frameNo >= -1);
+   vg_assert(dname1 && dname2 && n_dname > 1);
+   vg_assert(described);
+   vg_assert(var && var->name);
+   have_descr = VG_(sizeXA)(described) > 0
+                && *(UChar*)VG_(indexXA)(described,0) != '\0';
+   have_srcloc = var->fileName && var->lineNo > 0;
+
+   dname1[0] = dname2[0] = '\0';
+
+   /* ------ local cases ------ */
+
+   if ( frameNo >= 0 && (!have_srcloc) && (!have_descr) ) {
+      /* no srcloc, no description:
+         Location 0x7fefff6cf is 543 bytes inside local var "a",
+         in frame #1 of thread 1
+      */
+      VG_(snprintf)(
+         dname1, n_dname,
+         "Location 0x%lx is %lu byte%s inside local var \"%s\",",
+         data_addr, var_offset, vo_plural, var->name );
+      VG_(snprintf)(
+         dname2, n_dname,
+         "in frame #%d of thread %d", frameNo, (Int)tid);
+   } 
+   else
+   if ( frameNo >= 0 && have_srcloc && (!have_descr) ) {
+      /* no description:
+         Location 0x7fefff6cf is 543 bytes inside local var "a"
+         declared at dsyms7.c:17, in frame #1 of thread 1
+      */
+      VG_(snprintf)(
+         dname1, n_dname,
+         "Location 0x%lx is %lu byte%s inside local var \"%s\"",
+         data_addr, var_offset, vo_plural, var->name );
+      VG_(snprintf)(
+         dname2, n_dname,
+         "declared at %s:%d, in frame #%d of thread %d",
+         var->fileName, var->lineNo, frameNo, (Int)tid);
+   }
+   else
+   if ( frameNo >= 0 && (!have_srcloc) && have_descr ) {
+      /* no srcloc:
+         Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2
+         in frame #1 of thread 1
+      */
+      VG_(snprintf)(
+         dname1, n_dname,
+         "Location 0x%lx is %lu byte%s inside %s%s",
+         data_addr, residual_offset, ro_plural, var->name,
+         VG_(indexXA)(described,0) );
+      VG_(snprintf)(
+         dname2, n_dname,
+         "in frame #%d of thread %d", frameNo, (Int)tid);
+   } 
+   else
+   if ( frameNo >= 0 && have_srcloc && have_descr ) {
+     /* Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2,
+        declared at dsyms7.c:17, in frame #1 of thread 1 */
+      VG_(snprintf)(
+         dname1, n_dname,
+         "Location 0x%lx is %lu byte%s inside %s%s,",
+         data_addr, residual_offset, ro_plural, var->name,
+         VG_(indexXA)(described,0) );
+      VG_(snprintf)(
+         dname2, n_dname,
+         "declared at %s:%d, in frame #%d of thread %d",
+         var->fileName, var->lineNo, frameNo, (Int)tid);
+   }
+   else
+   /* ------ global cases ------ */
+   if ( frameNo >= -1 && (!have_srcloc) && (!have_descr) ) {
+      /* no srcloc, no description:
+         Location 0x7fefff6cf is 543 bytes inside global var "a"
+      */
+      VG_(snprintf)(
+         dname1, n_dname,
+         "Location 0x%lx is %lu byte%s inside global var \"%s\"",
+         data_addr, var_offset, vo_plural, var->name );
+   } 
+   else
+   if ( frameNo >= -1 && have_srcloc && (!have_descr) ) {
+      /* no description:
+         Location 0x7fefff6cf is 543 bytes inside global var "a"
+         declared at dsyms7.c:17
+      */
+      VG_(snprintf)(
+         dname1, n_dname,
+         "Location 0x%lx is %lu byte%s inside global var \"%s\"",
+         data_addr, var_offset, vo_plural, var->name );
+      VG_(snprintf)(
+         dname2, n_dname,
+         "declared at %s:%d",
+         var->fileName, var->lineNo);
+   }
+   else
+   if ( frameNo >= -1 && (!have_srcloc) && have_descr ) {
+      /* no srcloc:
+         Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2,
+         a global variable
+      */
+      VG_(snprintf)(
+         dname1, n_dname,
+         "Location 0x%lx is %lu byte%s inside %s%s,",
+         data_addr, residual_offset, ro_plural, var->name,
+         VG_(indexXA)(described,0) );
+      VG_(snprintf)(
+         dname2, n_dname,
+         "a global variable");
+   } 
+   else
+   if ( frameNo >= -1 && have_srcloc && have_descr ) {
+     /* Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2,
+        a global variable declared at dsyms7.c:17 */
+      VG_(snprintf)(
+         dname1, n_dname,
+         "Location 0x%lx is %lu byte%s inside %s%s,",
+         data_addr, residual_offset, ro_plural, var->name,
+         VG_(indexXA)(described,0) );
+      VG_(snprintf)(
+         dname2, n_dname,
+         "a global variable declared at %s:%d",
+         var->fileName, var->lineNo);
+   }
+   else 
+      vg_assert(0);
+
+   dname1[n_dname-1] = dname2[n_dname-1] = 0;
 }
 
-SizeT VG_(seginfo_size)(const SegInfo* si)
+
+/* Determine if data_addr is a local variable in the frame
+   characterised by (ip,sp,fp), and if so write its description into
+   dname{1,2}[0..n_dname-1], and return True.  If not, return
+   False. */
+static 
+Bool consider_vars_in_frame ( /*OUT*/Char* dname1,
+                              /*OUT*/Char* dname2,
+                              Int n_dname,
+                              Addr data_addr,
+                              Addr ip, Addr sp, Addr fp,
+                              /* shown to user: */
+                              ThreadId tid, Int frameNo )
 {
-   return si->text_size;
-}
+   Word       i;
+   DebugInfo* di;
+   RegSummary regs;
+   Bool debug = False;
 
-const UChar* VG_(seginfo_soname)(const SegInfo* si)
-{
-   return si->soname;
-}
+   static UInt n_search = 0;
+   static UInt n_steps = 0;
+   n_search++;
+   if (debug)
+      VG_(printf)("QQQQ: cvif: ip,sp,fp %p,%p,%p\n", ip,sp,fp);
+   /* first, find the DebugInfo that pertains to 'ip'. */
+   for (di = debugInfo_list; di; di = di->next) {
+      n_steps++;
+      /* text segment missing? unlikely, but handle it .. */
+      if (!di->text_present || di->text_size == 0)
+         continue;
+      /* Ok.  So does this text mapping bracket the ip? */
+      if (di->text_avma <= ip && ip < di->text_avma + di->text_size)
+         break;
+   }
+ 
+   /* Didn't find it.  Strange -- means ip is a code address outside
+      of any mapped text segment.  Unlikely but not impossible -- app
+      could be generating code to run. */
+   if (!di)
+      return False;
 
-const UChar* VG_(seginfo_filename)(const SegInfo* si)
-{
-   return si->filename;
-}
+   if (0 && ((n_search & 0x1) == 0))
+      VG_(printf)("consider_vars_in_frame: %u searches, "
+                  "%u DebugInfos looked at\n", 
+                  n_search, n_steps);
+   /* Start of performance-enhancing hack: once every ??? (chosen
+      hackily after profiling) successful searches, move the found
+      DebugInfo one step closer to the start of the list.  This makes
+      future searches cheaper. */
+   if ((n_search & 0xFFFF) == 0) {
+      /* Move si one step closer to the start of the list. */
+      move_DebugInfo_one_step_forward( di );
+   }
+   /* End of performance-enhancing hack. */
 
-ULong VG_(seginfo_sym_offset)(const SegInfo* si)
-{
-   return si->text_bias;
-}
+   /* any var info at all? */
+   if (!di->varinfo)
+      return False;
 
-VgSectKind VG_(seginfo_sect_kind)(Addr a)
-{
-   SegInfo* si;
-   VgSectKind ret = Vg_SectUnknown;
+   /* Work through the scopes from most deeply nested outwards,
+      looking for code address ranges that bracket 'ip'.  The
+      variables on each such address range found are in scope right
+      now.  Don't descend to level zero as that is the global
+      scope. */
+   regs.ip = ip;
+   regs.sp = sp;
+   regs.fp = fp;
 
-   for (si = segInfo_list; si != NULL; si = si->next) {
-      if (a >= si->text_start_avma 
-          && a < si->text_start_avma + si->text_size) {
-
-	 if (0)
-	    VG_(printf)(
-               "addr=%p si=%p %s got=%p %d  plt=%p %d data=%p %d bss=%p %d\n",
-               a, si, si->filename, 
-               si->got_start_avma,  si->got_size,
-               si->plt_start_avma,  si->plt_size,
-               si->data_start_avma, si->data_size,
-               si->bss_start_avma,  si->bss_size);
-
-	 ret = Vg_SectText;
-
-	 if (a >= si->data_start_avma && a < si->data_start_avma + si->data_size)
-	    ret = Vg_SectData;
-	 else 
-         if (a >= si->bss_start_avma && a < si->bss_start_avma + si->bss_size)
-	    ret = Vg_SectBSS;
-	 else 
-         if (a >= si->plt_start_avma && a < si->plt_start_avma + si->plt_size)
-	    ret = Vg_SectPLT;
-	 else 
-         if (a >= si->got_start_avma && a < si->got_start_avma + si->got_size)
-	    ret = Vg_SectGOT;
+   /* "for each scope, working outwards ..." */
+   for (i = VG_(sizeXA)(di->varinfo) - 1; i >= 1; i--) {
+      XArray*      vars;
+      Word         j;
+      DiAddrRange* arange;
+      OSet*        this_scope 
+         = *(OSet**)VG_(indexXA)( di->varinfo, i );
+      if (debug)
+         VG_(printf)("QQQQ:   considering scope %ld\n", (Word)i);
+      if (!this_scope)
+         continue;
+      /* Find the set of variables in this scope that
+         bracket the program counter. */
+      arange = VG_(OSetGen_LookupWithCmp)(
+                  this_scope, &ip, 
+                  ML_(cmp_for_DiAddrRange_range)
+               );
+      if (!arange)
+         continue;
+      /* stay sane */
+      vg_assert(arange->aMin <= arange->aMax);
+      /* It must bracket the ip we asked for, else
+         ML_(cmp_for_DiAddrRange_range) is somehow broken. */
+      vg_assert(arange->aMin <= ip && ip <= arange->aMax);
+      /* It must have an attached XArray of DiVariables. */
+      vars = arange->vars;
+      vg_assert(vars);
+      /* But it mustn't cover the entire address range.  We only
+         expect that to happen for the global scope (level 0), which
+         we're not looking at here.  Except, it may cover the entire
+         address range, but in that case the vars array must be
+         empty. */
+      vg_assert(! (arange->aMin == (Addr)0
+                   && arange->aMax == ~(Addr)0
+                   && VG_(sizeXA)(vars) > 0) );
+      for (j = 0; j < VG_(sizeXA)( vars ); j++) {
+         DiVariable* var = (DiVariable*)VG_(indexXA)( vars, j );
+         SizeT       offset;
+         if (debug)
+            VG_(printf)("QQQQ:    var:name=%s %p-%p %p\n", 
+                        var->name,arange->aMin,arange->aMax,ip);
+         if (data_address_is_in_var( &offset, var, &regs, data_addr,
+                                     di->data_bias )) {
+            OffT residual_offset = 0;
+            XArray* described = ML_(describe_type)( &residual_offset,
+                                                    var->type, offset );
+            format_message( dname1, dname2, n_dname, 
+                            data_addr, var, offset, residual_offset,
+                            described, frameNo, tid );
+            VG_(deleteXA)( described );
+            return True;
+         }
       }
    }
 
-   return ret;
+   return False;
 }
 
-Char* VG_(seginfo_sect_kind_name)(Addr a, Char* buf, UInt n_buf)
+/* Try to form some description of data_addr by looking at the DWARF3
+   debug info we have.  This considers all global variables, and all
+   frames in the stacks of all threads.  Result (or as much as will
+   fit) is put into into dname{1,2}[0 .. n_dname-1] and is guaranteed
+   to be zero terminated. */
+Bool VG_(get_data_description)( /*OUT*/Char* dname1,
+                                /*OUT*/Char* dname2,
+                                Int  n_dname,
+                                Addr data_addr )
 {
-   switch (VG_(seginfo_sect_kind)(a)) {
-      case Vg_SectUnknown:
-         VG_(snprintf)(buf, n_buf, "Unknown");
-         break;
-      case Vg_SectText:
-         VG_(snprintf)(buf, n_buf, "Text");
-         break;
-      case Vg_SectData:
-         VG_(snprintf)(buf, n_buf, "Data");
-         break;
-      case Vg_SectBSS:
-         VG_(snprintf)(buf, n_buf, "BSS");
-         break;
-      case Vg_SectGOT:
-         VG_(snprintf)(buf, n_buf, "GOT");
-         break;
-      case Vg_SectPLT:
-         VG_(snprintf)(buf, n_buf, "PLT");
-         break;
-      default:
-         vg_assert(0);
+#  define N_FRAMES 8
+   Addr ips[N_FRAMES], sps[N_FRAMES], fps[N_FRAMES];
+   UInt n_frames;
+
+   Addr       stack_min, stack_max;
+   ThreadId   tid;
+   Bool       found;
+   DebugInfo* di;
+   Word       j;
+
+   vg_assert(n_dname > 1);
+   dname1[n_dname-1] = dname2[n_dname-1] = 0;
+
+   if (0) VG_(printf)("get_data_description: dataaddr %p\n", data_addr);
+   /* First, see if data_addr is (or is part of) a global variable.
+      Loop over the DebugInfos we have.  Check data_addr against the
+      outermost scope of all of them, as that should be a global
+      scope. */
+   for (di = debugInfo_list; di != NULL; di = di->next) {
+      OSet*        global_scope;
+      Int          gs_size;
+      Addr         zero;
+      DiAddrRange* global_arange;
+      Word         i;
+      XArray*      vars;
+
+      /* text segment missing? unlikely, but handle it .. */
+      if (!di->text_present || di->text_size == 0)
+         continue;
+      /* any var info at all? */
+      if (!di->varinfo)
+         continue;
+      /* perhaps this object didn't contribute any vars at all? */
+      if (VG_(sizeXA)( di->varinfo ) == 0)
+         continue;
+      global_scope = *(OSet**)VG_(indexXA)( di->varinfo, 0 );
+      vg_assert(global_scope);
+      gs_size = VG_(OSetGen_Size)( global_scope );
+      /* The global scope might be completely empty if this
+         compilation unit declared locals but nothing global. */
+      if (gs_size == 0)
+          continue;
+      /* But if it isn't empty, then it must contain exactly one
+         element, which covers the entire address range. */
+      vg_assert(gs_size == 1);
+      /* Fish out the global scope and check it is as expected. */
+      zero = 0;
+      global_arange 
+         = VG_(OSetGen_Lookup)( global_scope, &zero );
+      /* The global range from (Addr)0 to ~(Addr)0 must exist */
+      vg_assert(global_arange);
+      vg_assert(global_arange->aMin == (Addr)0
+                && global_arange->aMax == ~(Addr)0);
+      /* Any vars in this range? */
+      if (!global_arange->vars)
+         continue;
+      /* Ok, there are some vars in the global scope of this
+         DebugInfo.  Wade through them and see if the data addresses
+         of any of them bracket data_addr. */
+      vars = global_arange->vars;
+      for (i = 0; i < VG_(sizeXA)( vars ); i++) {
+         SizeT offset;
+         DiVariable* var = (DiVariable*)VG_(indexXA)( vars, i );
+         vg_assert(var->name);
+         /* Note we use a NULL RegSummary* here.  It can't make any
+            sense for a global variable to have a location expression
+            which depends on a SP/FP/IP value.  So don't supply any.
+            This means, if the evaluation of the location
+            expression/list requires a register, we have to let it
+            fail. */
+         if (data_address_is_in_var( &offset, var, 
+                                     NULL/* RegSummary* */, 
+                                     data_addr, di->data_bias )) {
+            OffT residual_offset = 0;
+            XArray* described = ML_(describe_type)( &residual_offset,
+                                                    var->type, offset );
+            format_message( dname1, dname2, n_dname, 
+                            data_addr, var, offset, residual_offset,
+                            described, -1/*frameNo*/, tid );
+            VG_(deleteXA)( described );
+            dname1[n_dname-1] = dname2[n_dname-1] = 0;
+            return True;
+         }
+      }
    }
-   return buf;
+
+   /* Ok, well it's not a global variable.  So now let's snoop around
+      in the stacks of all the threads.  First try to figure out which
+      thread's stack data_addr is in. */
+
+   /* --- KLUDGE --- Try examining the top frame of all thread stacks.
+      This finds variables which are not stack allocated but are not
+      globally visible either; specifically it appears to pick up
+      variables which are visible only within a compilation unit.
+      These will have the address range of the compilation unit and
+      tend to live at Scope level 1. */
+   VG_(thread_stack_reset_iter)(&tid);
+   while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) {
+      if (stack_min >= stack_max)
+         continue; /* ignore obviously stupid cases */
+      if (consider_vars_in_frame( dname1, dname2, n_dname,
+                                  data_addr,
+                                  VG_(get_IP)(tid),
+                                  VG_(get_SP)(tid), 
+                                  VG_(get_FP)(tid), tid, 0 )) {
+         dname1[n_dname-1] = dname2[n_dname-1] = 0;
+         return True;
+      }
+   }
+   /* --- end KLUDGE --- */
+
+   /* Perhaps it's on a thread's stack? */
+   found = False;
+   VG_(thread_stack_reset_iter)(&tid);
+   while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) {
+      if (stack_min >= stack_max)
+         continue; /* ignore obviously stupid cases */
+      if (stack_min - VG_STACK_REDZONE_SZB <= data_addr
+          && data_addr <= stack_max) {
+         found = True;
+         break;
+      }
+   }
+   if (!found) {
+      dname1[n_dname-1] = dname2[n_dname-1] = 0;
+      return False;
+   }
+
+   /* We conclude data_addr is in thread tid's stack.  Unwind the
+      stack to get a bunch of (ip,sp,fp) triples describing the
+      frames, and for each frame, consider the local variables. */
+   n_frames = VG_(get_StackTrace)( tid, ips, N_FRAMES,
+                                   sps, fps, 0/*first_ip_delta*/ );
+   /* Re ip_delta in the next loop: There's a subtlety in the meaning
+      of the IP values in a stack obtained from VG_(get_StackTrace).
+      The innermost value really is simply the thread's program
+      counter at the time the snapshot was taken.  However, all the
+      other values are actually return addresses, and so point just
+      after the call instructions.  Hence they notionally reflect not
+      what the program counters were at the time those calls were
+      made, but what they will be when those calls return.  This can
+      be of significance should an address range happen to end at the
+      end of a call instruction -- we may ignore the range when in
+      fact it should be considered.  Hence, back up the IPs by 1 for
+      all non-innermost IPs.  Note that VG_(get_StackTrace_wrk) itself
+      has to use the same trick in order to use CFI data to unwind the
+      stack (as documented therein in comments). */
+   /* As a result of KLUDGE above, starting the loop at j = 0
+      duplicates examination of the top frame and so isn't necessary.
+      Oh well. */
+   vg_assert(n_frames >= 0 && n_frames <= N_FRAMES);
+   for (j = 0; j < n_frames; j++) {
+      Word ip_delta = j == 0 ? 0 : 1;
+      if (consider_vars_in_frame( dname1, dname2, n_dname,
+                                  data_addr,
+                                  ips[j] - ip_delta, 
+                                  sps[j], fps[j], tid, j )) {
+         dname1[n_dname-1] = dname2[n_dname-1] = 0;
+         return True;
+      }
+      /* Now, it appears that gcc sometimes appears to produce
+         location lists whose ranges don't actually cover the call
+         instruction, even though the address of the variable in
+         question is passed as a parameter in the call.  AFAICS this
+         is simply a bug in gcc - how can the variable be claimed not
+         exist in memory (on the stack) for the duration of a call in
+         which its address is passed?  But anyway, in the particular
+         case I investigated (memcheck/tests/varinfo6.c, call to croak
+         on line 2999, local var budget declared at line 3115
+         appearing not to exist across the call to mainSort on line
+         3143, "gcc.orig (GCC) 3.4.4 20050721 (Red Hat 3.4.4-2)" on
+         amd64), the variable's location list does claim it exists
+         starting at the first byte of the first instruction after the
+         call instruction.  So, call consider_vars_in_frame a second
+         time, but this time don't subtract 1 from the IP.  GDB
+         handles this example with no difficulty, which leads me to
+         believe that either (1) I misunderstood something, or (2) GDB
+         has an equivalent kludge. */
+      if (consider_vars_in_frame( dname1, dname2, n_dname,
+                                  data_addr,
+                                  ips[j], 
+                                  sps[j], fps[j], tid, j )) {
+         dname1[n_dname-1] = dname2[n_dname-1] = 0;
+         return True;
+      }
+   }
+
+   /* We didn't find anything useful. */
+   dname1[n_dname-1] = dname2[n_dname-1] = 0;
+   return False;
+#  undef N_FRAMES
 }
 
-Int VG_(seginfo_syms_howmany) ( const SegInfo *si )
+
+/*------------------------------------------------------------*/
+/*--- DebugInfo accessor functions                         ---*/
+/*------------------------------------------------------------*/
+
+const DebugInfo* VG_(next_seginfo)(const DebugInfo* di)
+{
+   if (di == NULL)
+      return debugInfo_list;
+   return di->next;
+}
+
+Addr VG_(seginfo_get_text_avma)(const DebugInfo* di)
+{
+   return di->text_present ? di->text_avma : 0; 
+}
+
+SizeT VG_(seginfo_get_text_size)(const DebugInfo* di)
+{
+   return di->text_present ? di->text_size : 0; 
+}
+
+const UChar* VG_(seginfo_soname)(const DebugInfo* di)
+{
+   return di->soname;
+}
+
+const UChar* VG_(seginfo_filename)(const DebugInfo* di)
+{
+   return di->filename;
+}
+
+ULong VG_(seginfo_get_text_bias)(const DebugInfo* di)
+{
+   return di->text_present ? di->text_bias : 0;
+}
+
+Int VG_(seginfo_syms_howmany) ( const DebugInfo *si )
 {
    return si->symtab_used;
 }
 
-void VG_(seginfo_syms_getidx) ( const SegInfo *si, 
+void VG_(seginfo_syms_getidx) ( const DebugInfo *si, 
                                       Int idx,
-                               /*OUT*/Addr*   addr,
+                               /*OUT*/Addr*   avma,
                                /*OUT*/Addr*   tocptr,
                                /*OUT*/UInt*   size,
-                               /*OUT*/HChar** name )
+                               /*OUT*/HChar** name,
+                               /*OUT*/Bool*   isText )
 {
    vg_assert(idx >= 0 && idx < si->symtab_used);
-   if (addr)   *addr   = si->symtab[idx].addr;
+   if (avma)   *avma   = si->symtab[idx].addr;
    if (tocptr) *tocptr = si->symtab[idx].tocptr;
    if (size)   *size   = si->symtab[idx].size;
    if (name)   *name   = (HChar*)si->symtab[idx].name;
+   if (isText) *isText = si->symtab[idx].isText;
+}
+
+
+/*------------------------------------------------------------*/
+/*--- SectKind query functions                             ---*/
+/*------------------------------------------------------------*/
+
+/* Convert a VgSectKind to a string, which must be copied if you want
+   to change it. */
+const HChar* VG_(pp_SectKind)( VgSectKind kind )
+{
+   switch (kind) {
+      case Vg_SectUnknown: return "Unknown";
+      case Vg_SectText:    return "Text";
+      case Vg_SectData:    return "Data";
+      case Vg_SectBSS:     return "BSS";
+      case Vg_SectGOT:     return "GOT";
+      case Vg_SectPLT:     return "PLT";
+      case Vg_SectOPD:     return "OPD";
+      default:             vg_assert(0);
+   }
+}
+
+/* Given an address 'a', make a guess of which section of which object
+   it comes from.  If name is non-NULL, then the last n_name-1
+   characters of the object's name is put in name[0 .. n_name-2], and
+   name[n_name-1] is set to zero (guaranteed zero terminated). */
+
+VgSectKind VG_(seginfo_sect_kind)( /*OUT*/UChar* name, SizeT n_name, 
+                                   Addr a)
+{
+   DebugInfo* di;
+   VgSectKind res = Vg_SectUnknown;
+
+   for (di = debugInfo_list; di != NULL; di = di->next) {
+
+      if (0)
+         VG_(printf)(
+            "addr=%p di=%p %s got=%p,%ld plt=%p,%ld data=%p,%ld bss=%p,%ld\n",
+            a, di, di->filename,
+            di->got_avma,  di->got_size,
+            di->plt_avma,  di->plt_size,
+            di->data_avma, di->data_size,
+            di->bss_avma,  di->bss_size);
+
+      if (di->text_present
+          && di->text_size > 0
+          && a >= di->text_avma && a < di->text_avma + di->text_size) {
+         res = Vg_SectText;
+         break;
+      }
+      if (di->data_present
+          && di->data_size > 0
+          && a >= di->data_avma && a < di->data_avma + di->data_size) {
+         res = Vg_SectData;
+         break;
+      }
+      if (di->sdata_present
+          && di->sdata_size > 0
+          && a >= di->sdata_avma && a < di->sdata_avma + di->sdata_size) {
+         res = Vg_SectData;
+         break;
+      }
+      if (di->bss_present
+          && di->bss_size > 0
+          && a >= di->bss_avma && a < di->bss_avma + di->bss_size) {
+         res = Vg_SectBSS;
+         break;
+      }
+      if (di->plt_present
+          && di->plt_size > 0
+          && a >= di->plt_avma && a < di->plt_avma + di->plt_size) {
+         res = Vg_SectPLT;
+         break;
+      }
+      if (di->got_present
+          && di->got_size > 0
+          && a >= di->got_avma && a < di->got_avma + di->got_size) {
+         res = Vg_SectGOT;
+         break;
+      }
+      if (di->opd_present
+          && di->opd_size > 0
+          && a >= di->opd_avma && a < di->opd_avma + di->opd_size) {
+         res = Vg_SectOPD;
+         break;
+      }
+      /* we could also check for .eh_frame, if anyone really cares */
+   }
+
+   vg_assert( (di == NULL && res == Vg_SectUnknown)
+              || (di != NULL && res != Vg_SectUnknown) );
+
+   if (name) {
+
+      vg_assert(n_name >= 8);
+
+      if (di && di->filename) {
+         Int i, j;
+         Int fnlen = VG_(strlen)(di->filename);
+         Int start_at = 1 + fnlen - n_name;
+         if (start_at < 0) start_at = 0;
+         vg_assert(start_at < fnlen);
+         i = start_at; j = 0;
+         while (True) {
+            vg_assert(j >= 0 && j+1 < n_name);
+            vg_assert(i >= 0 && i <= fnlen);
+            name[j] = di->filename[i];
+            name[j+1] = 0;
+            if (di->filename[i] == 0) break;
+            i++; j++;
+         }
+      } else {
+         VG_(snprintf)(name, n_name, "%s", "???");
+      }
+
+      name[n_name-1] = 0;
+   }
+
+   return res;
+
 }
 
 
diff --git a/coregrind/m_debuginfo/misc.c b/coregrind/m_debuginfo/misc.c
new file mode 100644
index 0000000..cd31ae9
--- /dev/null
+++ b/coregrind/m_debuginfo/misc.c
@@ -0,0 +1,65 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Misc simple stuff lacking a better home.              misc.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 OpenWorks LLP
+      info@open-works.co.uk
+
+   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., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+
+   Neither the names of the U.S. Department of Energy nor the
+   University of California nor the names of its contributors may be
+   used to endorse or promote products derived from this software
+   without prior written permission.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_xarray.h"
+
+#include "priv_misc.h"            /* self */
+
+
+void* ML_(dinfo_zalloc) ( SizeT szB ) {
+   void* v;
+   vg_assert(szB > 0);
+   v = VG_(arena_malloc)( VG_AR_DINFO, szB );
+   vg_assert(v);
+   VG_(memset)(v, 0, szB);
+   return v;
+}
+
+void ML_(dinfo_free) ( void* v ) {
+   VG_(arena_free)( VG_AR_DINFO, v );
+}
+
+UChar* ML_(dinfo_strdup) ( const UChar* str ) {
+   return VG_(arena_strdup)( VG_AR_DINFO, str );
+}
+
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                   misc.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/priv_d3basics.h b/coregrind/m_debuginfo/priv_d3basics.h
new file mode 100644
index 0000000..a445e09
--- /dev/null
+++ b/coregrind/m_debuginfo/priv_d3basics.h
@@ -0,0 +1,643 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Basic definitions and helper functions for DWARF3.           ---*/
+/*---                                              priv_d3basics.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 OpenWorks LLP and others; see below
+      info@open-works.co.uk
+
+   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., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+
+   -------------
+
+   Some of this code (DWARF3 enumerations) is taken from FSF's
+   gdb-6.6/include/elf/dwarf2.h, which is Copyright (C) 1992 to 2006
+   Free Software Foundation, Inc and is also GPL-2-or-later.
+*/
+
+#ifndef __PRIV_D3BASICS_H
+#define __PRIV_D3BASICS_H
+
+
+/* This stuff is taken from gdb-6.6/include/elf/dwarf2.h, which is
+   GPL2+.
+*/
+/* Tag names and codes.  */
+typedef enum 
+  {
+    DW_TAG_padding = 0x00,
+    DW_TAG_array_type = 0x01,
+    DW_TAG_class_type = 0x02,
+    DW_TAG_entry_point = 0x03,
+    DW_TAG_enumeration_type = 0x04,
+    DW_TAG_formal_parameter = 0x05,
+    DW_TAG_imported_declaration = 0x08,
+    DW_TAG_label = 0x0a,
+    DW_TAG_lexical_block = 0x0b,
+    DW_TAG_member = 0x0d,
+    DW_TAG_pointer_type = 0x0f,
+    DW_TAG_reference_type = 0x10,
+    DW_TAG_compile_unit = 0x11,
+    DW_TAG_string_type = 0x12,
+    DW_TAG_structure_type = 0x13,
+    DW_TAG_subroutine_type = 0x15,
+    DW_TAG_typedef = 0x16,
+    DW_TAG_union_type = 0x17,
+    DW_TAG_unspecified_parameters = 0x18,
+    DW_TAG_variant = 0x19,
+    DW_TAG_common_block = 0x1a,
+    DW_TAG_common_inclusion = 0x1b,
+    DW_TAG_inheritance = 0x1c,
+    DW_TAG_inlined_subroutine = 0x1d,
+    DW_TAG_module = 0x1e,
+    DW_TAG_ptr_to_member_type = 0x1f,
+    DW_TAG_set_type = 0x20,
+    DW_TAG_subrange_type = 0x21,
+    DW_TAG_with_stmt = 0x22,
+    DW_TAG_access_declaration = 0x23,
+    DW_TAG_base_type = 0x24,
+    DW_TAG_catch_block = 0x25,
+    DW_TAG_const_type = 0x26,
+    DW_TAG_constant = 0x27,
+    DW_TAG_enumerator = 0x28,
+    DW_TAG_file_type = 0x29,
+    DW_TAG_friend = 0x2a,
+    DW_TAG_namelist = 0x2b,
+    DW_TAG_namelist_item = 0x2c,
+    DW_TAG_packed_type = 0x2d,
+    DW_TAG_subprogram = 0x2e,
+    DW_TAG_template_type_param = 0x2f,
+    DW_TAG_template_value_param = 0x30,
+    DW_TAG_thrown_type = 0x31,
+    DW_TAG_try_block = 0x32,
+    DW_TAG_variant_part = 0x33,
+    DW_TAG_variable = 0x34,
+    DW_TAG_volatile_type = 0x35,
+    /* DWARF 3.  */
+    DW_TAG_dwarf_procedure = 0x36,
+    DW_TAG_restrict_type = 0x37,
+    DW_TAG_interface_type = 0x38,
+    DW_TAG_namespace = 0x39,
+    DW_TAG_imported_module = 0x3a,
+    DW_TAG_unspecified_type = 0x3b,
+    DW_TAG_partial_unit = 0x3c,
+    DW_TAG_imported_unit = 0x3d,
+    DW_TAG_condition = 0x3f,
+    DW_TAG_shared_type = 0x40,
+    /* SGI/MIPS Extensions.  */
+    DW_TAG_MIPS_loop = 0x4081,
+    /* HP extensions.  See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .  */
+    DW_TAG_HP_array_descriptor = 0x4090,
+    /* GNU extensions.  */
+    DW_TAG_format_label = 0x4101,	/* For FORTRAN 77 and Fortran 90.  */
+    DW_TAG_function_template = 0x4102,	/* For C++.  */
+    DW_TAG_class_template = 0x4103,	/* For C++.  */
+    DW_TAG_GNU_BINCL = 0x4104,
+    DW_TAG_GNU_EINCL = 0x4105,
+    /* Extensions for UPC.  See: http://upc.gwu.edu/~upc.  */
+    DW_TAG_upc_shared_type = 0x8765,
+    DW_TAG_upc_strict_type = 0x8766,
+    DW_TAG_upc_relaxed_type = 0x8767,
+    /* PGI (STMicroelectronics) extensions.  No documentation available.  */
+    DW_TAG_PGI_kanji_type      = 0xA000,
+    DW_TAG_PGI_interface_block = 0xA020
+  }
+  DW_TAG;
+
+#define DW_TAG_lo_user	0x4080
+#define DW_TAG_hi_user	0xffff
+
+/* Flag that tells whether entry has a child or not.  */
+typedef enum
+  {
+    DW_children_no = 0,
+    DW_children_yes = 1
+  }
+  DW_children;
+
+/* Source language names and codes.  */
+typedef enum dwarf_source_language
+  {
+    DW_LANG_C89 = 0x0001,
+    DW_LANG_C = 0x0002,
+    DW_LANG_Ada83 = 0x0003,
+    DW_LANG_C_plus_plus = 0x0004,
+    DW_LANG_Cobol74 = 0x0005,
+    DW_LANG_Cobol85 = 0x0006,
+    DW_LANG_Fortran77 = 0x0007,
+    DW_LANG_Fortran90 = 0x0008,
+    DW_LANG_Pascal83 = 0x0009,
+    DW_LANG_Modula2 = 0x000a,
+    /* DWARF 3.  */
+    DW_LANG_Java = 0x000b,
+    DW_LANG_C99 = 0x000c,
+    DW_LANG_Ada95 = 0x000d,
+    DW_LANG_Fortran95 = 0x000e,
+    DW_LANG_PLI = 0x000f,
+    DW_LANG_ObjC = 0x0010,
+    DW_LANG_ObjC_plus_plus = 0x0011,
+    DW_LANG_UPC = 0x0012,
+    DW_LANG_D = 0x0013,
+    /* MIPS.  */
+    DW_LANG_Mips_Assembler = 0x8001,
+    /* UPC.  */
+    DW_LANG_Upc = 0x8765
+  }
+  DW_LANG;
+
+/* Form names and codes.  */
+typedef enum
+  {
+    DW_FORM_addr = 0x01,
+    DW_FORM_block2 = 0x03,
+    DW_FORM_block4 = 0x04,
+    DW_FORM_data2 = 0x05,
+    DW_FORM_data4 = 0x06,
+    DW_FORM_data8 = 0x07,
+    DW_FORM_string = 0x08,
+    DW_FORM_block = 0x09,
+    DW_FORM_block1 = 0x0a,
+    DW_FORM_data1 = 0x0b,
+    DW_FORM_flag = 0x0c,
+    DW_FORM_sdata = 0x0d,
+    DW_FORM_strp = 0x0e,
+    DW_FORM_udata = 0x0f,
+    DW_FORM_ref_addr = 0x10,
+    DW_FORM_ref1 = 0x11,
+    DW_FORM_ref2 = 0x12,
+    DW_FORM_ref4 = 0x13,
+    DW_FORM_ref8 = 0x14,
+    DW_FORM_ref_udata = 0x15,
+    DW_FORM_indirect = 0x16
+  }
+  DW_FORM;
+
+/* Attribute names and codes.  */
+typedef enum
+  {
+    DW_AT_sibling = 0x01,
+    DW_AT_location = 0x02,
+    DW_AT_name = 0x03,
+    DW_AT_ordering = 0x09,
+    DW_AT_subscr_data = 0x0a,
+    DW_AT_byte_size = 0x0b,
+    DW_AT_bit_offset = 0x0c,
+    DW_AT_bit_size = 0x0d,
+    DW_AT_element_list = 0x0f,
+    DW_AT_stmt_list = 0x10,
+    DW_AT_low_pc = 0x11,
+    DW_AT_high_pc = 0x12,
+    DW_AT_language = 0x13,
+    DW_AT_member = 0x14,
+    DW_AT_discr = 0x15,
+    DW_AT_discr_value = 0x16,
+    DW_AT_visibility = 0x17,
+    DW_AT_import = 0x18,
+    DW_AT_string_length = 0x19,
+    DW_AT_common_reference = 0x1a,
+    DW_AT_comp_dir = 0x1b,
+    DW_AT_const_value = 0x1c,
+    DW_AT_containing_type = 0x1d,
+    DW_AT_default_value = 0x1e,
+    DW_AT_inline = 0x20,
+    DW_AT_is_optional = 0x21,
+    DW_AT_lower_bound = 0x22,
+    DW_AT_producer = 0x25,
+    DW_AT_prototyped = 0x27,
+    DW_AT_return_addr = 0x2a,
+    DW_AT_start_scope = 0x2c,
+    DW_AT_stride_size = 0x2e,
+    DW_AT_upper_bound = 0x2f,
+    DW_AT_abstract_origin = 0x31,
+    DW_AT_accessibility = 0x32,
+    DW_AT_address_class = 0x33,
+    DW_AT_artificial = 0x34,
+    DW_AT_base_types = 0x35,
+    DW_AT_calling_convention = 0x36,
+    DW_AT_count = 0x37,
+    DW_AT_data_member_location = 0x38,
+    DW_AT_decl_column = 0x39,
+    DW_AT_decl_file = 0x3a,
+    DW_AT_decl_line = 0x3b,
+    DW_AT_declaration = 0x3c,
+    DW_AT_discr_list = 0x3d,
+    DW_AT_encoding = 0x3e,
+    DW_AT_external = 0x3f,
+    DW_AT_frame_base = 0x40,
+    DW_AT_friend = 0x41,
+    DW_AT_identifier_case = 0x42,
+    DW_AT_macro_info = 0x43,
+    DW_AT_namelist_items = 0x44,
+    DW_AT_priority = 0x45,
+    DW_AT_segment = 0x46,
+    DW_AT_specification = 0x47,
+    DW_AT_static_link = 0x48,
+    DW_AT_type = 0x49,
+    DW_AT_use_location = 0x4a,
+    DW_AT_variable_parameter = 0x4b,
+    DW_AT_virtuality = 0x4c,
+    DW_AT_vtable_elem_location = 0x4d,
+    /* DWARF 3 values.  */
+    DW_AT_allocated     = 0x4e,
+    DW_AT_associated    = 0x4f,
+    DW_AT_data_location = 0x50,
+    DW_AT_stride        = 0x51,
+    DW_AT_entry_pc      = 0x52,
+    DW_AT_use_UTF8      = 0x53,
+    DW_AT_extension     = 0x54,
+    DW_AT_ranges        = 0x55,
+    DW_AT_trampoline    = 0x56,
+    DW_AT_call_column   = 0x57,
+    DW_AT_call_file     = 0x58,
+    DW_AT_call_line     = 0x59,
+    DW_AT_description   = 0x5a,
+    DW_AT_binary_scale  = 0x5b,
+    DW_AT_decimal_scale = 0x5c,
+    DW_AT_small         = 0x5d,
+    DW_AT_decimal_sign  = 0x5e,
+    DW_AT_digit_count   = 0x5f,
+    DW_AT_picture_string = 0x60,
+    DW_AT_mutable       = 0x61,
+    DW_AT_threads_scaled = 0x62,
+    DW_AT_explicit      = 0x63,
+    DW_AT_object_pointer = 0x64,
+    DW_AT_endianity     = 0x65,
+    DW_AT_elemental     = 0x66,
+    DW_AT_pure          = 0x67,
+    DW_AT_recursive     = 0x68,
+    /* SGI/MIPS extensions.  */
+    DW_AT_MIPS_fde = 0x2001,
+    DW_AT_MIPS_loop_begin = 0x2002,
+    DW_AT_MIPS_tail_loop_begin = 0x2003,
+    DW_AT_MIPS_epilog_begin = 0x2004,
+    DW_AT_MIPS_loop_unroll_factor = 0x2005,
+    DW_AT_MIPS_software_pipeline_depth = 0x2006,
+    DW_AT_MIPS_linkage_name = 0x2007,
+    DW_AT_MIPS_stride = 0x2008,
+    DW_AT_MIPS_abstract_name = 0x2009,
+    DW_AT_MIPS_clone_origin = 0x200a,
+    DW_AT_MIPS_has_inlines = 0x200b,
+    /* HP extensions.  */
+    DW_AT_HP_block_index         = 0x2000,
+    DW_AT_HP_unmodifiable        = 0x2001, /* Same as DW_AT_MIPS_fde.  */
+    DW_AT_HP_actuals_stmt_list   = 0x2010,
+    DW_AT_HP_proc_per_section    = 0x2011,
+    DW_AT_HP_raw_data_ptr        = 0x2012,
+    DW_AT_HP_pass_by_reference   = 0x2013,
+    DW_AT_HP_opt_level           = 0x2014,
+    DW_AT_HP_prof_version_id     = 0x2015,
+    DW_AT_HP_opt_flags           = 0x2016,
+    DW_AT_HP_cold_region_low_pc  = 0x2017,
+    DW_AT_HP_cold_region_high_pc = 0x2018,
+    DW_AT_HP_all_variables_modifiable = 0x2019,
+    DW_AT_HP_linkage_name        = 0x201a,
+    DW_AT_HP_prof_flags          = 0x201b,  /* In comp unit of procs_info for -g.  */
+    /* GNU extensions.  */
+    DW_AT_sf_names   = 0x2101,
+    DW_AT_src_info   = 0x2102,
+    DW_AT_mac_info   = 0x2103,
+    DW_AT_src_coords = 0x2104,
+    DW_AT_body_begin = 0x2105,
+    DW_AT_body_end   = 0x2106,
+    DW_AT_GNU_vector = 0x2107,
+    /* VMS extensions.  */
+    DW_AT_VMS_rtnbeg_pd_address = 0x2201,
+    /* UPC extension.  */
+    DW_AT_upc_threads_scaled = 0x3210,
+    /* PGI (STMicroelectronics) extensions.  */
+    DW_AT_PGI_lbase    = 0x3a00,
+    DW_AT_PGI_soffset  = 0x3a01,
+    DW_AT_PGI_lstride  = 0x3a02
+  }
+  DW_AT;
+
+#define DW_AT_lo_user	0x2000	/* Implementation-defined range start.  */
+#define DW_AT_hi_user	0x3ff0	/* Implementation-defined range end.  */
+
+/* Type encodings.  */
+typedef enum
+  {
+    DW_ATE_void = 0x0,
+    DW_ATE_address = 0x1,
+    DW_ATE_boolean = 0x2,
+    DW_ATE_complex_float = 0x3,
+    DW_ATE_float = 0x4,
+    DW_ATE_signed = 0x5,
+    DW_ATE_signed_char = 0x6,
+    DW_ATE_unsigned = 0x7,
+    DW_ATE_unsigned_char = 0x8,
+    /* DWARF 3.  */
+    DW_ATE_imaginary_float = 0x9,
+    DW_ATE_packed_decimal = 0xa,
+    DW_ATE_numeric_string = 0xb,
+    DW_ATE_edited = 0xc,
+    DW_ATE_signed_fixed = 0xd,
+    DW_ATE_unsigned_fixed = 0xe,
+    DW_ATE_decimal_float = 0xf,
+    /* HP extensions.  */
+    DW_ATE_HP_float80            = 0x80, /* Floating-point (80 bit).  */
+    DW_ATE_HP_complex_float80    = 0x81, /* Complex floating-point (80 bit).  */
+    DW_ATE_HP_float128           = 0x82, /* Floating-point (128 bit).  */
+    DW_ATE_HP_complex_float128   = 0x83, /* Complex floating-point (128 bit).  */
+    DW_ATE_HP_floathpintel       = 0x84, /* Floating-point (82 bit IA64).  */
+    DW_ATE_HP_imaginary_float80  = 0x85,
+    DW_ATE_HP_imaginary_float128 = 0x86
+  }
+  DW_ATE;
+
+
+/* Expression operations. */
+typedef enum
+  {
+    DW_OP_addr = 0x03,
+    DW_OP_deref = 0x06,
+    DW_OP_const1u = 0x08,
+    DW_OP_const1s = 0x09,
+    DW_OP_const2u = 0x0a,
+    DW_OP_const2s = 0x0b,
+    DW_OP_const4u = 0x0c,
+    DW_OP_const4s = 0x0d,
+    DW_OP_const8u = 0x0e,
+    DW_OP_const8s = 0x0f,
+    DW_OP_constu = 0x10,
+    DW_OP_consts = 0x11,
+    DW_OP_dup = 0x12,
+    DW_OP_drop = 0x13,
+    DW_OP_over = 0x14,
+    DW_OP_pick = 0x15,
+    DW_OP_swap = 0x16,
+    DW_OP_rot = 0x17,
+    DW_OP_xderef = 0x18,
+    DW_OP_abs = 0x19,
+    DW_OP_and = 0x1a,
+    DW_OP_div = 0x1b,
+    DW_OP_minus = 0x1c,
+    DW_OP_mod = 0x1d,
+    DW_OP_mul = 0x1e,
+    DW_OP_neg = 0x1f,
+    DW_OP_not = 0x20,
+    DW_OP_or = 0x21,
+    DW_OP_plus = 0x22,
+    DW_OP_plus_uconst = 0x23,
+    DW_OP_shl = 0x24,
+    DW_OP_shr = 0x25,
+    DW_OP_shra = 0x26,
+    DW_OP_xor = 0x27,
+    DW_OP_bra = 0x28,
+    DW_OP_eq = 0x29,
+    DW_OP_ge = 0x2a,
+    DW_OP_gt = 0x2b,
+    DW_OP_le = 0x2c,
+    DW_OP_lt = 0x2d,
+    DW_OP_ne = 0x2e,
+    DW_OP_skip = 0x2f,
+    DW_OP_lit0 = 0x30,
+    DW_OP_lit1 = 0x31,
+    DW_OP_lit2 = 0x32,
+    DW_OP_lit3 = 0x33,
+    DW_OP_lit4 = 0x34,
+    DW_OP_lit5 = 0x35,
+    DW_OP_lit6 = 0x36,
+    DW_OP_lit7 = 0x37,
+    DW_OP_lit8 = 0x38,
+    DW_OP_lit9 = 0x39,
+    DW_OP_lit10 = 0x3a,
+    DW_OP_lit11 = 0x3b,
+    DW_OP_lit12 = 0x3c,
+    DW_OP_lit13 = 0x3d,
+    DW_OP_lit14 = 0x3e,
+    DW_OP_lit15 = 0x3f,
+    DW_OP_lit16 = 0x40,
+    DW_OP_lit17 = 0x41,
+    DW_OP_lit18 = 0x42,
+    DW_OP_lit19 = 0x43,
+    DW_OP_lit20 = 0x44,
+    DW_OP_lit21 = 0x45,
+    DW_OP_lit22 = 0x46,
+    DW_OP_lit23 = 0x47,
+    DW_OP_lit24 = 0x48,
+    DW_OP_lit25 = 0x49,
+    DW_OP_lit26 = 0x4a,
+    DW_OP_lit27 = 0x4b,
+    DW_OP_lit28 = 0x4c,
+    DW_OP_lit29 = 0x4d,
+    DW_OP_lit30 = 0x4e,
+    DW_OP_lit31 = 0x4f,
+    DW_OP_reg0 = 0x50,
+    DW_OP_reg1 = 0x51,
+    DW_OP_reg2 = 0x52,
+    DW_OP_reg3 = 0x53,
+    DW_OP_reg4 = 0x54,
+    DW_OP_reg5 = 0x55,
+    DW_OP_reg6 = 0x56,
+    DW_OP_reg7 = 0x57,
+    DW_OP_reg8 = 0x58,
+    DW_OP_reg9 = 0x59,
+    DW_OP_reg10 = 0x5a,
+    DW_OP_reg11 = 0x5b,
+    DW_OP_reg12 = 0x5c,
+    DW_OP_reg13 = 0x5d,
+    DW_OP_reg14 = 0x5e,
+    DW_OP_reg15 = 0x5f,
+    DW_OP_reg16 = 0x60,
+    DW_OP_reg17 = 0x61,
+    DW_OP_reg18 = 0x62,
+    DW_OP_reg19 = 0x63,
+    DW_OP_reg20 = 0x64,
+    DW_OP_reg21 = 0x65,
+    DW_OP_reg22 = 0x66,
+    DW_OP_reg23 = 0x67,
+    DW_OP_reg24 = 0x68,
+    DW_OP_reg25 = 0x69,
+    DW_OP_reg26 = 0x6a,
+    DW_OP_reg27 = 0x6b,
+    DW_OP_reg28 = 0x6c,
+    DW_OP_reg29 = 0x6d,
+    DW_OP_reg30 = 0x6e,
+    DW_OP_reg31 = 0x6f,
+    DW_OP_breg0 = 0x70,
+    DW_OP_breg1 = 0x71,
+    DW_OP_breg2 = 0x72,
+    DW_OP_breg3 = 0x73,
+    DW_OP_breg4 = 0x74,
+    DW_OP_breg5 = 0x75,
+    DW_OP_breg6 = 0x76,
+    DW_OP_breg7 = 0x77,
+    DW_OP_breg8 = 0x78,
+    DW_OP_breg9 = 0x79,
+    DW_OP_breg10 = 0x7a,
+    DW_OP_breg11 = 0x7b,
+    DW_OP_breg12 = 0x7c,
+    DW_OP_breg13 = 0x7d,
+    DW_OP_breg14 = 0x7e,
+    DW_OP_breg15 = 0x7f,
+    DW_OP_breg16 = 0x80,
+    DW_OP_breg17 = 0x81,
+    DW_OP_breg18 = 0x82,
+    DW_OP_breg19 = 0x83,
+    DW_OP_breg20 = 0x84,
+    DW_OP_breg21 = 0x85,
+    DW_OP_breg22 = 0x86,
+    DW_OP_breg23 = 0x87,
+    DW_OP_breg24 = 0x88,
+    DW_OP_breg25 = 0x89,
+    DW_OP_breg26 = 0x8a,
+    DW_OP_breg27 = 0x8b,
+    DW_OP_breg28 = 0x8c,
+    DW_OP_breg29 = 0x8d,
+    DW_OP_breg30 = 0x8e,
+    DW_OP_breg31 = 0x8f,
+    DW_OP_regx = 0x90,
+    DW_OP_fbreg = 0x91,
+    DW_OP_bregx = 0x92,
+    DW_OP_piece = 0x93,
+    DW_OP_deref_size = 0x94,
+    DW_OP_xderef_size = 0x95,
+    DW_OP_nop = 0x96,
+    /* DWARF 3 extensions.  */
+    DW_OP_push_object_address = 0x97,
+    DW_OP_call2 = 0x98,
+    DW_OP_call4 = 0x99,
+    DW_OP_call_ref = 0x9a,
+    DW_OP_form_tls_address = 0x9b,
+    DW_OP_call_frame_cfa = 0x9c,
+    DW_OP_bit_piece = 0x9d,
+    /* GNU extensions.  */
+    DW_OP_GNU_push_tls_address = 0xe0,
+    /* HP extensions.  */
+    DW_OP_HP_unknown     = 0xe0, /* Ouch, the same as GNU_push_tls_address.  */
+    DW_OP_HP_is_value    = 0xe1,
+    DW_OP_HP_fltconst4   = 0xe2,
+    DW_OP_HP_fltconst8   = 0xe3,
+    DW_OP_HP_mod_range   = 0xe4,
+    DW_OP_HP_unmod_range = 0xe5,
+    DW_OP_HP_tls         = 0xe6
+  }
+  DW_OP;
+
+HChar* ML_(pp_DW_children) ( DW_children hashch );
+HChar* ML_(pp_DW_TAG)      ( DW_TAG tag );
+HChar* ML_(pp_DW_FORM)     ( DW_FORM form );
+HChar* ML_(pp_DW_AT)       ( DW_AT attr );
+
+
+/* --- To do with evaluation of Dwarf expressions --- */
+
+/* Guarded Dwarf3 expressions, which can be linked together to form a
+   list.  The payload field contains a variable length array of bytes
+   which hold the guarded expressions.  The length can be inferred by
+   inspecting the payload bytes and so does not need to be stored
+   explicitly.
+
+   Guarded-Expression format is similar but not identical to the
+   DWARF3 location-list format.  The format of each returned block is:
+
+      UChar biasMe;
+      UChar isEnd;
+      followed by zero or more of
+
+      (Addr aMin;  Addr aMax;  UShort nbytes;  ..bytes..;  UChar isEnd)
+
+   '..bytes..' is an standard DWARF3 location expression which is
+   valid when aMin <= pc <= aMax (possibly after suitable biasing).
+
+   The number of bytes in '..bytes..' is nbytes.
+
+   The end of the sequence is marked by an isEnd == 1 value.  All
+   previous isEnd values must be zero.
+
+   biasMe is 1 if the aMin/aMax fields need this DebugInfo's text_bias
+   added before use, and 0 if the GX is this is not necessary (is
+   ready to go).
+
+   Hence the block can be quickly parsed and is self-describing.  Note
+   that aMax is 1 less than the corresponding value in a DWARF3
+   location list.  Zero length ranges, with aMax == aMin-1, are not
+   allowed.
+*/
+typedef
+   struct _GExpr { 
+      struct _GExpr* next;
+      UChar payload[0];
+   }
+   GExpr;
+
+/* Show a so-called guarded expression */
+void ML_(pp_GX) ( GExpr* gx );
+
+/* Evaluation of a DWARF3 expression (and hence of a GExpr) may
+   require knowing a suitably contextualising set of values for the
+   instruction, frame and stack pointers (and, in general, all
+   registers, though we punt on such generality here).  Here's a
+   struct to carry the bare essentials.  ip, fp and sp are expected to
+   be provided for all platforms. */
+typedef
+   struct { Addr ip; Addr sp; Addr fp; }
+   RegSummary;
+
+/* This describes the result of evaluating a DWARF3 expression.
+   GXR_Failure: failed; .word is an asciiz string summarising why
+   GXR_Value:   evaluated to a value, in .word
+   GXR_RegNo:   evaluated to a DWARF3 register number, in .word
+*/
+typedef
+   struct { 
+      enum { GXR_Failure, GXR_Value, GXR_RegNo } kind;
+      UWord word;
+   }
+   GXResult;
+
+void ML_(pp_GXResult) ( GXResult res );
+
+/* Evaluate a guarded expression.  If regs is NULL, then gx is assumed
+   (and checked) to contain just a single guarded expression, with a
+   guard which covers the entire address space and so always evaluates
+   to True (iow, gx is a single unconditional expression).  If regs is
+   non-NULL then its .ip value is used to select which of the
+   embedded DWARF3 location expressions to use, and that is duly
+   evaluated.
+
+   If as part of the evaluation, a frame base value needs to be
+   computed, then fbGX can provide an expression for it.  If fbGX is
+   NULL but the frame base is still needed, then evaluation of gx as a
+   whole will fail. */
+GXResult ML_(evaluate_GX)( GExpr* gx, GExpr* fbGX,
+                           RegSummary* regs, Addr data_bias );
+
+/* This is a subsidiary of ML_(evaluate_GX), which just evaluates a
+   single standard DWARF3 expression.  Conventions w.r.t regs and fbGX
+   are as for ML_(evaluate_GX).  If push_initial_zero is True, then an
+   initial zero word is pushed on the evaluation stack at the start.
+   This is needed for computing structure field offsets.  Note that
+   ML_(evaluate_GX) and ML_(evaluate_Dwarf3_Expr) are mutually
+   recursive. */
+GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB, 
+                                     GExpr* fbGX, RegSummary* regs,
+                                     Addr data_bias,
+                                     Bool push_initial_zero );
+
+#endif /* ndef __PRIV_D3BASICS_H */
+
+/*--------------------------------------------------------------------*/
+/*--- end                                          priv_d3basics.h ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/priv_misc.h b/coregrind/m_debuginfo/priv_misc.h
new file mode 100644
index 0000000..c04ee1a
--- /dev/null
+++ b/coregrind/m_debuginfo/priv_misc.h
@@ -0,0 +1,59 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Misc simple stuff lacking a better home.        priv_misc.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 OpenWorks LLP
+      info@open-works.co.uk
+
+   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., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+
+   Neither the names of the U.S. Department of Energy nor the
+   University of California nor the names of its contributors may be
+   used to endorse or promote products derived from this software
+   without prior written permission.
+*/
+
+#ifndef __PRIV_MISC_H
+#define __PRIV_MISC_H
+
+
+#ifdef HAVE_BUILTIN_EXPECT
+#define LIKELY(cond)   __builtin_expect(!!(cond),1)
+#define UNLIKELY(cond) __builtin_expect(!!(cond),0)
+#else
+#define LIKELY(cond)   (cond)
+#define UNLIKELY(cond) (cond)
+#endif
+
+
+/* Allocate(zeroed), free, strdup, all in VG_AR_DINFO. */
+void*  ML_(dinfo_zalloc)( SizeT szB );
+void   ML_(dinfo_free)( void* v );
+UChar* ML_(dinfo_strdup)( const UChar* str );
+
+
+#endif /* ndef __PRIV_MISC_H */
+
+/*--------------------------------------------------------------------*/
+/*--- end                                              priv_misc.h ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/priv_readdwarf.h b/coregrind/m_debuginfo/priv_readdwarf.h
index 70ffa3f..410c5fc 100644
--- a/coregrind/m_debuginfo/priv_readdwarf.h
+++ b/coregrind/m_debuginfo/priv_readdwarf.h
@@ -39,21 +39,21 @@
 
 
 /* --------------------
-   DWARF2 reader
+   DWARF3 reader
    -------------------- */
 extern
-void ML_(read_debuginfo_dwarf2)
-        ( struct _SegInfo* si, OffT debug_offset,
-          UChar* debuginfo,   Int debug_info_sz,  /* .debug_info */
-          UChar* debugabbrev,                     /* .debug_abbrev */
-          UChar* debugline,   Int debug_line_sz,  /* .debug_line */
-          UChar* debugstr );
+void ML_(read_debuginfo_dwarf3)
+        ( struct _DebugInfo* di,
+          UChar* debug_info_img, Word debug_info_sz,  /* .debug_info */
+          UChar* debug_abbv_img, Word debug_abbv_sz,  /* .debug_abbrev */
+          UChar* debug_line_img, Word debug_line_sz,  /* .debug_line */
+          UChar* debug_str_img,  Word debug_str_sz ); /* .debug_str */
 
 /* --------------------
    DWARF1 reader
    -------------------- */
 extern
-void ML_(read_debuginfo_dwarf1) ( struct _SegInfo* si,
+void ML_(read_debuginfo_dwarf1) ( struct _DebugInfo* di,
                                   UChar* dwarf1d, Int dwarf1d_sz,
                                   UChar* dwarf1l, Int dwarf1l_sz );
 
@@ -62,8 +62,7 @@
    -------------------- */
 extern
 void ML_(read_callframe_info_dwarf3)
-    ( /*OUT*/struct _SegInfo* si, 
-      UChar* ehframe, Int ehframe_sz, Addr ehframe_addr );
+    ( /*OUT*/struct _DebugInfo* di, UChar* ehframe );
 
 
 #endif /* ndef __PRIV_READDWARF_H */
diff --git a/coregrind/m_debuginfo/priv_readdwarf3.h b/coregrind/m_debuginfo/priv_readdwarf3.h
new file mode 100644
index 0000000..8405155
--- /dev/null
+++ b/coregrind/m_debuginfo/priv_readdwarf3.h
@@ -0,0 +1,57 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Read DWARF3 ".debug_info" sections (DIE trees).              ---*/
+/*---                                            priv_readdwarf3.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 OpenWorks LLP
+      info@open-works.co.uk
+
+   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., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+
+   Neither the names of the U.S. Department of Energy nor the
+   University of California nor the names of its contributors may be
+   used to endorse or promote products derived from this software
+   without prior written permission.
+*/
+
+#ifndef __PRIV_READDWARF3_H
+#define __PRIV_READDWARF3_H
+
+
+/* Read DWARF3 ".debug_info" sections. */
+void 
+ML_(new_dwarf3_reader) (
+   struct _DebugInfo* di,
+   UChar* debug_info_img,   SizeT debug_info_sz,
+   UChar* debug_abbv_img,   SizeT debug_abbv_sz,
+   UChar* debug_line_img,   SizeT debug_line_sz,
+   UChar* debug_str_img,    SizeT debug_str_sz,
+   UChar* debug_ranges_img, SizeT debug_ranges_sz,
+   UChar* debug_loc_img,    SizeT debug_loc_sz
+);
+
+#endif /* ndef __PRIV_READDWARF3_H */
+
+/*--------------------------------------------------------------------*/
+/*--- end                                        priv_readdwarf3.h ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/priv_readelf.h b/coregrind/m_debuginfo/priv_readelf.h
index ae1d4fe..279d728 100644
--- a/coregrind/m_debuginfo/priv_readelf.h
+++ b/coregrind/m_debuginfo/priv_readelf.h
@@ -40,7 +40,7 @@
 
 /* Identify an ELF object file by peering at the first few bytes of
    it. */
-extern Bool ML_(is_elf_object_file)( const void* buf );
+extern Bool ML_(is_elf_object_file)( void* image, SizeT n_image );
 
 /* The central function for reading ELF debug info.  For the
    object/exe specified by the SegInfo, find ELF sections, then read
@@ -48,7 +48,7 @@
    info) and anything else we want, into the tables within the
    supplied SegInfo.
 */
-extern Bool ML_(read_elf_debug_info) ( struct _SegInfo* si );
+extern Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di );
 
 
 #endif /* ndef __PRIV_READELF_H */
diff --git a/coregrind/m_debuginfo/priv_readstabs.h b/coregrind/m_debuginfo/priv_readstabs.h
index 97d9a13..c2d5387 100644
--- a/coregrind/m_debuginfo/priv_readstabs.h
+++ b/coregrind/m_debuginfo/priv_readstabs.h
@@ -41,7 +41,7 @@
    Stabs reader
    -------------------- */
 extern
-void ML_(read_debuginfo_stabs) ( struct _SegInfo* si, OffT debug_offset,
+void ML_(read_debuginfo_stabs) ( struct _DebugInfo* di, OffT debug_offset,
                                  UChar* stabC,   Int stab_sz,
                                  UChar* stabstr, Int stabstr_sz );
 
diff --git a/coregrind/m_debuginfo/priv_readxcoff.h b/coregrind/m_debuginfo/priv_readxcoff.h
index 452b169..02b6a7b 100644
--- a/coregrind/m_debuginfo/priv_readxcoff.h
+++ b/coregrind/m_debuginfo/priv_readxcoff.h
@@ -39,9 +39,7 @@
 
 /* Read whatever info we can from an XCOFF object file. */
 extern
-Bool ML_(read_xcoff_debug_info) ( struct _SegInfo* si,
-                                  Addr   data_addr,
-                                  SSizeT data_len,
+Bool ML_(read_xcoff_debug_info) ( struct _DebugInfo* di,
                                   Bool   is_mainexe );
 
 #endif /* ndef __PRIV_READXCOFF_H */
diff --git a/coregrind/m_debuginfo/priv_storage.h b/coregrind/m_debuginfo/priv_storage.h
index c1d0ee1..7822fd3 100644
--- a/coregrind/m_debuginfo/priv_storage.h
+++ b/coregrind/m_debuginfo/priv_storage.h
@@ -37,8 +37,8 @@
 /* See comment at top of debuginfo.c for explanation of
    the _svma / _avma / _image / _bias naming scheme.
 */
-/* Note this is not freestanding; needs pub_core_xarray.h to be
-   included before it. */
+/* Note this is not freestanding; needs pub_core_xarray.h and
+   priv_tytypes.h to be included before it. */
 
 #ifndef __PRIV_STORAGE_H
 #define __PRIV_STORAGE_H
@@ -50,8 +50,9 @@
    struct { 
       Addr  addr;   /* lowest address of entity */
       Addr  tocptr; /* ppc64-linux only: value that R2 should have */
-      UInt  size;   /* size in bytes */
       UChar *name;  /* name */
+      UInt  size;   /* size in bytes */
+      Bool  isText;
    }
    DiSym;
 
@@ -69,7 +70,7 @@
 #define MAX_LOC_SIZE   ((1 << LOC_SIZE_BITS) - 1)
 
 /* Number used to detect line number overflows; if one line is
-   60000-odd smaller than the previous, is was probably an overflow.
+   60000-odd smaller than the previous, it was probably an overflow.
  */
 #define OVERFLOW_DIFFERENCE     (LINENO_OVERFLOW - 5000)
 
@@ -207,33 +208,150 @@
 
 extern void ML_(ppCfiExpr)( XArray* src, Int ix );
 
-/* --------------------- SEGINFO --------------------- */
+/* --------------------- VARIABLES --------------------- */
+
+typedef
+   struct {
+      Addr    aMin;
+      Addr    aMax;
+      XArray* /* of DiVariable */ vars;
+   }
+   DiAddrRange;
+
+typedef
+   struct {
+      UChar* name;  /* in DebugInfo.strchunks */
+      Type*  type;  /* on DebugInfo.admin list */
+      GExpr* gexpr; /* on DebugInfo.gexprs list */
+      GExpr* fbGX;  /* SHARED. */
+      UChar* fileName; /* where declared; may be NULL. in
+                          DebugInfo.strchunks */
+      Int    lineNo;   /* where declared; may be zero. */
+   }
+   DiVariable;
+
+Word 
+ML_(cmp_for_DiAddrRange_range) ( const void* keyV, const void* elemV );
+
+/* --------------------- DEBUGINFO --------------------- */
 
 /* This is the top-level data type.  It's a structure which contains
-   information pertaining to one mapped text segment.  This type is
+   information pertaining to one mapped ELF object.  This type is
    exported only abstractly - in pub_tool_debuginfo.h. */
 
 #define SEGINFO_STRCHUNKSIZE (64*1024)
 
-struct _SegInfo {
-   struct _SegInfo* next;	/* list of SegInfos */
+struct _DebugInfo {
 
-   /* Description of the mapped segment. */
-   Addr   text_start_avma;
-   UInt   text_size;
-   UChar* filename; /* in mallocville */
-   UChar* memname;  /* malloc'd.  AIX5 only: .a member name */
-   OffT   foffset;  /* file offset for mapped text section - UNUSED */
+   /* Admin stuff */
+
+   struct _DebugInfo* next;   /* list of DebugInfos */
+   Bool               mark;   /* marked for deletion? */
+
+   /* Used for debugging only - indicate what stuff to dump whilst
+      reading stuff into the seginfo.  Are computed as early in the
+      lifetime of the DebugInfo as possible -- at the point when it is
+      created.  Use these when deciding what to spew out; do not use
+      the global VG_(clo_blah) flags. */
+
+   Bool trace_symtab; /* symbols, our style */
+   Bool trace_cfi;    /* dwarf frame unwind, our style */
+   Bool ddump_syms;   /* mimic /usr/bin/readelf --syms */
+   Bool ddump_line;   /* mimic /usr/bin/readelf --debug-dump=line */
+   Bool ddump_frames; /* mimic /usr/bin/readelf --debug-dump=frames */
+
+   /* Fields that must be filled in before we can start reading
+      anything from the ELF file.  These fields are filled in by
+      VG_(di_notify_mmap) and its immediate helpers. */
+
+   UChar* filename; /* in mallocville (VG_AR_DINFO) */
+   UChar* memname;  /* also in VG_AR_DINFO.  AIX5 only: .a member name */
+
+   Bool  have_rx_map; /* did we see a r?x mapping yet for the file? */
+   Bool  have_rw_map; /* did we see a rw? mapping yet for the file? */
+
+   Addr  rx_map_avma; /* these fields record the file offset, length */
+   SizeT rx_map_size; /* and map address of the r?x mapping we believe */
+   OffT  rx_map_foff; /* is the .text segment mapping */
+
+   Addr  rw_map_avma; /* ditto, for the rw? mapping we believe is the */
+   SizeT rw_map_size; /* .data segment mapping */
+   OffT  rw_map_foff;
+
+   /* Once both a rw? and r?x mapping for .filename have been
+      observed, we can go on to read the symbol tables and debug info.
+      .have_dinfo flags when that has happened. */
+   /* If have_dinfo is False, then all fields except "*rx_map*" and
+      "*rw_map*" are invalid and should not be consulted. */
+   Bool  have_dinfo; /* initially False */
+
+   /* All the rest of the fields in this structure are filled in once
+      we have committed to reading the symbols and debug info (that
+      is, at the point where .have_dinfo is set to True). */
+
+   /* The file's soname.  FIXME: ensure this is always allocated in
+      VG_AR_DINFO. */
    UChar* soname;
 
+   /* Description of some important mapped segments.  The presence or
+      absence of the mapping is denoted by the _present field, since
+      in some obscure circumstances (to do with data/sdata/bss) it is
+      possible for the mapping to be present but have zero size.
+      Certainly text_ is mandatory on all platforms; not sure about
+      the rest though. */
+   /* .text */
+   Bool   text_present;
+   Addr   text_avma;
+   Addr   text_svma;
+   SizeT  text_size;
+   OffT   text_bias;
+   /* .data */
+   Bool   data_present;
+   Addr   data_svma;
+   Addr   data_avma;
+   SizeT  data_size;
+   OffT   data_bias;
+   /* .sdata */
+   Bool   sdata_present;
+   Addr   sdata_svma;
+   Addr   sdata_avma;
+   SizeT  sdata_size;
+   OffT   sdata_bias;
+   /* .bss */
+   Bool   bss_present;
+   Addr   bss_svma;
+   Addr   bss_avma;
+   SizeT  bss_size;
+   OffT   bss_bias;
+   /* .plt */
+   Bool   plt_present;
+   Addr	  plt_avma;
+   SizeT  plt_size;
+   /* .got */
+   Bool   got_present;
+   Addr   got_avma;
+   SizeT  got_size;
+   /* .opd -- needed on ppc64-linux for finding symbols */
+   Bool   opd_present;
+   Addr   opd_avma;
+   SizeT  opd_size;
+   /* .ehframe -- needed on amd64-linux for stack unwinding */
+   Bool   ehframe_present;
+   Addr   ehframe_avma;
+   SizeT  ehframe_size;
+
+   /* Sorted tables of stuff we snarfed from the file.  This is the
+      eventual product of reading the debug info.  All this stuff
+      lives in VG_AR_DINFO. */
+
    /* An expandable array of symbols. */
    DiSym*  symtab;
-   UInt    symtab_used;
-   UInt    symtab_size;
+   UWord   symtab_used;
+   UWord   symtab_size;
    /* An expandable array of locations. */
    DiLoc*  loctab;
-   UInt    loctab_used;
-   UInt    loctab_size;
+   UWord   loctab_used;
+   UWord   loctab_size;
    /* An expandable array of CFI summary info records.  Also includes
       summary address bounds, showing the min and max address covered
       by any of the records, as an aid to fast searching.  And, if the
@@ -242,48 +360,51 @@
    DiCfSI* cfsi;
    UInt    cfsi_used;
    UInt    cfsi_size;
-   Addr    cfsi_minaddr;
-   Addr    cfsi_maxaddr;
-   XArray* cfsi_exprs; /* XArray of CfSiExpr */
+   Addr    cfsi_minavma;
+   Addr    cfsi_maxavma;
+   XArray* cfsi_exprs; /* XArray of CfiExpr */
 
    /* Expandable arrays of characters -- the string table.  Pointers
       into this are stable (the arrays are not reallocated). */
    struct strchunk {
       UInt   strtab_used;
-      struct strchunk *next;
+      struct strchunk* next;
       UChar  strtab[SEGINFO_STRCHUNKSIZE];
    } *strchunks;
 
-   /* 'text_bias' is what needs to be added to an address in the
-      address space of the library as stored on disk [a so-called
-      stated VMA] (which is not 0-based for executables or prelinked
-      libraries) to get an address in memory for the object loaded at
-      'text_start_avma'.  At least for text symbols. */
-   OffT   text_bias;
+   /* Variable scope information, as harvested from Dwarf3 files.
 
-   /* Bounds of data, BSS, PLT, GOT and OPD (for ppc64-linux) so that
-      tools can see what section an address is in.  In the running
-      image! */
-   Addr	  plt_start_avma;
-   UInt   plt_size;
-   Addr   got_start_avma;
-   UInt   got_size;
-   Addr   opd_start_avma;
-   UInt   opd_size;
-   Addr   data_start_avma;
-   UInt   data_size;
-   Addr   bss_start_avma;
-   UInt   bss_size;
+      In short it's an
 
-   /* Used for debugging only - indicate what stuff to dump whilst
-      reading stuff into the seginfo.  Are computed as early in the
-      lifetime of the SegInfo as possible.  Use these when deciding
-      what to spew out; do not use the global VG_(clo_blah) flags. */
-   Bool trace_symtab; /* symbols, our style */
-   Bool trace_cfi;    /* dwarf frame unwind, our style */
-   Bool ddump_syms;   /* mimic /usr/bin/readelf --syms */
-   Bool ddump_line;   /* mimic /usr/bin/readelf --debug-dump=line */
-   Bool ddump_frames; /* mimic /usr/bin/readelf --debug-dump=frames */
+         array of (array of PC address ranges and variables)
+
+      The outer array indexes over scopes, with Entry 0 containing
+      information on variables which exist for any value of the program
+      counter (PC) -- that is, the outermost scope.  Entries 1, 2, 3,
+      etc contain information on increasinly deeply nested variables.
+
+      Each inner array is an array of (an address range, and a set
+      of variables that are in scope over that address range).  
+
+      The address ranges may not overlap.
+ 
+      Since Entry 0 in the outer array holds information on variables
+      that exist for any value of the PC (that is, global vars), it
+      follows that Entry 0's inner array can only have one address
+      range pair, one that covers the entire address space.
+   */
+   XArray* /* of OSet of DiAddrRange */varinfo;
+
+   /* These are lists of the relevant typed objects, held here
+      expressly for the purposes of visiting each object exactly once
+      when we need to delete them. */
+
+   /* A list of TyAdmin structs, and the payloads that they refer
+      to. */
+   TyAdmin* admin_tyadmins;
+
+   /* A list of guarded DWARF3 expressions. */
+   GExpr*   admin_gexprs;
 };
 
 /* --------------------- functions --------------------- */
@@ -291,46 +412,61 @@
 /* ------ Adding ------ */
 
 /* Add a symbol to si's symbol table. */
-extern void ML_(addSym) ( struct _SegInfo* si, DiSym* sym );
+extern void ML_(addSym) ( struct _DebugInfo* di, DiSym* sym );
 
-/* Add a line-number record to a SegInfo. */
+/* Add a line-number record to a DebugInfo. */
 extern
-void ML_(addLineInfo) ( struct _SegInfo* si, 
+void ML_(addLineInfo) ( struct _DebugInfo* di, 
                         UChar*   filename, 
                         UChar*   dirname,  /* NULL is allowable */
                         Addr this, Addr next, Int lineno, Int entry);
 
 /* Add a CFI summary record.  The supplied DiCfSI is copied. */
-extern void ML_(addDiCfSI) ( struct _SegInfo* si, DiCfSI* cfsi );
+extern void ML_(addDiCfSI) ( struct _DebugInfo* di, DiCfSI* cfsi );
 
-/* Add a string to the string table of a SegInfo.  If len==-1,
+/* Add a string to the string table of a DebugInfo.  If len==-1,
    ML_(addStr) will itself measure the length of the string. */
-extern UChar* ML_(addStr) ( struct _SegInfo* si, UChar* str, Int len );
+extern UChar* ML_(addStr) ( struct _DebugInfo* di, UChar* str, Int len );
 
-/* Canonicalise the tables held by 'si', in preparation for use.  Call
+extern void ML_(addVar)( struct _DebugInfo* di,
+                         Int    level,
+                         Addr   aMin,
+                         Addr   aMax,
+                         UChar* name,
+                         Type*  type,
+                         GExpr* gexpr,
+                         GExpr* fbGX, /* SHARED. */
+                         UChar* fileName, /* where decl'd - may be NULL */
+                         Int    lineNo, /* where decl'd - may be zero */
+                         Bool   show );
+
+/* Canonicalise the tables held by 'di', in preparation for use.  Call
    this after finishing adding entries to these tables. */
-extern void ML_(canonicaliseTables) ( struct _SegInfo* si );
+extern void ML_(canonicaliseTables) ( struct _DebugInfo* di );
 
 /* ------ Searching ------ */
 
 /* Find a symbol-table index containing the specified pointer, or -1
    if not found.  Binary search.  */
-extern Int ML_(search_one_symtab) ( struct _SegInfo* si, Addr ptr,
-                                    Bool match_anywhere_in_fun );
+extern Int ML_(search_one_symtab) ( struct _DebugInfo* di, Addr ptr,
+                                    Bool match_anywhere_in_sym,
+                                    Bool findText );
 
 /* Find a location-table index containing the specified pointer, or -1
    if not found.  Binary search.  */
-extern Int ML_(search_one_loctab) ( struct _SegInfo* si, Addr ptr );
+extern Int ML_(search_one_loctab) ( struct _DebugInfo* di, Addr ptr );
 
 /* Find a CFI-table index containing the specified pointer, or -1 if
    not found.  Binary search.  */
-extern Int ML_(search_one_cfitab) ( struct _SegInfo* si, Addr ptr );
+extern Int ML_(search_one_cfitab) ( struct _DebugInfo* di, Addr ptr );
 
 /* ------ Misc ------ */
 
 /* Show a non-fatal debug info reading error.  Use vg_panic if
-   terminal. */
-extern void ML_(symerr) ( HChar* msg );
+   terminal.  'serious' errors are always shown, not 'serious' ones
+   are shown only at verbosity level 2 and above. */
+extern 
+void ML_(symerr) ( struct _DebugInfo* di, Bool serious, HChar* msg );
 
 /* Print a symbol. */
 extern void ML_(ppSym) ( Int idx, DiSym* sym );
@@ -340,7 +476,7 @@
 
 
 #define TRACE_SYMTAB(format, args...) \
-   if (si->trace_symtab) { VG_(printf)(format, ## args); }
+   if (di->trace_symtab) { VG_(printf)(format, ## args); }
 
 
 #endif /* ndef __PRIV_STORAGE_H */
diff --git a/coregrind/m_debuginfo/priv_tytypes.h b/coregrind/m_debuginfo/priv_tytypes.h
new file mode 100644
index 0000000..646de97
--- /dev/null
+++ b/coregrind/m_debuginfo/priv_tytypes.h
@@ -0,0 +1,170 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Representation of source level types.         priv_tytypes.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 OpenWorks LLP
+      info@open-works.co.uk
+
+   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., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+
+   Neither the names of the U.S. Department of Energy nor the
+   University of California nor the names of its contributors may be
+   used to endorse or promote products derived from this software
+   without prior written permission.
+*/
+
+#ifndef __PRIV_TYTYPES_H
+#define __PRIV_TYTYPES_H
+
+typedef  struct _TyAdmin   TyAdmin;
+typedef  struct _TyAtom    TyAtom;
+typedef  struct _TyField   TyField;
+typedef  struct _TyBounds  TyBounds;
+typedef  struct _D3Expr    D3Expr;
+typedef  struct _Type      Type;
+
+#define TyBounds_MAGIC 0x0d573990UL
+
+typedef
+   enum { TyA_Atom=10, TyA_Field, TyA_Bounds, TyA_Expr, TyA_Type } 
+   TyAdminTag;
+
+struct _TyAdmin {
+   UWord      cuOff;
+   void*      payload;
+   TyAdmin*   next;
+   TyAdminTag tag;
+};
+
+/* an enumeration value */
+struct _TyAtom {
+   UChar* name; /* in DebugInfo.strchunks */
+   Long   value;
+};
+
+struct _TyField {
+   UChar*  name; /* in DebugInfo.strchunks */
+   Type*   typeR;
+   D3Expr* loc;
+   Bool    isStruct;
+};
+
+struct _TyBounds {
+   UInt magic;
+   Bool knownL;
+   Bool knownU;
+   Long boundL;
+   Long boundU;
+};
+
+struct _D3Expr {
+   UChar* bytes; /* in DebugInfo.strchunks */
+   UWord  nbytes;
+};
+
+struct _Type {
+   enum { Ty_Base=30, Ty_PorR, Ty_TyDef, Ty_StOrUn, 
+          Ty_Enum, Ty_Array, Ty_Fn, Ty_Qual, Ty_Void } tag;
+   union {
+      struct {
+         UChar* name; /* in DebugInfo.strchunks */
+         Int    szB;
+         UChar  enc; /* S:signed U:unsigned F:floating C:complex float */
+      } Base;
+      struct {
+         Int   szB;
+         Type* typeR;
+         Bool  isPtr;
+      } PorR;
+      struct {
+         UChar* name;  /* in DebugInfo.strchunks */
+         Type*  typeR; /* MAY BE NULL, denoting unknown */
+      } TyDef;
+      struct {
+         UChar*  name; /* in DebugInfo.strchunks */
+         UWord   szB;
+         XArray* /* of TyField* */ fields;
+         Bool    complete;
+         Bool    isStruct;
+      } StOrUn;
+      struct {
+         UChar*  name; /* in DebugInfo.strchunks */
+         Int     szB;
+         XArray* /* of TyAtom* */ atomRs;
+      } Enum;
+      struct {
+         Type*   typeR;
+         XArray* /* of TyBounds* */ bounds;
+      } Array;
+      struct {
+      } Fn;
+      struct {
+         UChar qual; /* C:const V:volatile */
+         Type* typeR;
+      } Qual;
+      struct {
+         Bool isFake; /* True == introduced by the reader */
+      } Void;
+   } Ty;
+};
+
+TyAdmin*  ML_(new_TyAdmin)  ( UWord cuOff, TyAdmin* next );
+TyAtom*   ML_(new_TyAtom)   ( UChar* name, Long value );
+TyField*  ML_(new_TyField)  ( UChar* name, Type* typeR, D3Expr* loc );
+TyBounds* ML_(new_TyBounds) ( void );
+Type*     ML_(new_Type)     ( void );
+D3Expr*   ML_(new_D3Expr)   ( UChar* bytes, UWord nbytes );
+
+void ML_(delete_TyAdmin_and_payload) ( TyAdmin* ad );
+
+void ML_(pp_TyAdmin)  ( TyAdmin* admin );
+void ML_(pp_TyAtom)   ( TyAtom* atom );
+void ML_(pp_TyField)  ( TyField* field );
+void ML_(pp_TyBounds) ( TyBounds* bounds );
+void ML_(pp_Type)     ( Type* ty );
+void ML_(pp_D3Expr)   ( D3Expr* expr );
+
+/* NOTE: this assumes that the types have all been 'resolved' (that
+   is, inter-type references expressed as .debug_info offsets have
+   been converted into pointers) */
+void ML_(pp_Type_C_ishly) ( Type* ty );
+
+/* How big is this type?  (post-resolved only)  If .b in the
+   returned struct is False, the size is unknown. */
+/* FIXME: check all pointers before dereferencing */
+
+typedef struct { UWord w; Bool b; } MaybeUWord;
+
+MaybeUWord ML_(sizeOfType)( Type* ty );
+
+/* Describe where in the type 'offset' falls.  Caller must
+   deallocate the resulting XArray. */
+XArray* /*UChar*/ ML_(describe_type)( /*OUT*/OffT* residual_offset,
+                                      Type* ty, OffT offset );
+
+
+#endif /* ndef __PRIV_TYTYPES_H */
+
+/*--------------------------------------------------------------------*/
+/*--- end                                           priv_tytypes.h ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/readdwarf.c b/coregrind/m_debuginfo/readdwarf.c
index 4baffec..d7f5f9b 100644
--- a/coregrind/m_debuginfo/readdwarf.c
+++ b/coregrind/m_debuginfo/readdwarf.c
@@ -37,9 +37,11 @@
 #include "pub_core_libcbase.h"
 #include "pub_core_libcassert.h"
 #include "pub_core_libcprint.h"
-#include "pub_core_mallocfree.h"
 #include "pub_core_options.h"
 #include "pub_core_xarray.h"
+#include "priv_misc.h"             /* dinfo_zalloc/free/strdup */
+#include "priv_d3basics.h"
+#include "priv_tytypes.h"
 #include "priv_storage.h"
 #include "priv_readdwarf.h"        /* self */
 
@@ -75,7 +77,7 @@
 {
    if (wa->tab) {
       vg_assert(wa->tab_size > 0);
-      VG_(arena_free)(VG_AR_SYMTAB, wa->tab);
+      ML_(dinfo_free)(wa->tab);
    }
    init_WordArray(wa);
 }
@@ -97,14 +99,13 @@
       vg_assert( (wa->tab_size == 0 && wa->tab == NULL)
                  || (wa->tab_size != 0 && wa->tab != NULL) );
       new_size = wa->tab_size == 0 ? 8 : 2 * wa->tab_size;
-      new_tab  = VG_(arena_malloc)(VG_AR_SYMTAB, 
-                                   new_size * sizeof(Word));
+      new_tab  = ML_(dinfo_zalloc)(new_size * sizeof(Word));
       vg_assert(new_tab != NULL);
       for (i = 0; i < wa->tab_used; i++)
          new_tab[i] = wa->tab[i];
       wa->tab_size = new_size;
       if (wa->tab)
-         VG_(arena_free)(VG_AR_SYMTAB, wa->tab);
+         ML_(dinfo_free)(wa->tab);
       wa->tab = new_tab;
    }
 
@@ -203,6 +204,7 @@
 } LineSMR;
 
 
+/* FIXME: duplicated in readdwarf3.c */
 static 
 ULong read_leb128 ( UChar* data, Int* length_return, Int sign )
 {
@@ -229,7 +231,7 @@
     * length_return = num_read;
 
   if (sign && (shift < 64) && (byte & 0x40))
-    result |= (-1ULL) << shift;
+    result |= -(1ULL << shift);
 
   return result;
 }
@@ -237,6 +239,7 @@
 /* Small helper functions easier to use
  * value is returned and the given pointer is
  * moved past end of leb128 data */
+/* FIXME: duplicated in readdwarf3.c */
 static ULong read_leb128U( UChar **data )
 {
   Int len;
@@ -246,6 +249,7 @@
 }
 
 /* Same for signed data */
+/* FIXME: duplicated in readdwarf3.c */
 static Long read_leb128S( UChar **data )
 {
    Int len;
@@ -327,7 +331,7 @@
 /* Handled an extended line op starting at 'data'.  Returns the number
    of bytes that 'data' should be advanced by. */
 static 
-Word process_extended_line_op( struct _SegInfo* si, OffT debug_offset,
+Word process_extended_line_op( struct _DebugInfo* di,
                                WordArray* filenames, 
                                WordArray* dirnames, 
                                WordArray* fnidx2dir, 
@@ -357,7 +361,7 @@
    switch (op_code) {
       case DW_LNE_end_sequence:
          if (0) VG_(printf)("1001: si->o %p, smr.a %p\n", 
-                            debug_offset, state_machine_regs.address );
+                            di->text_bias, state_machine_regs.address );
          /* JRS: added for compliance with spec; is pointless due to
             reset_state_machine below */
          state_machine_regs.end_sequence = 1; 
@@ -371,18 +375,18 @@
                if (!inRange || !filename)
                   filename = "???";
                ML_(addLineInfo) (
-                  si, 
+                  di, 
                   filename, 
                   lookupDir( state_machine_regs.last_file,
                              fnidx2dir, dirnames ),
-                  debug_offset + state_machine_regs.last_address, 
-                  debug_offset + state_machine_regs.address, 
+                  di->text_bias + state_machine_regs.last_address, 
+                  di->text_bias + state_machine_regs.address, 
                   state_machine_regs.last_line, 0
                );
             }
          }
          reset_state_machine (is_stmt);
-         if (si->ddump_line)
+         if (di->ddump_line)
             VG_(printf)("  Extended opcode %d: End of Sequence\n\n", 
                         (Int)op_code);
          break;
@@ -390,26 +394,26 @@
       case DW_LNE_set_address:
          adr = *((Addr *)data);
          state_machine_regs.address = adr;
-         if (si->ddump_line)
+         if (di->ddump_line)
             VG_(printf)("  Extended opcode %d: set Address to 0x%lx\n",
                         (Int)op_code, (Addr)adr);
          break;
 
       case DW_LNE_define_file:
          name = data;
-         addto_WordArray( filenames, (Word)ML_(addStr)(si,name,-1) );
+         addto_WordArray( filenames, (Word)ML_(addStr)(di,name,-1) );
          data += VG_(strlen) ((char *) data) + 1;
          read_leb128 (data, & bytes_read, 0);
          data += bytes_read;
          read_leb128 (data, & bytes_read, 0);
          data += bytes_read;
          read_leb128 (data, & bytes_read, 0);
-         if (si->ddump_line)
+         if (di->ddump_line)
             VG_(printf)("  DWARF2-line: set_address\n");
          break;
 
       default:
-         if (si->ddump_line)
+         if (di->ddump_line)
             VG_(printf)("process_extended_line_op:default\n");
          break;
    }
@@ -430,7 +434,7 @@
  * Output: - si debug info structures get updated
  */
 static 
-void read_dwarf2_lineblock ( struct _SegInfo*  si, OffT debug_offset,
+void read_dwarf2_lineblock ( struct _DebugInfo* di,
                              UnitInfo* ui, 
                              UChar*    theBlock, /* IMAGE */
                              Int       noLargerThan )
@@ -483,21 +487,22 @@
    addto_WordArray( &filenames, (Word)NULL );
 
    if (ui->compdir)
-      addto_WordArray( &dirnames, (Word)ML_(addStr)(si, ui->compdir, -1) );
+      addto_WordArray( &dirnames, (Word)ML_(addStr)(di, ui->compdir, -1) );
    else
-      addto_WordArray( &dirnames, (Word)ML_(addStr)(si, ".", -1) );
+      addto_WordArray( &dirnames, (Word)ML_(addStr)(di, ".", -1) );
 
    addto_WordArray( &fnidx2dir, (Word)0 );  /* compilation dir */
 
    info.li_length = read_initial_length_field( external, &is64 );
    external += is64 ? 12 : 4;
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("  Length:                      %llu\n", 
                   info.li_length);
 
    /* Check the length of the block.  */
    if (info.li_length > noLargerThan) {
-      ML_(symerr)("DWARF line info appears to be corrupt "
+      ML_(symerr)(di, True,
+                  "DWARF line info appears to be corrupt "
                   "- the section is too small");
       goto out;
    }
@@ -505,12 +510,13 @@
    /* Check its version number.  */
    info.li_version = * ((UShort *)external);
    external += 2;
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("  DWARF Version:               %d\n", 
                   (Int)info.li_version);
 
    if (info.li_version != 2) {
-      ML_(symerr)("Only DWARF version 2 line info "
+      ML_(symerr)(di, True,
+                  "Only DWARF version 2 line info "
                   "is currently supported.");
       goto out;
    }
@@ -518,19 +524,19 @@
    info.li_header_length = ui->dw64 ? *((ULong*)external) 
                                     : (ULong)(*((UInt*)external));
    external += ui->dw64 ? 8 : 4;
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("  Prologue Length:             %llu\n", 
                   info.li_header_length);
 
    info.li_min_insn_length = * ((UChar *)external);
    external += 1;
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("  Minimum Instruction Length:  %d\n", 
                   (Int)info.li_min_insn_length);
 
    info.li_default_is_stmt = * ((UChar *)external);
    external += 1;
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("  Initial value of 'is_stmt':  %d\n", 
                   (Int)info.li_default_is_stmt);
 
@@ -557,19 +563,19 @@
    info.li_line_base = * ((UChar *)external);
    info.li_line_base = (Int)(signed char)info.li_line_base;
    external += 1;
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("  Line Base:                   %d\n", 
                   info.li_line_base);
 
    info.li_line_range = * ((UChar *)external);
    external += 1;
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("  Line Range:                  %d\n", 
                   (Int)info.li_line_range);
 
    info.li_opcode_base = * ((UChar *)external);
    external += 1;
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("  Opcode Base:                 %d\n\n", 
                   info.li_opcode_base);
 
@@ -585,7 +591,7 @@
 
    /* Read the contents of the Opcodes table.  */
    standard_opcodes = external;
-   if (si->ddump_line) {
+   if (di->ddump_line) {
       VG_(printf)(" Opcodes:\n");
       for (i = 1; i < (Int)info.li_opcode_base; i++) {
          VG_(printf)("  Opcode %d has %d args\n", 
@@ -597,7 +603,7 @@
    /* Read the contents of the Directory table.  */
    data = standard_opcodes + info.li_opcode_base - 1;
 
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)(" The Directory Table%s\n", 
                   *data == 0 ? " is empty." : ":" );
 
@@ -606,7 +612,7 @@
 #     define NBUF 4096
       static Char buf[NBUF];
 
-      if (si->ddump_line)
+      if (di->ddump_line)
          VG_(printf)("  %s\n", data);
 
       /* If data[0] is '/', then 'data' is an absolute path and we
@@ -625,11 +631,11 @@
          VG_(strcat)(buf, "/");
          VG_(strcat)(buf, data);
          vg_assert(VG_(strlen)(buf) < NBUF);
-         addto_WordArray( &dirnames, (Word)ML_(addStr)(si,buf,-1) );
+         addto_WordArray( &dirnames, (Word)ML_(addStr)(di,buf,-1) );
          if (0) VG_(printf)("rel path  %s\n", buf);
       } else {
          /* just use 'data'. */
-         addto_WordArray( &dirnames, (Word)ML_(addStr)(si,data,-1) );
+         addto_WordArray( &dirnames, (Word)ML_(addStr)(di,data,-1) );
          if (0) VG_(printf)("abs path  %s\n", data);
       }
 
@@ -638,11 +644,12 @@
 #     undef NBUF
    }
 
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("\n");
 
    if (*data != 0) {
-      ML_(symerr)("can't find NUL at end of DWARF2 directory table");
+      ML_(symerr)(di, True,
+                  "can't find NUL at end of DWARF2 directory table");
       goto out;
    }
    data ++;
@@ -650,7 +657,7 @@
    /* Read the contents of the File Name table.  This produces a bunch
       of file names, and for each, an index to the corresponding
       directory name entry. */
-   if (si->ddump_line) {
+   if (di->ddump_line) {
       VG_(printf)(" The File Name Table:\n");
       VG_(printf)("  Entry	Dir	Time	Size	Name\n");
    }
@@ -670,25 +677,26 @@
       uu_size = read_leb128 (data, & bytes_read, 0);
       data += bytes_read;
 
-      addto_WordArray( &filenames, (Word)ML_(addStr)(si,name,-1) );
+      addto_WordArray( &filenames, (Word)ML_(addStr)(di,name,-1) );
       addto_WordArray( &fnidx2dir, (Word)diridx );
       if (0) VG_(printf)("file %s diridx %d\n", name, diridx );
-      if (si->ddump_line)
+      if (di->ddump_line)
          VG_(printf)("  %d\t%d\t%d\t%d\t%s\n", 
                      i, diridx, uu_time, uu_size, name);
       i++;
    }
 
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("\n");
 
    if (*data != 0) {
-      ML_(symerr)("can't find NUL at end of DWARF2 file name table");
+      ML_(symerr)(di, True,
+                  "can't find NUL at end of DWARF2 file name table");
       goto out;
    }
    data ++;
 
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)(" Line Number Statements:\n");
 
    /* Now display the statements.  */
@@ -714,11 +722,11 @@
 
          if (0) VG_(printf)("smr.a += %p\n", adv );
          adv = (op_code % info.li_line_range) + info.li_line_base;
-         if (0) VG_(printf)("1002: si->o %p, smr.a %p\n", 
-                            debug_offset, state_machine_regs.address );
+         if (0) VG_(printf)("1002: di->o %p, smr.a %p\n", 
+                            di->text_bias, state_machine_regs.address );
          state_machine_regs.line += adv;
 
-         if (si->ddump_line)
+         if (di->ddump_line)
             VG_(printf)("  Special opcode %d: advance Address by %d "
                         "to 0x%lx and Line by %d to %d\n", 
                         (Int)op_code, advAddr, state_machine_regs.address,
@@ -734,12 +742,12 @@
                if (!inRange || !filename)
                   filename = "???";
                ML_(addLineInfo)(
-                  si, 
+                  di, 
                   filename,
                   lookupDir( state_machine_regs.last_file,
                              &fnidx2dir, &dirnames ),
-                  debug_offset + state_machine_regs.last_address, 
-                  debug_offset + state_machine_regs.address, 
+                  di->text_bias + state_machine_regs.last_address, 
+                  di->text_bias + state_machine_regs.address, 
                   state_machine_regs.last_line, 
                   0
                );
@@ -756,13 +764,13 @@
       switch (op_code) {
          case DW_LNS_extended_op:
             data += process_extended_line_op (
-                       si, debug_offset, &filenames, &dirnames, &fnidx2dir,
+                       di, &filenames, &dirnames, &fnidx2dir,
                        data, info.li_default_is_stmt);
             break;
 
          case DW_LNS_copy:
-            if (0) VG_(printf)("1002: si->o %p, smr.a %p\n", 
-                               debug_offset, state_machine_regs.address );
+            if (0) VG_(printf)("1002: di->o %p, smr.a %p\n", 
+                               di->text_bias, state_machine_regs.address );
             if (state_machine_regs.is_stmt) {
                /* only add a statement if there was a previous boundary */
                if (state_machine_regs.last_address) {
@@ -773,12 +781,12 @@
                   if (!inRange || !filename)
                      filename = "???";
                   ML_(addLineInfo)(
-                     si, 
+                     di, 
                      filename,
                      lookupDir( state_machine_regs.last_file,
                                 &fnidx2dir, &dirnames ),
-                     debug_offset + state_machine_regs.last_address, 
-                     debug_offset + state_machine_regs.address,
+                     di->text_bias + state_machine_regs.last_address, 
+                     di->text_bias + state_machine_regs.address,
                      state_machine_regs.last_line, 
                      0
                   );
@@ -788,7 +796,7 @@
                state_machine_regs.last_line = state_machine_regs.line;
             }
             state_machine_regs.basic_block = 0; /* JRS added */
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  Copy\n");
             break;
 
@@ -798,7 +806,7 @@
             data += bytes_read;
             state_machine_regs.address += adv;
             if (0) VG_(printf)("smr.a += %p\n", adv );
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  Advance PC by %d to 0x%lx\n", 
                            (Int)adv, state_machine_regs.address);
             break;
@@ -807,7 +815,7 @@
             adv = read_leb128 (data, & bytes_read, 1);
             data += bytes_read;
             state_machine_regs.line += adv;
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  Advance Line by %d to %d\n", 
                            (Int)adv, (Int)state_machine_regs.line);
             break;
@@ -816,7 +824,7 @@
             adv = read_leb128 (data, & bytes_read, 0);
             data += bytes_read;
             state_machine_regs.file = adv;
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  Set File Name to entry %d in the File Name Table\n",
                            (Int)adv);
             break;
@@ -825,7 +833,7 @@
             adv = read_leb128 (data, & bytes_read, 0);
             data += bytes_read;
             state_machine_regs.column = adv;
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  DWARF2-line: set_column\n");
             break;
 
@@ -833,13 +841,13 @@
             adv = state_machine_regs.is_stmt;
             adv = ! adv;
             state_machine_regs.is_stmt = adv;
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  DWARF2-line: negate_stmt\n");
             break;
 
          case DW_LNS_set_basic_block:
             state_machine_regs.basic_block = 1;
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  DWARF2-line: set_basic_block\n");
             break;
 
@@ -848,7 +856,7 @@
                    * info.li_min_insn_length);
             state_machine_regs.address += adv;
             if (0) VG_(printf)("smr.a += %p\n", adv );
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  Advance PC by constant %d to 0x%lx\n", 
                            (Int)adv, (Addr)state_machine_regs.address);
             break;
@@ -859,24 +867,24 @@
             data += 2;
             state_machine_regs.address += adv;
             if (0) VG_(printf)("smr.a += %p\n", adv );
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  DWARF2-line: fixed_advance_pc\n");
             break;
 
          case DW_LNS_set_prologue_end:
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  DWARF2-line: set_prologue_end\n");
             break;
 
          case DW_LNS_set_epilogue_begin:
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  DWARF2-line: set_epilogue_begin\n");
             break;
 
          case DW_LNS_set_isa:
             adv = read_leb128 (data, & bytes_read, 0);
             data += bytes_read;
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  DWARF2-line: set_isa\n");
             break;
 
@@ -886,7 +894,7 @@
                read_leb128 (data, &bytes_read, 0);
                data += bytes_read;
             }
-            if (si->ddump_line)
+            if (di->ddump_line)
                VG_(printf)("  Unknown opcode %d\n", (Int)op_code);
             break;
          }
@@ -896,7 +904,7 @@
 
    } /* while (data < end_of_sequence) */
 
-   if (si->ddump_line)
+   if (di->ddump_line)
       VG_(printf)("\n");
 
   out:
@@ -952,7 +960,7 @@
                                   UChar*    unitblock_img,
                                   UChar*    debugabbrev_img,
                                   UChar*    debugstr_img,
-                                  struct _SegInfo* si )
+                                  struct _DebugInfo* di )
 {
    UInt   acode, abcode;
    ULong  atoffs, blklen;
@@ -1108,60 +1116,67 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
-
-/* Collect the debug info from dwarf2 debugging sections
+/* Collect the debug info from DWARF3 debugging sections
  * of a given module.
  * 
  * Inputs: given .debug_xxx sections
- * Output: update si to contain all the dwarf2 debug infos
+ * Output: update di to contain all the DWARF3 debug infos
  */
-void ML_(read_debuginfo_dwarf2) 
-        ( struct _SegInfo* si, OffT debug_offset,
-          UChar* debuginfo_img,   Int debug_info_sz, /* .debug_info */
-          UChar* debugabbrev_img,                    /* .debug_abbrev */
-          UChar* debugline_img,   Int debug_line_sz, /* .debug_line */
-          UChar* debugstr_img )                      /* .debug_str */
+void ML_(read_debuginfo_dwarf3)
+        ( struct _DebugInfo* di,
+          UChar* debug_info_img, Word debug_info_sz, /* .debug_info */
+          UChar* debug_abbv_img, Word debug_abbv_sz, /* .debug_abbrev */
+          UChar* debug_line_img, Word debug_line_sz, /* .debug_line */
+          UChar* debug_str_img,  Word debug_str_sz ) /* .debug_str */
 {
    UnitInfo ui;
    UShort   ver;
    UChar*   block_img;
-   UChar*   end_img = debuginfo_img + debug_info_sz;
+   UChar*   end1_img;
    ULong    blklen;
    Bool     blklen_is_64;
-   Int      blklen_len = 0;
+   Int      blklen_len;
+
+   end1_img  = debug_info_img + debug_info_sz;
+   blklen_len = 0;
 
    /* Make sure we at least have a header for the first block */
    if (debug_info_sz < 4) {
-     ML_(symerr)( "Last block truncated in .debug_info; ignoring" );
+      ML_(symerr)( di, True, 
+                   "Last block truncated in .debug_info; ignoring" );
       return;
    }
 
    /* Iterate on all the blocks we find in .debug_info */
-   for ( block_img = debuginfo_img; block_img < end_img - 4; 
-                                    block_img += blklen + blklen_len ) {
+   for ( block_img = debug_info_img; 
+         block_img < end1_img - 4; 
+         block_img += blklen + blklen_len ) {
 
       /* Read the compilation unit header in .debug_info section - See
          p 70 */
       /* This block length */
       blklen     = read_initial_length_field( block_img, &blklen_is_64 );
       blklen_len = blklen_is_64 ? 12 : 4;
-      if ( block_img + blklen + blklen_len > end_img ) {
-         ML_(symerr)( "Last block truncated in .debug_info; ignoring" );
+      if ( block_img + blklen + blklen_len > end1_img ) {
+         ML_(symerr)( di, True,
+                      "Last block truncated in .debug_info; ignoring" );
          return;
       }
 
       /* version should be 2 */
       ver = *((UShort*)( block_img + blklen_len ));
       if ( ver != 2 ) {
-         ML_(symerr)( "Ignoring non-dwarf2 block in .debug_info" );
+         ML_(symerr)( di, True,
+                      "Ignoring non-dwarf2 block in .debug_info" );
          continue;
       }
       
       /* Fill ui with offset in .debug_line and compdir */
       if (0)
          VG_(printf)( "Reading UnitInfo at 0x%x.....\n", 
-                      block_img - debuginfo_img );
-      read_unitinfo_dwarf2( &ui, block_img, debugabbrev_img, debugstr_img, si );
+                      block_img - debug_info_img );
+      read_unitinfo_dwarf2( &ui, block_img, 
+                                 debug_abbv_img, debug_str_img, di );
       if (0)
          VG_(printf)( "   => LINES=0x%llx    NAME=%s     DIR=%s\n", 
                       ui.stmt_list, ui.name, ui.compdir );
@@ -1175,8 +1190,8 @@
                      debug_line_sz, ui.stmt_list, ui.name );
       /* Read the .debug_line block for this compile unit */
       read_dwarf2_lineblock( 
-         si, debug_offset, &ui, debugline_img + ui.stmt_list, 
-                                debug_line_sz - ui.stmt_list );
+         di, &ui, debug_line_img + ui.stmt_list, 
+                  debug_line_sz  - ui.stmt_list );
    }
 }
 
@@ -1337,7 +1352,7 @@
 /* end of enums taken from gdb-6.0 sources */
 
 void ML_(read_debuginfo_dwarf1) ( 
-        struct _SegInfo* si, 
+        struct _DebugInfo* di, 
         UChar* dwarf1d, Int dwarf1d_sz, 
         UChar* dwarf1l, Int dwarf1l_sz )
 {
@@ -1442,7 +1457,7 @@
          UChar* ptr;
          UInt   prev_line, prev_delta;
 
-         curr_filenm = ML_(addStr) ( si, src_filename, -1 );
+         curr_filenm = ML_(addStr) ( di, src_filename, -1 );
          prev_line = prev_delta = 0;
 
          ptr = dwarf1l + stmt_list;
@@ -1463,7 +1478,7 @@
 	    if (delta > 0 && prev_line > 0) {
 	       if (0) VG_(printf) ("     %d  %d-%d\n",
                                    prev_line, prev_delta, delta-1);
-	       ML_(addLineInfo) ( si, curr_filenm, NULL,
+	       ML_(addLineInfo) ( di, curr_filenm, NULL,
 		 	          base + prev_delta, base + delta,
 			          prev_line, 0 );
 	    }
@@ -1515,7 +1530,7 @@
    the CFA]. 
 
    JRS: on amd64, the dwarf register numbering is, as per
-   gdb-6.3/gdb/tdep-amd64.c and also amd64-abi-0.98.pdf:
+   gdb-6.3/gdb/amd64-tdep.c and also amd64-abi-0.98.pdf:
 
       0    1    2    3    4    5    6    7
       RAX  RDX  RCX  RBX  RSI  RDI  RBP  RSP
@@ -1771,7 +1786,7 @@
 #  define SP_REG         1
 #  define RA_REG_DEFAULT 8     // CAB: What's a good default ?
 #else
-#  error Unknown platform
+#  error "Unknown platform"
 #endif
 
 /* the number of regs we are prepared to unwind */
@@ -1947,13 +1962,6 @@
    VG_(printf)("\n");
 }
 
-static void* symtab_alloc ( SizeT szB ) {
-   return VG_(arena_malloc)( VG_AR_SYMTAB, szB );
-}
-static void symtab_free ( void* v ) {
-   VG_(arena_free)( VG_AR_SYMTAB, v );
-}
-
 static void initUnwindContext ( /*OUT*/UnwindContext* ctx )
 {
    Int i;
@@ -2019,7 +2027,7 @@
 static Bool summarise_context( /*OUT*/DiCfSI* si,
                                Addr loc_start,
 	                       UnwindContext* ctx,
-                               struct _SegInfo* seginfo )
+                               struct _DebugInfo* debuginfo )
 {
    Int why = 0;
    initCfiSI(si);
@@ -2030,12 +2038,12 @@
       XArray *src, *dst;
       Int    conv;
       src = ctx->exprs;
-      dst = seginfo->cfsi_exprs;
+      dst = debuginfo->cfsi_exprs;
       if (src && (VG_(sizeXA)(src) > 0) && (!dst)) {
-         dst = VG_(newXA)( symtab_alloc, symtab_free,
+         dst = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
                            sizeof(CfiExpr) );
          vg_assert(dst);
-         seginfo->cfsi_exprs = dst;
+         debuginfo->cfsi_exprs = dst;
       }
       conv = copy_convert_CfiExpr_tree
                     ( dst, ctx, ctx->cfa_expr_ix );
@@ -2043,7 +2051,7 @@
       if (conv == -1) { why = 6; goto failed; }
       si->cfa_how = CFIC_EXPR;
       si->cfa_off = conv;
-      if (0 && seginfo->ddump_frames)
+      if (0 && debuginfo->ddump_frames)
          ML_(ppCfiExpr)(dst, conv);
    } else
    if (ctx->cfa_is_regoff && ctx->cfa_reg == SP_REG) {
@@ -2072,12 +2080,13 @@
          XArray *src, *dst;                                   \
          Int    conv;                                         \
          src = ctx->exprs;                                    \
-         dst = seginfo->cfsi_exprs;                           \
+         dst = debuginfo->cfsi_exprs;                         \
          if (src && (VG_(sizeXA)(src) > 0) && (!dst)) {       \
-            dst = VG_(newXA)( symtab_alloc, symtab_free,      \
+            dst = VG_(newXA)( ML_(dinfo_zalloc),              \
+                              ML_(dinfo_free),                \
                               sizeof(CfiExpr) );              \
             vg_assert(dst);                                   \
-            seginfo->cfsi_exprs = dst;                        \
+            debuginfo->cfsi_exprs = dst;                      \
          }                                                    \
          conv = copy_convert_CfiExpr_tree                     \
                        ( dst, ctx, _ctxreg.arg );             \
@@ -2085,7 +2094,7 @@
          if (conv == -1) { why = 7; goto failed; }            \
          _how = CFIR_EXPR;                                    \
          _off = conv;                                         \
-         if (0 && seginfo->ddump_frames)                      \
+         if (0 && debuginfo->ddump_frames)                    \
             ML_(ppCfiExpr)(dst, conv);                        \
          break;                                               \
       }                                                       \
@@ -2125,7 +2134,7 @@
    return True;
 
   failed:
-   if (VG_(clo_verbosity) > 2 || seginfo->trace_cfi) {
+   if (VG_(clo_verbosity) > 2 || debuginfo->trace_cfi) {
       VG_(message)(Vg_DebugMsg,
                   "summarise_context(loc_start = %p)"
                   ": cannot summarise(why=%d):   ", loc_start, why);
@@ -2436,175 +2445,6 @@
 
 /* ------------ Run/show DWARF3 expressions ---------- */
 
-/* Taken from binutils-2.17/include/elf/dwarf2.h */
-enum dwarf_location_atom
-  {
-    DW_OP_addr = 0x03,
-    DW_OP_deref = 0x06,
-    DW_OP_const1u = 0x08,
-    DW_OP_const1s = 0x09,
-    DW_OP_const2u = 0x0a,
-    DW_OP_const2s = 0x0b,
-    DW_OP_const4u = 0x0c,
-    DW_OP_const4s = 0x0d,
-    DW_OP_const8u = 0x0e,
-    DW_OP_const8s = 0x0f,
-    DW_OP_constu = 0x10,
-    DW_OP_consts = 0x11,
-    DW_OP_dup = 0x12,
-    DW_OP_drop = 0x13,
-    DW_OP_over = 0x14,
-    DW_OP_pick = 0x15,
-    DW_OP_swap = 0x16,
-    DW_OP_rot = 0x17,
-    DW_OP_xderef = 0x18,
-    DW_OP_abs = 0x19,
-    DW_OP_and = 0x1a,
-    DW_OP_div = 0x1b,
-    DW_OP_minus = 0x1c,
-    DW_OP_mod = 0x1d,
-    DW_OP_mul = 0x1e,
-    DW_OP_neg = 0x1f,
-    DW_OP_not = 0x20,
-    DW_OP_or = 0x21,
-    DW_OP_plus = 0x22,
-    DW_OP_plus_uconst = 0x23,
-    DW_OP_shl = 0x24,
-    DW_OP_shr = 0x25,
-    DW_OP_shra = 0x26,
-    DW_OP_xor = 0x27,
-    DW_OP_bra = 0x28,
-    DW_OP_eq = 0x29,
-    DW_OP_ge = 0x2a,
-    DW_OP_gt = 0x2b,
-    DW_OP_le = 0x2c,
-    DW_OP_lt = 0x2d,
-    DW_OP_ne = 0x2e,
-    DW_OP_skip = 0x2f,
-    DW_OP_lit0 = 0x30,
-    DW_OP_lit1 = 0x31,
-    DW_OP_lit2 = 0x32,
-    DW_OP_lit3 = 0x33,
-    DW_OP_lit4 = 0x34,
-    DW_OP_lit5 = 0x35,
-    DW_OP_lit6 = 0x36,
-    DW_OP_lit7 = 0x37,
-    DW_OP_lit8 = 0x38,
-    DW_OP_lit9 = 0x39,
-    DW_OP_lit10 = 0x3a,
-    DW_OP_lit11 = 0x3b,
-    DW_OP_lit12 = 0x3c,
-    DW_OP_lit13 = 0x3d,
-    DW_OP_lit14 = 0x3e,
-    DW_OP_lit15 = 0x3f,
-    DW_OP_lit16 = 0x40,
-    DW_OP_lit17 = 0x41,
-    DW_OP_lit18 = 0x42,
-    DW_OP_lit19 = 0x43,
-    DW_OP_lit20 = 0x44,
-    DW_OP_lit21 = 0x45,
-    DW_OP_lit22 = 0x46,
-    DW_OP_lit23 = 0x47,
-    DW_OP_lit24 = 0x48,
-    DW_OP_lit25 = 0x49,
-    DW_OP_lit26 = 0x4a,
-    DW_OP_lit27 = 0x4b,
-    DW_OP_lit28 = 0x4c,
-    DW_OP_lit29 = 0x4d,
-    DW_OP_lit30 = 0x4e,
-    DW_OP_lit31 = 0x4f,
-    DW_OP_reg0 = 0x50,
-    DW_OP_reg1 = 0x51,
-    DW_OP_reg2 = 0x52,
-    DW_OP_reg3 = 0x53,
-    DW_OP_reg4 = 0x54,
-    DW_OP_reg5 = 0x55,
-    DW_OP_reg6 = 0x56,
-    DW_OP_reg7 = 0x57,
-    DW_OP_reg8 = 0x58,
-    DW_OP_reg9 = 0x59,
-    DW_OP_reg10 = 0x5a,
-    DW_OP_reg11 = 0x5b,
-    DW_OP_reg12 = 0x5c,
-    DW_OP_reg13 = 0x5d,
-    DW_OP_reg14 = 0x5e,
-    DW_OP_reg15 = 0x5f,
-    DW_OP_reg16 = 0x60,
-    DW_OP_reg17 = 0x61,
-    DW_OP_reg18 = 0x62,
-    DW_OP_reg19 = 0x63,
-    DW_OP_reg20 = 0x64,
-    DW_OP_reg21 = 0x65,
-    DW_OP_reg22 = 0x66,
-    DW_OP_reg23 = 0x67,
-    DW_OP_reg24 = 0x68,
-    DW_OP_reg25 = 0x69,
-    DW_OP_reg26 = 0x6a,
-    DW_OP_reg27 = 0x6b,
-    DW_OP_reg28 = 0x6c,
-    DW_OP_reg29 = 0x6d,
-    DW_OP_reg30 = 0x6e,
-    DW_OP_reg31 = 0x6f,
-    DW_OP_breg0 = 0x70,
-    DW_OP_breg1 = 0x71,
-    DW_OP_breg2 = 0x72,
-    DW_OP_breg3 = 0x73,
-    DW_OP_breg4 = 0x74,
-    DW_OP_breg5 = 0x75,
-    DW_OP_breg6 = 0x76,
-    DW_OP_breg7 = 0x77,
-    DW_OP_breg8 = 0x78,
-    DW_OP_breg9 = 0x79,
-    DW_OP_breg10 = 0x7a,
-    DW_OP_breg11 = 0x7b,
-    DW_OP_breg12 = 0x7c,
-    DW_OP_breg13 = 0x7d,
-    DW_OP_breg14 = 0x7e,
-    DW_OP_breg15 = 0x7f,
-    DW_OP_breg16 = 0x80,
-    DW_OP_breg17 = 0x81,
-    DW_OP_breg18 = 0x82,
-    DW_OP_breg19 = 0x83,
-    DW_OP_breg20 = 0x84,
-    DW_OP_breg21 = 0x85,
-    DW_OP_breg22 = 0x86,
-    DW_OP_breg23 = 0x87,
-    DW_OP_breg24 = 0x88,
-    DW_OP_breg25 = 0x89,
-    DW_OP_breg26 = 0x8a,
-    DW_OP_breg27 = 0x8b,
-    DW_OP_breg28 = 0x8c,
-    DW_OP_breg29 = 0x8d,
-    DW_OP_breg30 = 0x8e,
-    DW_OP_breg31 = 0x8f,
-    DW_OP_regx = 0x90,
-    DW_OP_fbreg = 0x91,
-    DW_OP_bregx = 0x92,
-    DW_OP_piece = 0x93,
-    DW_OP_deref_size = 0x94,
-    DW_OP_xderef_size = 0x95,
-    DW_OP_nop = 0x96,
-    /* DWARF 3 extensions.  */
-    DW_OP_push_object_address = 0x97,
-    DW_OP_call2 = 0x98,
-    DW_OP_call4 = 0x99,
-    DW_OP_call_ref = 0x9a,
-    DW_OP_form_tls_address = 0x9b,
-    DW_OP_call_frame_cfa = 0x9c,
-    DW_OP_bit_piece = 0x9d,
-    /* GNU extensions.  */
-    DW_OP_GNU_push_tls_address = 0xe0,
-    /* HP extensions.  */
-    DW_OP_HP_unknown     = 0xe0, /* Ouch, the same as GNU_push_tls_address.  */
-    DW_OP_HP_is_value    = 0xe1,
-    DW_OP_HP_fltconst4   = 0xe2,
-    DW_OP_HP_fltconst8   = 0xe3,
-    DW_OP_HP_mod_range   = 0xe4,
-    DW_OP_HP_unmod_range = 0xe5,
-    DW_OP_HP_tls         = 0xe6
-  };
-
-
 /* Convert the DWARF3 expression in expr[0 .. exprlen-1] into a dag
    (of CfiExprs) stored in ctx->exprs, and return the index in
    ctx->exprs of the root node.  Or fail in which case return -1. */
@@ -2791,7 +2631,7 @@
                                 UChar* instr,
                                 UnwindContext* restore_ctx,
                                 AddressDecodingInfo* adi,
-                                struct _SegInfo* si )
+                                struct _DebugInfo* di )
 {
    Int    off, reg, reg2, nleb, len;
    UInt   delta;
@@ -2800,13 +2640,13 @@
    Int    i   = 0;
    UChar  hi2 = (instr[i] >> 6) & 3;
    UChar  lo6 = instr[i] & 0x3F;
-   Addr   printing_bias = ((Addr)ctx->initloc) - ((Addr)si->text_bias);
+   Addr   printing_bias = ((Addr)ctx->initloc) - ((Addr)di->text_bias);
    i++;
 
    if (hi2 == DW_CFA_advance_loc) {
       delta = (UInt)lo6;
       ctx->loc += delta;
-      if (si->ddump_frames)
+      if (di->ddump_frames)
          VG_(printf)("  DW_CFA_advance_loc: %d to %08lx\n", 
                      (Int)delta, (Addr)ctx->loc + printing_bias);
       return i;
@@ -2821,7 +2661,7 @@
          return 0; /* fail */
       ctx->reg[reg].tag = RR_CFAOff;
       ctx->reg[reg].arg = off * ctx->data_a_f;
-      if (si->ddump_frames)
+      if (di->ddump_frames)
          VG_(printf)("  DW_CFA_offset: r%d at cfa%s%d\n",
                      (Int)reg, ctx->reg[reg].arg < 0 ? "" : "+", 
                      (Int)ctx->reg[reg].arg );
@@ -2835,7 +2675,7 @@
       if (restore_ctx == NULL)
          return 0; /* fail */
       ctx->reg[reg] = restore_ctx->reg[reg];
-      if (si->ddump_frames)
+      if (di->ddump_frames)
          VG_(printf)("  DW_CFA_restore: r%d\n", (Int)reg);
       return i;
    }
@@ -2844,7 +2684,7 @@
 
    switch (lo6) {
       case DW_CFA_nop: 
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_nop\n");
          break;
       case DW_CFA_set_loc:
@@ -2856,27 +2696,27 @@
             DWARF3 spec. */
          ctx->loc = read_encoded_Addr(&len, adi, &instr[i]);
          i += len;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_set_loc\n");
          break;
       case DW_CFA_advance_loc1:
          delta = (UInt)read_UChar(&instr[i]); i+= sizeof(UChar);
          ctx->loc += delta;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_advance_loc1: %d to %08lx\n", 
                         (Int)delta, (Addr)ctx->loc + printing_bias);
          break;
       case DW_CFA_advance_loc2:
          delta = (UInt)read_UShort(&instr[i]); i+= sizeof(UShort);
          ctx->loc += delta;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_advance_loc2: %d to %08lx\n", 
                         (Int)delta, (Addr)ctx->loc + printing_bias);
          break;
       case DW_CFA_advance_loc4:
          delta = (UInt)read_UInt(&instr[i]); i+= sizeof(UInt);
          ctx->loc += delta;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_advance_loc4: %d to %08lx\n", 
                         (Int)delta, (Addr)ctx->loc + printing_bias);
          break;
@@ -2892,7 +2732,7 @@
          ctx->cfa_expr_ix   = 0;
          ctx->cfa_reg       = reg;
          ctx->cfa_off       = off;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_def_cfa: r%d ofs %d\n", (Int)reg, (Int)off);
          break;
 
@@ -2907,7 +2747,7 @@
          ctx->cfa_expr_ix   = 0;
          ctx->cfa_reg       = reg;
          ctx->cfa_off       = off * ctx->data_a_f;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_def_cfa_sf\n");
          break;
 
@@ -2922,7 +2762,7 @@
             return 0; /* fail */
          ctx->reg[reg].tag = RR_Reg;
          ctx->reg[reg].arg = reg2;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_register: r%d in r%d\n", 
                         (Int)reg, (Int)reg2);
          break;
@@ -2936,7 +2776,7 @@
             return 0; /* fail */
          ctx->reg[reg].tag = RR_CFAOff;
          ctx->reg[reg].arg = off * ctx->data_a_f;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_offset_extended\n");
          break;
 
@@ -2949,7 +2789,7 @@
             return 0; /* fail */
          ctx->reg[reg].tag = RR_CFAOff;
          ctx->reg[reg].arg = off * ctx->data_a_f;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_offset_extended_sf: r%d at cfa%s%d\n", 
                         reg, ctx->reg[reg].arg < 0 ? "" : "+", 
                         (Int)ctx->reg[reg].arg);
@@ -2964,7 +2804,7 @@
             return 0; /* fail */
          ctx->reg[reg].tag = RR_CFAOff;
          ctx->reg[reg].arg = (-off) * ctx->data_a_f;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_GNU_negative_offset_extended\n");
          break;
 
@@ -2976,7 +2816,7 @@
 	 if (restore_ctx == NULL)
 	    return 0; /* fail */
 	 ctx->reg[reg] = restore_ctx->reg[reg];
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_restore_extended\n");
          break;
 
@@ -2989,7 +2829,7 @@
             return 0; /* fail */
          ctx->reg[reg].tag = RR_CFAValOff;
          ctx->reg[reg].arg = off * ctx->data_a_f;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_val_offset\n");
          break;
 
@@ -3002,7 +2842,7 @@
             return 0; /* fail */
          ctx->reg[reg].tag = RR_CFAValOff;
          ctx->reg[reg].arg = off * ctx->data_a_f;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_val_offset_sf\n");
          break;
 
@@ -3015,7 +2855,7 @@
          ctx->cfa_expr_ix   = 0;
          ctx->cfa_reg       = reg;
          /* ->cfa_off unchanged */
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_def_cfa_reg: r%d\n", (Int)reg );
          break;
 
@@ -3026,7 +2866,7 @@
          ctx->cfa_expr_ix   = 0;
          /* ->reg is unchanged */
          ctx->cfa_off       = off;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_def_cfa_offset: %d\n", (Int)off);
          break;
 
@@ -3037,7 +2877,7 @@
          ctx->cfa_expr_ix   = 0;
          /* ->reg is unchanged */
          ctx->cfa_off       = off * ctx->data_a_f;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_def_cfa_offset_sf: %d\n", ctx->cfa_off);
          break;
 
@@ -3048,7 +2888,7 @@
             return 0; /* fail */
          ctx->reg[reg].tag = RR_Undef;
          ctx->reg[reg].arg = 0;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_undefined\n");
          break;
 
@@ -3057,7 +2897,7 @@
             ignores these. */
          off = read_leb128( &instr[i], &nleb, 0 );
          i += nleb;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_GNU_args_size (ignored)\n");
          break;
 
@@ -3073,14 +2913,14 @@
          i += len;
          if (reg < 0 || reg >= N_CFI_REGS)
             return 0; /* fail */
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_expression: r%d (", 
                         (Int)reg);
          /* Convert the expression into a dag rooted at ctx->exprs index j,
             or fail. */
          j = dwarfexpr_to_dag ( ctx, expr, len, True/*push CFA at start*/, 
-                                si->ddump_frames);
-         if (si->ddump_frames)
+                                di->ddump_frames);
+         if (di->ddump_frames)
             VG_(printf)(")\n");
          vg_assert(j >= -1);
          if (j >= 0) {
@@ -3104,14 +2944,14 @@
          i += len;
          if (reg < 0 || reg >= N_CFI_REGS)
             return 0; /* fail */
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_val_expression: r%d (", 
                         (Int)reg);
          /* Convert the expression into a dag rooted at ctx->exprs index j,
             or fail. */
          j = dwarfexpr_to_dag ( ctx, expr, len, True/*push CFA at start*/, 
-                                si->ddump_frames);
-         if (si->ddump_frames)
+                                di->ddump_frames);
+         if (di->ddump_frames)
             VG_(printf)(")\n");
          vg_assert(j >= -1);
          if (j >= 0) {
@@ -3129,13 +2969,13 @@
          i += nleb;
          expr = &instr[i];
          i += len;
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  DW_CFA_def_cfa_expression (");
          /* Convert the expression into a dag rooted at ctx->exprs index j,
             or fail. */
          j = dwarfexpr_to_dag ( ctx, expr, len, True/*push CFA at start*/, 
-                                si->ddump_frames);
-         if (si->ddump_frames)
+                                di->ddump_frames);
+         if (di->ddump_frames)
             VG_(printf)(")\n");
          ctx->cfa_is_regoff = False;
          ctx->cfa_reg       = 0;
@@ -3146,14 +2986,14 @@
       case DW_CFA_GNU_window_save:
          /* Ignored.  This appears to be sparc-specific; quite why it
             turns up in SuSE-supplied x86 .so's beats me. */
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("DW_CFA_GNU_window_save\n");
          break;
 
       default: 
          VG_(message)(Vg_DebugMsg, "DWARF2 CFI reader: unhandled CFI "
                                    "instruction 0:%d", (Int)lo6); 
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  rci:run_CF_instruction:default\n");
          i = 0;
          break;
@@ -3410,7 +3250,7 @@
    reached, or until there is a failure.  Return True iff success. 
 */
 static 
-Bool run_CF_instructions ( struct _SegInfo* si,
+Bool run_CF_instructions ( struct _DebugInfo* di,
                            Bool record,
                            UnwindContext* ctx, UChar* instrs, Int ilen,
                            UWord fde_arange,
@@ -3428,17 +3268,17 @@
       if (i >= ilen) break;
       if (0) (void)show_CF_instruction( &instrs[i], adi, 
                                         ctx->code_a_f, ctx->data_a_f );
-      j = run_CF_instruction( ctx, &instrs[i], restore_ctx, adi, si );
+      j = run_CF_instruction( ctx, &instrs[i], restore_ctx, adi, di );
       if (j == 0)
          return False; /* execution failed */
       i += j;
       if (0) ppUnwindContext(ctx);
       if (record && loc_prev != ctx->loc) {
-         summ_ok = summarise_context ( &cfsi, loc_prev, ctx, si );
+         summ_ok = summarise_context ( &cfsi, loc_prev, ctx, di );
          if (summ_ok) {
-            ML_(addDiCfSI)(si, &cfsi);
-            if (si->trace_cfi)
-               ML_(ppDiCfSI)(si->cfsi_exprs, &cfsi);
+            ML_(addDiCfSI)(di, &cfsi);
+            if (di->trace_cfi)
+               ML_(ppDiCfSI)(di->cfsi_exprs, &cfsi);
          }
       }
    }
@@ -3446,11 +3286,11 @@
       loc_prev = ctx->loc;
       ctx->loc = fde_arange;
       if (record) {
-         summ_ok = summarise_context ( &cfsi, loc_prev, ctx, si );
+         summ_ok = summarise_context ( &cfsi, loc_prev, ctx, di );
          if (summ_ok) {
-            ML_(addDiCfSI)(si, &cfsi);
-            if (si->trace_cfi)
-               ML_(ppDiCfSI)(si->cfsi_exprs, &cfsi);
+            ML_(addDiCfSI)(di, &cfsi);
+            if (di->trace_cfi)
+               ML_(ppDiCfSI)(di->cfsi_exprs, &cfsi);
          }
       }
    }
@@ -3496,8 +3336,7 @@
 
 
 void ML_(read_callframe_info_dwarf3)
-        ( /*OUT*/struct _SegInfo* si, 
-          UChar* ehframe_image, Int ehframe_sz, Addr ehframe_avma )
+        ( /*OUT*/struct _DebugInfo* di, UChar* ehframe_image )
 {
    Int    nbytes;
    HChar* how = NULL;
@@ -3509,12 +3348,13 @@
    return;
 #  endif
 
-   if (si->trace_cfi) {
+   if (di->trace_cfi) {
       VG_(printf)("\n-----------------------------------------------\n");
-      VG_(printf)("CFI info: szB %d, _avma %p, _image %p\n",
-	          ehframe_sz, (void*)ehframe_avma, (void*)ehframe_image );
+      VG_(printf)("CFI info: szB %ld, _avma %p, _image %p\n",
+                  di->ehframe_size, (void*)di->ehframe_avma, 
+                  (void*)ehframe_image );
       VG_(printf)("CFI info: name %s\n",
-		  si->filename );
+                  di->filename );
    }
 
    /* Loop over CIEs/FDEs */
@@ -3545,11 +3385,11 @@
       Bool   dw64;
 
       /* Are we done? */
-      if (data == ehframe_image + ehframe_sz)
+      if (data == ehframe_image + di->ehframe_size)
          return;
 
       /* Overshot the end?  Means something is wrong */
-      if (data > ehframe_image + ehframe_sz) {
+      if (data > ehframe_image + di->ehframe_size) {
          how = "overran the end of .eh_frame";
          goto bad;
       }
@@ -3558,12 +3398,12 @@
          Figure out which it is. */
 
       ciefde_start = data;
-      if (si->trace_cfi) 
+      if (di->trace_cfi) 
          VG_(printf)("\ncie/fde.start   = %p (ehframe_image + 0x%lx)\n", 
                      ciefde_start, ciefde_start - ehframe_image);
 
       ciefde_len = (ULong) read_UInt(data); data += sizeof(UInt);
-      if (si->trace_cfi) 
+      if (di->trace_cfi) 
          VG_(printf)("cie/fde.length  = %lld\n", ciefde_len);
 
       /* Apparently, if the .length field is zero, we are at the end
@@ -3571,7 +3411,7 @@
          Specification (see comments far above here) and is one of the
          places where .eh_frame and .debug_frame data differ. */
       if (ciefde_len == 0) {
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("%08lx ZERO terminator\n\n",
                         ((Addr)ciefde_start) - ((Addr)ehframe_image));
          return;
@@ -3596,7 +3436,7 @@
          data += sizeof(UInt); /* XXX see XXX below */
       }
 
-      if (si->trace_cfi) 
+      if (di->trace_cfi) 
          VG_(printf)("cie.pointer     = %lld\n", cie_pointer);
 
       /* If cie_pointer is zero, we've got a CIE; else it's an FDE. */
@@ -3607,7 +3447,7 @@
          UChar* cie_augmentation;
 
          /* --------- CIE --------- */
-	 if (si->trace_cfi) 
+	 if (di->trace_cfi) 
             VG_(printf)("------ new CIE (#%d of 0 .. %d) ------\n", 
                         n_CIEs, N_CIEs - 1);
 
@@ -3626,16 +3466,16 @@
             later when looking at an FDE. */
          the_CIEs[this_CIE].offset = (ULong)(ciefde_start - ehframe_image);
 
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("%08lx %08lx %08lx CIE\n",
                         ((Addr)ciefde_start) - ((Addr)ehframe_image),
                         (Addr)ciefde_len,
                         (Addr)(UWord)cie_pointer );
 
          cie_version = read_UChar(data); data += sizeof(UChar);
-         if (si->trace_cfi)
+         if (di->trace_cfi)
             VG_(printf)("cie.version     = %d\n", (Int)cie_version);
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  Version:               %d\n", (Int)cie_version);
          if (cie_version != 1) {
             how = "unexpected CIE version (not 1)";
@@ -3644,9 +3484,9 @@
 
          cie_augmentation = data;
          data += 1 + VG_(strlen)(cie_augmentation);
-         if (si->trace_cfi) 
+         if (di->trace_cfi) 
             VG_(printf)("cie.augment     = \"%s\"\n", cie_augmentation);
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  Augmentation:          \"%s\"\n", cie_augmentation);
 
          if (cie_augmentation[0] == 'e' && cie_augmentation[1] == 'h') {
@@ -3656,28 +3496,28 @@
 
          the_CIEs[this_CIE].code_a_f = read_leb128( data, &nbytes, 0);
          data += nbytes;
-         if (si->trace_cfi) 
+         if (di->trace_cfi) 
             VG_(printf)("cie.code_af     = %d\n", 
                         the_CIEs[this_CIE].code_a_f);
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  Code alignment factor: %d\n",
                         (Int)the_CIEs[this_CIE].code_a_f);
 
          the_CIEs[this_CIE].data_a_f = read_leb128( data, &nbytes, 1);
          data += nbytes;
-         if (si->trace_cfi) 
+         if (di->trace_cfi) 
             VG_(printf)("cie.data_af     = %d\n",
                         the_CIEs[this_CIE].data_a_f);
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  Data alignment factor: %d\n",
                         (Int)the_CIEs[this_CIE].data_a_f);
 
          the_CIEs[this_CIE].ra_reg = (Int)read_UChar(data); 
          data += sizeof(UChar);
-         if (si->trace_cfi) 
+         if (di->trace_cfi) 
             VG_(printf)("cie.ra_reg      = %d\n", 
                         the_CIEs[this_CIE].ra_reg);
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("  Return address column: %d\n",
                         (Int)the_CIEs[this_CIE].ra_reg);
 
@@ -3694,7 +3534,7 @@
             data += nbytes;
             the_CIEs[this_CIE].instrs = data + length;
             cie_augmentation++;
-            if (si->ddump_frames) {
+            if (di->ddump_frames) {
                UInt i;
                VG_(printf)("  Augmentation data:    ");
                for (i = 0; i < length; i++)
@@ -3738,20 +3578,20 @@
 
         done_augmentation:
 
-         if (si->trace_cfi) 
+         if (di->trace_cfi) 
             VG_(printf)("cie.encoding    = 0x%x\n", 
                         the_CIEs[this_CIE].address_encoding);
 
          the_CIEs[this_CIE].instrs = data;
          the_CIEs[this_CIE].ilen
             = ciefde_start + ciefde_len + sizeof(UInt) - data;
-         if (si->trace_cfi) {
+         if (di->trace_cfi) {
             VG_(printf)("cie.instrs      = %p\n", the_CIEs[this_CIE].instrs);
             VG_(printf)("cie.ilen        = %d\n", the_CIEs[this_CIE].ilen);
 	 }
 
          if (the_CIEs[this_CIE].ilen < 0
-             || the_CIEs[this_CIE].ilen > ehframe_sz) {
+             || the_CIEs[this_CIE].ilen > di->ehframe_size) {
             how = "implausible # cie initial insns";
             goto bad;
          }
@@ -3760,22 +3600,22 @@
 
          /* Show the CIE's instructions (the preamble for each FDE
             that uses this CIE). */ 
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("\n");
 
-         if (si->trace_cfi || si->ddump_frames) {
+         if (di->trace_cfi || di->ddump_frames) {
             AddressDecodingInfo adi;
             adi.encoding      = the_CIEs[this_CIE].address_encoding;
             adi.ehframe_image = ehframe_image;
-            adi.ehframe_avma  = ehframe_avma;
-            adi.text_bias     = si->text_bias;
+            adi.ehframe_avma  = di->ehframe_avma;
+            adi.text_bias     = di->text_bias;
             show_CF_instructions( the_CIEs[this_CIE].instrs, 
                                   the_CIEs[this_CIE].ilen, &adi,
                                   the_CIEs[this_CIE].code_a_f,
                                   the_CIEs[this_CIE].data_a_f );
          }
 
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("\n");
 
       } else {
@@ -3814,17 +3654,17 @@
 
          adi.encoding      = the_CIEs[cie].address_encoding;
          adi.ehframe_image = ehframe_image;
-         adi.ehframe_avma  = ehframe_avma;
-         adi.text_bias     = si->text_bias;
+         adi.ehframe_avma  = di->ehframe_avma;
+         adi.text_bias     = di->text_bias;
          fde_initloc = read_encoded_Addr(&nbytes, &adi, data);
          data += nbytes;
-         if (si->trace_cfi) 
+         if (di->trace_cfi) 
             VG_(printf)("fde.initloc     = %p\n", (void*)fde_initloc);
 
          adi.encoding      = the_CIEs[cie].address_encoding & 0xf;
          adi.ehframe_image = ehframe_image;
-         adi.ehframe_avma  = ehframe_avma;
-         adi.text_bias     = si->text_bias;
+         adi.ehframe_avma  = di->ehframe_avma;
+         adi.text_bias     = di->text_bias;
 
          /* WAS (incorrectly):
             fde_arange = read_encoded_Addr(&nbytes, &adi, data);
@@ -3844,22 +3684,22 @@
            }
          }
 
-         if (si->trace_cfi) 
+         if (di->trace_cfi) 
             VG_(printf)("fde.arangec     = %p\n", (void*)fde_arange);
 
-         if (si->ddump_frames)
+         if (di->ddump_frames)
             VG_(printf)("%08lx %08lx %08lx FDE cie=%08lx pc=%08lx..%08lx\n",
                         ((Addr)ciefde_start) - ((Addr)ehframe_image),
                         (Addr)ciefde_len,
                         (Addr)(UWord)cie_pointer,
                         (Addr)look_for, 
-                        ((Addr)fde_initloc) - si->text_bias, 
-                        ((Addr)fde_initloc) - si->text_bias + fde_arange);
+                        ((Addr)fde_initloc) - di->text_bias, 
+                        ((Addr)fde_initloc) - di->text_bias + fde_arange);
 
          if (the_CIEs[cie].saw_z_augmentation) {
             UInt length = read_leb128( data, &nbytes, 0);
             data += nbytes;
-            if (si->ddump_frames && (length > 0)) {
+            if (di->ddump_frames && (length > 0)) {
                UInt i;
                VG_(printf)("  Augmentation data:    ");
                for (i = 0; i < length; i++)
@@ -3871,12 +3711,12 @@
 
          fde_instrs = data;
          fde_ilen   = ciefde_start + ciefde_len + sizeof(UInt) - data;
-         if (si->trace_cfi) {
+         if (di->trace_cfi) {
             VG_(printf)("fde.instrs      = %p\n", fde_instrs);
             VG_(printf)("fde.ilen        = %d\n", (Int)fde_ilen);
 	 }
 
-         if (fde_ilen < 0 || fde_ilen > ehframe_sz) {
+         if (fde_ilen < 0 || fde_ilen > di->ehframe_size) {
             how = "implausible # fde insns";
             goto bad;
          }
@@ -3885,10 +3725,10 @@
 
          adi.encoding      = the_CIEs[cie].address_encoding;
          adi.ehframe_image = ehframe_image;
-         adi.ehframe_avma  = ehframe_avma;
-         adi.text_bias     = si->text_bias;
+         adi.ehframe_avma  = di->ehframe_avma;
+         adi.text_bias     = di->text_bias;
 
-         if (si->trace_cfi)
+         if (di->trace_cfi)
             show_CF_instructions( fde_instrs, fde_ilen, &adi,
                                   the_CIEs[cie].code_a_f,
                                   the_CIEs[cie].data_a_f );
@@ -3898,7 +3738,7 @@
          ctx.data_a_f = the_CIEs[cie].data_a_f;
          ctx.initloc  = fde_initloc;
          ctx.ra_reg   = the_CIEs[cie].ra_reg;
-         ctx.exprs    = VG_(newXA)( symtab_alloc, symtab_free, 
+         ctx.exprs    = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free), 
                                     sizeof(CfiExpr) );
          vg_assert(ctx.exprs);
 
@@ -3908,14 +3748,14 @@
             at the time the CIE was first encountered.  Note, not
             thread safe - if this reader is ever made threaded, should
             fix properly. */
-	 { Bool hack = si->ddump_frames; 
-           si->ddump_frames = False;
+	 { Bool hack = di->ddump_frames; 
+           di->ddump_frames = False;
            initUnwindContext(&restore_ctx);
            ok = run_CF_instructions(
-                   si, False, &ctx, the_CIEs[cie].instrs, 
+                   di, False, &ctx, the_CIEs[cie].instrs, 
                    the_CIEs[cie].ilen, 0, NULL, &adi
                 );
-           si->ddump_frames = hack;
+           di->ddump_frames = hack;
          }
          /* And now run the instructions for the FDE, starting from
             the state created by running the CIE preamble
@@ -3923,10 +3763,10 @@
          if (ok) {
             restore_ctx = ctx;
 	    ok = run_CF_instructions(
-                    si, True, &ctx, fde_instrs, fde_ilen, fde_arange, 
+                    di, True, &ctx, fde_instrs, fde_ilen, fde_arange, 
                     &restore_ctx, &adi
                  );
-            if (si->ddump_frames)
+            if (di->ddump_frames)
                VG_(printf)("\n");
 	 }
 
diff --git a/coregrind/m_debuginfo/readdwarf3.c b/coregrind/m_debuginfo/readdwarf3.c
new file mode 100644
index 0000000..b7fa2c2
--- /dev/null
+++ b/coregrind/m_debuginfo/readdwarf3.c
@@ -0,0 +1,3437 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Read DWARF3 ".debug_info" sections (DIE trees).              ---*/
+/*---                                                 readdwarf3.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 OpenWorks LLP
+      info@open-works.co.uk
+
+   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., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+
+   Neither the names of the U.S. Department of Energy nor the
+   University of California nor the names of its contributors may be
+   used to endorse or promote products derived from this software
+   without prior written permission.
+*/
+
+/* REFERENCE (without which this code will not make much sense):
+
+   DWARF Debugging Information Format, Version 3, 
+   dated 20 December 2005 (the "D3 spec").
+
+   Available at http://www.dwarfstd.org/Dwarf3.pdf.  There's also a
+   .doc (MS Word) version, but for some reason the section numbers
+   between the Word and PDF versions differ by 1 in the first digit.
+   All section references in this code are to the PDF version.
+
+   CURRENT HACKS:
+
+   DW_TAG_{const,volatile}_type no DW_AT_type is allowed; it is
+      assumed to mean "const void" or "volatile void" respectively.
+      GDB appears to interpret them like this, anyway.
+
+   In many cases it is important to know the svma of a CU (the "base
+   address of the CU", as the D3 spec calls it).  There are some
+   situations in which the spec implies this value is unknown, but the
+   Dwarf3 produced by gcc-4.1 seems to assume is not unknown but
+   merely zero when not explicitly stated.  So we too have to make
+   that assumption.
+
+   TODO, 2008 Feb 17:
+
+   get rid of cu_svma_known and document the assumed-zero svma hack.
+
+   ML_(sizeOfType): differentiate between zero sized types and types
+   for which the size is unknown.  Is this important?  I don't know.
+
+   DW_AT_array_types: deal with explicit sizes (currently we compute
+   the size from the bounds and the element size, although that's
+   fragile, if the bounds incompletely specified, or completely
+   absent)
+
+   Document reason for difference (by 1) of stack preening depth in
+   parse_var_DIE vs parse_type_DIE.
+
+   Don't hand to ML_(addVars), vars whose locations are entirely in
+   registers (DW_OP_reg*).  This is merely a space-saving
+   optimisation, as ML_(evaluate_Dwarf3_Expr) should handle these
+   expressions correctly, by failing to evaluate them and hence
+   effectively ignoring the variable with which they are associated.
+
+   Deal with DW_AT_array_types which have element size != stride
+
+   In some cases, the info for a variable is split between two
+   different DIEs (generally a declarer and a definer).  We punt on
+   these.  Could do better here.
+
+   The 'data_bias' argument passed to the expression evaluator
+   (ML_(evaluate_Dwarf3_Expr)) should really be changed to a
+   MaybeUWord, to make it clear when we do vs don't know what it is
+   for the evaluation of an expression.  At the moment zero is passed
+   for this parameter in the don't know case.  That's a bit fragile
+   and obscure; using a MaybeUWord would be clearer.
+
+   POTENTIAL PERFORMANCE IMPROVEMENTS:
+
+   The number of type entities that end up in the list of TyAdmins
+   rapidly becomes huge (eg, for libQtGui.so.4.3.2 (amd64-linux, size
+   80729047 bytes), there are 786860 entries in the list).  Mostly
+   this seems to be caused by g++ adding type DIEs for all the basic
+   types once for each source file contributing to the compilation
+   unit, and for a large library they add up quickly.  That causes
+   both a lot of work for this reader module, and also wastes vast
+   amounts of memory storing this duplicated information.  We could
+   surely do a lot better here.
+
+   Handle interaction between read_DIE and parse_{var,type}_DIE
+   better.  Currently read_DIE reads the entire DIE just to find where
+   the end is (and for debug printing), so that it can later reliably
+   move the cursor to the end regardless of what parse_{var,type}_DIE
+   do.  This means many DIEs (most, even?) are read twice.  It would
+   be smarter to make parse_{var,type}_DIE return a Bool indicating
+   whether or not they advanced the DIE cursor, and only if they
+   didn't should read_DIE itself read through the DIE.
+
+   ML_(addVar) and add_var_to_arange: quite a lot of DiAddrRanges have
+   zero variables in their .vars XArray.  Rather than have an XArray
+   with zero elements (which uses 2 malloc'd blocks), allow the .vars
+   pointer to be NULL in this case.
+
+   More generally, reduce the amount of memory allocated and freed
+   while reading Dwarf3 type/variable information.  Even modest (20MB)
+   objects cause this module to allocate and free hundreds of
+   thousands of small blocks, and ML_(arena_malloc) and its various
+   groupies always show up at the top of performance profiles. */
+
+#include "pub_core_basics.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_options.h"
+#include "pub_core_xarray.h"
+#include "priv_misc.h"             /* dinfo_zalloc/free */
+#include "priv_tytypes.h"
+#include "priv_d3basics.h"
+#include "priv_storage.h"
+#include "priv_readdwarf3.h"       /* self */
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Basic machinery for parsing DIEs.                    ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+#define TRACE_D3(format, args...) \
+   if (td3) { VG_(printf)(format, ## args); }
+
+#define D3_INVALID_CUOFF  ((void*)(-1UL))
+#define D3_FAKEVOID_CUOFF ((void*)(-2UL))
+
+typedef
+   struct {
+      UChar* region_start_img;
+      UWord  region_szB;
+      UWord  region_next;
+      __attribute__((noreturn)) void (*barf)( HChar* );
+      HChar* barfstr;
+   }
+   Cursor;
+
+static inline Bool is_sane_Cursor ( Cursor* c ) {
+   if (!c)                return False;
+   if (!c->barf)          return False;
+   if (!c->barfstr)       return False;
+   return True;
+}
+
+static void init_Cursor ( Cursor* c,
+                          UChar*  region_start_img,
+                          UWord   region_szB,
+                          UWord   region_next,
+                          __attribute__((noreturn)) void (*barf)( HChar* ),
+                          HChar*  barfstr )
+{
+   vg_assert(c);
+   VG_(memset)(c, 0, sizeof(*c));
+   c->region_start_img = region_start_img;
+   c->region_szB       = region_szB;
+   c->region_next      = region_next;
+   c->barf             = barf;
+   c->barfstr          = barfstr;
+   vg_assert(is_sane_Cursor(c));
+}
+
+static Bool is_at_end_Cursor ( Cursor* c ) {
+   vg_assert(is_sane_Cursor(c));
+   return c->region_next >= c->region_szB;
+}
+
+static inline UWord get_position_of_Cursor ( Cursor* c ) {
+   vg_assert(is_sane_Cursor(c));
+   return c->region_next;
+}
+static inline void set_position_of_Cursor ( Cursor* c, UWord pos ) {
+   c->region_next = pos;
+   vg_assert(is_sane_Cursor(c));
+}
+
+static /*signed*/Word get_remaining_length_Cursor ( Cursor* c ) {
+   vg_assert(is_sane_Cursor(c));
+   return c->region_szB - c->region_next;
+}
+
+static UChar* get_address_of_Cursor ( Cursor* c ) {
+   vg_assert(is_sane_Cursor(c));
+   return &c->region_start_img[ c->region_next ];
+}
+
+__attribute__((noreturn)) 
+static void failWith ( Cursor* c, HChar* str ) {
+   vg_assert(c);
+   vg_assert(c->barf);
+   c->barf(str);
+   /*NOTREACHED*/
+   vg_assert(0);
+}
+
+/* FIXME: document assumptions on endianness for
+   get_UShort/UInt/ULong. */
+static inline UChar get_UChar ( Cursor* c ) {
+   UChar r;
+   /* vg_assert(is_sane_Cursor(c)); */
+   if (c->region_next + sizeof(UChar) > c->region_szB) {
+      c->barf(c->barfstr);
+      /*NOTREACHED*/
+      vg_assert(0);
+   }
+   r = * (UChar*) &c->region_start_img[ c->region_next ];
+   c->region_next += sizeof(UChar);
+   return r;
+}
+static UShort get_UShort ( Cursor* c ) {
+   UShort r;
+   vg_assert(is_sane_Cursor(c));
+   if (c->region_next + sizeof(UShort) > c->region_szB) {
+      c->barf(c->barfstr);
+      /*NOTREACHED*/
+      vg_assert(0);
+   }
+   r = * (UShort*) &c->region_start_img[ c->region_next ];
+   c->region_next += sizeof(UShort);
+   return r;
+}
+static UInt get_UInt ( Cursor* c ) {
+   UInt r;
+   vg_assert(is_sane_Cursor(c));
+   if (c->region_next + sizeof(UInt) > c->region_szB) {
+      c->barf(c->barfstr);
+      /*NOTREACHED*/
+      vg_assert(0);
+   }
+   r = * (UInt*) &c->region_start_img[ c->region_next ];
+   c->region_next += sizeof(UInt);
+   return r;
+}
+static ULong get_ULong ( Cursor* c ) {
+   ULong r;
+   vg_assert(is_sane_Cursor(c));
+   if (c->region_next + sizeof(ULong) > c->region_szB) {
+      c->barf(c->barfstr);
+      /*NOTREACHED*/
+      vg_assert(0);
+   }
+   r = * (ULong*) &c->region_start_img[ c->region_next ];
+   c->region_next += sizeof(ULong);
+   return r;
+}
+static inline ULong get_ULEB128 ( Cursor* c ) {
+   ULong result;
+   Int   shift;
+   UChar byte;
+   /* unroll first iteration */
+   byte = get_UChar( c );
+   result = (ULong)(byte & 0x7f);
+   if (LIKELY(!(byte & 0x80))) return result;
+   shift = 7;
+   /* end unroll first iteration */
+   do {
+      byte = get_UChar( c );
+      result |= ((ULong)(byte & 0x7f)) << shift;
+      shift += 7;
+   } while (byte & 0x80);
+   return result;
+}
+static Long get_SLEB128 ( Cursor* c ) {
+   ULong  result = 0;
+   Int    shift = 0;
+   UChar  byte;
+   do {
+      byte = get_UChar(c);
+      result |= ((ULong)(byte & 0x7f)) << shift;
+      shift += 7;
+   } while (byte & 0x80);
+   if (shift < 64 && (byte & 0x40))
+      result |= -(1ULL << shift);
+   return result;
+}
+
+/* Assume 'c' points to the start of a string.  Return the absolute
+   address of whatever it points at, and advance it past the
+   terminating zero.  This makes it safe for the caller to then copy
+   the string with ML_(addStr), since (w.r.t. image overruns) the
+   process of advancing past the terminating zero will already have
+   "vetted" the string. */
+static UChar* get_AsciiZ ( Cursor* c ) {
+   UChar  uc;
+   UChar* res = get_address_of_Cursor(c);
+   do { uc = get_UChar(c); } while (uc != 0);
+   return res;
+}
+
+static ULong peek_ULEB128 ( Cursor* c ) {
+   Word here = c->region_next;
+   ULong r = get_ULEB128( c );
+   c->region_next = here;
+   return r;
+}
+static UChar peek_UChar ( Cursor* c ) {
+   Word here = c->region_next;
+   UChar r = get_UChar( c );
+   c->region_next = here;
+   return r;
+}
+
+static ULong get_Dwarfish_UWord ( Cursor* c, Bool is_dw64 ) {
+   return is_dw64 ? get_ULong(c) : (ULong) get_UInt(c);
+}
+
+static UWord get_UWord ( Cursor* c ) {
+   vg_assert(sizeof(UWord) == sizeof(void*));
+   if (sizeof(UWord) == 4) return get_UInt(c);
+   if (sizeof(UWord) == 8) return get_ULong(c);
+   vg_assert(0);
+}
+
+
+/* Read a DWARF3 'Initial Length' field */
+static ULong get_Initial_Length ( /*OUT*/Bool* is64,
+                                  Cursor* c, 
+                                  HChar* barfMsg )
+{
+   ULong w64;
+   UInt  w32;
+   *is64 = False;
+   w32 = get_UInt( c );
+   if (w32 >= 0xFFFFFFF0 && w32 < 0xFFFFFFFF) {
+      c->barf( barfMsg );
+   }
+   else if (w32 == 0xFFFFFFFF) {
+      *is64 = True;
+      w64   = get_ULong( c );
+   } else {
+      *is64 = False;
+      w64 = (ULong)w32;
+   }
+   return w64;
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- "CUConst" structure                                  ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+#define N_ABBV_CACHE 32
+
+/* Holds information that is constant through the parsing of a
+   Compilation Unit.  This is basically plumbed through to
+   everywhere. */
+typedef
+   struct {
+      /* Call here if anything goes wrong */
+      __attribute__((noreturn)) void (*barf)( HChar* );
+      /* Is this 64-bit DWARF ? */
+      Bool   is_dw64;
+      /* Which DWARF version ?  (2 or 3) */
+      UShort version;
+      /* Length of this Compilation Unit, excluding its Header */
+      ULong  unit_length;
+      /* Offset of start of this unit in .debug_info */
+      UWord  cu_start_offset;
+      /* SVMA for this CU.  In the D3 spec, is known as the "base
+         address of the compilation unit (last para sec 3.1.1).
+         Needed for (amongst things) interpretation of location-list
+         values. */
+      Addr   cu_svma;
+      Bool   cu_svma_known;
+      /* The debug_abbreviations table to be used for this Unit */
+      UChar* debug_abbv;
+      /* Upper bound on size thereof (an overestimate, in general) */
+      UWord  debug_abbv_maxszB;
+      /* Where is .debug_str ? */
+      UChar* debug_str_img;
+      UWord  debug_str_sz;
+      /* Where is .debug_ranges ? */
+      UChar* debug_ranges_img;
+      UWord  debug_ranges_sz;
+      /* Where is .debug_loc ? */
+      UChar* debug_loc_img;
+      UWord  debug_loc_sz;
+      /* Where is .debug_line? */
+      UChar* debug_line_img;
+      UWord  debug_line_sz;
+      /* --- Needed so we can add stuff to the string table. --- */
+      struct _DebugInfo* di;
+      /* --- a cache for set_abbv_Cursor --- */
+      /* abbv_code == (ULong)-1 for an unused entry. */
+      struct { ULong abbv_code; UWord posn; } saC_cache[N_ABBV_CACHE];
+      UWord saC_cache_queries;
+      UWord saC_cache_misses;
+   }
+   CUConst;
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Helper functions for Guarded Expressions             ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+/* Parse the location list starting at img-offset 'debug_loc_offset'
+   in .debug_loc.  Results are biased with 'svma_of_referencing_CU'
+   and so I believe are correct SVMAs for the object as a whole.  This
+   function allocates the UChar*, and the caller must deallocate it.
+   The resulting block is in so-called Guarded-Expression format.
+
+   Guarded-Expression format is similar but not identical to the DWARF3
+   location-list format.  The format of each returned block is:
+
+      UChar biasMe;
+      UChar isEnd;
+      followed by zero or more of
+
+      (Addr aMin;  Addr aMax;  UShort nbytes;  ..bytes..;  UChar isEnd)
+
+   '..bytes..' is an standard DWARF3 location expression which is
+   valid when aMin <= pc <= aMax (possibly after suitable biasing).
+
+   The number of bytes in '..bytes..' is nbytes.
+
+   The end of the sequence is marked by an isEnd == 1 value.  All
+   previous isEnd values must be zero.
+
+   biasMe is 1 if the aMin/aMax fields need this DebugInfo's
+   text_bias added before use, and 0 if the GX is this is not
+   necessary (is ready to go).
+
+   Hence the block can be quickly parsed and is self-describing.  Note
+   that aMax is 1 less than the corresponding value in a DWARF3
+   location list.  Zero length ranges, with aMax == aMin-1, are not
+   allowed.
+*/
+void ML_(pp_GX) ( GExpr* gx ) {
+   Addr   aMin, aMax;
+   UChar  uc;
+   UShort nbytes;
+   UChar* p = &gx->payload[0];
+   uc = *p++;
+   VG_(printf)("GX(%s){", uc == 0 ? "final" : "Breqd" );
+   vg_assert(uc == 0 || uc == 1);
+   while (True) {
+      uc = *p++;
+      if (uc == 1)
+         break; /*isEnd*/
+      vg_assert(uc == 0);
+      aMin   = * (Addr*)p;  p += sizeof(Addr);
+      aMax   = * (Addr*)p;  p += sizeof(Addr);
+      nbytes = * (UShort*)p; p += sizeof(UShort);
+      VG_(printf)("[%p,%p]=", aMin, aMax);
+      while (nbytes > 0) {
+         VG_(printf)("%02x", (UInt)*p++);
+         nbytes--;
+      }
+      if (*p == 0)
+         VG_(printf)(",");
+   }
+   VG_(printf)("}");
+}
+
+static void bias_GX ( /*MOD*/GExpr* gx, Addr bias )
+{
+   UShort nbytes;
+   UChar* p = &gx->payload[0];
+   UChar  uc;
+   uc = *p++; /*biasMe*/
+   if (uc == 0)
+      return;
+   vg_assert(uc == 1);
+   p[-1] = 0; /* mark it as done */
+   while (True) {
+      uc = *p++;
+      if (uc == 1)
+         break; /*isEnd*/
+      vg_assert(uc == 0);
+      * ((Addr*)p) += bias; /*aMin*/  p += sizeof(Addr);
+      * ((Addr*)p) += bias; /*aMax*/  p += sizeof(Addr);
+      nbytes = * (UShort*)p; p += sizeof(UShort);
+      p += nbytes;
+   }
+}
+
+__attribute__((noinline))
+static GExpr* make_singleton_GX ( UChar* block, UWord nbytes )
+{
+   SizeT  bytesReqd;
+   GExpr* gx;
+   UChar *p, *pstart;
+
+   vg_assert(sizeof(UWord) == sizeof(Addr));
+   vg_assert(nbytes <= 0xFFFF); /* else we overflow the nbytes field */
+   bytesReqd
+      =   sizeof(UChar)  /*biasMe*/    + sizeof(UChar) /*!isEnd*/
+        + sizeof(UWord)  /*aMin*/      + sizeof(UWord) /*aMax*/
+        + sizeof(UShort) /*nbytes*/    + nbytes
+        + sizeof(UChar); /*isEnd*/
+
+   gx = ML_(dinfo_zalloc)( sizeof(GExpr) + bytesReqd );
+   vg_assert(gx);
+
+   p = pstart = &gx->payload[0];
+
+   * ((UChar*)p)  = 0;          /*biasMe*/ p += sizeof(UChar);
+   * ((UChar*)p)  = 0;          /*!isEnd*/ p += sizeof(UChar);
+   * ((Addr*)p)   = 0;          /*aMin*/   p += sizeof(Addr);
+   * ((Addr*)p)   = ~((Addr)0); /*aMax */  p += sizeof(Addr);
+   * ((UShort*)p) = (UShort)nbytes; /*nbytes*/ p += sizeof(UShort);
+   VG_(memcpy)(p, block, nbytes); p += nbytes;
+   * ((UChar*)p)  = 1;          /*isEnd*/  p += sizeof(UChar);
+
+   vg_assert( (SizeT)(p - pstart) == bytesReqd);
+   vg_assert( &gx->payload[bytesReqd] 
+              == ((UChar*)gx) + sizeof(GExpr) + bytesReqd );
+
+   gx->next = NULL;
+   return gx;
+}
+
+__attribute__((noinline))
+static GExpr* make_general_GX ( CUConst* cc,
+                                Bool     td3,
+                                UWord    debug_loc_offset,
+                                Addr     svma_of_referencing_CU )
+{
+   Addr      base;
+   Cursor    loc;
+   XArray*   xa; /* XArray of UChar */
+   GExpr*    gx;
+   Word      nbytes;
+
+   vg_assert(sizeof(UWord) == sizeof(Addr));
+   if (cc->debug_loc_sz == 0)
+      cc->barf("make_general_GX: .debug_loc is empty/missing");
+
+   init_Cursor( &loc, cc->debug_loc_img, 
+                cc->debug_loc_sz, 0, cc->barf,
+                "Overrun whilst reading .debug_loc section(2)" );
+   set_position_of_Cursor( &loc, debug_loc_offset );
+
+   TRACE_D3("make_general_GX (.debug_loc_offset = %lu, img = %p) {\n",
+            debug_loc_offset, get_address_of_Cursor( &loc ) );
+
+   /* Who frees this xa?  It is freed before this fn exits. */
+   xa = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                    sizeof(UChar) );
+
+   { UChar c = 1; /*biasMe*/ VG_(addBytesToXA)( xa, &c, sizeof(c) ); }
+
+   base = 0;
+   while (True) {
+      Bool  acquire;
+      UWord len;
+      /* Read a (host-)word pair.  This is something of a hack since
+         the word size to read is really dictated by the ELF file;
+         however, we assume we're reading a file with the same
+         word-sizeness as the host.  Reasonably enough. */
+      UWord w1 = get_UWord( &loc );
+      UWord w2 = get_UWord( &loc );
+
+      TRACE_D3("   %08lx %08lx\n", w1, w2);
+      if (w1 == 0 && w2 == 0)
+         break; /* end of list */
+
+      if (w1 == -1UL) {
+         /* new value for 'base' */
+         base = w2;
+         continue;
+      }
+
+      /* else a location expression follows */
+      /* else enumerate [w1+base, w2+base) */
+      /* w2 is 1 past end of range, as per D3 defn for "DW_AT_high_pc"
+         (sec 2.17.2) */
+      if (w1 > w2) {
+         TRACE_D3("negative range is for .debug_loc expr at "
+                  "file offset %lu\n", 
+                  debug_loc_offset);
+         cc->barf( "negative range in .debug_loc section" );
+      }
+
+      /* ignore zero length ranges */
+      acquire = w1 < w2;
+      len     = (UWord)get_UShort( &loc );
+
+      if (acquire) {
+         UWord  w;
+         UShort s;
+         UChar  c;
+         c = 0; /* !isEnd*/
+         VG_(addBytesToXA)( xa, &c, sizeof(c) );
+         w = w1    + base + svma_of_referencing_CU;
+         VG_(addBytesToXA)( xa, &w, sizeof(w) );
+         w = w2 -1 + base + svma_of_referencing_CU;
+         VG_(addBytesToXA)( xa, &w, sizeof(w) );
+         s = (UShort)len;
+         VG_(addBytesToXA)( xa, &s, sizeof(s) );
+      }
+
+      while (len > 0) {
+         UChar byte = get_UChar( &loc );
+         TRACE_D3("%02x", (UInt)byte);
+         if (acquire)
+            VG_(addBytesToXA)( xa, &byte, 1 );
+         len--;
+      }
+      TRACE_D3("\n");
+   }
+
+   { UChar c = 1; /*isEnd*/ VG_(addBytesToXA)( xa, &c, sizeof(c) ); }
+
+   nbytes = VG_(sizeXA)( xa );
+   vg_assert(nbytes >= 1);
+
+   gx = ML_(dinfo_zalloc)( sizeof(GExpr) + nbytes );
+   vg_assert(gx);
+   VG_(memcpy)( &gx->payload[0], (UChar*)VG_(indexXA)(xa,0), nbytes );
+   vg_assert( &gx->payload[nbytes] 
+              == ((UChar*)gx) + sizeof(GExpr) + nbytes );
+
+   VG_(deleteXA)( xa );
+
+   gx->next = NULL;
+
+   TRACE_D3("}\n");
+
+   return gx;
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Helper functions for range lists and CU headers      ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+/* Denotes an address range.  Both aMin and aMax are included in the
+   range; hence a complete range is (0, ~0) and an empty range is any
+   (X, X-1) for X > 0.*/
+typedef 
+   struct { Addr aMin; Addr aMax; }
+   AddrRange;
+
+
+__attribute__((noinline))
+static XArray* /* of AddrRange */ empty_range_list ( void )
+{
+   XArray* xa; /* XArray of AddrRange */
+   /* Who frees this xa?  varstack_preen() does. */
+   xa = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                    sizeof(AddrRange) );
+   return xa;
+}
+
+
+static XArray* unitary_range_list ( Addr aMin, Addr aMax )
+{
+   XArray*   xa;
+   AddrRange pair;
+   vg_assert(aMin <= aMax);
+   /* Who frees this xa?  varstack_preen() does. */
+   xa = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                    sizeof(AddrRange) );
+   pair.aMin = aMin;
+   pair.aMax = aMax;
+   VG_(addToXA)( xa, &pair );
+   return xa;
+}
+
+
+/* Enumerate the address ranges starting at img-offset
+   'debug_ranges_offset' in .debug_ranges.  Results are biased with
+   'svma_of_referencing_CU' and so I believe are correct SVMAs for the
+   object as a whole.  This function allocates the XArray, and the
+   caller must deallocate it. */
+__attribute__((noinline))
+static XArray* /* of AddrRange */
+       get_range_list ( CUConst* cc,
+                        Bool     td3,
+                        UWord    debug_ranges_offset,
+                        Addr     svma_of_referencing_CU )
+{
+   Addr      base;
+   Cursor    ranges;
+   XArray*   xa; /* XArray of AddrRange */
+   AddrRange pair;
+
+   if (cc->debug_ranges_sz == 0)
+      cc->barf("get_range_list: .debug_ranges is empty/missing");
+
+   init_Cursor( &ranges, cc->debug_ranges_img, 
+                cc->debug_ranges_sz, 0, cc->barf,
+                "Overrun whilst reading .debug_ranges section(2)" );
+   set_position_of_Cursor( &ranges, debug_ranges_offset );
+
+   /* Who frees this xa?  varstack_preen() does. */
+   xa = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                    sizeof(AddrRange) );
+   base = 0;
+   while (True) {
+      /* Read a (host-)word pair.  This is something of a hack since
+         the word size to read is really dictated by the ELF file;
+         however, we assume we're reading a file with the same
+         word-sizeness as the host.  Reasonably enough. */
+      UWord w1 = get_UWord( &ranges );
+      UWord w2 = get_UWord( &ranges );
+
+      if (w1 == 0 && w2 == 0)
+         break; /* end of list. */
+
+      if (w1 == -1UL) {
+         /* new value for 'base' */
+         base = w2;
+         continue;
+      }
+
+      /* else enumerate [w1+base, w2+base) */
+      /* w2 is 1 past end of range, as per D3 defn for "DW_AT_high_pc"
+         (sec 2.17.2) */
+      if (w1 > w2)
+         cc->barf( "negative range in .debug_ranges section" );
+      if (w1 < w2) {
+         pair.aMin = w1     + base + svma_of_referencing_CU;
+         pair.aMax = w2 - 1 + base + svma_of_referencing_CU;
+         vg_assert(pair.aMin <= pair.aMax);
+         VG_(addToXA)( xa, &pair );
+      }
+   }
+   return xa;
+}
+
+
+/* Parse the Compilation Unit header indicated at 'c' and 
+   initialise 'cc' accordingly. */
+static __attribute__((noinline))
+void parse_CU_Header ( /*OUT*/CUConst* cc,
+                       Bool td3,
+                       Cursor* c, 
+                       UChar* debug_abbv_img, UWord debug_abbv_sz )
+{
+   UChar  address_size;
+   UWord  debug_abbrev_offset;
+   Int    i;
+
+   VG_(memset)(cc, 0, sizeof(*cc));
+   vg_assert(c && c->barf);
+   cc->barf = c->barf;
+
+   /* initial_length field */
+   cc->unit_length 
+      = get_Initial_Length( &cc->is_dw64, c, 
+           "parse_CU_Header: invalid initial-length field" );
+
+   TRACE_D3("   Length:        %lld\n", cc->unit_length );
+
+   /* version */
+   cc->version = get_UShort( c );
+   if (cc->version != 2 && cc->version != 3)
+      cc->barf( "parse_CU_Header: is neither DWARF2 nor DWARF3" );
+   TRACE_D3("   Version:       %d\n", (Int)cc->version );
+
+   /* debug_abbrev_offset */
+   debug_abbrev_offset = get_Dwarfish_UWord( c, cc->is_dw64 );
+   if (debug_abbrev_offset >= debug_abbv_sz)
+      cc->barf( "parse_CU_Header: invalid debug_abbrev_offset" );
+   TRACE_D3("   Abbrev Offset: %ld\n", debug_abbrev_offset );
+
+   /* address size.  If this isn't equal to the host word size, just
+      give up.  This makes it safe to assume elsewhere that
+      DW_FORM_addr can be treated as a host word. */
+   address_size = get_UChar( c );
+   if (address_size != sizeof(void*))
+      cc->barf( "parse_CU_Header: invalid address_size" );
+   TRACE_D3("   Pointer Size:  %d\n", (Int)address_size );
+
+   /* Set up so that cc->debug_abbv points to the relevant table for
+      this CU.  Set the szB so that at least we can't read off the end
+      of the debug_abbrev section -- potentially (and quite likely)
+      too big, if this isn't the last table in the section, but at
+      least it's safe. */
+   cc->debug_abbv        = debug_abbv_img + debug_abbrev_offset;
+   cc->debug_abbv_maxszB = debug_abbv_sz  - debug_abbrev_offset;
+   /* and empty out the set_abbv_Cursor cache */
+   if (0) VG_(printf)("XXXXXX initialise set_abbv_Cursor cache\n");
+   for (i = 0; i < N_ABBV_CACHE; i++) {
+      cc->saC_cache[i].abbv_code = (ULong)-1; /* unused */
+      cc->saC_cache[i].posn = 0;
+   }
+   cc->saC_cache_queries = 0;
+   cc->saC_cache_misses = 0;
+}
+
+
+/* Set up 'c' so it is ready to parse the abbv table entry code
+   'abbv_code' for this compilation unit.  */
+static __attribute__((noinline))
+void set_abbv_Cursor ( /*OUT*/Cursor* c, Bool td3,
+                       CUConst* cc, ULong abbv_code )
+{
+   Int   i;
+   ULong acode;
+
+   if (abbv_code == 0)
+      cc->barf("set_abbv_Cursor: abbv_code == 0" );
+
+   /* (ULong)-1 is used to represent an empty cache slot.  So we can't
+      allow it.  In any case no valid DWARF3 should make a reference
+      to a negative abbreviation code.  [at least, they always seem to
+      be numbered upwards from zero as far as I have seen] */
+   vg_assert(abbv_code != (ULong)-1);
+
+   /* First search the cache. */
+   if (0) VG_(printf)("XXXXXX search set_abbv_Cursor cache\n");
+   cc->saC_cache_queries++;
+   for (i = 0; i < N_ABBV_CACHE; i++) {
+      /* No need to test the cached abbv_codes for -1 (empty), since
+         we just asserted that abbv_code is not -1. */
+     if (cc->saC_cache[i].abbv_code == abbv_code) {
+        /* Found it.  Cool.  Set up the parser using the cached
+           position, and move this cache entry 1 step closer to the
+           front. */
+        if (0) VG_(printf)("XXXXXX found in set_abbv_Cursor cache\n");
+        init_Cursor( c, cc->debug_abbv,
+                     cc->debug_abbv_maxszB, cc->saC_cache[i].posn, 
+                     cc->barf,
+                     "Overrun whilst parsing .debug_abbrev section(1)" );
+        if (i > 0) {
+           ULong t_abbv_code = cc->saC_cache[i].abbv_code;
+           UWord t_posn = cc->saC_cache[i].posn;
+           while (i > 0) {
+              cc->saC_cache[i] = cc->saC_cache[i-1];
+              cc->saC_cache[0].abbv_code = t_abbv_code;
+              cc->saC_cache[0].posn = t_posn;
+              i--;
+           }
+        }
+        return;
+     }
+   }
+
+   /* No.  It's not in the cache.  We have to search through
+      .debug_abbrev, of course taking care to update the cache
+      when done. */
+
+   cc->saC_cache_misses++;
+   init_Cursor( c, cc->debug_abbv, cc->debug_abbv_maxszB, 0, cc->barf,
+               "Overrun whilst parsing .debug_abbrev section(2)" );
+
+   /* Now iterate though the table until we find the requested
+      entry. */
+   while (True) {
+      ULong atag;
+      UInt  has_children;
+      acode = get_ULEB128( c );
+      if (acode == 0) break; /* end of the table */
+      if (acode == abbv_code) break; /* found it */
+      atag         = get_ULEB128( c );
+      has_children = get_UChar( c );
+      //TRACE_D3("   %llu      %s    [%s]\n", 
+      //         acode, pp_DW_TAG(atag), pp_DW_children(has_children));
+      while (True) {
+         ULong at_name = get_ULEB128( c );
+         ULong at_form = get_ULEB128( c );
+         if (at_name == 0 && at_form == 0) break;
+         //TRACE_D3("    %18s %s\n", 
+         //         pp_DW_AT(at_name), pp_DW_FORM(at_form));
+      }
+   }
+
+   if (acode == 0) {
+      /* Not found.  This is fatal. */
+      cc->barf("set_abbv_Cursor: abbv_code not found");
+   }
+
+   /* Otherwise, 'c' is now set correctly to parse the relevant entry,
+      starting from the abbreviation entry's tag.  So just cache
+      the result, and return. */
+   for (i = N_ABBV_CACHE-1; i > N_ABBV_CACHE/2; i--) {
+      cc->saC_cache[i] = cc->saC_cache[i-1];
+   }
+   if (0) VG_(printf)("XXXXXX update set_abbv_Cursor cache\n");
+   cc->saC_cache[N_ABBV_CACHE/2].abbv_code = abbv_code;
+   cc->saC_cache[N_ABBV_CACHE/2].posn = get_position_of_Cursor(c);
+}
+
+
+/* From 'c', get the Form data into the lowest 1/2/4/8 bytes of *cts.
+
+   If *cts itself contains the entire result, then *ctsSzB is set to
+   1,2,4 or 8 accordingly and *ctsMemSzB is set to zero.
+
+   Alternatively, the result can be a block of data (in the
+   transiently mapped-in object, so-called "image" space).  If so then
+   the lowest sizeof(void*)/8 bytes of *cts hold a pointer to said
+   image, *ctsSzB is zero, and *ctsMemSzB is the size of the block.
+
+   Unfortunately this means it is impossible to represent a zero-size
+   image block since that would have *ctsSzB == 0 and *ctsMemSzB == 0
+   and so is ambiguous (which case it is?)
+
+   Invariant on successful return: 
+      (*ctsSzB > 0 && *ctsMemSzB == 0)
+      || (*ctsSzB == 0 && *ctsMemSzB > 0)
+*/
+static
+void get_Form_contents ( /*OUT*/ULong* cts,
+                         /*OUT*/Int*   ctsSzB,
+                         /*OUT*/UWord* ctsMemSzB,
+                         CUConst* cc, Cursor* c,
+                         Bool td3, DW_FORM form )
+{
+   *cts       = 0;
+   *ctsSzB    = 0;
+   *ctsMemSzB = 0;
+   switch (form) {
+      case DW_FORM_data1:
+         *cts = (ULong)(UChar)get_UChar(c);
+         *ctsSzB = 1;
+         TRACE_D3("%u", (UInt)*cts);
+         break;
+      case DW_FORM_data2:
+         *cts = (ULong)(UShort)get_UShort(c);
+         *ctsSzB = 2;
+         TRACE_D3("%u", (UInt)*cts);
+         break;
+      case DW_FORM_data4:
+         *cts = (ULong)(UInt)get_UInt(c);
+         *ctsSzB = 4;
+         TRACE_D3("%u", (UInt)*cts);
+         break;
+      case DW_FORM_sdata:
+         *cts = (ULong)(Long)get_SLEB128(c);
+         *ctsSzB = 8;
+         TRACE_D3("%lld", (Long)*cts);
+         break;
+      case DW_FORM_addr:
+         /* note, this is a hack.  DW_FORM_addr is defined as getting
+            a word the size of the target machine as defined by the
+            address_size field in the CU Header.  However,
+            parse_CU_Header() rejects all inputs except those for
+            which address_size == sizeof(Word), hence we can just
+            treat it as a (host) Word.  */
+         *cts = (ULong)(UWord)get_UWord(c);
+         *ctsSzB = sizeof(UWord);
+         TRACE_D3("0x%lx", (UWord)*cts);
+         break;
+      case DW_FORM_strp: {
+         /* this is an offset into .debug_str */
+         UChar* str;
+         UWord uw = (UWord)get_Dwarfish_UWord( c, cc->is_dw64 );
+         if (cc->debug_str_img == NULL || uw >= cc->debug_str_sz)
+            cc->barf("read_and_show_Form: DW_FORM_strp "
+                     "points outside .debug_str");
+         /* FIXME: check the entire string lies inside debug_str,
+            not just the first byte of it. */
+         str = (UChar*)cc->debug_str_img + uw;
+         TRACE_D3("(indirect string, offset: 0x%lx): %s", uw, str);
+         *cts = (ULong)(UWord)str;
+         *ctsMemSzB = 1 + (ULong)VG_(strlen)(str);
+         break;
+      }
+      case DW_FORM_string: {
+         UChar* str = get_AsciiZ(c);
+         TRACE_D3("%s", str);
+         *cts = (ULong)(UWord)str;
+         /* strlen is safe because get_AsciiZ already 'vetted' the
+            entire string */
+         *ctsMemSzB = 1 + (ULong)VG_(strlen)(str);
+         break;
+      }
+      case DW_FORM_ref4: {
+         UInt  u32 = get_UInt(c);
+         UWord res = cc->cu_start_offset + (UWord)u32;
+         *cts = (ULong)res;
+         *ctsSzB = sizeof(UWord);
+         TRACE_D3("<%lx>", res);
+         break;
+      }
+      case DW_FORM_flag: {
+         UChar u8 = get_UChar(c);
+         TRACE_D3("%u", (UInt)u8);
+         *cts = (ULong)u8;
+         *ctsSzB = 1;
+         break;
+      }
+      case DW_FORM_block1: {
+         ULong  u64b;
+         ULong  u64 = (ULong)get_UChar(c);
+         UChar* block = get_address_of_Cursor(c);
+         TRACE_D3("%llu byte block: ", u64);
+         for (u64b = u64; u64b > 0; u64b--) {
+            UChar u8 = get_UChar(c);
+            TRACE_D3("%x ", (UInt)u8);
+         }
+         *cts = (ULong)(UWord)block;
+         *ctsMemSzB = (UWord)u64;
+         break;
+      }
+      default:
+         VG_(printf)("get_Form_contents: unhandled %lld (%s)\n",
+                     form, ML_(pp_DW_FORM)(form));
+         c->barf("get_Form_contents: unhandled DW_FORM");
+   }
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Parsing of variable-related DIEs                     ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+typedef
+   struct _TempVar {
+      struct _TempVar* next;
+      UChar*  name; /* in DebugInfo's .strchunks */
+      /* Represent ranges economically.  nRanges is the number of
+         ranges.  Cases:
+         0: .rngOneMin .rngOneMax .manyRanges are all zero
+         1: .rngOneMin .rngOneMax hold the range; .rngMany is NULL
+         2: .rngOneMin .rngOneMax are zero; .rngMany holds the ranges.
+         This is merely an optimisation to avoid having to allocate
+         and free the XArray in the common (98%) of cases where there
+         is zero or one address ranges. */
+      UWord   nRanges;
+      Addr    rngOneMin;
+      Addr    rngOneMax;
+      XArray* rngMany; /* of AddrRange.  UNIQUE PTR in AR_DINFO. */
+      /* --- */
+      Int     level;
+      Type*   typeR;
+      GExpr*  gexpr; /* for this variable */
+      GExpr*  fbGX;  /* to find the frame base of the enclosing fn, if
+                        any */
+      UChar*  fName; /* declaring file name, or NULL */
+      Int     fLine; /* declaring file line number, or zero */
+      /* offset in .debug_info, so that abstract instances can be
+         found to satisfy references from concrete instances. */
+      UWord   dioff;
+      UWord   absOri; /* so the absOri fields refer to dioff fields
+                         in some other, related TempVar. */
+   }
+   TempVar;
+
+#define N_D3_VAR_STACK 24
+
+typedef
+   struct {
+      /* Contains the range stack: a stack of address ranges, one
+         stack entry for each nested scope.  
+
+         Some scope entries are created by function definitions
+         (DW_AT_subprogram), and for those, we also note the GExpr
+         derived from its DW_AT_frame_base attribute, if any.
+         Consequently it should be possible to find, for any
+         variable's DIE, the GExpr for the the containing function's
+         DW_AT_frame_base by scanning back through the stack to find
+         the nearest entry associated with a function.  This somewhat
+         elaborate scheme is provided so as to make it possible to
+         obtain the correct DW_AT_frame_base expression even in the
+         presence of nested functions (or to be more precise, in the
+         presence of nested DW_AT_subprogram DIEs). 
+      */
+      Int     sp; /* [sp] is innermost active entry; sp==-1 for empty
+                     stack */
+      XArray* ranges[N_D3_VAR_STACK]; /* XArray of AddrRange */
+      Int     level[N_D3_VAR_STACK];  /* D3 DIE levels */
+      Bool    isFunc[N_D3_VAR_STACK]; /* from DW_AT_subprogram? */
+      GExpr*  fbGX[N_D3_VAR_STACK];   /* if isFunc, contains the FB
+                                         expr, else NULL */
+      /* The file name table.  Is a mapping from integer index to the
+         (permanent) copy of the string, iow a non-img area. */
+      XArray* /* of UChar* */ filenameTable;
+   }
+   D3VarParser;
+
+static void varstack_show ( D3VarParser* parser, HChar* str ) {
+   Word i, j;
+   VG_(printf)("  varstack (%s) {\n", str);
+   for (i = 0; i <= parser->sp; i++) {
+      XArray* xa = parser->ranges[i];
+      vg_assert(xa);
+      VG_(printf)("    [%ld] (level %d)", i, parser->level[i]);
+      if (parser->isFunc[i]) {
+         VG_(printf)(" (fbGX=%p)", parser->fbGX[i]);
+      } else {
+         vg_assert(parser->fbGX[i] == NULL);
+      }
+      VG_(printf)(": ");
+      if (VG_(sizeXA)( xa ) == 0) {
+         VG_(printf)("** empty PC range array **");
+      } else {
+         for (j = 0; j < VG_(sizeXA)( xa ); j++) {
+            AddrRange* range = (AddrRange*) VG_(indexXA)( xa, j );
+            vg_assert(range);
+            VG_(printf)("[%p,%p] ", range->aMin, range->aMax);
+         }
+      }
+      VG_(printf)("\n");
+   }
+   VG_(printf)("  }\n");
+}
+
+/* Remove from the stack, all entries with .level > 'level' */
+static 
+void varstack_preen ( D3VarParser* parser, Bool td3, Int level )
+{
+   Bool changed = False;
+   vg_assert(parser->sp < N_D3_VAR_STACK);
+   while (True) {
+      vg_assert(parser->sp >= -1);
+      if (parser->sp == -1) break;
+      if (parser->level[parser->sp] <= level) break;
+      if (0) 
+         TRACE_D3("BBBBAAAA varstack_pop [newsp=%d]\n", parser->sp-1);
+      vg_assert(parser->ranges[parser->sp]);
+      /* Who allocated this xa?  get_range_list() or
+         unitary_range_list(). */
+      VG_(deleteXA)( parser->ranges[parser->sp] );
+      parser->ranges[parser->sp] = NULL;
+      parser->level[parser->sp]  = 0;
+      parser->isFunc[parser->sp] = False;
+      parser->fbGX[parser->sp]   = NULL;
+      parser->sp--;
+      changed = True;
+   }
+   if (changed && td3)
+      varstack_show( parser, "after preen" );
+}
+
+static void varstack_push ( CUConst* cc,
+                            D3VarParser* parser,
+                            Bool td3,
+                            XArray* ranges, Int level,
+                            Bool    isFunc, GExpr* fbGX ) {
+   if (0)
+   TRACE_D3("BBBBAAAA varstack_push[newsp=%d]: %d  %p\n",
+            parser->sp+1, level, ranges);
+
+   /* First we need to zap everything >= 'level', as we are about to
+      replace any previous entry at 'level', so .. */
+   varstack_preen(parser, /*td3*/False, level-1);
+
+   vg_assert(parser->sp >= -1);
+   vg_assert(parser->sp < N_D3_VAR_STACK);
+   if (parser->sp == N_D3_VAR_STACK-1)
+      cc->barf("varstack_push: N_D3_VAR_STACK is too low; "
+               "increase and recompile");
+   if (parser->sp >= 0)
+      vg_assert(parser->level[parser->sp] < level);
+   parser->sp++;
+   vg_assert(parser->ranges[parser->sp] == NULL);
+   vg_assert(parser->level[parser->sp]  == 0);
+   vg_assert(parser->isFunc[parser->sp] == False);
+   vg_assert(parser->fbGX[parser->sp]   == NULL);
+   vg_assert(ranges != NULL);
+   if (!isFunc) vg_assert(fbGX == NULL);
+   parser->ranges[parser->sp] = ranges;
+   parser->level[parser->sp]  = level;
+   parser->isFunc[parser->sp] = isFunc;
+   parser->fbGX[parser->sp]   = fbGX;
+   if (td3)
+      varstack_show( parser, "after push" );
+}
+
+
+/* cts, ctsSzB, ctsMemSzB are derived from a DW_AT_location and so
+   refer either to a location expression or to a location list.
+   Figure out which, and in both cases bundle the expression or
+   location list into a so-called GExpr (guarded expression). */
+__attribute__((noinline))
+static GExpr* get_GX ( CUConst* cc, Bool td3, 
+                       ULong cts, Int ctsSzB, UWord ctsMemSzB )
+{
+   GExpr* gexpr = NULL;
+   if (ctsMemSzB > 0 && ctsSzB == 0) {
+      /* represents an in-line location expression, and cts points
+         right at it */
+      gexpr = make_singleton_GX( (UChar*)(UWord)cts, ctsMemSzB );
+   }
+   else 
+   if (ctsMemSzB == 0 && ctsSzB > 0) {
+      /* represents location list.  cts is the offset of it in
+         .debug_loc. */
+      if (!cc->cu_svma_known)
+         cc->barf("get_GX: location list, but CU svma is unknown");
+      gexpr = make_general_GX( cc, td3, (UWord)cts, cc->cu_svma );
+   }
+   else {
+      vg_assert(0); /* else caller is bogus */
+   }
+   return gexpr;
+}
+
+
+static 
+void read_filename_table( /*MOD*/D3VarParser* parser,
+                          CUConst* cc, UWord debug_line_offset,
+                          Bool td3 )
+{
+   Bool   is_dw64;
+   Cursor c;
+   Word   i;
+   ULong  unit_length;
+   UShort version;
+   ULong  header_length;
+   UChar  minimum_instruction_length;
+   UChar  default_is_stmt;
+   Char   line_base;
+   UChar  line_range;
+   UChar  opcode_base;
+   UChar* str;
+
+   vg_assert(parser && cc && cc->barf);
+   if ((!cc->debug_line_img) 
+       || cc->debug_line_sz <= debug_line_offset)
+      cc->barf("read_filename_table: .debug_line is missing?");
+
+   init_Cursor( &c, cc->debug_line_img, 
+                cc->debug_line_sz, debug_line_offset, cc->barf, 
+                "Overrun whilst reading .debug_line section(1)" );
+
+   unit_length 
+      = get_Initial_Length( &is_dw64, &c,
+           "read_filename_table: invalid initial-length field" );
+   version = get_UShort( &c );
+   if (version != 2)
+     cc->barf("read_filename_table: Only DWARF version 2 line info "
+              "is currently supported.");
+   header_length = (ULong)get_Dwarfish_UWord( &c, is_dw64 );
+   minimum_instruction_length = get_UChar( &c );
+   default_is_stmt            = get_UChar( &c );
+   line_base                  = (Char)get_UChar( &c );
+   line_range                 = get_UChar( &c );
+   opcode_base                = get_UChar( &c );
+   /* skip over "standard_opcode_lengths" */
+   for (i = 1; i < (Word)opcode_base; i++)
+     (void)get_UChar( &c );
+
+   /* skip over the directory names table */
+   while (peek_UChar(&c) != 0) {
+     (void)get_AsciiZ(&c);
+   }
+   (void)get_UChar(&c); /* skip terminating zero */
+
+   /* Read and record the file names table */
+   vg_assert(parser->filenameTable);
+   vg_assert( VG_(sizeXA)( parser->filenameTable ) == 0 );
+   /* Add a dummy index-zero entry.  DWARF3 numbers its files
+      from 1, for some reason. */
+   str = ML_(addStr)( cc->di, "<unknown_file>", -1 );
+   VG_(addToXA)( parser->filenameTable, &str );
+   while (peek_UChar(&c) != 0) {
+      str = get_AsciiZ(&c);
+      TRACE_D3("  read_filename_table: %ld %s\n",
+               VG_(sizeXA)(parser->filenameTable), str);
+      str = ML_(addStr)( cc->di, str, -1 );
+      VG_(addToXA)( parser->filenameTable, &str );
+      (void)get_ULEB128( &c ); /* skip directory index # */
+      (void)get_ULEB128( &c ); /* skip last mod time */
+      (void)get_ULEB128( &c ); /* file size */
+   }
+   /* We're done!  The rest of it is not interesting. */
+}
+
+
+__attribute__((noinline))
+static void parse_var_DIE ( /*OUT*/TempVar** tempvars,
+                            /*OUT*/GExpr** gexprs,
+                            /*MOD*/D3VarParser* parser,
+                            DW_TAG dtag,
+                            UWord posn,
+                            Int level,
+                            Cursor* c_die,
+                            Cursor* c_abbv,
+                            CUConst* cc,
+                            Bool td3 )
+{
+   ULong       cts;
+   Int         ctsSzB;
+   UWord       ctsMemSzB;
+
+   UWord saved_die_c_offset  = get_position_of_Cursor( c_die );
+   UWord saved_abbv_c_offset = get_position_of_Cursor( c_abbv );
+
+   varstack_preen( parser, td3, level-1 );
+
+   if (dtag == DW_TAG_compile_unit) {
+      Bool have_lo    = False;
+      Bool have_hi1   = False;
+      Bool have_range = False;
+      Addr ip_lo    = 0;
+      Addr ip_hi1   = 0;
+      Addr rangeoff = 0;
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_low_pc && ctsSzB > 0) {
+            ip_lo   = cts;
+            have_lo = True;
+         }
+         if (attr == DW_AT_high_pc && ctsSzB > 0) {
+            ip_hi1   = cts;
+            have_hi1 = True;
+         }
+         if (attr == DW_AT_ranges && ctsSzB > 0) {
+            rangeoff = cts;
+            have_range = True;
+         }
+         if (attr == DW_AT_stmt_list && ctsSzB > 0) {
+            read_filename_table( parser, cc, (UWord)cts, td3 );
+         }
+      }
+      /* Now, does this give us an opportunity to find this
+         CU's svma? */
+#if 0
+      if (level == 0 && have_lo) {
+         vg_assert(!cc->cu_svma_known); /* if this fails, it must be
+         because we've already seen a DW_TAG_compile_unit DIE at level
+         0.  But that can't happen, because DWARF3 only allows exactly
+         one top level DIE per CU. */
+         cc->cu_svma_known = True;
+         cc->cu_svma = ip_lo;
+         if (1)
+            TRACE_D3("BBBBAAAA acquire CU_SVMA of %p\n", cc->cu_svma);
+         /* Now, it may be that this DIE doesn't tell us the CU's
+            SVMA, by way of not having a DW_AT_low_pc.  That's OK --
+            the CU doesn't *have* to have its SVMA specified.
+
+            But as per last para D3 spec sec 3.1.1 ("Normal and
+            Partial Compilation Unit Entries", "If the base address
+            (viz, the SVMA) is undefined, then any DWARF entry of
+            structure defined interms of the base address of that
+            compilation unit is not valid.".  So that means, if whilst
+            processing the children of this top level DIE (or their
+            children, etc) we see a DW_AT_range, and cu_svma_known is
+            False, then the DIE that contains it is (per the spec)
+            invalid, and we can legitimately stop and complain. */
+      }
+#else
+      /* .. whereas The Reality is, simply assume the SVMA is zero
+         if it isn't specified. */
+      if (level == 0) {
+         vg_assert(!cc->cu_svma_known);
+         cc->cu_svma_known = True;
+         if (have_lo)
+            cc->cu_svma = ip_lo;
+         else
+            cc->cu_svma = 0;
+      }
+#endif
+      /* Do we have something that looks sane? */
+      if (have_lo && have_hi1 && (!have_range)) {
+         if (ip_lo < ip_hi1)
+            varstack_push( cc, parser, td3, 
+                           unitary_range_list(ip_lo, ip_hi1 - 1),
+                           level,
+                           False/*isFunc*/, NULL/*fbGX*/ );
+      } else
+      if ((!have_lo) && (!have_hi1) && have_range) {
+         varstack_push( cc, parser, td3, 
+                        get_range_list( cc, td3,
+                                        rangeoff, cc->cu_svma ),
+                        level,
+                        False/*isFunc*/, NULL/*fbGX*/ );
+      } else
+      if ((!have_lo) && (!have_hi1) && (!have_range)) {
+         /* CU has no code, presumably? */
+         varstack_push( cc, parser, td3, 
+                        empty_range_list(),
+                        level,
+                        False/*isFunc*/, NULL/*fbGX*/ );
+      } else
+         goto bad_DIE;
+   }
+
+   if (dtag == DW_TAG_lexical_block || dtag == DW_TAG_subprogram) {
+      Bool   have_lo    = False;
+      Bool   have_hi1   = False;
+      Bool   have_range = False;
+      Addr   ip_lo      = 0;
+      Addr   ip_hi1     = 0;
+      Addr   rangeoff   = 0;
+      Bool   isFunc     = dtag == DW_TAG_subprogram;
+      GExpr* fbGX       = NULL;
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_low_pc && ctsSzB > 0) {
+            ip_lo   = cts;
+            have_lo = True;
+         }
+         if (attr == DW_AT_high_pc && ctsSzB > 0) {
+            ip_hi1   = cts;
+            have_hi1 = True;
+         }
+         if (attr == DW_AT_ranges && ctsSzB > 0) {
+            rangeoff = cts;
+            have_range = True;
+         }
+         if (isFunc
+             && attr == DW_AT_frame_base
+             && ((ctsMemSzB > 0 && ctsSzB == 0)
+                 || (ctsMemSzB == 0 && ctsSzB > 0))) {
+            fbGX = get_GX( cc, False/*td3*/, cts, ctsSzB, ctsMemSzB );
+            vg_assert(fbGX);
+            vg_assert(!fbGX->next);
+            fbGX->next = *gexprs;
+            *gexprs = fbGX;
+         }
+      }
+      /* Do we have something that looks sane? */
+      if (dtag == DW_TAG_subprogram 
+          && (!have_lo) && (!have_hi1) && (!have_range)) {
+         /* This is legit - ignore it. Sec 3.3.3: "A subroutine entry
+            representing a subroutine declaration that is not also a
+            definition does not have code address or range
+            attributes." */
+      } else
+      if (dtag == DW_TAG_lexical_block
+          && (!have_lo) && (!have_hi1) && (!have_range)) {
+         /* I believe this is legit, and means the lexical block
+            contains no insns (whatever that might mean).  Ignore. */
+      } else
+      if (have_lo && have_hi1 && (!have_range)) {
+         /* This scope supplies just a single address range. */
+         if (ip_lo < ip_hi1)
+            varstack_push( cc, parser, td3, 
+                           unitary_range_list(ip_lo, ip_hi1 - 1),
+                           level, isFunc, fbGX );
+      } else
+      if ((!have_lo) && (!have_hi1) && have_range) {
+         /* This scope supplies multiple address ranges via the use of
+            a range list. */
+         varstack_push( cc, parser, td3, 
+                        get_range_list( cc, td3,
+                                        rangeoff, cc->cu_svma ),
+                        level, isFunc, fbGX );
+      } else
+      if (have_lo && (!have_hi1) && (!have_range)) {
+         /* This scope is bogus.  The D3 spec sec 3.4 (Lexical Block
+            Entries) says fairly clearly that a scope must have either
+            _range or (_low_pc and _high_pc). */
+         /* The spec is a bit ambiguous though.  Perhaps a single byte
+            range is intended?  See sec 2.17 (Code Addresses And Ranges) */
+         /* This case is here because icc9 produced this:
+         <2><13bd>: DW_TAG_lexical_block
+            DW_AT_decl_line   : 5229
+            DW_AT_decl_column : 37
+            DW_AT_decl_file   : 1
+            DW_AT_low_pc      : 0x401b03
+         */
+         /* Ignore (seems safe than pushing a single byte range) */
+      } else
+         goto bad_DIE;
+   }
+
+   if (dtag == DW_TAG_variable || dtag == DW_TAG_formal_parameter) {
+      UChar* name        = NULL;
+      Type*  typeR       = D3_INVALID_CUOFF;
+      Bool   external    = False;
+      GExpr* gexpr       = NULL;
+      Int    n_attrs     = 0;
+      UWord  abs_ori     = (UWord)D3_INVALID_CUOFF;
+      Bool   declaration = False;
+      Int    lineNo      = 0;
+      UChar* fileName    = NULL;
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         n_attrs++;
+         if (attr == DW_AT_name && ctsMemSzB > 0) {
+            name = ML_(addStr)( cc->di, (UChar*)(UWord)cts, -1 );
+         }
+         if (attr == DW_AT_location
+             && ((ctsMemSzB > 0 && ctsSzB == 0)
+                 || (ctsMemSzB == 0 && ctsSzB > 0))) {
+            gexpr = get_GX( cc, False/*td3*/, cts, ctsSzB, ctsMemSzB );
+            vg_assert(gexpr);
+            vg_assert(!gexpr->next);
+            gexpr->next = *gexprs;
+            *gexprs = gexpr;
+         }
+         if (attr == DW_AT_type && ctsSzB > 0) {
+            typeR = (Type*)(UWord)cts;
+         }
+         if (attr == DW_AT_external && ctsSzB > 0 && cts > 0) {
+            external = True;
+         }
+         if (attr == DW_AT_abstract_origin && ctsSzB > 0) {
+            abs_ori = (UWord)cts;
+         }
+         if (attr == DW_AT_declaration && ctsSzB > 0 && cts > 0) {
+            declaration = True;
+         }
+         if (attr == DW_AT_decl_line && ctsSzB > 0) {
+            lineNo = (Int)cts;
+         }
+         if (attr == DW_AT_decl_file && ctsSzB > 0) {
+            Int ftabIx = (Int)cts;
+            if (ftabIx >= 1
+                && ftabIx < VG_(sizeXA)( parser->filenameTable )) {
+               fileName = *(UChar**)
+                          VG_(indexXA)( parser->filenameTable, ftabIx );
+               vg_assert(fileName);
+            }
+            if (0) VG_(printf)("XXX filename = %s\n", fileName);
+         }
+      }
+      /* We'll collect it under if one of the following three
+         conditions holds:
+         (1) has location and type    -> completed
+         (2) has type only            -> is an abstract instance
+         (3) has location and abs_ori -> is a concrete instance
+         Name, filename and line number are all option frills.
+      */
+      if ( /* 1 */ (gexpr && typeR != D3_INVALID_CUOFF) 
+           /* 2 */ || (typeR != D3_INVALID_CUOFF)
+           /* 3 */ || (gexpr && abs_ori != (UWord)D3_INVALID_CUOFF) ) {
+
+         /* Add this variable to the list of interesting looking
+            variables.  Crucially, note along with it the address
+            range(s) associated with the variable, which for locals
+            will be the address ranges at the top of the varparser's
+            stack. */
+         GExpr*   fbGX = NULL;
+         Word     i, nRanges;
+         XArray*  /* of AddrRange */ xa;
+         TempVar* tv;
+         /* Stack can't be empty; we put a dummy entry on it for the
+            entire address range before starting with the DIEs for
+            this CU. */
+         vg_assert(parser->sp >= 0);
+
+         /* If this is a local variable (non-external), try to find
+            the GExpr for the DW_AT_frame_base of the containing
+            function.  It should have been pushed on the stack at the
+            time we encountered its DW_TAG_subprogram DIE, so the way
+            to find it is to scan back down the stack looking for it.
+            If there isn't an enclosing stack entry marked 'isFunc'
+            then we must be seeing variable or formal param DIEs
+            outside of a function, so we deem the Dwarf to be
+            malformed if that happens.  Note that the fbGX may be NULL
+            if the containing DT_TAG_subprogram didn't supply a
+            DW_AT_frame_base -- that's OK, but there must actually be
+            a containing DW_TAG_subprogram. */
+         if (!external) {
+            Bool found = False;
+            for (i = parser->sp; i >= 0; i--) {
+               if (parser->isFunc[i]) {
+                  fbGX = parser->fbGX[i];
+                  found = True;
+                  break;
+               }
+            }
+            if (!found) {
+               if (0 && VG_(clo_verbosity) >= 0) {
+                  VG_(message)(Vg_DebugMsg, 
+                     "warning: parse_var_DIE: non-external variable "
+                     "outside DW_TAG_subprogram");
+               }
+               /* goto bad_DIE; */
+               /* This seems to happen a lot.  Just ignore it -- if,
+                  when we come to evaluation of the location (guarded)
+                  expression, it requires a frame base value, and
+                  there's no expression for that, then evaluation as a
+                  whole will fail.  Harmless - a bit of a waste of
+                  cycles but nothing more. */
+            }
+         }
+
+         /* re "external ? 0 : parser->sp" (twice), if the var is
+            marked 'external' then we must put it at the global scope,
+            as only the global scope (level 0) covers the entire PC
+            address space.  It is asserted elsewhere that level 0 
+            always covers the entire address space. */
+         xa = parser->ranges[external ? 0 : parser->sp];
+         nRanges = VG_(sizeXA)(xa);
+         vg_assert(nRanges >= 0);
+
+         tv = ML_(dinfo_zalloc)( sizeof(TempVar) );
+         tv->name   = name;
+         tv->level  = external ? 0 : parser->sp;
+         tv->typeR  = typeR;
+         tv->gexpr  = gexpr;
+         tv->fbGX   = fbGX;
+         tv->fName  = fileName;
+         tv->fLine  = lineNo;
+         tv->dioff  = posn;
+         tv->absOri = abs_ori;
+
+         /* See explanation on definition of type TempVar for the
+            reason for this elaboration. */
+         tv->nRanges = nRanges;
+         tv->rngOneMin = 0;
+         tv->rngOneMax = 0;
+         tv->rngMany = NULL;
+         if (nRanges == 1) {
+            AddrRange* range = VG_(indexXA)(xa, 0);
+            tv->rngOneMin = range->aMin;
+            tv->rngOneMax = range->aMax;
+         }
+         else if (nRanges > 1) {
+            tv->rngMany = VG_(cloneXA)( xa ); /* free when 'tv' freed */
+         }
+
+         tv->next  = *tempvars;
+         *tempvars = tv;
+
+         TRACE_D3("  Recording this variable, with %ld PC range(s)\n",
+                  VG_(sizeXA)(xa) );
+         /* collect stats on how effective the ->ranges special
+            casing is */
+         if (0) {
+           static Int ntot=0, ngt=0;
+           ntot++;
+           if (tv->rngMany) ngt++;
+           if (0 == (ntot % 100000))
+              VG_(printf)("XXXX %d tot, %d cloned\n", ntot, ngt);
+         }
+
+      }
+
+      /* Here are some other weird cases seen in the wild:
+
+            We have a variable with a name and a type, but no
+            location.  I guess that's a sign that it has been
+            optimised away.  Ignore it.  Here's an example:
+
+            static Int lc_compar(void* n1, void* n2) {
+               MC_Chunk* mc1 = *(MC_Chunk**)n1;
+               MC_Chunk* mc2 = *(MC_Chunk**)n2;
+               return (mc1->data < mc2->data ? -1 : 1);
+            }
+
+            Both mc1 and mc2 are like this
+            <2><5bc>: Abbrev Number: 21 (DW_TAG_variable)
+                DW_AT_name        : mc1
+                DW_AT_decl_file   : 1
+                DW_AT_decl_line   : 216
+                DW_AT_type        : <5d3>
+
+            whereas n1 and n2 do have locations specified.
+
+            ---------------------------------------------
+
+            We see a DW_TAG_formal_parameter with a type, but
+            no name and no location.  It's probably part of a function type
+            construction, thusly, hence ignore it:
+         <1><2b4>: Abbrev Number: 12 (DW_TAG_subroutine_type)
+             DW_AT_sibling     : <2c9>
+             DW_AT_prototyped  : 1
+             DW_AT_type        : <114>
+         <2><2be>: Abbrev Number: 13 (DW_TAG_formal_parameter)
+             DW_AT_type        : <13e>
+         <2><2c3>: Abbrev Number: 13 (DW_TAG_formal_parameter)
+             DW_AT_type        : <133>
+
+            ---------------------------------------------
+
+            Is very minimal, like this:
+            <4><81d>: Abbrev Number: 44 (DW_TAG_variable)
+                DW_AT_abstract_origin: <7ba>
+            What that signifies I have no idea.  Ignore.
+
+            ----------------------------------------------
+
+            Is very minimal, like this:
+            <200f>: DW_TAG_formal_parameter
+                DW_AT_abstract_ori: <1f4c>
+                DW_AT_location    : 13440
+            What that signifies I have no idea.  Ignore. 
+            It might be significant, though: the variable at least
+            has a location and so might exist somewhere.
+            Maybe we should handle this.
+
+            ---------------------------------------------
+
+            <22407>: DW_TAG_variable
+              DW_AT_name        : (indirect string, offset: 0x6579):
+                                  vgPlain_trampoline_stuff_start
+              DW_AT_decl_file   : 29
+              DW_AT_decl_line   : 56
+              DW_AT_external    : 1
+              DW_AT_declaration : 1
+
+            Nameless and typeless variable that has a location?  Who
+            knows.  Not me.
+            <2><3d178>: Abbrev Number: 22 (DW_TAG_variable)
+                 DW_AT_location    : 9 byte block: 3 c0 c7 13 38 0 0 0 0
+                                     (DW_OP_addr: 3813c7c0)
+
+            No, really.  Check it out.  gcc is quite simply borked.
+            <3><168cc>: Abbrev Number: 141 (DW_TAG_variable)
+            // followed by no attributes, and the next DIE is a sibling,
+            // not a child
+            */
+   }
+   return;
+
+  bad_DIE:
+   set_position_of_Cursor( c_die,  saved_die_c_offset );
+   set_position_of_Cursor( c_abbv, saved_abbv_c_offset );
+   VG_(printf)("\nparse_var_DIE: confused by:\n");
+   VG_(printf)(" <%d><%lx>: %s\n", level, posn, ML_(pp_DW_TAG)( dtag ) );
+   while (True) {
+      DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+      DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+      if (attr == 0 && form == 0) break;
+      VG_(printf)("     %18s: ", ML_(pp_DW_AT)(attr));
+      /* Get the form contents, so as to print them */
+      get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                         cc, c_die, True, form );
+      VG_(printf)("\t\n");
+   }
+   VG_(printf)("\n");
+   cc->barf("parse_var_DIE: confused by the above DIE");
+   /*NOTREACHED*/
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Parsing of type-related DIEs                         ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+#define N_D3_TYPE_STACK 16
+
+typedef
+   struct {
+      /* What source language?  'C'=C/C++, 'F'=Fortran, '?'=other
+         Established once per compilation unit. */
+      UChar language;
+      /* A stack of types which are currently under construction */
+      Int   sp; /* [sp] is innermost active entry; sp==-1 for empty
+                   stack */
+      Type* qparent[N_D3_TYPE_STACK];
+      Int   qlevel[N_D3_TYPE_STACK];
+
+   }
+   D3TypeParser;
+
+static void typestack_show ( D3TypeParser* parser, HChar* str ) {
+   Word i;
+   VG_(printf)("  typestack (%s) {\n", str);
+   for (i = 0; i <= parser->sp; i++) {
+      VG_(printf)("    [%ld] (level %d): ", i, parser->qlevel[i]);
+      ML_(pp_Type)( parser->qparent[i] );
+      VG_(printf)("\n");
+   }
+   VG_(printf)("  }\n");
+}
+
+/* Remove from the stack, all entries with .level > 'level' */
+static 
+void typestack_preen ( D3TypeParser* parser, Bool td3, Int level )
+{
+   Bool changed = False;
+   vg_assert(parser->sp < N_D3_TYPE_STACK);
+   while (True) {
+      vg_assert(parser->sp >= -1);
+      if (parser->sp == -1) break;
+      if (parser->qlevel[parser->sp] <= level) break;
+      if (0) 
+         TRACE_D3("BBBBAAAA typestack_pop [newsp=%d]\n", parser->sp-1);
+      vg_assert(parser->qparent[parser->sp]);
+      parser->qparent[parser->sp] = NULL;
+      parser->qlevel[parser->sp]  = 0;
+      parser->sp--;
+      changed = True;
+   }
+   if (changed && td3)
+      typestack_show( parser, "after preen" );
+}
+
+static Bool typestack_is_empty ( D3TypeParser* parser ) {
+   vg_assert(parser->sp >= -1 && parser->sp < N_D3_TYPE_STACK);
+   return parser->sp == -1;
+}
+
+static void typestack_push ( CUConst* cc,
+                             D3TypeParser* parser,
+                             Bool td3,
+                             Type* parent, Int level ) {
+   if (0)
+   TRACE_D3("BBBBAAAA typestack_push[newsp=%d]: %d  %p\n",
+            parser->sp+1, level, parent);
+
+   /* First we need to zap everything >= 'level', as we are about to
+      replace any previous entry at 'level', so .. */
+   typestack_preen(parser, /*td3*/False, level-1);
+
+   vg_assert(parser->sp >= -1);
+   vg_assert(parser->sp < N_D3_TYPE_STACK);
+   if (parser->sp == N_D3_TYPE_STACK-1)
+      cc->barf("typestack_push: N_D3_TYPE_STACK is too low; "
+               "increase and recompile");
+   if (parser->sp >= 0)
+      vg_assert(parser->qlevel[parser->sp] < level);
+   parser->sp++;
+   vg_assert(parser->qparent[parser->sp] == NULL);
+   vg_assert(parser->qlevel[parser->sp]  == 0);
+   vg_assert(parent != NULL);
+   parser->qparent[parser->sp] = parent;
+   parser->qlevel[parser->sp]  = level;
+   if (td3)
+      typestack_show( parser, "after push" );
+}
+
+
+/* Parse a type-related DIE.  'parser' holds the current parser state.
+   'admin' is where the completed types are dumped.  'dtag' is the tag
+   for this DIE.  'c_die' points to the start of the data fields (FORM
+   stuff) for the DIE.  c_abbv points to the start of the (name,form)
+   pairs which describe the DIE.
+
+   We may find the DIE uninteresting, in which case we should ignore
+   it.
+*/
+__attribute__((noinline))
+static void parse_type_DIE ( /*OUT*/TyAdmin** admin,
+                             /*MOD*/D3TypeParser* parser,
+                             DW_TAG dtag,
+                             UWord posn,
+                             Int level,
+                             Cursor* c_die,
+                             Cursor* c_abbv,
+                             CUConst* cc,
+                             Bool td3 )
+{
+   ULong     cts;
+   Int       ctsSzB;
+   UWord     ctsMemSzB;
+   Type*     type   = NULL;
+   TyAtom*   atom   = NULL;
+   TyField*  field  = NULL;
+   D3Expr*   expr   = NULL;
+   TyBounds* bounds = NULL;
+
+   UWord saved_die_c_offset  = get_position_of_Cursor( c_die );
+   UWord saved_abbv_c_offset = get_position_of_Cursor( c_abbv );
+
+   /* If we've returned to a level at or above any previously noted
+      parent, un-note it, so we don't believe we're still collecting
+      its children. */
+   typestack_preen( parser, td3, level-1 );
+
+   if (dtag == DW_TAG_compile_unit) {
+      /* See if we can find DW_AT_language, since it is important for
+         establishing array bounds (see DW_TAG_subrange_type below in
+         this fn) */
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr != DW_AT_language)
+            continue;
+         if (ctsSzB == 0)
+           goto bad_DIE;
+         switch (cts) {
+            case DW_LANG_C89: case DW_LANG_C:
+            case DW_LANG_C_plus_plus: case DW_LANG_ObjC:
+            case DW_LANG_ObjC_plus_plus: case DW_LANG_UPC:
+            case DW_LANG_Upc:
+               parser->language = 'C'; break;
+            case DW_LANG_Fortran77: case DW_LANG_Fortran90:
+            case DW_LANG_Fortran95:
+               parser->language = 'F'; break;
+            case DW_LANG_Ada83: case DW_LANG_Cobol74:
+            case DW_LANG_Cobol85: case DW_LANG_Pascal83:
+            case DW_LANG_Modula2: case DW_LANG_Java:
+            case DW_LANG_C99: case DW_LANG_Ada95:
+            case DW_LANG_PLI: case DW_LANG_D:
+            case DW_LANG_Mips_Assembler:
+               parser->language = '?'; break;
+            default:
+               goto bad_DIE;
+         }
+      }
+   }
+
+   if (dtag == DW_TAG_base_type) {
+      /* We can pick up a new base type any time. */
+      type = ML_(new_Type)();
+      type->tag = Ty_Base;
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_name && ctsMemSzB > 0) {
+            type->Ty.Base.name
+               = ML_(addStr)( cc->di, (UChar*)(UWord)cts, -1 );
+         }
+         if (attr == DW_AT_byte_size && ctsSzB > 0) {
+            type->Ty.Base.szB = cts;
+         }
+         if (attr == DW_AT_encoding && ctsSzB > 0) {
+            switch (cts) {
+               case DW_ATE_unsigned: case DW_ATE_unsigned_char:
+               case DW_ATE_boolean:/* FIXME - is this correct? */
+                  type->Ty.Base.enc = 'U'; break;
+               case DW_ATE_signed: case DW_ATE_signed_char:
+                  type->Ty.Base.enc = 'S'; break;
+               case DW_ATE_float:
+                  type->Ty.Base.enc = 'F'; break;
+               case DW_ATE_complex_float:
+                  type->Ty.Base.enc = 'C'; break;
+               default:
+                  goto bad_DIE;
+            }
+         }
+      }
+
+      /* Invent a name if it doesn't have one.  gcc-4.3
+         -ftree-vectorize is observed to emit nameless base types. */
+      if (!type->Ty.Base.name)
+         type->Ty.Base.name 
+            = ML_(addStr)( cc->di, "<anon_base_type>", -1 );
+
+      /* Do we have something that looks sane? */
+      if (/* must have a name */
+          type->Ty.Base.name == NULL
+          /* and a plausible size.  Yes, really 32: "complex long
+             double" apparently has size=32 */
+          || type->Ty.Base.szB < 0 || type->Ty.Base.szB > 32
+          /* and a plausible encoding */
+          || (type->Ty.Base.enc != 'U'
+              && type->Ty.Base.enc != 'S' 
+              && type->Ty.Base.enc != 'F'
+              && type->Ty.Base.enc != 'C'))
+         goto bad_DIE;
+      /* Last minute hack: if we see this
+         <1><515>: DW_TAG_base_type
+             DW_AT_byte_size   : 0
+             DW_AT_encoding    : 5
+             DW_AT_name        : void
+         convert it into a real Void type. */
+      if (type->Ty.Base.szB == 0
+          && 0 == VG_(strcmp)("void", type->Ty.Base.name)) {
+         VG_(memset)(type, 0, sizeof(*type));
+         type->tag = Ty_Void;
+         type->Ty.Void.isFake = False; /* it's a real one! */
+      }
+      goto acquire_Type;
+   }
+
+   if (dtag == DW_TAG_pointer_type || dtag == DW_TAG_reference_type
+       || dtag == DW_TAG_ptr_to_member_type) {
+      /* This seems legit for _pointer_type and _reference_type.  I
+         don't know if rolling _ptr_to_member_type in here really is
+         legit, but it's better than not handling it at all. */
+      type = ML_(new_Type)();
+      type->tag = Ty_PorR;
+      /* target type defaults to void */
+      type->Ty.PorR.typeR = D3_FAKEVOID_CUOFF;
+      type->Ty.PorR.isPtr = dtag == DW_TAG_pointer_type
+                            || dtag == DW_TAG_ptr_to_member_type;
+      /* Pointer types don't *have* to specify their size, in which
+         case we assume it's a machine word.  But if they do specify
+         it, it must be a machine word :-) This probably assumes that
+         the word size of the Dwarf3 we're reading is the same size as
+         that on the machine.  gcc appears to give a size whereas icc9
+         doesn't. */
+      if (type->Ty.PorR.isPtr)
+         type->Ty.PorR.szB = sizeof(Word);
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_byte_size && ctsSzB > 0) {
+            type->Ty.PorR.szB = cts;
+         }
+         if (attr == DW_AT_type && ctsSzB > 0) {
+            type->Ty.PorR.typeR = (Type*)(UWord)cts;
+         }
+      }
+      /* Do we have something that looks sane? */
+      if (type->Ty.PorR.szB != sizeof(Word))
+         goto bad_DIE;
+      else
+         goto acquire_Type;
+   }
+
+   if (dtag == DW_TAG_enumeration_type) {
+      /* Create a new Type to hold the results. */
+      type = ML_(new_Type)();
+      type->tag = Ty_Enum;
+      type->Ty.Enum.name = NULL;
+      type->Ty.Enum.atomRs
+         = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                       sizeof(TyAtom*) );
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_name && ctsMemSzB > 0) {
+            type->Ty.Enum.name
+               = ML_(addStr)( cc->di, (UChar*)(UWord)cts, -1 );
+         }
+         if (attr == DW_AT_byte_size && ctsSzB > 0) {
+            type->Ty.Enum.szB = cts;
+         }
+      }
+      /* Do we have something that looks sane? */
+      if (type->Ty.Enum.szB == 0 /* we must know the size */
+          /* But the name can be present, or not */)
+         goto bad_DIE;
+      /* On't stack! */
+      typestack_push( cc, parser, td3, type, level );
+      goto acquire_Type;
+   }
+
+   if (dtag == DW_TAG_enumerator) {
+      Bool have_value = False;
+      atom = ML_(new_TyAtom)( NULL, 0 );
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_name && ctsMemSzB > 0) {
+            atom->name = ML_(addStr)( cc->di, (UChar*)(UWord)cts, -1 );
+         }
+         if (attr == DW_AT_const_value && ctsSzB > 0) {
+            atom->value = cts;
+            have_value = True;
+         }
+      }
+      /* Do we have something that looks sane? */
+      if ((!have_value) || atom->name == NULL)
+         goto bad_DIE;
+      /* Do we have a plausible parent? */
+      if (typestack_is_empty(parser)) goto bad_DIE;
+      vg_assert(parser->qparent[parser->sp]);
+      if (level != parser->qlevel[parser->sp]+1) goto bad_DIE;
+      if (parser->qparent[parser->sp]->tag != Ty_Enum) goto bad_DIE;
+      /* Record this child in the parent */
+      vg_assert(parser->qparent[parser->sp]->Ty.Enum.atomRs);
+      VG_(addToXA)( parser->qparent[parser->sp]->Ty.Enum.atomRs, &atom );
+      /* And record the child itself */
+      goto acquire_Atom;
+   }
+
+   if (dtag == DW_TAG_structure_type || dtag == DW_TAG_union_type) {
+      Bool have_szB = False;
+      Bool is_decl  = False;
+      Bool is_spec  = False;
+      /* Create a new Type to hold the results. */
+      type = ML_(new_Type)();
+      type->tag = Ty_StOrUn;
+      type->Ty.StOrUn.name = NULL;
+      type->Ty.StOrUn.fields
+         = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                       sizeof(TyAtom*) );
+      type->Ty.StOrUn.complete = True;
+      type->Ty.StOrUn.isStruct = dtag == DW_TAG_structure_type;
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_name && ctsMemSzB > 0) {
+            type->Ty.StOrUn.name
+               = ML_(addStr)( cc->di, (UChar*)(UWord)cts, -1 );
+         }
+         if (attr == DW_AT_byte_size && ctsSzB >= 0) {
+            type->Ty.StOrUn.szB = cts;
+            have_szB = True;
+         }
+         if (attr == DW_AT_declaration && ctsSzB > 0 && cts > 0) {
+            is_decl = True;
+         }
+         if (attr == DW_AT_specification && ctsSzB > 0 && cts > 0) {
+            is_spec = True;
+         }
+      }
+      /* Do we have something that looks sane? */
+      if (is_decl && (!is_spec)) {
+         /* It's a DW_AT_declaration.  We require the name but
+            nothing else. */
+         if (type->Ty.StOrUn.name == NULL)
+            goto bad_DIE;
+         type->Ty.StOrUn.complete = False;
+         goto acquire_Type;
+      }
+      if ((!is_decl) /* && (!is_spec) */) {
+         /* this is the common, ordinary case */
+         if ((!have_szB) /* we must know the size */
+             /* But the name can be present, or not */)
+            goto bad_DIE;
+         /* On't stack! */
+         typestack_push( cc, parser, td3, type, level );
+         goto acquire_Type;
+      }
+      else {
+         /* don't know how to handle any other variants just now */
+         goto bad_DIE;
+      }
+   }
+
+   if (dtag == DW_TAG_member) {
+      /* Acquire member entries for both DW_TAG_structure_type and
+         DW_TAG_union_type.  They differ minorly, in that struct
+         members must have a DW_AT_data_member_location expression
+         whereas union members must not. */
+      Bool parent_is_struct;
+      field = ML_(new_TyField)( NULL, NULL, NULL );
+      field->typeR = D3_INVALID_CUOFF;
+      expr  = NULL;
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_name && ctsMemSzB > 0) {
+            field->name = ML_(addStr)( cc->di, (UChar*)(UWord)cts, -1 );
+         }
+         if (attr == DW_AT_type && ctsSzB > 0) {
+            field->typeR = (Type*)(UWord)cts;
+         }
+         if (attr == DW_AT_data_member_location && ctsMemSzB > 0) {
+            UChar* copy = ML_(addStr)( cc->di, (UChar*)(UWord)cts, 
+                                               (Int)ctsMemSzB );
+            expr = ML_(new_D3Expr)( copy, (UWord)ctsMemSzB );
+         }
+      }
+      /* Do we have a plausible parent? */
+      if (typestack_is_empty(parser)) goto bad_DIE;
+      vg_assert(parser->qparent[parser->sp]);
+      if (level != parser->qlevel[parser->sp]+1) goto bad_DIE;
+      if (parser->qparent[parser->sp]->tag != Ty_StOrUn) goto bad_DIE;
+      /* Do we have something that looks sane?  If this a member of a
+         struct, we must have a location expression; but if a member
+         of a union that is irrelevant (D3 spec sec 5.6.6).  We ought
+         to reject in the latter case, but some compilers have been
+         observed to emit constant-zero expressions.  So just ignore
+         them. */
+      parent_is_struct
+         = parser->qparent[parser->sp]->Ty.StOrUn.isStruct;
+      if (!field->name)
+         field->name = ML_(addStr)(cc->di, "<anon_field>", -1);
+      if ((!field->name) || (field->typeR == D3_INVALID_CUOFF))
+         goto bad_DIE;
+      if (parent_is_struct && (!expr))
+         goto bad_DIE;
+      if ((!parent_is_struct) && expr) {
+         /* If this is a union type, pretend we haven't seen the data
+            member location expression, as it is by definition
+            redundant (it must be zero). */
+         expr = NULL;
+      }
+      /* Record this child in the parent */
+      field->isStruct = parent_is_struct;
+      if (expr)
+         field->loc = expr;
+      vg_assert(parser->qparent[parser->sp]->Ty.StOrUn.fields);
+      VG_(addToXA)( parser->qparent[parser->sp]->Ty.StOrUn.fields,
+                    &field );
+      /* And record the child itself */
+      goto acquire_Field_and_Expr;
+   }
+
+   if (dtag == DW_TAG_array_type) {
+      type = ML_(new_Type)();
+      type->tag = Ty_Array;
+      type->Ty.Array.typeR = D3_INVALID_CUOFF;
+      type->Ty.Array.bounds
+         = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                       sizeof(TyBounds*) );
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_type && ctsSzB > 0) {
+            type->Ty.Array.typeR = (Type*)(UWord)cts;
+         }
+      }
+      if (type->Ty.Array.typeR == D3_INVALID_CUOFF)
+         goto bad_DIE;
+      /* On't stack! */
+      typestack_push( cc, parser, td3, type, level );
+      goto acquire_Type;
+   }
+
+   if (dtag == DW_TAG_subrange_type) {
+      Bool have_lower = False;
+      Bool have_upper = False;
+      Bool have_count = False;
+      Long lower = 0;
+      Long upper = 0;
+      Long count = 0;
+
+      switch (parser->language) {
+         case 'C': have_lower = True;  lower = 0; break;
+         case 'F': have_lower = True;  lower = 1; break;
+         case '?': have_lower = False; break;
+         default:  vg_assert(0); /* assured us by handling of
+                                    DW_TAG_compile_unit in this fn */
+      }
+      bounds = ML_(new_TyBounds)();
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_lower_bound && ctsSzB > 0) {
+            lower      = (Long)cts;
+            have_lower = True;
+         }
+         if (attr == DW_AT_upper_bound && ctsSzB > 0) {
+            upper      = (Long)cts;
+            have_upper = True;
+         }
+         if (attr == DW_AT_count && ctsSzB > 0) {
+            count      = cts;
+            have_count = True;
+         }
+      }
+      /* FIXME: potentially skip the rest if no parent present, since
+         it could be the case that this subrange type is free-standing
+         (not being used to describe the bounds of a containing array
+         type) */
+      /* Do we have a plausible parent? */
+      if (typestack_is_empty(parser)) goto bad_DIE;
+      vg_assert(parser->qparent[parser->sp]);
+      if (level != parser->qlevel[parser->sp]+1) goto bad_DIE;
+      if (parser->qparent[parser->sp]->tag != Ty_Array) goto bad_DIE;
+
+      /* Figure out if we have a definite range or not */
+      if (have_lower && have_upper && (!have_count)) {
+         bounds->knownL = True;
+         bounds->knownU = True;
+         bounds->boundL = lower;
+         bounds->boundU = upper;
+      } 
+      else if (have_lower && (!have_upper) && (!have_count)) {
+         bounds->knownL = True;
+         bounds->knownU = False;
+         bounds->boundL = lower;
+         bounds->boundU = 0;
+      } else {
+         /* FIXME: handle more cases */
+         goto bad_DIE;
+      }
+
+      /* Record this bound in the parent */
+      vg_assert(parser->qparent[parser->sp]->Ty.Array.bounds);
+      VG_(addToXA)( parser->qparent[parser->sp]->Ty.Array.bounds,
+                    &bounds );
+      /* And record the child itself */
+      goto acquire_Bounds;
+   }
+
+   if (dtag == DW_TAG_typedef) {
+      /* We can pick up a new base type any time. */
+      type = ML_(new_Type)();
+      type->tag = Ty_TyDef;
+      type->Ty.TyDef.name = NULL;
+      type->Ty.TyDef.typeR = D3_INVALID_CUOFF;
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_name && ctsMemSzB > 0) {
+            type->Ty.TyDef.name
+               = ML_(addStr)( cc->di, (UChar*)(UWord)cts, -1 );
+         }
+         if (attr == DW_AT_type && ctsSzB > 0) {
+            type->Ty.TyDef.typeR = (Type*)(UWord)cts;
+         }
+      }
+      /* Do we have something that looks sane? */
+      if (/* must have a name */
+          type->Ty.TyDef.name == NULL
+          /* but the referred-to type can be absent */)
+         goto bad_DIE;
+      else
+         goto acquire_Type;
+   }
+
+   if (dtag == DW_TAG_subroutine_type) {
+      /* function type? just record that one fact and ask no
+         further questions. */
+      type = ML_(new_Type)();
+      type->tag = Ty_Fn;
+      goto acquire_Type;
+   }
+
+   if (dtag == DW_TAG_volatile_type || dtag == DW_TAG_const_type) {
+      Int have_ty = 0;
+      type = ML_(new_Type)();
+      type->tag = Ty_Qual;
+      type->Ty.Qual.qual
+         = dtag == DW_TAG_volatile_type ? 'V' : 'C';
+      /* target type defaults to 'void' */
+      type->Ty.Qual.typeR = D3_FAKEVOID_CUOFF;
+      while (True) {
+         DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+         DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+         if (attr == 0 && form == 0) break;
+         get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                            cc, c_die, False/*td3*/, form );
+         if (attr == DW_AT_type && ctsSzB > 0) {
+            type->Ty.Qual.typeR = (Type*)(UWord)cts;
+            have_ty++;
+         }
+      }
+      /* gcc sometimes generates DW_TAG_const/volatile_type without
+         DW_AT_type and GDB appears to interpret the type as 'const
+         void' (resp. 'volatile void').  So just allow it .. */
+      if (have_ty == 1 || have_ty == 0)
+         goto acquire_Type;
+      else
+         goto bad_DIE;
+   }
+
+   /* else ignore this DIE */
+   return;
+   /*NOTREACHED*/
+
+  acquire_Type:
+   if (0) VG_(printf)("YYYY Acquire Type\n");
+   vg_assert(type); vg_assert(!atom); vg_assert(!field);
+   vg_assert(!expr); vg_assert(!bounds);
+   *admin            = ML_(new_TyAdmin)( posn, *admin );
+   (*admin)->payload = type;
+   (*admin)->tag     = TyA_Type;
+   return;
+   /*NOTREACHED*/
+
+  acquire_Atom:
+   if (0) VG_(printf)("YYYY Acquire Atom\n");
+   vg_assert(!type); vg_assert(atom); vg_assert(!field);
+   vg_assert(!expr); vg_assert(!bounds);
+   *admin            = ML_(new_TyAdmin)( posn, *admin );
+   (*admin)->payload = atom;
+   (*admin)->tag     = TyA_Atom;
+   return;
+   /*NOTREACHED*/
+
+  acquire_Field_and_Expr:
+   /* For union members, Expr should be absent */
+   if (0) VG_(printf)("YYYY Acquire Field and Expr\n");
+   vg_assert(!type); vg_assert(!atom); vg_assert(field); 
+   /*vg_assert(expr);*/ vg_assert(!bounds);
+   if (expr) {
+      *admin            = ML_(new_TyAdmin)( (UWord)D3_INVALID_CUOFF,
+                                            *admin );
+      (*admin)->payload = expr;
+      (*admin)->tag     = TyA_Expr;
+   }
+   *admin            = ML_(new_TyAdmin)( posn, *admin );
+   (*admin)->payload = field;
+   (*admin)->tag     = TyA_Field;
+   return;
+   /*NOTREACHED*/
+
+  acquire_Bounds:
+   if (0) VG_(printf)("YYYY Acquire Bounds\n");
+   vg_assert(!type); vg_assert(!atom); vg_assert(!field);
+   vg_assert(!expr); vg_assert(bounds);
+   *admin            = ML_(new_TyAdmin)( posn, *admin );
+   (*admin)->payload = bounds;
+   (*admin)->tag     = TyA_Bounds;
+   return;
+   /*NOTREACHED*/
+
+  bad_DIE:
+   set_position_of_Cursor( c_die,  saved_die_c_offset );
+   set_position_of_Cursor( c_abbv, saved_abbv_c_offset );
+   VG_(printf)("\nparse_type_DIE: confused by:\n");
+   VG_(printf)(" <%d><%lx>: %s\n", level, posn, ML_(pp_DW_TAG)( dtag ) );
+   while (True) {
+      DW_AT   attr = (DW_AT)  get_ULEB128( c_abbv );
+      DW_FORM form = (DW_FORM)get_ULEB128( c_abbv );
+      if (attr == 0 && form == 0) break;
+      VG_(printf)("     %18s: ", ML_(pp_DW_AT)(attr));
+      /* Get the form contents, so as to print them */
+      get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                         cc, c_die, True, form );
+      VG_(printf)("\t\n");
+   }
+   VG_(printf)("\n");
+   cc->barf("parse_type_DIE: confused by the above DIE");
+   /*NOTREACHED*/
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Resolution of references to type DIEs                ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+static Int cmp_D3TyAdmin_by_cuOff ( void* v1, void* v2 ) {
+   TyAdmin* a1 = *(TyAdmin**)v1;
+   TyAdmin* a2 = *(TyAdmin**)v2;
+   if (a1->cuOff < a2->cuOff) return -1;
+   if (a1->cuOff > a2->cuOff) return 1;
+   return 0;
+}
+
+/* Look up 'cuOff' in 'map', to find the associated D3TyAdmin*.  Check
+   that the found D3TyAdmin has tag 'adtag'.  Sets *payload to be the
+   resulting payload pointer and returns True on success.
+
+   Also, if 'allow_invalid' is True, then if cuOff is
+   D3_INVALID_CUOFF, return NULL in *payload.
+
+   Otherwise (conceptually fails) and returns False. */
+__attribute__((noinline))
+static Bool resolve_binding ( /*OUT*/void** payload,
+                              XArray* map, void* cuOff,
+                              TyAdminTag tag, 
+                              Bool allow_invalid ) {
+   Bool    found;
+   Word    ixLo, ixHi;
+   TyAdmin dummy, *dummyP, *admin;
+
+   if (cuOff == D3_INVALID_CUOFF && allow_invalid) {
+      *payload = NULL;
+      return True;
+   }
+
+   VG_(memset)(&dummy, 0, sizeof(dummy));
+   dummy.cuOff = (UWord)cuOff;
+   dummyP = &dummy;
+   found = VG_(lookupXA)( map, &dummyP, &ixLo, &ixHi );
+   if (!found)
+      return False;
+   /* If this doesn't hold, we must have seen more than one DIE with
+      the same cuOff(set).  Which isn't possible. */
+   vg_assert(ixLo == ixHi);
+   admin = *(TyAdmin**)VG_(indexXA)( map, ixLo );
+   /* All payload pointers should be non-NULL.  Ensured by assertion in
+      loop in resolve_type_entities that creates 'map'.  Hence it is
+      safe to return NULL to indicate 'not found'. */
+   vg_assert(admin->payload);
+   vg_assert(admin->cuOff == (UWord)cuOff); /* stay sane */
+
+   if (admin->tag != tag)
+      return False;
+
+   *payload = admin->payload;
+   return True;
+}
+
+__attribute__((noinline))
+static void resolve_type_entities ( /*MOD*/TyAdmin* admin,
+                                    /*MOD*/TempVar* vars )
+{
+   Bool     ok;
+   void*    payload;
+   TyAdmin* adp;
+   XArray* /* of D3TyAdmin* */ map;
+
+   map = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                     sizeof(TyAdmin*) );
+   for (adp = admin; adp; adp = adp->next) {
+      vg_assert(adp);
+      vg_assert(adp->payload != NULL);
+      if (adp->cuOff != (UWord)D3_INVALID_CUOFF) {
+         VG_(addToXA)( map, &adp );
+      }
+   }
+
+   VG_(setCmpFnXA)( map, cmp_D3TyAdmin_by_cuOff );
+   if (0) 
+      VG_(printf)("XXXXXX sorting map with %d entries\n",
+                  (Int)VG_(sizeXA)(map));
+   VG_(sortXA)( map );
+
+   for (adp = admin; adp; adp = adp->next) {
+      vg_assert(adp->payload);
+      switch (adp->tag) {
+      case TyA_Bounds: {
+         TyBounds* bounds = (TyBounds*)adp->payload;
+         if (bounds->knownL && bounds->knownU 
+             && bounds->knownL > bounds->knownU) goto baaad;
+         break;
+      }
+      case TyA_Atom: {
+         TyAtom* atom = (TyAtom*)adp->payload;
+         if (!atom->name) goto baaad;
+         break;
+      }
+      case TyA_Expr: {
+         D3Expr* expr = (D3Expr*)adp->payload;
+         if (!expr->bytes) goto baaad;
+         break;
+      }
+      case TyA_Field: {
+         TyField* field = (TyField*)adp->payload;
+         if (!field->name) goto baaad;
+         if ( (field->isStruct && (!field->loc)) 
+              || ((!field->isStruct) && field->loc))
+            goto baaad;
+         ok = resolve_binding( &payload, map, field->typeR,
+                               TyA_Type, False/*!allow_invalid*/ );
+         if (!ok) goto baaad;
+         field->typeR = payload;
+         break;
+      }
+      case TyA_Type: {
+         UChar   enc;
+         XArray* xa;
+         Type* ty = (Type*)adp->payload;
+         switch (ty->tag) {
+            case Ty_Base:
+               enc = ty->Ty.Base.enc;
+               if ((!ty->Ty.Base.name) 
+                   || ty->Ty.Base.szB < 1 || ty->Ty.Base.szB > 32
+                   || (enc != 'S' && enc != 'U' && enc != 'F' && enc != 'C'))
+                  goto baaad;
+               break;
+            case Ty_TyDef:
+               if (!ty->Ty.TyDef.name) goto baaad;
+               ok = resolve_binding( &payload, map,
+                                     ty->Ty.TyDef.typeR, 
+                                     TyA_Type,
+                                     True/*allow_invalid*/ );
+               if (!ok) goto baaad;
+               ty->Ty.TyDef.typeR = payload;
+               break;
+            case Ty_PorR:
+               if (ty->Ty.PorR.szB != sizeof(Word)) goto baaad;
+               ok = resolve_binding( &payload, map,
+                                     ty->Ty.PorR.typeR, 
+                                     TyA_Type,
+                                     False/*!allow_invalid*/ );
+               if (!ok) goto baaad;
+               ty->Ty.PorR.typeR = payload;
+               break;
+            case Ty_Array:
+               if (!ty->Ty.Array.bounds) goto baaad;
+               ok = resolve_binding( &payload, map,
+                                     ty->Ty.Array.typeR, 
+                                     TyA_Type,
+                                     False/*!allow_invalid*/ );
+               if (!ok) goto baaad;
+               ty->Ty.Array.typeR = payload;
+               break;
+            case Ty_Enum:
+               if ((!ty->Ty.Enum.atomRs)
+                   || ty->Ty.Enum.szB < 1 
+                   || ty->Ty.Enum.szB > 8) goto baaad;
+               xa = ty->Ty.Enum.atomRs;
+               break;
+            case Ty_StOrUn:
+               xa = ty->Ty.StOrUn.fields;
+               if (!xa) goto baaad;
+               break;
+            case Ty_Fn:
+               break;
+            case Ty_Qual:
+               if (ty->Ty.Qual.qual != 'C' 
+                   && ty->Ty.Qual.qual != 'V') goto baaad;
+               ok = resolve_binding( &payload, map,
+                                     ty->Ty.Qual.typeR, 
+                                     TyA_Type,
+                                     False/*!allow_invalid*/ );
+               if (!ok) goto baaad;
+               ty->Ty.Qual.typeR = payload;
+               break;
+            case Ty_Void:
+               if (ty->Ty.Void.isFake != False 
+                   && ty->Ty.Void.isFake != True) goto baaad;
+               break;
+            default:
+               goto baaad;
+         }
+         break;
+      }
+      baaad:
+      default:
+         VG_(printf)("valgrind: bad D3TyAdmin: ");
+         ML_(pp_TyAdmin)(adp);
+         VG_(printf)("\n");
+      }
+   }
+
+   /* Now resolve the variables list */
+   for (; vars; vars = vars->next) {
+      payload = NULL;
+      ok = resolve_binding( &payload, map, vars->typeR,
+                            TyA_Type, True/*allow_invalid*/ );
+
+      if (0 && !ok)
+         VG_(printf)("Can't resolve type reference 0x%lx\n",
+                     (UWord)vars->typeR);
+      //vg_assert(ok);
+      vars->typeR = payload;
+   }
+
+   VG_(deleteXA)( map );
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Parsing of Compilation Units                         ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+static Int cmp_TempVar_by_dioff ( void* v1, void* v2 ) {
+   TempVar* t1 = *(TempVar**)v1;
+   TempVar* t2 = *(TempVar**)v2;
+   if (t1->dioff < t2->dioff) return -1;
+   if (t1->dioff > t2->dioff) return 1;
+   return 0;
+}
+
+static void read_DIE ( /*OUT*/TyAdmin** admin,
+                       /*OUT*/TempVar** tempvars,
+                       /*OUT*/GExpr** gexprs,
+                       /*MOD*/D3TypeParser* typarser,
+                       /*MOD*/D3VarParser* varparser,
+                       Cursor* c, Bool td3, CUConst* cc, Int level )
+{
+   Cursor abbv;
+   ULong  atag, abbv_code;
+   UWord  posn;
+   UInt   has_children;
+   UWord  start_die_c_offset, start_abbv_c_offset;
+   UWord  after_die_c_offset, after_abbv_c_offset;
+
+   /* --- Deal with this DIE --- */
+   posn      = get_position_of_Cursor( c );
+   abbv_code = get_ULEB128( c );
+   set_abbv_Cursor( &abbv, td3, cc, abbv_code );
+   atag      = get_ULEB128( &abbv );
+   TRACE_D3("\n");
+   TRACE_D3(" <%d><%lx>: Abbrev Number: %llu (%s)\n",
+            level, posn, abbv_code, ML_(pp_DW_TAG)( atag ) );
+
+   if (atag == 0)
+      cc->barf("read_DIE: invalid zero tag on DIE");
+
+   has_children = get_UChar( &abbv );
+   if (has_children != DW_children_no && has_children != DW_children_yes)
+      cc->barf("read_DIE: invalid has_children value");
+
+   /* We're set up to look at the fields of this DIE.  Hand it off to
+      any parser(s) that want to see it.  Since they will in general
+      advance both the DIE and abbrev cursors, remember their current
+      settings so that we can then back up and do one final pass over
+      the DIE, to print out its contents. */
+
+   start_die_c_offset  = get_position_of_Cursor( c );
+   start_abbv_c_offset = get_position_of_Cursor( &abbv );
+
+   while (True) {
+      ULong cts;
+      Int   ctsSzB;
+      UWord ctsMemSzB;
+      ULong at_name = get_ULEB128( &abbv );
+      ULong at_form = get_ULEB128( &abbv );
+      if (at_name == 0 && at_form == 0) break;
+      TRACE_D3("     %18s: ", ML_(pp_DW_AT)(at_name));
+      /* Get the form contents, but ignore them; the only purpose is
+         to print them, if td3 is True */
+      get_Form_contents( &cts, &ctsSzB, &ctsMemSzB,
+                         cc, c, td3, (DW_FORM)at_form );
+      TRACE_D3("\t");
+      TRACE_D3("\n");
+   }
+
+   after_die_c_offset  = get_position_of_Cursor( c );
+   after_abbv_c_offset = get_position_of_Cursor( &abbv );
+
+   set_position_of_Cursor( c,     start_die_c_offset );
+   set_position_of_Cursor( &abbv, start_abbv_c_offset );
+
+   parse_type_DIE( admin,
+                   typarser,
+                   (DW_TAG)atag,
+                   posn,
+                   level,
+                   c,     /* DIE cursor */
+                   &abbv, /* abbrev cursor */
+                   cc,
+                   td3 );
+
+   set_position_of_Cursor( c,     start_die_c_offset );
+   set_position_of_Cursor( &abbv, start_abbv_c_offset );
+
+   parse_var_DIE( tempvars,
+                  gexprs,
+                  varparser,
+                  (DW_TAG)atag,
+                  posn,
+                  level,
+                  c,     /* DIE cursor */
+                  &abbv, /* abbrev cursor */
+                  cc,
+                  td3 );
+
+   set_position_of_Cursor( c,     after_die_c_offset );
+   set_position_of_Cursor( &abbv, after_abbv_c_offset );
+
+   /* --- Now recurse into its children, if any --- */
+   if (has_children == DW_children_yes) {
+      if (0) TRACE_D3("BEGIN children of level %d\n", level);
+      while (True) {
+         atag = peek_ULEB128( c );
+         if (atag == 0) break;
+         read_DIE( admin, tempvars, gexprs, typarser, varparser,
+                   c, td3, cc, level+1 );
+      }
+      /* Now we need to eat the terminating zero */
+      atag = get_ULEB128( c );
+      vg_assert(atag == 0);
+      if (0) TRACE_D3("END children of level %d\n", level);
+   }
+
+}
+
+
+static
+void new_dwarf3_reader_wrk ( 
+   struct _DebugInfo* di,
+   __attribute__((noreturn))
+   void (*barf)( HChar* ),
+   UChar* debug_info_img,   SizeT debug_info_sz,
+   UChar* debug_abbv_img,   SizeT debug_abbv_sz,
+   UChar* debug_line_img,   SizeT debug_line_sz,
+   UChar* debug_str_img,    SizeT debug_str_sz,
+   UChar* debug_ranges_img, SizeT debug_ranges_sz,
+   UChar* debug_loc_img,    SizeT debug_loc_sz
+)
+{
+   TyAdmin *admin, *adminp;
+   TempVar *tempvars, *varp, *varp2;
+   GExpr *gexprs, *gexpr;
+   Cursor abbv; /* for showing .debug_abbrev */
+   Cursor info; /* primary cursor for parsing .debug_info */
+   Cursor ranges; /* for showing .debug_ranges */
+   D3TypeParser typarser;
+   D3VarParser varparser;
+   Addr  dr_base;
+   UWord dr_offset;
+   Word  i;
+   Bool td3 = di->trace_symtab;
+   XArray* /* of TempVar* */ dioff_lookup_tab;
+
+#if 0
+   /* This doesn't work properly because it assumes all entries are
+      packed end to end, with no holes.  But that doesn't always
+      appear to be the case, so it loses sync.  And the D3 spec
+      doesn't appear to require a no-hole situation either. */
+   /* Display .debug_loc */
+   Addr  dl_base;
+   UWord dl_offset;
+   Cursor loc; /* for showing .debug_loc */
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("\n------ The contents of .debug_loc ------\n");
+   TRACE_SYMTAB("    Offset   Begin    End      Expression\n");
+   init_Cursor( &loc, debug_loc_img, 
+                debug_loc_sz, 0, barf, 
+                "Overrun whilst reading .debug_loc section(1)" );
+   dl_base = 0;
+   dl_offset = 0;
+   while (True) {
+      UWord  w1, w2;
+      UWord  len;
+      if (is_at_end_Cursor( &loc ))
+         break;
+
+      /* Read a (host-)word pair.  This is something of a hack since
+         the word size to read is really dictated by the ELF file;
+         however, we assume we're reading a file with the same
+         word-sizeness as the host.  Reasonably enough. */
+      w1 = get_UWord( &loc );
+      w2 = get_UWord( &loc );
+
+      if (w1 == 0 && w2 == 0) {
+         /* end of list.  reset 'base' */
+         TRACE_D3("    %08lx <End of list>\n", dl_offset);
+         dl_base = 0;
+         dl_offset = get_position_of_Cursor( &loc );
+         continue;
+      }
+
+      if (w1 == -1UL) {
+         /* new value for 'base' */
+         TRACE_D3("    %08lx %16lx %08lx (base address)\n",
+                  dl_offset, w1, w2);
+         dl_base = w2;
+         continue;
+      }
+
+      /* else a location expression follows */
+      TRACE_D3("    %08lx %08lx %08lx ",
+               dl_offset, w1 + dl_base, w2 + dl_base);
+      len = (UWord)get_UShort( &loc );
+      while (len > 0) {
+         UChar byte = get_UChar( &loc );
+         TRACE_D3("%02x", (UInt)byte);
+         len--;
+      }
+      TRACE_SYMTAB("\n");
+   }
+#endif
+
+   /* Display .debug_ranges */
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("\n------ The contents of .debug_ranges ------\n");
+   TRACE_SYMTAB("    Offset   Begin    End\n");
+   init_Cursor( &ranges, debug_ranges_img, 
+                debug_ranges_sz, 0, barf, 
+                "Overrun whilst reading .debug_ranges section(1)" );
+   dr_base = 0;
+   dr_offset = 0;
+   while (True) {
+      UWord  w1, w2;
+
+      if (is_at_end_Cursor( &ranges ))
+         break;
+
+      /* Read a (host-)word pair.  This is something of a hack since
+         the word size to read is really dictated by the ELF file;
+         however, we assume we're reading a file with the same
+         word-sizeness as the host.  Reasonably enough. */
+      w1 = get_UWord( &ranges );
+      w2 = get_UWord( &ranges );
+
+      if (w1 == 0 && w2 == 0) {
+         /* end of list.  reset 'base' */
+         TRACE_D3("    %08lx <End of list>\n", dr_offset);
+         dr_base = 0;
+         dr_offset = get_position_of_Cursor( &ranges );
+         continue;
+      }
+
+      if (w1 == -1UL) {
+         /* new value for 'base' */
+         TRACE_D3("    %08lx %16lx %08lx (base address)\n",
+                  dr_offset, w1, w2);
+         dr_base = w2;
+         continue;
+      }
+
+      /* else a range [w1+base, w2+base) is denoted */
+      TRACE_D3("    %08lx %08lx %08lx\n",
+               dr_offset, w1 + dr_base, w2 + dr_base);
+   }
+
+
+   /* Display .debug_abbrev */
+   init_Cursor( &abbv, debug_abbv_img, debug_abbv_sz, 0, barf, 
+                "Overrun whilst reading .debug_abbrev section" );
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("\n------ The contents of .debug_abbrev ------\n");
+   while (True) {
+      if (is_at_end_Cursor( &abbv ))
+         break;
+      /* Read one abbreviation table */
+      TRACE_D3("  Number TAG\n");
+      while (True) {
+         ULong atag;
+         UInt  has_children;
+         ULong acode = get_ULEB128( &abbv );
+         if (acode == 0) break; /* end of the table */
+         atag = get_ULEB128( &abbv );
+         has_children = get_UChar( &abbv );
+         TRACE_D3("   %llu      %s    [%s]\n", 
+                  acode, ML_(pp_DW_TAG)(atag),
+                         ML_(pp_DW_children)(has_children));
+         while (True) {
+            ULong at_name = get_ULEB128( &abbv );
+            ULong at_form = get_ULEB128( &abbv );
+            if (at_name == 0 && at_form == 0) break;
+            TRACE_D3("    %18s %s\n", 
+                     ML_(pp_DW_AT)(at_name), ML_(pp_DW_FORM)(at_form));
+         }
+      }
+   }
+   TRACE_SYMTAB("\n");
+
+   /* Now loop over the Compilation Units listed in the .debug_info
+      section (see D3SPEC sec 7.5) paras 1 and 2.  Each compilation
+      unit contains a Compilation Unit Header followed by precisely
+      one DW_TAG_compile_unit or DW_TAG_partial_unit DIE. */
+   init_Cursor( &info, debug_info_img, debug_info_sz, 0, barf,
+                "Overrun whilst reading .debug_info section" );
+
+   /* We'll park the harvested type information in here.  Also create
+      a fake "void" entry with offset D3_FAKEVOID_CUOFF, so we always
+      have at least one type entry to refer to.  D3_FAKEVOID_CUOFF is
+      huge and presumably will not occur in any valid DWARF3 file --
+      it would need to have a .debug_info section 4GB long for that to
+      happen.  These type entries end up in the DebugInfo. */
+   admin = NULL;
+   { Type* tVoid = ML_(new_Type)();
+     tVoid->tag = Ty_Void;
+     tVoid->Ty.Void.isFake = True;
+     admin = ML_(new_TyAdmin)( (UWord)D3_FAKEVOID_CUOFF, admin );
+     admin->payload = tVoid;
+     admin->tag     = TyA_Type;
+   }
+
+   /* List of variables we're accumulating.  These don't end up in the
+      DebugInfo; instead their contents are handed to ML_(addVar) and
+      the list elements are then deleted. */
+   tempvars = NULL;
+
+   /* List of GExprs we're accumulating.  These wind up in the
+      DebugInfo. */
+   gexprs = NULL;
+
+   /* We need a D3TypeParser to keep track of partially constructed
+      types.  It'll be discarded as soon as we've completed the CU,
+      since the resulting information is tipped in to 'admin' as it is
+      generated. */
+   VG_(memset)( &typarser, 0, sizeof(typarser) );
+   typarser.sp = -1;
+   typarser.language = '?';
+
+   VG_(memset)( &varparser, 0, sizeof(varparser) );
+   varparser.sp = -1;
+
+   TRACE_D3("\n------ Parsing .debug_info section ------\n");
+   while (True) {
+      UWord   cu_start_offset, cu_offset_now;
+      CUConst cc;
+
+      /* It seems icc9 finishes the DIE info before debug_info_sz
+         bytes have been used up.  So be flexible, and declare the
+         sequence complete if there is not enough remaining bytes to
+         hold even the smallest conceivable CU header.  (11 bytes I
+         reckon). */
+      Word avail = get_remaining_length_Cursor( &info );
+      if (avail < 11) {
+         if (avail > 0)
+            TRACE_D3("new_dwarf3_reader_wrk: warning: "
+                     "%ld unused bytes after end of DIEs\n", avail);
+         break;
+      }
+
+      /* Check the varparser's stack is in a sane state. */
+      vg_assert(varparser.sp == -1);
+      for (i = 0; i < N_D3_VAR_STACK; i++) {
+         vg_assert(varparser.ranges[i] == NULL);
+         vg_assert(varparser.level[i] == 0);
+      }
+      for (i = 0; i < N_D3_TYPE_STACK; i++) {
+         vg_assert(typarser.qparent[i] == NULL);
+         vg_assert(typarser.qlevel[i] == 0);
+      }
+
+      cu_start_offset = get_position_of_Cursor( &info );
+      TRACE_D3("\n");
+      TRACE_D3("  Compilation Unit @ offset 0x%lx:\n", cu_start_offset);
+      /* parse_CU_header initialises the CU's set_abbv_Cursor cache
+         (saC_cache) */
+      parse_CU_Header( &cc, td3, &info,
+                       (UChar*)debug_abbv_img, debug_abbv_sz );
+      cc.debug_str_img    = debug_str_img;
+      cc.debug_str_sz     = debug_str_sz;
+      cc.debug_ranges_img = debug_ranges_img;
+      cc.debug_ranges_sz  = debug_ranges_sz;
+      cc.debug_loc_img    = debug_loc_img;
+      cc.debug_loc_sz     = debug_loc_sz;
+      cc.debug_line_img   = debug_line_img;
+      cc.debug_line_sz    = debug_line_sz;
+      cc.cu_start_offset  = cu_start_offset;
+      cc.di = di;
+      /* The CU's svma can be deduced by looking at the AT_low_pc
+         value in the top level TAG_compile_unit, which is the topmost
+         DIE.  We'll leave it for the 'varparser' to acquire that info
+         and fill it in -- since it is the only party to want to know
+         it. */
+      cc.cu_svma_known = False;
+      cc.cu_svma       = 0;
+
+      /* Create a fake outermost-level range covering the entire
+         address range.  So we always have *something* to catch all
+         variable declarations. */
+      varstack_push( &cc, &varparser, td3, 
+                     unitary_range_list(0UL, ~0UL),
+                     -1, False/*isFunc*/, NULL/*fbGX*/ );
+
+      /* And set up the file name table.  When we come across the top
+         level DIE for this CU (which is what the next call to
+         read_DIE should process) we will copy all the file names out
+         of the .debug_line img area and use this table to look up the
+         copies when we later see filename numbers in DW_TAG_variables
+         etc. */
+      vg_assert(!varparser.filenameTable );
+      varparser.filenameTable 
+         = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                       sizeof(UChar*) );
+      vg_assert(varparser.filenameTable );
+
+      /* Now read the one-and-only top-level DIE for this CU. */
+      vg_assert(varparser.sp == 0);
+      read_DIE( &admin, &tempvars, &gexprs, &typarser, &varparser,
+                &info, td3, &cc, 0 );
+
+      cu_offset_now = get_position_of_Cursor( &info );
+      if (1) TRACE_D3("offset now %ld, d-i-size %ld\n",
+                      cu_offset_now, debug_info_sz);
+      if (cu_offset_now > debug_info_sz)
+         barf("toplevel DIEs beyond end of CU");
+      if (cu_offset_now == debug_info_sz)
+         break;
+
+      /* Preen to level -2.  DIEs have level >= 0 so -2 cannot occur
+         anywhere else at all.  Our fake the-entire-address-space
+         range is at level -1, so preening to -2 should completely
+         empty the stack out. */
+      TRACE_D3("\n");
+      varstack_preen( &varparser, td3, -2 );
+      /* Similarly, empty the type stack out. */
+      typestack_preen( &typarser, td3, -2 );
+      /* else keep going */
+
+      TRACE_D3("set_abbv_Cursor cache: %lu queries, %lu misses\n",
+               cc.saC_cache_queries, cc.saC_cache_misses);
+
+      vg_assert(varparser.filenameTable );
+      VG_(deleteXA)( varparser.filenameTable );
+      varparser.filenameTable = NULL;
+   }
+
+   /* Put the type entry list the right way round.  Not strictly
+      necessary, but makes it easier to read. */
+   vg_assert(admin);
+   if (admin) { 
+      TyAdmin *next, *prev = NULL;
+      for (adminp = admin; adminp; adminp = next) {
+         next = adminp->next;
+         adminp->next = prev;
+         prev = adminp;
+      }
+      admin = prev;
+   }
+
+   /* Put the variable list the right way round.  Not strictly
+      necessary, but makes it easier to read. */
+   if (tempvars) { 
+      TempVar *next, *prev = NULL;
+      for (varp = tempvars; varp; varp = next) {
+         next = varp->next;
+         varp->next = prev;
+         prev = varp;
+      }
+      tempvars = prev;
+   }
+
+   TRACE_D3("\n");
+   TRACE_D3("------ Acquired the following type entities: ------\n");
+   for (adminp = admin; adminp; adminp = adminp->next) {
+      TRACE_D3("   ");
+      if (td3) ML_(pp_TyAdmin)( adminp );
+      TRACE_D3("\n");
+   }
+   TRACE_D3("\n");
+   TRACE_D3("------ Resolving type entries ------\n");
+
+   resolve_type_entities( admin, tempvars );
+   for (gexpr = gexprs; gexpr; gexpr = gexpr->next) {
+      bias_GX( gexpr, di->text_bias );
+   }
+
+   TRACE_D3("\n");
+   TRACE_D3("------ Acquired the following variables: ------\n\n");
+
+   /* Park (pointers to) all the vars in an XArray, so we can look up
+      abstract origins quickly.  The array is sorted (hence, looked-up
+      by) the .dioff fields.  Since the .dioffs should be instrictly
+      ascending order, there is no need to sort the array after
+      construction.  The ascendingness is however asserted for. */
+   dioff_lookup_tab
+      = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free), 
+                    sizeof(TempVar*) );
+   vg_assert(dioff_lookup_tab);
+   varp2 = NULL;
+   for (varp = tempvars; varp; varp = varp->next) {
+      if (varp2)
+         vg_assert(varp2->dioff < varp->dioff);
+      VG_(addToXA)( dioff_lookup_tab, &varp );
+      varp2 = varp;
+   }
+   VG_(setCmpFnXA)( dioff_lookup_tab, cmp_TempVar_by_dioff );
+   VG_(sortXA)( dioff_lookup_tab ); /* POINTLESS; FIXME: rm */
+
+   /* Now visit each var.  Collect up as much info as possible for
+      each var and hand it to ML_(addVar). */
+   for (varp = tempvars; varp; varp = varp->next) {
+
+      /* Possibly show .. */
+      if (td3) {
+         VG_(printf)("<%lx> addVar: level %d: %s :: ",
+                     varp->dioff,
+                     varp->level,
+                     varp->name ? varp->name : (UChar*)"<anon_var>" );
+         if (varp->typeR) {
+            ML_(pp_Type_C_ishly)( varp->typeR );
+         } else {
+            VG_(printf)("NULL");
+         }
+         VG_(printf)("\n  Loc=");
+         if (varp->gexpr) {
+            ML_(pp_GX)(varp->gexpr);
+         } else {
+            VG_(printf)("NULL");
+         }
+         VG_(printf)("\n");
+         if (varp->fbGX) {
+            VG_(printf)("  FrB=");
+            ML_(pp_GX)( varp->fbGX );
+            VG_(printf)("\n");
+         } else {
+            VG_(printf)("  FrB=none\n");
+         }
+         VG_(printf)("  declared at: %s:%d\n",
+                     varp->fName ? varp->fName : (UChar*)"NULL",
+                     varp->fLine );
+         if (varp->absOri != (UWord)D3_INVALID_CUOFF)
+            VG_(printf)("  abstract origin: <%lx>\n", varp->absOri);
+      }
+
+      /* Skip variables which have no location.  These must be
+         abstract instances; they are useless as-is since with no
+         location they have no specified memory location.  They will
+         presumably be referred to via the absOri fields of other
+         variables. */
+      if (!varp->gexpr) {
+         TRACE_D3("  SKIP (no location)\n\n");
+         continue;
+      }
+
+      /* So it has a location, at least.  If it refers to some other
+         entry through its absOri field, pull in further info through
+         that. */
+      if (varp->absOri != (UWord)D3_INVALID_CUOFF) {
+         Bool found;
+         Word ixFirst, ixLast;
+         TempVar key;
+         TempVar* keyp = &key;
+         TempVar *varAI;
+         VG_(memset)(&key, 0, sizeof(key)); /* not necessary */
+         key.dioff = varp->absOri; /* this is what we want to find */
+         found = VG_(lookupXA)( dioff_lookup_tab, &keyp,
+                                &ixFirst, &ixLast );
+         if (!found)
+            barf("DW_AT_abstract_origin can't be resolved");
+         /* If the following fails, there is more than one entry with
+            the same dioff.  Which can't happen. */
+         vg_assert(ixFirst == ixLast);
+         varAI = *(TempVar**)VG_(indexXA)( dioff_lookup_tab, ixFirst );
+         /* stay sane */
+         vg_assert(varAI);
+         vg_assert(varAI->dioff == varp->absOri);
+
+         /* Copy what useful info we can. */
+         if (varAI->typeR && !varp->typeR)
+            varp->typeR = varAI->typeR;
+         if (varAI->name && !varp->name)
+            varp->name = varAI->name;
+         if (varAI->fName && !varp->fName)
+            varp->fName = varAI->fName;
+         if (varAI->fLine > 0 && varp->fLine == 0)
+            varp->fLine = varAI->fLine;
+      }
+
+      /* Give it a name if it doesn't have one. */
+      if (!varp->name)
+         varp->name = ML_(addStr)( di, "<anon_var>", -1 );
+
+      /* So now does it have enough info to be useful? */
+      /* NOTE: re typeR: this is a hack.  If typeR is NULL then the
+         type didn't get resolved.  Really, in that case something's
+         broken earlier on, and should be fixed, rather than just
+         skipping the variable. */
+      if (!varp->typeR) continue;
+      vg_assert(varp->gexpr);
+      vg_assert(varp->name);
+      vg_assert(varp->typeR);
+      vg_assert(varp->level >= 0);
+
+      /* Ok.  So we're going to keep it.  Call ML_(addVar) once for
+         each address range in which the variable exists. */
+      TRACE_D3("  ACQUIRE for range(s) ");
+      { AddrRange  oneRange;
+        AddrRange* varPcRanges;
+        Word       nVarPcRanges;
+        /* Set up to iterate over address ranges, however
+           represented. */
+        if (varp->nRanges == 0 || varp->nRanges == 1) {
+           vg_assert(!varp->rngMany);
+           if (varp->nRanges == 0) {
+              vg_assert(varp->rngOneMin == 0);
+              vg_assert(varp->rngOneMax == 0);
+           }
+           nVarPcRanges = varp->nRanges;
+           oneRange.aMin = varp->rngOneMin;
+           oneRange.aMax = varp->rngOneMax;
+           varPcRanges = &oneRange;
+        } else {
+           vg_assert(varp->rngMany);
+           vg_assert(varp->rngOneMin == 0);
+           vg_assert(varp->rngOneMax == 0);
+           nVarPcRanges = VG_(sizeXA)(varp->rngMany);
+           vg_assert(nVarPcRanges >= 2);
+           vg_assert(nVarPcRanges == (Word)varp->nRanges);
+           varPcRanges = VG_(indexXA)(varp->rngMany, 0);
+        }
+        if (varp->level == 0)
+           vg_assert( nVarPcRanges == 1 );
+        /* and iterate */
+        for (i = 0; i < nVarPcRanges; i++) {
+           Addr pcMin = varPcRanges[i].aMin;
+           Addr pcMax = varPcRanges[i].aMax;
+           vg_assert(pcMin <= pcMax);
+           /* Level 0 is the global address range.  So at level 0 we
+              don't want to bias pcMin/pcMax; but at all other levels
+              we do since those are derived from svmas in the Dwarf
+              we're reading.  Be paranoid ... */
+           if (varp->level == 0) {
+              vg_assert(pcMin == (Addr)0);
+              vg_assert(pcMax == ~(Addr)0);
+           } else {
+              /* vg_assert(pcMin > (Addr)0);
+                 No .. we can legitimately expect to see ranges like 
+                 0x0-0x11D (pre-biasing, of course). */
+              vg_assert(pcMax < ~(Addr)0);
+           }
+
+           if (i > 0 && (i%2) == 0) TRACE_D3("\n                       ");
+           TRACE_D3("[%p,%p] ", pcMin, pcMax );
+
+           ML_(addVar)(
+              di, varp->level, 
+                  pcMin + (varp->level==0 ? 0 : di->text_bias),
+                  pcMax + (varp->level==0 ? 0 : di->text_bias), 
+                  varp->name, (void*)varp->typeR,
+                  varp->gexpr, varp->fbGX,
+                  varp->fName, varp->fLine, td3 
+           );
+        }
+      }
+
+      TRACE_D3("\n\n");
+      /* and move on to the next var */
+   }
+
+   /* Now free all the TempVars */
+   for (varp = tempvars; varp; varp = varp2) {
+      varp2 = varp->next;
+      if (varp->rngMany)
+         VG_(deleteXA)(varp->rngMany);
+      ML_(dinfo_free)(varp);
+   }
+   tempvars = NULL;
+
+   /* And get rid of the temporary mapping table. */
+   VG_(deleteXA)( dioff_lookup_tab );
+
+   /* record the TyAdmins and the GExprs in di so they can be freed
+      later */
+   vg_assert(!di->admin_tyadmins);
+   di->admin_tyadmins = admin;
+   vg_assert(!di->admin_gexprs);
+   di->admin_gexprs = gexprs;
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- The "new" DWARF3 reader -- top level control logic   ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
+#include <setjmp.h>   /* For jmp_buf */
+/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
+
+static Bool    d3rd_jmpbuf_valid  = False;
+static HChar*  d3rd_jmpbuf_reason = NULL;
+static jmp_buf d3rd_jmpbuf;
+
+static __attribute__((noreturn)) void barf ( HChar* reason ) {
+   vg_assert(d3rd_jmpbuf_valid);
+   d3rd_jmpbuf_reason = reason;
+   __builtin_longjmp(&d3rd_jmpbuf, 1);
+   /*NOTREACHED*/
+   vg_assert(0);
+}
+
+
+void 
+ML_(new_dwarf3_reader) (
+   struct _DebugInfo* di,
+   UChar* debug_info_img,   SizeT debug_info_sz,
+   UChar* debug_abbv_img,   SizeT debug_abbv_sz,
+   UChar* debug_line_img,   SizeT debug_line_sz,
+   UChar* debug_str_img,    SizeT debug_str_sz,
+   UChar* debug_ranges_img, SizeT debug_ranges_sz,
+   UChar* debug_loc_img,    SizeT debug_loc_sz
+)
+{
+   volatile Int  jumped;
+   volatile Bool td3 = di->trace_symtab;
+
+   /* Run the _wrk function to read the dwarf3.  If it succeeds, it
+      just returns normally.  If there is any failure, it longjmp's
+      back here, having first set d3rd_jmpbuf_reason to something
+      useful. */
+   vg_assert(d3rd_jmpbuf_valid  == False);
+   vg_assert(d3rd_jmpbuf_reason == NULL);
+
+   d3rd_jmpbuf_valid = True;
+   jumped = __builtin_setjmp(&d3rd_jmpbuf);
+   if (jumped == 0) {
+      /* try this ... */
+      new_dwarf3_reader_wrk( di, barf,
+                             debug_info_img,   debug_info_sz,
+                             debug_abbv_img,   debug_abbv_sz,
+                             debug_line_img,   debug_line_sz,
+                             debug_str_img,    debug_str_sz,
+                             debug_ranges_img, debug_ranges_sz,
+                             debug_loc_img,    debug_loc_sz );
+      d3rd_jmpbuf_valid = False;
+      TRACE_D3("\n------ .debug_info reading was successful ------\n");
+   } else {
+      /* It longjmp'd. */
+      d3rd_jmpbuf_valid = False;
+      /* Can't longjump without giving some sort of reason. */
+      vg_assert(d3rd_jmpbuf_reason != NULL);
+
+      TRACE_D3("\n------ .debug_info reading failed ------\n");
+
+      ML_(symerr)(di, True, d3rd_jmpbuf_reason);
+   }
+
+   d3rd_jmpbuf_valid  = False;
+   d3rd_jmpbuf_reason = NULL;
+}
+
+
+
+/* --- Unused code fragments which might be useful one day. --- */
+
+#if 0
+   /* Read the arange tables */
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("\n------ The contents of .debug_arange ------\n");
+   init_Cursor( &aranges, debug_aranges_img, 
+                debug_aranges_sz, 0, barf, 
+                "Overrun whilst reading .debug_aranges section" );
+   while (True) {
+      ULong  len, d_i_offset;
+      Bool   is64;
+      UShort version;
+      UChar  asize, segsize;
+
+      if (is_at_end_Cursor( &aranges ))
+         break;
+      /* Read one arange thingy */
+      /* initial_length field */
+      len = get_Initial_Length( &is64, &aranges, 
+               "in .debug_aranges: invalid initial-length field" );
+      version    = get_UShort( &aranges );
+      d_i_offset = get_Dwarfish_UWord( &aranges, is64 );
+      asize      = get_UChar( &aranges );
+      segsize    = get_UChar( &aranges );
+      TRACE_D3("  Length:                   %llu\n", len);
+      TRACE_D3("  Version:                  %d\n", (Int)version);
+      TRACE_D3("  Offset into .debug_info:  %llx\n", d_i_offset);
+      TRACE_D3("  Pointer Size:             %d\n", (Int)asize);
+      TRACE_D3("  Segment Size:             %d\n", (Int)segsize);
+      TRACE_D3("\n");
+      TRACE_D3("    Address            Length\n");
+
+      while ((get_position_of_Cursor( &aranges ) % (2 * asize)) > 0) {
+         (void)get_UChar( & aranges );
+      }
+      while (True) {
+         ULong address = get_Dwarfish_UWord( &aranges, asize==8 );
+         ULong length = get_Dwarfish_UWord( &aranges, asize==8 );
+         TRACE_D3("    0x%016llx 0x%llx\n", address, length);
+         if (address == 0 && length == 0) break;
+      }
+   }
+   TRACE_SYMTAB("\n");
+#endif
+
+/*--------------------------------------------------------------------*/
+/*--- end                                             readdwarf3.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/readelf.c b/coregrind/m_debuginfo/readelf.c
index 5a5272f..ca9ef0c 100644
--- a/coregrind/m_debuginfo/readelf.c
+++ b/coregrind/m_debuginfo/readelf.c
@@ -42,14 +42,17 @@
 #include "pub_core_libcfile.h"
 #include "pub_core_aspacemgr.h"    /* for mmaping debuginfo files */
 #include "pub_core_machine.h"      /* VG_ELF_CLASS */
-#include "pub_core_mallocfree.h"
 #include "pub_core_options.h"
 #include "pub_core_oset.h"
 #include "pub_core_tooliface.h"    /* VG_(needs) */
 #include "pub_core_xarray.h"
+#include "priv_misc.h"             /* dinfo_zalloc/free/strdup */
+#include "priv_d3basics.h"
+#include "priv_tytypes.h"
 #include "priv_storage.h"
 #include "priv_readelf.h"          /* self */
 #include "priv_readdwarf.h"        /* 'cos ELF contains DWARF */
+#include "priv_readdwarf3.h"
 #include "priv_readstabs.h"        /* and stabs, if we're unlucky */
 
 /* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
@@ -106,11 +109,14 @@
 /* Identify an ELF object file by peering at the first few bytes of
    it. */
 
-Bool ML_(is_elf_object_file)( const void* buf )
+Bool ML_(is_elf_object_file)( void* image, SizeT n_image )
 {
-   ElfXX_Ehdr* ehdr = (ElfXX_Ehdr*)buf;
+   ElfXX_Ehdr* ehdr = (ElfXX_Ehdr*)image;
    Int ok = 1;
 
+   if (n_image < sizeof(ElfXX_Ehdr))
+      return False;
+
    ok &= (ehdr->e_ident[EI_MAG0] == 0x7F
           && ehdr->e_ident[EI_MAG1] == 'E'
           && ehdr->e_ident[EI_MAG2] == 'L'
@@ -136,7 +142,7 @@
 
 static
 void show_raw_elf_symbol ( Int i, 
-                           ElfXX_Sym* sym, Char* sym_name, Addr sym_addr,
+                           ElfXX_Sym* sym, Char* sym_name, Addr sym_svma,
                            Bool ppc64_linux_format )
 {
    HChar* space = ppc64_linux_format ? "                  " : "";
@@ -159,8 +165,8 @@
       case STT_HIPROC:  VG_(printf)("hip "); break;
       default:          VG_(printf)("??? "); break;
    }
-   VG_(printf)(": val %010p, %ssz %4d  %s\n",
-               sym_addr, space, sym->st_size,
+   VG_(printf)(": svma %010p, %ssz %4d  %s\n",
+               sym_svma, space, sym->st_size,
                ( sym->st_name ? sym_name : (Char*)"NONAME" ) ); 
 }               
 
@@ -170,6 +176,12 @@
    this is straightforward - the name, address, size are copied out
    unchanged.
 
+   There is a bit of a kludge re data symbols (see KLUDGED BSS CHECK
+   below): we assume that the .bss is mapped immediately after .data,
+   and so accept any data symbol which exists in the range [start of
+   .data, size of .data + size of .bss).  I don't know if this is
+   really correct/justifiable, or not.
+
    For ppc64-linux it's more complex.  If the symbol is seen to be in
    the .opd section, it is taken to be a function descriptor, and so
    a dereference is attempted, in order to get hold of the real entry
@@ -189,27 +201,30 @@
 static 
 Bool get_elf_symbol_info ( 
         /* INPUTS */
-        struct _SegInfo* si,  /* containing SegInfo */
-        ElfXX_Sym* sym,       /* ELF symbol */
-        Char*      sym_name,  /* name */
-        Addr       sym_addr,  /* declared address */
-        UChar*     opd_filea, /* oimage of .opd sec (ppc64-linux only) */
-        OffT       opd_offset, /* base address assumed in oimage */
+        struct _DebugInfo* di, /* containing DebugInfo */
+        ElfXX_Sym* sym,        /* ELF symbol */
+        Char*      sym_name,   /* name */
+        Addr       sym_svma,   /* address as stated in the object file */
+        UChar*     opd_img,    /* oimage of .opd sec (ppc64-linux only) */
+        OffT       opd_bias,   /* for biasing AVMAs found in .opd */
         /* OUTPUTS */
         Char** sym_name_out,   /* name we should record */
-        Addr*  sym_addr_out,   /* addr we should record */
+        Addr*  sym_avma_out,   /* addr we should record */
         Int*   sym_size_out,   /* symbol size */
         Addr*  sym_tocptr_out, /* ppc64-linux only: R2 value to be
                                   used on entry */
-        Bool*  from_opd_out    /* ppc64-linux only: did we deref an
-                                  .opd entry? */ 
+        Bool*  from_opd_out,   /* ppc64-linux only: did we deref an
+                                  .opd entry? */
+        Bool*  is_text_out     /* is this a text symbol? */
      )
 {
    Bool plausible, is_in_opd;
+   Bool in_text, in_data, in_sdata, in_bss;
 
    /* Set defaults */
    *sym_name_out   = sym_name;
-   *sym_addr_out   = sym_addr;
+   *sym_avma_out   = sym_svma; /* we will bias this shortly */
+   *is_text_out    = True;
    *sym_size_out   = (Int)sym->st_size;
    *sym_tocptr_out = 0; /* unknown/inapplicable */
    *from_opd_out   = False;
@@ -223,20 +238,69 @@
         )
         &&
         (ELFXX_ST_TYPE(sym->st_info) == STT_FUNC 
-         || (VG_(needs).data_syms 
-             && ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT)
+         || ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT
         );
 
+   /* Now bias sym_avma_out accordingly */
+#if 0
+   /* This works, but seems a bit crude */
+   if (ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT) {
+      *is_text_out = False;
+      *sym_avma_out += di->data_bias;
+   } else {
+      *is_text_out = True;
+      *sym_avma_out += di->text_bias;
+   }
+#else
+   /* Try to figure out exactly which section the symbol is from and
+      bias accordingly.  Screws up if the previously deduced section
+      svma address ranges are wrong. */
+   if (di->text_present
+       && di->text_size > 0
+       && sym_svma >= di->text_svma 
+       && sym_svma < di->text_svma + di->text_size) {
+      *is_text_out = True;
+      *sym_avma_out += di->text_bias;
+   } else
+   if (di->data_present
+       && di->data_size > 0
+       && sym_svma >= di->data_svma 
+       && sym_svma < di->data_svma + di->data_size) {
+      *is_text_out = False;
+      *sym_avma_out += di->data_bias;
+   } else
+   if (di->sdata_present
+       && di->sdata_size > 0
+       && sym_svma >= di->sdata_svma 
+       && sym_svma < di->sdata_svma + di->sdata_size) {
+      *is_text_out = False;
+      *sym_avma_out += di->sdata_bias;
+   } else
+   if (di->bss_present
+       && di->bss_size > 0
+       && sym_svma >= di->bss_svma 
+       && sym_svma < di->bss_svma + di->bss_size) {
+      *is_text_out = False;
+      *sym_avma_out += di->bss_bias;
+   } else {
+      /* Assume it's in .text.  Is this a good idea? */
+      *is_text_out = True;
+      *sym_avma_out += di->text_bias;
+   }
+#endif
+
 #  if defined(VGP_ppc64_linux)
    /* Allow STT_NOTYPE in the very special case where we're running on
       ppc64-linux and the symbol is one which the .opd-chasing hack
       below will chase. */
    if (!plausible
+       && *is_text_out
        && ELFXX_ST_TYPE(sym->st_info) == STT_NOTYPE
        && sym->st_size > 0
-       && si->opd_start_avma != 0
-       && sym_addr >= si->opd_start_avma
-       && sym_addr <  si->opd_start_avma + si->opd_size)
+       && di->opd_present
+       && di->opd_size > 0
+       && *sym_avma_out >= di->opd_avma
+       && *sym_avma_out <  di->opd_avma + di->opd_size)
       plausible = True;
 #  endif
 
@@ -263,15 +327,17 @@
 
    /* If it's apparently in a GOT or PLT, it's really a reference to a
       symbol defined elsewhere, so ignore it. */
-   if (si->got_start_avma != 0
-       && sym_addr >= si->got_start_avma 
-       && sym_addr <  si->got_start_avma + si->got_size) {
+   if (di->got_present
+       && di->got_size > 0
+       && *sym_avma_out >= di->got_avma 
+       && *sym_avma_out <  di->got_avma + di->got_size) {
       TRACE_SYMTAB("    ignore -- in GOT: %s\n", sym_name);
       return False;
    }
-   if (si->plt_start_avma != 0
-       && sym_addr >= si->plt_start_avma
-       && sym_addr <  si->plt_start_avma + si->plt_size) {
+   if (di->plt_present
+       && di->plt_size > 0
+       && *sym_avma_out >= di->plt_avma
+       && *sym_avma_out <  di->plt_avma + di->plt_size) {
       TRACE_SYMTAB("    ignore -- in PLT: %s\n", sym_name);
       return False;
    }
@@ -286,30 +352,33 @@
    */
    is_in_opd = False;
 
-   if (si->opd_start_avma != 0
-       && sym_addr >= si->opd_start_avma
-       && sym_addr <  si->opd_start_avma + si->opd_size) {
+   if (di->opd_present
+       && di->opd_size > 0
+       && *sym_avma_out >= di->opd_avma
+       && *sym_avma_out <  di->opd_avma + di->opd_size) {
 #     if !defined(VGP_ppc64_linux)
       TRACE_SYMTAB("    ignore -- in OPD: %s\n", sym_name);
       return False;
 #     else
       Int    offset_in_opd;
       ULong* fn_descr;
+      Bool   details = 1||False;
 
-      if (0) VG_(printf)("opdXXX: opd_offset %p, sym_addr %p\n", 
-                         (void*)(opd_offset), (void*)sym_addr);
+      if (details)
+         TRACE_SYMTAB("opdXXX: opd_bias %p, sym_svma_out %p\n", 
+                      (void*)(opd_bias), (void*)*sym_avma_out);
 
-      if (!VG_IS_8_ALIGNED(sym_addr)) {
+      if (!VG_IS_8_ALIGNED(*sym_avma_out)) {
          TRACE_SYMTAB("    ignore -- not 8-aligned: %s\n", sym_name);
          return False;
       }
 
-      /* sym_addr is a vma pointing into the .opd section.  We know
-         the vma of the opd section start, so we can figure out how
-         far into the opd section this is. */
+      /* *sym_avma_out is a vma pointing into the .opd section.  We
+         know the vma of the opd section start, so we can figure out
+         how far into the opd section this is. */
 
-      offset_in_opd = (Addr)sym_addr - (Addr)(si->opd_start_avma);
-      if (offset_in_opd < 0 || offset_in_opd >= si->opd_size) {
+      offset_in_opd = (Addr)(*sym_avma_out) - (Addr)(di->opd_avma);
+      if (offset_in_opd < 0 || offset_in_opd >= di->opd_size) {
          TRACE_SYMTAB("    ignore -- invalid OPD offset: %s\n", sym_name);
          return False;
       }
@@ -317,29 +386,34 @@
       /* Now we want to know what's at that offset in the .opd
          section.  We can't look in the running image since it won't
          necessarily have been mapped.  But we can consult the oimage.
-         opd_filea is the start address of the .opd in the oimage.
+         opd_img is the start address of the .opd in the oimage.
          Hence: */
 
-      fn_descr = (ULong*)(opd_filea + offset_in_opd);
+      fn_descr = (ULong*)(opd_img + offset_in_opd);
 
-      if (0) VG_(printf)("opdXXY: offset %d,  fn_descr %p\n", 
-                         offset_in_opd, fn_descr);
-      if (0) VG_(printf)("opdXXZ: *fn_descr %p\n", (void*)(fn_descr[0]));
+      if (details) 
+         TRACE_SYMTAB("opdXXY: offset %d,  fn_descr %p\n", 
+                      offset_in_opd, fn_descr);
+      if (details) 
+         TRACE_SYMTAB("opdXXZ: *fn_descr %p\n", (void*)(fn_descr[0]));
 
-      /* opd_offset is the difference between si->start (where the
-         library got mapped) and the address space used for addresses
-         within the library file. */
-
-      sym_addr        = fn_descr[0] + opd_offset;
-      *sym_addr_out   = sym_addr;
-      *sym_tocptr_out = fn_descr[1] + opd_offset;
+      /* opd_bias is the what we have to add to SVMAs found in .opd to
+         get plausible .text AVMAs for the entry point, and .data
+         AVMAs (presumably) for the TOC locations.  We use the caller
+         supplied value (which is di->text_bias) for both of these.
+         Not sure why that is correct - it seems to work, and sounds
+         OK for fn_descr[0], but surely we need to use the data bias
+         and not the text bias for fn_descr[1] ?  Oh Well.
+      */
+      *sym_avma_out   = fn_descr[0] + opd_bias;
+      *sym_tocptr_out = fn_descr[1] + opd_bias;
       *from_opd_out   = True;
       is_in_opd = True;
 
       /* Do a final sanity check: if the symbol falls outside the
-         SegInfo's mapped range, ignore it.  Since sym_addr has been
-         updated, that can be achieved simply by falling through to
-         the test below. */
+         DebugInfo's mapped range, ignore it.  Since *sym_avma_out has
+         been updated, that can be achieved simply by falling through
+         to the test below. */
 
 #     endif /* ppc64-linux nasty hack */
    }
@@ -347,7 +421,7 @@
    /* Here's yet another ppc64-linux hack.  Get rid of leading dot if
       the symbol is outside .opd. */
 #  if defined(VGP_ppc64_linux)
-   if (si->opd_start_avma != 0
+   if (di->opd_size > 0
        && !is_in_opd
        && sym_name[0] == '.') {
       vg_assert(!(*from_opd_out));
@@ -357,13 +431,48 @@
 
    /* If no part of the symbol falls within the mapped range,
       ignore it. */
-   if (*sym_addr_out + *sym_size_out <= si->text_start_avma
-       || *sym_addr_out >= si->text_start_avma + si->text_size) {
-      TRACE_SYMTAB( "ignore -- %p .. %p outside mapped range %p .. %p\n",
-                    *sym_addr_out, *sym_addr_out + *sym_size_out,
-                    si->text_start_avma,
-                    si->text_start_avma + si->text_size);
-      return False;
+   
+   in_text 
+      = di->text_present
+        && di->text_size > 0
+        && !(*sym_avma_out + *sym_size_out <= di->text_avma
+             || *sym_avma_out >= di->text_avma + di->text_size);
+
+   in_data 
+      = di->data_present
+        && di->data_size > 0
+        && !(*sym_avma_out + *sym_size_out <= di->data_avma
+             || *sym_avma_out >= di->data_avma + di->data_size);
+
+   in_sdata 
+      = di->sdata_present
+        && di->sdata_size > 0
+        && !(*sym_avma_out + *sym_size_out <= di->sdata_avma
+             || *sym_avma_out >= di->sdata_avma + di->sdata_size);
+
+   in_bss 
+      = di->bss_present
+        && di->bss_size > 0
+        && !(*sym_avma_out + *sym_size_out <= di->bss_avma
+             || *sym_avma_out >= di->bss_avma + di->bss_size);
+
+
+   if (*is_text_out) {
+      if (!in_text) {
+         TRACE_SYMTAB(
+            "ignore -- %p .. %p outside .text svma range %p .. %p\n",
+            *sym_avma_out, *sym_avma_out + *sym_size_out,
+            di->text_avma,
+            di->text_avma + di->text_size);
+         return False;
+      }
+   } else {
+     if (!(in_data || in_sdata || in_bss)) {
+         TRACE_SYMTAB(
+            "ignore -- %p .. %p outside .data / .sdata / .bss svma ranges\n",
+            *sym_avma_out, *sym_avma_out + *sym_size_out);
+         return False;
+      }
    }
 
 #  if defined(VGP_ppc64_linux)
@@ -371,9 +480,9 @@
       section.  This would completely mess up function redirection and
       intercepting.  This assert ensures that any symbols that make it
       into the symbol table on ppc64-linux don't point into .opd. */
-   if (si->opd_start_avma != 0) {
-      vg_assert(*sym_addr_out + *sym_size_out <= si->opd_start_avma
-                || *sym_addr_out >= si->opd_start_avma + si->opd_size);
+   if (di->opd_present && di->opd_size > 0) {
+      vg_assert(*sym_avma_out + *sym_size_out <= di->opd_avma
+                || *sym_avma_out >= di->opd_avma + di->opd_size);
    }
 #  endif
 
@@ -387,61 +496,63 @@
 static
 __attribute__((unused)) /* not referred to on all targets */
 void read_elf_symtab__normal( 
-        struct _SegInfo* si, UChar* tab_name,
-        ElfXX_Sym* o_symtab, UInt o_symtab_sz, OffT o_symtab_offset,
-        UChar*     o_strtab, UInt o_strtab_sz,
-        UChar*     opd_filea, OffT opd_offset /* ppc64-linux only */ 
+        struct _DebugInfo* di, UChar* tab_name,
+        ElfXX_Sym* symtab_img, SizeT symtab_szB,
+        UChar*     strtab_img, SizeT strtab_szB,
+        UChar*     opd_img /* ppc64-linux only */ 
      )
 {
-   Int        i;
-   Addr       sym_addr, sym_addr_really;
+   Word       i;
+   Addr       sym_svma, sym_avma_really;
    Char      *sym_name, *sym_name_really;
    Int        sym_size;
    Addr       sym_tocptr;
-   Bool       from_opd;
+   Bool       from_opd, is_text;
    DiSym      risym;
    ElfXX_Sym *sym;
 
-   if (o_strtab == NULL || o_symtab == NULL) {
+   if (strtab_img == NULL || symtab_img == NULL) {
       Char buf[80];
       vg_assert(VG_(strlen)(tab_name) < 40);
       VG_(sprintf)(buf, "   object doesn't have a %s", tab_name);
-      ML_(symerr)(buf);
+      ML_(symerr)(di, False, buf);
       return;
    }
 
-   TRACE_SYMTAB("\nReading (ELF, standard) %s (%d entries)\n", tab_name, 
-                o_symtab_sz/sizeof(ElfXX_Sym) );
+   TRACE_SYMTAB("\n--- Reading (ELF, standard) %s (%d entries) ---\n",
+                tab_name, symtab_szB/sizeof(ElfXX_Sym) );
 
    /* Perhaps should start at i = 1; ELF docs suggest that entry
       0 always denotes 'unknown symbol'. */
-   for (i = 1; i < (Int)(o_symtab_sz/sizeof(ElfXX_Sym)); i++) {
-      sym      = & o_symtab[i];
-      sym_name = (Char*)(o_strtab + sym->st_name);
-      sym_addr = o_symtab_offset + sym->st_value;
+   for (i = 1; i < (Word)(symtab_szB/sizeof(ElfXX_Sym)); i++) {
+      sym      = & symtab_img[i];
+      sym_name = (UChar*)(strtab_img + sym->st_name);
+      sym_svma = sym->st_value;
 
-      if (si->trace_symtab)
-         show_raw_elf_symbol(i, sym, sym_name, sym_addr, False);
+      if (di->trace_symtab)
+         show_raw_elf_symbol(i, sym, sym_name, sym_svma, False);
 
-      if (get_elf_symbol_info(si, sym, sym_name, sym_addr,
-                              opd_filea, opd_offset,
+      if (get_elf_symbol_info(di, sym, sym_name, sym_svma,
+                              opd_img, di->text_bias,
                               &sym_name_really, 
-                              &sym_addr_really,
+                              &sym_avma_really,
                               &sym_size,
                               &sym_tocptr,
-                              &from_opd)) {
+                              &from_opd, &is_text)) {
 
-         risym.addr   = sym_addr_really;
+         risym.addr   = sym_avma_really;
          risym.size   = sym_size;
-         risym.name   = ML_(addStr) ( si, sym_name_really, -1 );
+         risym.name   = ML_(addStr) ( di, sym_name_really, -1 );
          risym.tocptr = sym_tocptr;
+         risym.isText = is_text;
          vg_assert(risym.name != NULL);
          vg_assert(risym.tocptr == 0); /* has no role except on ppc64-linux */
-         ML_(addSym) ( si, &risym );
+         ML_(addSym) ( di, &risym );
 
-         if (si->trace_symtab) {
-            VG_(printf)("    record [%4d]:          "
-                        " val %010p, sz %4d  %s\n",
+         if (di->trace_symtab) {
+            VG_(printf)("    rec(%c) [%4d]:          "
+                        "  val %010p, sz %4d  %s\n",
+                        is_text ? 't' : 'd',
                         i, (void*)risym.addr, (Int)risym.size, 
                            (HChar*)risym.name
             );
@@ -468,6 +579,7 @@
       Addr       tocptr;
       Int        size;
       Bool       from_opd;
+      Bool       is_text;
    }
    TempSym;
 
@@ -476,28 +588,23 @@
    if (key1->addr > elem2->key.addr) return 1;
    return (Word)VG_(strcmp)(key1->name, elem2->key.name);
 }
-static void* oset_malloc ( SizeT szB ) { 
-   return VG_(arena_malloc)(VG_AR_SYMTAB, szB);
-}
-static void oset_free ( void* p ) {
-   VG_(arena_free)(VG_AR_SYMTAB, p);
-}
 
 static
 __attribute__((unused)) /* not referred to on all targets */
 void read_elf_symtab__ppc64_linux( 
-        struct _SegInfo* si, UChar* tab_name,
-        ElfXX_Sym* o_symtab, UInt o_symtab_sz, OffT o_symtab_offset,
-        UChar*     o_strtab, UInt o_strtab_sz,
-        UChar*     opd_filea, OffT opd_offset /* ppc64-linux only */ 
+        struct _DebugInfo* di, UChar* tab_name,
+        ElfXX_Sym* symtab_img, SizeT symtab_szB,
+        UChar*     strtab_img, SizeT strtab_szB,
+        UChar*     opd_img /* ppc64-linux only */ 
      )
 {
-   Int         i, old_size;
-   Addr        sym_addr, sym_addr_really;
+   Word        i;
+   Int         old_size;
+   Addr        sym_svma, sym_avma_really;
    Char       *sym_name, *sym_name_really;
    Int         sym_size;
    Addr        sym_tocptr, old_tocptr;
-   Bool        from_opd, modify_size, modify_tocptr;
+   Bool        from_opd, modify_size, modify_tocptr, is_text;
    DiSym       risym;
    ElfXX_Sym  *sym;
    OSet       *oset;
@@ -505,42 +612,42 @@
    TempSym    *elem;
    TempSym    *prev;
 
-   if (o_strtab == NULL || o_symtab == NULL) {
+   if (strtab_img == NULL || symtab_img == NULL) {
       Char buf[80];
       vg_assert(VG_(strlen)(tab_name) < 40);
       VG_(sprintf)(buf, "   object doesn't have a %s", tab_name);
-      ML_(symerr)(buf);
+      ML_(symerr)(di, False, buf);
       return;
    }
 
-   TRACE_SYMTAB("\nReading (ELF, ppc64-linux) %s (%d entries)\n", tab_name, 
-                o_symtab_sz/sizeof(ElfXX_Sym) );
+   TRACE_SYMTAB("\n--- Reading (ELF, ppc64-linux) %s (%d entries) ---\n",
+                tab_name, symtab_szB/sizeof(ElfXX_Sym) );
 
    oset = VG_(OSetGen_Create)( offsetof(TempSym,key), 
                                (OSetCmp_t)cmp_TempSymKey, 
-                               oset_malloc, oset_free );
+                               ML_(dinfo_zalloc), ML_(dinfo_free) );
    vg_assert(oset);
 
    /* Perhaps should start at i = 1; ELF docs suggest that entry
       0 always denotes 'unknown symbol'. */
-   for (i = 1; i < (Int)(o_symtab_sz/sizeof(ElfXX_Sym)); i++) {
-      sym      = & o_symtab[i];
-      sym_name = (Char*)(o_strtab + sym->st_name);
-      sym_addr = o_symtab_offset + sym->st_value;
+   for (i = 1; i < (Word)(symtab_szB/sizeof(ElfXX_Sym)); i++) {
+      sym      = & symtab_img[i];
+      sym_name = (Char*)(strtab_img + sym->st_name);
+      sym_svma = sym->st_value;
 
-      if (si->trace_symtab)
-         show_raw_elf_symbol(i, sym, sym_name, sym_addr, True);
+      if (di->trace_symtab)
+         show_raw_elf_symbol(i, sym, sym_name, sym_svma, True);
 
-      if (get_elf_symbol_info(si, sym, sym_name, sym_addr,
-                              opd_filea, opd_offset,
+      if (get_elf_symbol_info(di, sym, sym_name, sym_svma,
+                              opd_img, di->text_bias,
                               &sym_name_really, 
-                              &sym_addr_really,
+                              &sym_avma_really,
                               &sym_size,
                               &sym_tocptr,
-                              &from_opd)) {
+                              &from_opd, &is_text)) {
 
          /* Check if we've seen this (name,addr) key before. */
-         key.addr = sym_addr_really;
+         key.addr = sym_avma_really;
          key.name = sym_name_really;
          prev = VG_(OSetGen_Lookup)( oset, &key );
 
@@ -550,7 +657,7 @@
             modify_size   = False;
             modify_tocptr = False;
             old_size   = 0;
-	    old_tocptr = 0;
+            old_tocptr = 0;
 
             if (prev->from_opd && !from_opd 
                 && (prev->size == 24 || prev->size == 16)
@@ -579,9 +686,9 @@
             }
 
             /* Only one or the other is possible (I think) */
-	    vg_assert(!(modify_size && modify_tocptr));
+            vg_assert(!(modify_size && modify_tocptr));
 
-            if (modify_size && si->trace_symtab) {
+            if (modify_size && di->trace_symtab) {
                VG_(printf)("    modify (old sz %4d)    "
                            " val %010p, toc %010p, sz %4d  %s\n",
                            old_size,
@@ -591,7 +698,7 @@
                            (HChar*)prev->key.name
                );
             }
-            if (modify_tocptr && si->trace_symtab) {
+            if (modify_tocptr && di->trace_symtab) {
                VG_(printf)("    modify (upd tocptr)     "
                            " val %010p, toc %010p, sz %4d  %s\n",
                             (void*) prev->key.addr, 
@@ -610,10 +717,11 @@
             elem->tocptr   = sym_tocptr;
             elem->size     = sym_size;
             elem->from_opd = from_opd;
+            elem->is_text  = is_text;
             VG_(OSetGen_Insert)(oset, elem);
-            if (si->trace_symtab) {
+            if (di->trace_symtab) {
                VG_(printf)("   to-oset [%4d]:          "
-                           " val %010p, toc %010p, sz %4d  %s\n",
+                           "  val %010p, toc %010p, sz %4d  %s\n",
                            i, (void*) elem->key.addr,
                               (void*) elem->tocptr,
                               (Int)   elem->size, 
@@ -634,14 +742,16 @@
    while ( (elem = VG_(OSetGen_Next)(oset)) ) {
       risym.addr   = elem->key.addr;
       risym.size   = elem->size;
-      risym.name   = ML_(addStr) ( si, elem->key.name, -1 );
+      risym.name   = ML_(addStr) ( di, elem->key.name, -1 );
       risym.tocptr = elem->tocptr;
+      risym.isText = elem->is_text;
       vg_assert(risym.name != NULL);
 
-      ML_(addSym) ( si, &risym );
-      if (si->trace_symtab) {
-         VG_(printf)("    record [%4d]:          "
-                     " val %010p, toc %010p, sz %4d  %s\n",
+      ML_(addSym) ( di, &risym );
+      if (di->trace_symtab) {
+         VG_(printf)("    rec(%c) [%4d]:          "
+                     "   val %010p, toc %010p, sz %4d  %s\n",
+                     risym.isText ? 't' : 'd',
                      i, (void*) risym.addr,
                         (void*) risym.tocptr,
                         (Int)   risym.size, 
@@ -730,7 +840,7 @@
  * not match the value from the main object file.
  */
 static
-Addr open_debug_file( Char* name, UInt crc, UInt* size )
+Addr open_debug_file( Char* name, UInt crc, /*OUT*/UWord* size )
 {
    SysRes fd, sres;
    struct vki_stat stat_buf;
@@ -746,7 +856,7 @@
    }
 
    if (VG_(clo_verbosity) > 1)
-      VG_(message)(Vg_DebugMsg, "Reading debug info from %s...", name);
+      VG_(message)(Vg_DebugMsg, "Reading debug info from %s ..", name);
 
    *size = stat_buf.st_size;
    
@@ -763,7 +873,8 @@
       SysRes res = VG_(am_munmap_valgrind)(sres.res, *size);
       vg_assert(!res.isError);
       if (VG_(clo_verbosity) > 1)
-	 VG_(message)(Vg_DebugMsg, "... CRC mismatch (computed %08x wanted %08x)", calccrc, crc);
+         VG_(message)(Vg_DebugMsg, 
+            ".. CRC mismatch (computed %08x wanted %08x)", calccrc, crc);
       return 0;
    }
    
@@ -774,9 +885,11 @@
  * Try to find a separate debug file for a given object file.
  */
 static
-Addr find_debug_file( Char* objpath, Char* debugname, UInt crc, UInt* size )
+Addr find_debug_file( struct _DebugInfo* di,
+                      Char* objpath, Char* debugname, 
+                      UInt crc, /*OUT*/UWord* size )
 {
-   Char *objdir = VG_(arena_strdup)(VG_AR_SYMTAB, objpath);
+   Char *objdir = ML_(dinfo_strdup)(objpath);
    Char *objdirptr;
    Char *debugpath;
    Addr addr = 0;
@@ -784,7 +897,8 @@
    if ((objdirptr = VG_(strrchr)(objdir, '/')) != NULL)
       *objdirptr = '\0';
 
-   debugpath = VG_(arena_malloc)(VG_AR_SYMTAB, VG_(strlen)(objdir) + VG_(strlen)(debugname) + 16);
+   debugpath = ML_(dinfo_zalloc)(
+                  VG_(strlen)(objdir) + VG_(strlen)(debugname) + 32);
    
    VG_(sprintf)(debugpath, "%s/%s", objdir, debugname);
 
@@ -796,53 +910,166 @@
       }
    }
 
-   VG_(arena_free)(VG_AR_SYMTAB, debugpath);
-   VG_(arena_free)(VG_AR_SYMTAB, objdir);
+   if (addr) {
+      TRACE_SYMTAB("\n");
+      TRACE_SYMTAB("------ Found a debuginfo file: %s\n", debugpath);
+   }
+
+   ML_(dinfo_free)(debugpath);
+   ML_(dinfo_free)(objdir);
    
    return addr;
 }
 
 
+static Bool contained_within ( Addr outer, UWord n_outer,
+                               Addr inner, UWord n_inner )
+{
+   if (n_outer == 0 || n_inner == 0)
+      return False;
+   /* Simplistic .. assumes no wraparound (reasonably enough) */
+   if (inner >= outer && inner+n_inner <= outer+n_outer)
+      return True;
+   return False;
+}
+
+static void* INDEX_BIS ( void* base, Word index, Word scale ) {
+   return (void*)( ((UChar*)base) + index * scale );
+}
+
+static Addr round_Addr_upwards ( Addr a, UInt align ) 
+{
+   if (align > 0) {
+      vg_assert(-1 != VG_(log2)(align));
+      a = VG_ROUNDUP(a, align);
+   }
+   return a;
+}
+
+
+/* Find the file offset corresponding to SVMA by using the program
+   headers.  This is taken from binutils-2.17/binutils/readelf.c
+   offset_from_vma(). */
+static
+Word file_offset_from_svma ( /*OUT*/Bool* ok,
+                             Addr         svma,
+                             ElfXX_Phdr*  phdr_img,
+                             Word         phdr_nent,
+                             Word         phdr_ent_szB )
+{
+   Word        i;
+   ElfXX_Phdr* seg;
+   for (i = 0; i < phdr_nent; i++) {
+      seg = INDEX_BIS( phdr_img, i, phdr_ent_szB );
+      if (seg->p_type != PT_LOAD)
+         continue;
+      if (svma >= (seg->p_vaddr & -seg->p_align)
+          && svma + 1 <= seg->p_vaddr + seg->p_filesz) {
+         *ok = True;
+         return svma - seg->p_vaddr + seg->p_offset;
+      }
+   }
+   *ok = False;
+   return 0;
+}
+
 /* The central function for reading ELF debug info.  For the
-   object/exe specified by the SegInfo, find ELF sections, then read
+   object/exe specified by the DebugInfo, find ELF sections, then read
    the symbols, line number info, file name info, CFA (stack-unwind
    info) and anything else we want, into the tables within the
-   supplied SegInfo.
+   supplied DebugInfo.
 */
-Bool ML_(read_elf_debug_info) ( struct _SegInfo* si )
+Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 {
-   Bool          res;
-   ElfXX_Ehdr*   ehdr;       /* The ELF header                   */
-   ElfXX_Shdr*   shdr;       /* The section table                */
-   UChar*        sh_strtab;  /* The section table's string table */
+   Bool          res, ok;
    SysRes        fd, sres;
-   Int           i;
-   Bool          ok;
-   Addr          oimage;
-   UInt          n_oimage;
-   OffT          offset_oimage = 0;
-   Addr          dimage = 0;
-   UInt          n_dimage = 0;
-   OffT          offset_dimage = 0;
+   Word          i;
+
+   /* Image addresses for the ELF file we're working with. */
+   Addr          oimage   = 0;
+   UWord         n_oimage = 0;
+
+   /* Ditto for any ELF debuginfo file that we might happen to load. */
+   Addr          dimage   = 0;
+   UWord         n_dimage = 0;
+
+   /* ELF header for the main file.  Should == oimage since is at
+      start of file. */
+   ElfXX_Ehdr* ehdr_img = NULL;
+
+   /* Program header table image addr, # entries, entry size */
+   ElfXX_Phdr* phdr_img     = NULL;
+   UWord       phdr_nent    = 0;
+   UWord       phdr_ent_szB = 0;
+
+   /* Section header image addr, # entries, entry size.  Also the
+      associated string table. */
+   ElfXX_Shdr* shdr_img        = NULL;
+   UWord       shdr_nent       = 0;
+   UWord       shdr_ent_szB    = 0;
+   UChar*      shdr_strtab_img = NULL;
+
+   /* To do with figuring out where .sbss is relative to .bss.  A
+      kludge at the best of times. */
+   SizeT sbss_size;
+   Addr  sbss_svma;
+   UInt  bss_align;
+   UInt  sbss_align;
+   UInt  data_align;
+   SizeT bss_totsize;
+   Addr  gen_bss_lowest_svma;
+
+   vg_assert(di);
+   vg_assert(di->have_rx_map == True);
+   vg_assert(di->have_rw_map == True);
+   vg_assert(di->rx_map_size > 0);
+   vg_assert(di->rw_map_size > 0);
+   vg_assert(di->have_dinfo == False);
+   vg_assert(di->filename);
+   vg_assert(!di->memname);
+   vg_assert(!di->symtab);
+   vg_assert(!di->loctab);
+   vg_assert(!di->cfsi);
+   vg_assert(!di->cfsi_exprs);
+   vg_assert(!di->strchunks);
+   vg_assert(!di->soname);
+
+   /* If these don't hold true, it means that m_syswrap/m_aspacemgr
+      managed to do a mapping where the start isn't page aligned.
+      Which sounds pretty bogus to me. */
+   vg_assert(VG_IS_PAGE_ALIGNED(di->rx_map_avma));
+   vg_assert(VG_IS_PAGE_ALIGNED(di->rw_map_avma));
+
+   /* ----------------------------------------------------------
+      At this point, there is very little information in the
+      DebugInfo.  We only know that something that looks like an ELF
+      file has been mapped rx-ishly as recorded with the di->*rx_map*
+      fields and has also been mapped rw-ishly as recorded with the
+      di->*rw_map* fields.  First we examine the file's ELF Program
+      Header, and, by comparing that against the di->*r{w,x}_map*
+      info, try to figure out the AVMAs for the sections we care
+      about, that should have been mapped: text, data, sdata, bss got,
+      plt, and toc.
+      ---------------------------------------------------------- */
 
    oimage = (Addr)NULL;
    if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))
       VG_(message)(Vg_DebugMsg, "Reading syms from %s (%p)", 
-                                si->filename, si->text_start_avma );
+                                di->filename, di->rx_map_avma );
 
    /* mmap the object image aboard, so that we can read symbols and
       line number info out of it.  It will be munmapped immediately
       thereafter; it is only aboard transiently. */
 
-   fd = VG_(open)(si->filename, VKI_O_RDONLY, 0);
+   fd = VG_(open)(di->filename, VKI_O_RDONLY, 0);
    if (fd.isError) {
-      ML_(symerr)("Can't open .so/.exe to read symbols?!");
+      ML_(symerr)(di, True, "Can't open .so/.exe to read symbols?!");
       return False;
    }
 
    n_oimage = VG_(fsize)(fd.res);
-   if (n_oimage < 0) {
-      ML_(symerr)("Can't stat .so/.exe (to determine its size)?!");
+   if (n_oimage <= 0) {
+      ML_(symerr)(di, True, "Can't stat .so/.exe (to determine its size)?!");
       VG_(close)(fd.res);
       return False;
    }
@@ -853,270 +1080,577 @@
    VG_(close)(fd.res);
 
    if (sres.isError) {
-      VG_(message)(Vg_UserMsg, "warning: mmap failed on %s", si->filename );
+      VG_(message)(Vg_UserMsg, "warning: mmap failed on %s", di->filename );
       VG_(message)(Vg_UserMsg, "         no symbols or debug info loaded" );
       return False;
    }
 
    oimage = sres.res;
+   /* Check against wraparound.  am_mmap_file_float_valgrind should
+      not produce a wrapped-around mapping. */
+   vg_assert(n_oimage > 0);
+   vg_assert(oimage + n_oimage > oimage);
 
    if (0) {
       VG_(printf)("read_elf_debug_info: OIMAGE = %p - %p\n", 
                   (void*)oimage, (void*)(oimage + (UWord)n_oimage));
    }
 
-   /* Ok, the object image is safely in oimage[0 .. n_oimage-1]. 
-      Now verify that it is a valid ELF .so or executable image.
-   */
-   res = False;
-   ok = (n_oimage >= sizeof(ElfXX_Ehdr));
-   ehdr = (ElfXX_Ehdr*)oimage;
+   /* Ok, the object image is safely in oimage[0 .. n_oimage-1].  Now
+      verify that it is a valid ELF .so or executable image. */
+   res      = False;
+   ok       = (n_oimage >= sizeof(ElfXX_Ehdr));
+   ehdr_img = (ElfXX_Ehdr*)oimage;
 
    if (ok)
-      ok &= ML_(is_elf_object_file)(ehdr);
+      ok &= ML_(is_elf_object_file)(ehdr_img, n_oimage);
 
    if (!ok) {
-      ML_(symerr)("Invalid ELF header, or missing stringtab/sectiontab.");
+      ML_(symerr)(di, True, "Invalid ELF Header");
       goto out;
    }
 
-   /* Walk the LOAD headers in the phdr and update the SegInfo to
-      include them all, so that this segment also contains data and
-      bss memory.  Also computes correct symbol offset value for this
-      ELF file. */
-   if (ehdr->e_phoff + ehdr->e_phnum*sizeof(ElfXX_Phdr) > n_oimage) {
-      ML_(symerr)("ELF program header is beyond image end?!");
+   /* Find where the program and section header tables are, and give
+      up if either is missing or outside the image (bogus). */
+   phdr_img     = (ElfXX_Phdr*)( ((UChar*)ehdr_img) + ehdr_img->e_phoff );
+   phdr_nent    = ehdr_img->e_phnum;
+   phdr_ent_szB = ehdr_img->e_phentsize;
+
+   shdr_img     = (ElfXX_Shdr*)( ((UChar*)ehdr_img) + ehdr_img->e_shoff );
+   shdr_nent    = ehdr_img->e_shnum;
+   shdr_ent_szB = ehdr_img->e_shentsize;
+
+   TRACE_SYMTAB("------ Basic facts about the object ------\n");
+   TRACE_SYMTAB("object: img %p n_oimage %ld\n",
+               (void*)oimage, n_oimage);
+   TRACE_SYMTAB("phdr:   img %p nent %ld ent_szB %ld\n",
+               phdr_img, phdr_nent, phdr_ent_szB);
+   TRACE_SYMTAB("shdr:   img %p nent %ld ent_szB %ld\n",
+               shdr_img, shdr_nent, shdr_ent_szB);
+
+   if (phdr_nent == 0
+       || !contained_within(
+             oimage, n_oimage,
+             (Addr)phdr_img, phdr_nent * phdr_ent_szB)) {
+      ML_(symerr)(di, True, "Missing or invalid ELF Program Header Table");
       goto out;
    }
+
+   if (shdr_nent == 0
+       || !contained_within(
+             oimage, n_oimage,
+             (Addr)shdr_img, shdr_nent * shdr_ent_szB)) {
+      ML_(symerr)(di, True, "Missing or invalid ELF Section Header Table");
+      goto out;
+   }
+
+   /* Also find the section header's string table, and validate. */
+   /* checked previously by is_elf_object_file: */
+   vg_assert( ehdr_img->e_shstrndx != SHN_UNDEF );
+
+   shdr_strtab_img
+      = (UChar*)( ((UChar*)ehdr_img)
+                  + shdr_img[ehdr_img->e_shstrndx].sh_offset);
+   if (!contained_within( oimage, n_oimage,
+                          (Addr)shdr_strtab_img,
+                          1/*bogus, but we don't know the real size*/ )) {
+      ML_(symerr)(di, True, "Invalid ELF Section Header String Table");
+      goto out;
+   }
+
+   TRACE_SYMTAB("shdr:   string table at %p\n", shdr_strtab_img );
+
+   /* Do another amazingly tedious thing: find out the .soname for
+      this object.  Apparently requires looking through the program
+      header table. */
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("------ Looking for the soname ------\n");
+   vg_assert(di->soname == NULL);
    {
-      Bool offset_set = False;
-      ElfXX_Addr prev_addr = 0;
-      Addr baseaddr = 0;
+      ElfXX_Addr prev_svma = 0;
 
-      vg_assert(si->soname == NULL);
+      for (i = 0; i < phdr_nent; i++) {
+         ElfXX_Phdr* phdr = INDEX_BIS( phdr_img, i, phdr_ent_szB );
 
-      for (i = 0; i < ehdr->e_phnum; i++) {
-	 ElfXX_Phdr *o_phdr;
-	 ElfXX_Addr mapped, mapped_end;
-
-	 o_phdr = &((ElfXX_Phdr *)(oimage + ehdr->e_phoff))[i];
+         /* Make sure the PT_LOADable entries are in order */
+         if (phdr->p_type == PT_LOAD) {
+            TRACE_SYMTAB("PT_LOAD in order?: %p %p\n",
+                         prev_svma, phdr->p_vaddr);
+            if (phdr->p_vaddr < prev_svma) {
+               ML_(symerr)(di, True,
+                           "ELF Program Headers are not in ascending order");
+               goto out;
+            }
+            prev_svma = phdr->p_vaddr;
+         }
 
          /* Try to get the soname.  If there isn't one, use "NONE".
             The seginfo needs to have some kind of soname in order to
             facilitate writing redirect functions, since all redirect
             specifications require a soname (pattern). */
-	 if (o_phdr->p_type == PT_DYNAMIC && si->soname == NULL) {
-	    const ElfXX_Dyn *dyn = (const ElfXX_Dyn *)(oimage + o_phdr->p_offset);
-	    Int stroff = -1;
-	    Char *strtab = NULL;
-	    Int j;
-	    
-	    for(j = 0; dyn[j].d_tag != DT_NULL; j++) {
-	       switch(dyn[j].d_tag) {
-	       case DT_SONAME:
-		  stroff = dyn[j].d_un.d_val;
-		  break;
-
-	       case DT_STRTAB:
-		  strtab = (Char *)oimage + dyn[j].d_un.d_ptr - baseaddr;
-		  break;
-	       }
-	    }
-
-	    if (stroff != -1 && strtab != 0) {
-	       TRACE_SYMTAB("soname=%s\n", strtab+stroff);
-	       si->soname = VG_(arena_strdup)(VG_AR_SYMTAB, strtab+stroff);
-	    }
-	 }
-
-	 if (o_phdr->p_type != PT_LOAD)
-	    continue;
-
-	 if (!offset_set) {
-	    offset_set = True;
-	    offset_oimage = si->text_start_avma - o_phdr->p_vaddr;
-	    baseaddr = o_phdr->p_vaddr;
-	 }
-
-         // Make sure the Phdrs are in order
-	 if (o_phdr->p_vaddr < prev_addr) {
-	    ML_(symerr)("ELF Phdrs are out of order!?");
-            goto out;
-	 }
-	 prev_addr = o_phdr->p_vaddr;
-
-         // Get the data and bss start/size if appropriate
-	 mapped = o_phdr->p_vaddr + offset_oimage;
-	 mapped_end = mapped + o_phdr->p_memsz;
-	 if (si->data_start_avma == 0 &&
-	     (o_phdr->p_flags & (PF_R|PF_W|PF_X)) == (PF_R|PF_W)) {
-	    si->data_start_avma = mapped;
-	    si->data_size       = o_phdr->p_filesz;
-	    si->bss_start_avma  = mapped + o_phdr->p_filesz;
-	    if (o_phdr->p_memsz > o_phdr->p_filesz)
-	       si->bss_size = o_phdr->p_memsz - o_phdr->p_filesz;
-	    else
-	       si->bss_size = 0;
-	 }
-
-         mapped = mapped & ~(VKI_PAGE_SIZE-1);
-	 mapped_end = (mapped_end + VKI_PAGE_SIZE - 1) & ~(VKI_PAGE_SIZE-1);
-
-	 if (VG_(needs).data_syms 
-             && mapped >= si->text_start_avma 
-             && mapped <= (si->text_start_avma + si->text_size)
-             && mapped_end > (si->text_start_avma + si->text_size)) {
-            /* XXX jrs 2007 Jan 11: what's going on here?  If data
-               syms are involved, surely we shouldn't be messing with
-               the segment's text_size unless there is an assumption
-               that the data segment has been mapped immediately after
-               the text segment.  Which doesn't sound good to me. */
-	    UInt newsz = mapped_end - si->text_start_avma;
-	    if (newsz > si->text_size) {
-	       if (0)
-		  VG_(printf)("extending mapping %p..%p %d -> ..%p %d\n", 
-			      si->text_start_avma, 
-                              si->text_start_avma + si->text_size, 
-                              si->text_size,
-			      si->text_start_avma + newsz, newsz);
-
-	       si->text_size = newsz;
-	    }
-	 }
-      }
-   }
-
-   si->text_bias = offset_oimage;
-
-   if (VG_(clo_verbosity) > 2 || VG_(clo_trace_redir))
-      VG_(message)(Vg_DebugMsg, "   svma %010p, avma %010p", 
-                                si->text_start_avma - si->text_bias,
-                                si->text_start_avma );
+         if (phdr->p_type == PT_DYNAMIC && di->soname == NULL) {
+            ElfXX_Dyn* dyn_img = (ElfXX_Dyn*)( ((UChar*)ehdr_img)
+                                               + phdr->p_offset);
+            Word   stroff = -1;
+            UChar* strtab = NULL;
+            Word   j;
+            for (j = 0; dyn_img[j].d_tag != DT_NULL; j++) {
+               switch (dyn_img[j].d_tag) {
+                  case DT_SONAME: {
+                     stroff = dyn_img[j].d_un.d_val;
+                     break;
+                  }
+                  case DT_STRTAB: {
+                     Bool ok2 = False;
+                     Word offset = file_offset_from_svma(
+                                      &ok2,
+                                      dyn_img[j].d_un.d_ptr,
+                                      phdr_img,
+                                      phdr_nent, phdr_ent_szB
+                                   );
+                     if (ok2 && strtab == NULL) {
+                        vg_assert(offset >= 0 && offset <= n_oimage);
+                        strtab = ((UChar*)ehdr_img) + offset;
+                     }
+                     break;
+                  }
+                  default:
+                     break;
+               }
+            }
+            if (stroff != -1 && strtab != NULL) {
+               TRACE_SYMTAB("Found soname = %s\n", strtab+stroff);
+               di->soname = ML_(dinfo_strdup)(strtab+stroff);
+            }
+         }
+      } /* for (i = 0; i < phdr_nent; i++) ... */
+   } /* look for the soname */
 
    /* If, after looking at all the program headers, we still didn't 
       find a soname, add a fake one. */
-   if (si->soname == NULL) {
-      TRACE_SYMTAB("soname(fake)=\"NONE\"\n");
-      si->soname = "NONE";
+   if (di->soname == NULL) {
+      TRACE_SYMTAB("No soname found; using (fake) \"NONE\"\n");
+      di->soname = "NONE";
    }
 
-   TRACE_SYMTAB("shoff = %d,  shnum = %d,  size = %d,  n_vg_oimage = %d\n",
-                ehdr->e_shoff, ehdr->e_shnum, sizeof(ElfXX_Shdr), n_oimage );
+   /*SizeT*/ sbss_size  = 0;
+   /*Addr */ sbss_svma  = 0;
+   /*UInt */ bss_align  = 0;
+   /*UInt */ sbss_align = 0;
 
-   if (ehdr->e_shoff + ehdr->e_shnum*sizeof(ElfXX_Shdr) > n_oimage) {
-      ML_(symerr)("ELF section header is beyond image end?!");
-      goto out;
+   /* UInt */  data_align = 0;
+   /* SizeT */ bss_totsize = 0;
+   /* Addr */  gen_bss_lowest_svma = ~((Addr)0);
+
+   /* Now read the section table. */
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("------ Examining the section headers "
+                "and program headers ------\n");
+   TRACE_SYMTAB("rx: at %p are mapped foffsets %ld .. %ld\n",
+               di->rx_map_avma,
+               di->rx_map_foff, di->rx_map_foff + di->rx_map_size - 1 );
+   TRACE_SYMTAB("rw: at %p are mapped foffsets %ld .. %ld\n",
+               di->rw_map_avma,
+               di->rw_map_foff, di->rw_map_foff + di->rw_map_size - 1 );
+
+   for (i = 0; i < shdr_nent; i++) {
+      ElfXX_Shdr* shdr = INDEX_BIS( shdr_img, i, shdr_ent_szB );
+      UChar* name = shdr_strtab_img + shdr->sh_name;
+      Addr   svma = shdr->sh_addr;
+      OffT   foff = shdr->sh_offset;
+      UWord  size = shdr->sh_size;
+      UInt   alyn = shdr->sh_addralign;
+      Bool   bits = !(shdr->sh_type == SHT_NOBITS);
+      Bool   inrx = foff >= di->rx_map_foff
+                    && foff < di->rx_map_foff + di->rx_map_size;
+      Bool   inrw = foff >= di->rw_map_foff
+                    && foff < di->rw_map_foff + di->rw_map_size;
+
+      TRACE_SYMTAB(" [sec %2ld]  %s %s  al%2u  foff %6ld .. %6ld  "
+                  "  svma %p  name \"%s\"\n", 
+                  i, inrx ? "rx" : "  ", inrw ? "rw" : "  ", alyn,
+                  foff, foff+size-1, (void*)svma, name );
+
+      /* Check for sane-sized segments.  SHT_NOBITS sections have zero
+         size in the file. */
+      if ((foff >= n_oimage) || (foff + (bits ? size : 0) > n_oimage)) {
+         ML_(symerr)(di, True, "ELF Section extends beyond image end");
+         goto out;
+      }
+
+      /* Check for a sane alignment value. */
+      if (alyn > 0 && -1 == VG_(log2)(alyn)) {
+         ML_(symerr)(di, True, "ELF Section contains invalid "
+                               ".sh_addralign value");
+         goto out;
+      }
+
+#     define BAD(_secname)                                 \
+         do { ML_(symerr)(di, True,                        \
+                          "Can't make sense of " _secname  \
+                          " section mapping");             \
+              goto out;                                    \
+         } while (0)
+
+      /* Find avma-s for: .text .data .sdata .bss .plt .got .opd
+         and .eh_frame */
+
+      /* Accept .text where mapped as rx (code) */
+      if (0 == VG_(strcmp)(name, ".text")) {
+         if (inrx && size > 0 && !di->text_present) {
+            di->text_present = True;
+            di->text_svma = svma;
+            di->text_avma = di->rx_map_avma + foff - di->rx_map_foff;
+            di->text_size = size;
+            di->text_bias = di->text_avma - svma;
+            TRACE_SYMTAB("acquiring .text svma = %p .. %p\n",
+                         di->text_svma, 
+                         di->text_svma + di->text_size - 1);
+            TRACE_SYMTAB("acquiring .text avma = %p .. %p\n",
+                         di->text_avma, 
+                         di->text_avma + di->text_size - 1);
+            TRACE_SYMTAB("acquiring .text bias = %p\n", di->text_bias);
+         } else {
+            BAD(".text");
+         }
+      }
+
+      /* Accept .data where mapped as rw (data), even if zero-sized */
+      if (0 == VG_(strcmp)(name, ".data")) {
+         if (inrw && size >= 0 && !di->data_present) {
+            if (alyn > data_align)
+               data_align = alyn;
+            di->data_present = True;
+            di->data_svma = svma;
+            di->data_avma = di->rw_map_avma + foff - di->rw_map_foff;
+            di->data_size = size;
+            di->data_bias = di->data_avma - svma;
+            TRACE_SYMTAB("acquiring .data svma = %p .. %p\n", 
+                         di->data_svma,
+                         di->data_svma + di->data_size - 1);
+            TRACE_SYMTAB("acquiring .data avma = %p .. %p\n", 
+                         di->data_avma,
+                         di->data_avma + di->data_size - 1);
+            TRACE_SYMTAB("acquiring .data bias = %p\n", di->data_bias);
+         } else {
+            BAD(".data");
+         }
+      }
+
+      /* Accept .sdata where mapped as rw (data) */
+      if (0 == VG_(strcmp)(name, ".sdata")) {
+         if (inrw && size > 0 && !di->sdata_present) {
+            if (alyn > data_align)
+               data_align = alyn;
+            di->sdata_present = True;
+            di->sdata_svma = svma;
+            di->sdata_avma = di->rw_map_avma + foff - di->rw_map_foff;
+            di->sdata_size = size;
+            di->sdata_bias = di->sdata_avma - svma;
+            TRACE_SYMTAB("acquiring .sdata svma = %p .. %p\n", 
+                         di->sdata_svma,
+                         di->sdata_svma + di->sdata_size - 1);
+            TRACE_SYMTAB("acquiring .sdata avma = %p .. %p\n", 
+                         di->sdata_avma,
+                         di->sdata_avma + di->sdata_size - 1);
+            TRACE_SYMTAB("acquiring .sdata bias = %p\n", di->sdata_bias);
+         } else {
+            BAD(".sdata");
+         }
+      }
+
+      /* Accept .bss where mapped as rw (data), even if zero-sized */
+      if (0 == VG_(strcmp)(name, ".bss")) {
+         if (inrw && size >= 0 && !di->bss_present) {
+            bss_totsize += round_Addr_upwards(size, alyn);
+            if (svma < gen_bss_lowest_svma)
+               gen_bss_lowest_svma = svma;
+            TRACE_SYMTAB("increasing total bss-like size to %ld\n",
+                         bss_totsize);
+            di->bss_present = True;
+            di->bss_svma = svma;
+            di->bss_avma = di->rw_map_avma + foff - di->rw_map_foff;
+            di->bss_size = size;
+            di->bss_bias = di->bss_avma - svma;
+            bss_align = alyn;
+            TRACE_SYMTAB("acquiring .bss svma = %p .. %p\n", 
+                         di->bss_svma,
+                         di->bss_svma + di->bss_size - 1);
+            TRACE_SYMTAB("acquiring .bss avma = %p .. %p\n", 
+                         di->bss_avma,
+                         di->bss_avma + di->bss_size - 1);
+            TRACE_SYMTAB("acquiring .bss bias = %p\n", di->bss_bias);
+         } else
+         if ((!inrw) && (!inrx) && size > 0 && !di->bss_present) {
+            /* File contains a .bss, but it didn't get mapped.  Ignore. */
+            di->bss_present = False;
+            di->bss_svma = 0;
+            di->bss_avma = 0;
+            di->bss_size = 0;
+            di->bss_bias = 0;
+            bss_align = 0;
+         } else {
+            BAD(".bss");
+         }
+      }
+
+      /* Accept .sbss where mapped as rw (data) */
+      if (0 == VG_(strcmp)(name, ".sbss")) {
+         if (inrw && size > 0 && sbss_size == 0) {
+            bss_totsize += round_Addr_upwards(size, alyn);
+            if (svma < gen_bss_lowest_svma)
+               gen_bss_lowest_svma = svma;
+            TRACE_SYMTAB("increasing total bss-like size to %ld\n",
+                         bss_totsize);
+            sbss_size  = size;
+            sbss_svma  = svma;
+            sbss_align = alyn;
+         } else {
+            BAD(".sbss");
+         }
+      }
+
+      /* Accept .dynbss where mapped as rw (data) */
+      if (0 == VG_(strcmp)(name, ".dynbss")) {
+	if (inrw && size > 0 /* && sbss_size == 0*/) {
+           bss_totsize += round_Addr_upwards(size, alyn);
+           if (svma < gen_bss_lowest_svma)
+              gen_bss_lowest_svma = svma;
+           TRACE_SYMTAB("increasing total bss-like size to %ld\n",
+                        bss_totsize);
+         } else {
+            BAD(".dynbss");
+         }
+      }
+
+      /* Accept .got where mapped as rw (data) */
+      if (0 == VG_(strcmp)(name, ".got")) {
+         if (inrw && size > 0 && !di->got_present) {
+            di->got_present = True;
+            di->got_avma = di->rw_map_avma + foff - di->rw_map_foff;
+            di->got_size = size;
+            TRACE_SYMTAB("acquiring .got avma = %p\n", di->got_avma);
+         } else {
+            BAD(".got");
+         }
+      }
+
+      /* PLT is different on different platforms, it seems. */
+#     if defined(VGP_x86_linux) || defined(VGP_amd64_linux)
+      /* Accept .plt where mapped as rx (code) */
+      if (0 == VG_(strcmp)(name, ".plt")) {
+         if (inrx && size > 0 && !di->plt_present) {
+            di->plt_present = True;
+            di->plt_avma = di->rx_map_avma + foff - di->rx_map_foff;
+            di->plt_size = size;
+            TRACE_SYMTAB("acquiring .plt avma = %p\n", di->plt_avma);
+         } else {
+            BAD(".plt");
+         }
+      }
+#     elif defined(VGP_ppc32_linux)
+      /* Accept .plt where mapped as rw (data) */
+      if (0 == VG_(strcmp)(name, ".plt")) {
+         if (inrw && size > 0 && !di->plt_present) {
+            di->plt_present = True;
+            di->plt_avma = di->rw_map_avma + foff - di->rw_map_foff;
+            di->plt_size = size;
+            TRACE_SYMTAB("acquiring .plt avma = %p\n", di->plt_avma);
+         } else {
+            BAD(".plt");
+         }
+      }
+#     elif defined(VGP_ppc64_linux)
+      /* Accept .plt where mapped as rw (data), or unmapped */
+      if (0 == VG_(strcmp)(name, ".plt")) {
+         if (inrw && size > 0 && !di->plt_present) {
+            di->plt_present = True;
+            di->plt_avma = di->rw_map_avma + foff - di->rw_map_foff;
+            di->plt_size = size;
+            TRACE_SYMTAB("acquiring .plt avma = %p\n", di->plt_avma);
+         } else 
+         if ((!inrw) && (!inrx) && size > 0 && !di->plt_present) {
+            /* File contains a .plt, but it didn't get mapped.
+               Presumably it is not required on this platform.  At
+               least don't reject the situation as invalid. */
+            di->plt_present = True;
+            di->plt_avma = 0;
+            di->plt_size = 0;
+         } else {
+            BAD(".plt");
+         }
+      }
+#     else
+#       error "Unsupported platform"
+#     endif
+
+      /* Accept .opd where mapped as rw (data) */
+      if (0 == VG_(strcmp)(name, ".opd")) {
+         if (inrw && size > 0 && !di->opd_present) {
+            di->opd_present = True;
+            di->opd_avma = di->rw_map_avma + foff - di->rw_map_foff;
+            di->opd_size = size;
+            TRACE_SYMTAB("acquiring .opd avma = %p\n", di->opd_avma);
+         } else {
+            BAD(".opd");
+         }
+      }
+
+      /* Accept .eh_frame where mapped as rx (code).  This seems to be
+         the common case.  However, if that doesn't pan out, try for
+         rw (data) instead. */
+      if (0 == VG_(strcmp)(name, ".eh_frame")) {
+         if (inrx && size > 0 && !di->ehframe_present) {
+            di->ehframe_present = True;
+            di->ehframe_avma = di->rx_map_avma + foff - di->rx_map_foff;
+            di->ehframe_size = size;
+            TRACE_SYMTAB("acquiring .eh_frame avma = %p\n", di->ehframe_avma);
+         } else
+         if (inrw && size > 0 && !di->ehframe_present) {
+            di->ehframe_present = True;
+            di->ehframe_avma = di->rw_map_avma + foff - di->rw_map_foff;
+            di->ehframe_size = size;
+            TRACE_SYMTAB("acquiring .eh_frame avma = %p\n", di->ehframe_avma);
+         } else {
+            BAD(".eh_frame");
+         }
+      }
+
+#    undef BAD
+
    }
 
-   shdr = (ElfXX_Shdr*)(oimage + ehdr->e_shoff);
-   sh_strtab = (UChar*)(oimage + shdr[ehdr->e_shstrndx].sh_offset);
+   /* Kludge: ignore all previous computations for .bss avma range,
+      and simply assume that .bss immediately follows .data/.sdata.*/
+   if (1) {
+      SizeT data_al = round_Addr_upwards(di->data_avma, data_align)
+                      - di->data_avma;
+      TRACE_SYMTAB("data_al = %ld\n", data_al);
+      bss_totsize += data_al;
+      di->bss_svma = gen_bss_lowest_svma;
+      di->bss_size = bss_totsize;
+      di->bss_avma = di->data_avma + (di->bss_svma - di->data_svma);
+      di->bss_bias = di->data_bias;
+      TRACE_SYMTAB("kludged .bss svma = %p .. %p\n", 
+                   di->bss_svma, di->bss_svma + di->bss_size - 1);
+      TRACE_SYMTAB("kludged .bss avma = %p .. %p\n",
+                   di->bss_avma, di->bss_avma + di->bss_size - 1);
+      TRACE_SYMTAB("kludged .bss bias = %p\n", di->bss_bias);
+   }
+
+   if (0) VG_(printf)("YYYY text_: avma %p  size %ld  bias %p\n", 
+                      di->text_avma, di->text_size, di->text_bias);
+
+   if (VG_(clo_verbosity) > 2 || VG_(clo_trace_redir))
+      VG_(message)(Vg_DebugMsg, "   svma %010p, avma %010p", 
+                                di->text_avma - di->text_bias,
+                                di->text_avma );
+
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("------ Finding image addresses "
+                "for debug-info sections ------\n");
 
    /* Find interesting sections, read the symbol table(s), read any debug
       information */
    {
-      /* IMAGE addresses: pointers to start of sections (in the
-         oimage, not in the running image) -- image addresses */
-      UChar*     strtab_img      = NULL; /* .strtab */
-      ElfXX_Sym* symtab_img      = NULL; /* .symtab */
-      UChar*     dynstr_img      = NULL; /* .dynstr */
-      ElfXX_Sym* dynsym_img      = NULL; /* .dynsym */
-      Char*      debuglink_img   = NULL; /* .gnu_debuglink */
-      UChar*     stab_img        = NULL; /* .stab         (stabs)  */
-      UChar*     stabstr_img     = NULL; /* .stabstr      (stabs)  */
-      UChar*     debug_line_img  = NULL; /* .debug_line   (dwarf2) */
-      UChar*     debug_info_img  = NULL; /* .debug_info   (dwarf2) */
-      UChar*     debug_abbv_img  = NULL; /* .debug_abbrev (dwarf2) */
-      UChar*     debug_str_img   = NULL; /* .debug_str    (dwarf2) */
-      UChar*     dwarf1d_img     = NULL; /* .debug        (dwarf1) */
-      UChar*     dwarf1l_img     = NULL; /* .line         (dwarf1) */
-      UChar*     ehframe_img     = NULL; /* .eh_frame     (dwarf2) */
-      UChar*     opd_filea_img   = NULL; /* .opd          (dwarf2, ppc64-linux) */
-      UChar*     dummy_filea_img = NULL;
-
-      OffT       symtab_offset   = offset_oimage;
-      OffT       dynsym_offset   = offset_oimage;
-      OffT       debug_offset    = offset_oimage;
-      OffT       opd_offset      = offset_oimage;
-
+      /* IMAGE addresses: pointers to start of sections in the
+         transiently loaded oimage, not in the fragments of the file
+         mapped in by the guest's dynamic linker. */
+      UChar*     strtab_img       = NULL; /* .strtab */
+      ElfXX_Sym* symtab_img       = NULL; /* .symtab */
+      UChar*     dynstr_img       = NULL; /* .dynstr */
+      ElfXX_Sym* dynsym_img       = NULL; /* .dynsym */
+      UChar*     debuglink_img    = NULL; /* .gnu_debuglink */
+      UChar*     stab_img         = NULL; /* .stab         (stabs)  */
+      UChar*     stabstr_img      = NULL; /* .stabstr      (stabs)  */
+      UChar*     debug_line_img   = NULL; /* .debug_line   (dwarf2) */
+      UChar*     debug_info_img   = NULL; /* .debug_info   (dwarf2) */
+      UChar*     debug_abbv_img   = NULL; /* .debug_abbrev (dwarf2) */
+      UChar*     debug_str_img    = NULL; /* .debug_str    (dwarf2) */
+      UChar*     debug_ranges_img = NULL; /* .debug_ranges (dwarf2) */
+      UChar*     debug_loc_img    = NULL; /* .debug_loc    (dwarf2) */
+      UChar*     dwarf1d_img      = NULL; /* .debug        (dwarf1) */
+      UChar*     dwarf1l_img      = NULL; /* .line         (dwarf1) */
+      UChar*     ehframe_img      = NULL; /* .eh_frame     (dwarf2) */
+      UChar*     opd_img          = NULL; /* .opd (dwarf2,
+                                                   ppc64-linux) */
       /* Section sizes, in bytes */
-      UInt       strtab_sz       = 0;
-      UInt       symtab_sz       = 0;
-      UInt       dynstr_sz       = 0;
-      UInt       dynsym_sz       = 0;
-      UInt       debuglink_sz    = 0;
-      UInt       stab_sz         = 0;
-      UInt       stabstr_sz      = 0;
-      UInt       debug_line_sz   = 0;
-      UInt       debug_info_sz   = 0;
-      UInt       debug_abbv_sz   = 0;
-      UInt       debug_str_sz    = 0;
-      UInt       dwarf1d_sz      = 0;
-      UInt       dwarf1l_sz      = 0;
-      UInt       ehframe_sz      = 0;
-
-      /* Section actual virtual addresses */
-      Addr       dummy_avma      = 0;
-      Addr       ehframe_avma    = 0;
+      SizeT      strtab_sz       = 0;
+      SizeT      symtab_sz       = 0;
+      SizeT      dynstr_sz       = 0;
+      SizeT      dynsym_sz       = 0;
+      SizeT      debuglink_sz    = 0;
+      SizeT      stab_sz         = 0;
+      SizeT      stabstr_sz      = 0;
+      SizeT      debug_line_sz   = 0;
+      SizeT      debug_info_sz   = 0;
+      SizeT      debug_abbv_sz   = 0;
+      SizeT      debug_str_sz    = 0;
+      SizeT      debug_ranges_sz = 0;
+      SizeT      debug_loc_sz    = 0;
+      SizeT      dwarf1d_sz      = 0;
+      SizeT      dwarf1l_sz      = 0;
+      SizeT      ehframe_sz      = 0;
+      SizeT      opd_sz_unused   = 0;
 
       /* Find all interesting sections */
 
       /* What FIND does: it finds the section called SEC_NAME.  The
-	 size of it is assigned to SEC_SIZE.  The address that it will
-	 appear in the running image is assigned to SEC_VMA (note,
-	 this will be meaningless for sections which are not marked
-	 loadable.  Even for sections which are marked loadable, the
-	 client's ld.so may not have loaded them yet, so there is no
-	 guarantee that we can safely prod around in any such area)
-	 The address of the section in the transiently loaded oimage
-	 is assigned to SEC_FILEA.  Because the entire object file is
-	 transiently mapped aboard for inspection, it's always safe to
-	 inspect that area. */
+         size of it is assigned to SEC_SIZE.  The address of the
+         section in the transiently loaded oimage is assigned to
+         SEC_FILEA.  Even for sections which are marked loadable, the
+         client's ld.so may not have loaded them yet, so there is no
+         guarantee that we can safely prod around in any such area).
+         Because the entire object file is transiently mapped aboard
+         for inspection, it's always safe to inspect that area. */
 
-      for (i = 0; i < ehdr->e_shnum; i++) {
+      for (i = 0; i < ehdr_img->e_shnum; i++) {
 
-#        define FIND(sec_name, sec_size, sec_filea, sec_vma) \
-         if (0 == VG_(strcmp)(sec_name, sh_strtab + shdr[i].sh_name)) { \
-            Bool nobits; \
-            sec_vma   = (Addr)(offset_oimage + shdr[i].sh_addr); \
-            sec_filea = (void*)(oimage + shdr[i].sh_offset); \
-            sec_size  = shdr[i].sh_size; \
-            nobits = shdr[i].sh_type == SHT_NOBITS; \
-            TRACE_SYMTAB( "%18s: filea %p .. %p, vma %p .. %p\n", \
-                          sec_name, (UChar*)sec_filea, \
-                                    ((UChar*)sec_filea) + sec_size - 1, \
-                          sec_vma, sec_vma + sec_size - 1); \
-            /* SHT_NOBITS sections have zero size in the file. */ \
-            if ( shdr[i].sh_offset + (nobits ? 0 : sec_size) > n_oimage ) { \
-               ML_(symerr)("   section beyond image end?!"); \
-               goto out; \
+#        define FIND(sec_name, sec_size, sec_img) \
+         do { ElfXX_Shdr* shdr \
+                 = INDEX_BIS( shdr_img, i, shdr_ent_szB ); \
+            if (0 == VG_(strcmp)(sec_name, shdr_strtab_img \
+                                           + shdr->sh_name)) { \
+               Bool nobits; \
+               sec_img  = (void*)(oimage + shdr->sh_offset); \
+               sec_size = shdr->sh_size; \
+               nobits   = shdr->sh_type == SHT_NOBITS; \
+               TRACE_SYMTAB( "%18s:  img %p .. %p\n", \
+                             sec_name, (UChar*)sec_img, \
+                             ((UChar*)sec_img) + sec_size - 1); \
+               /* SHT_NOBITS sections have zero size in the file. */ \
+               if ( shdr->sh_offset \
+                    + (nobits ? 0 : sec_size) > n_oimage ) { \
+                  ML_(symerr)(di, True, \
+                              "   section beyond image end?!"); \
+                  goto out; \
+               } \
             } \
-         }
+         } while (0);
 
-         /* Nb: must find where .got and .plt sections will be in the
-          * executable image, not in the object image transiently loaded. */
-         /*   NAME              SIZE           IMAGE addr       AVMA */
-         FIND(".dynsym",        dynsym_sz,     dynsym_img,      dummy_avma)
-         FIND(".dynstr",        dynstr_sz,     dynstr_img,      dummy_avma)
-         FIND(".symtab",        symtab_sz,     symtab_img,      dummy_avma)
-         FIND(".strtab",        strtab_sz,     strtab_img,      dummy_avma)
+         /*   NAME              SIZE             IMAGE addr */
+         FIND(".dynsym",        dynsym_sz,       dynsym_img)
+         FIND(".dynstr",        dynstr_sz,       dynstr_img)
+         FIND(".symtab",        symtab_sz,       symtab_img)
+         FIND(".strtab",        strtab_sz,       strtab_img)
 
-         FIND(".gnu_debuglink", debuglink_sz,  debuglink_img,   dummy_avma)
+         FIND(".gnu_debuglink", debuglink_sz,    debuglink_img)
 
-         FIND(".stab",          stab_sz,       stab_img,        dummy_avma)
-         FIND(".stabstr",       stabstr_sz,    stabstr_img,     dummy_avma)
+         FIND(".stab",          stab_sz,         stab_img)
+         FIND(".stabstr",       stabstr_sz,      stabstr_img)
 
-         FIND(".debug_line",    debug_line_sz, debug_line_img,  dummy_avma)
-         FIND(".debug_info",    debug_info_sz, debug_info_img,  dummy_avma)
-         FIND(".debug_abbrev",  debug_abbv_sz, debug_abbv_img,  dummy_avma)
-         FIND(".debug_str",     debug_str_sz,  debug_str_img,   dummy_avma)
+         FIND(".debug_line",    debug_line_sz,   debug_line_img)
+         FIND(".debug_info",    debug_info_sz,   debug_info_img)
+         FIND(".debug_abbrev",  debug_abbv_sz,   debug_abbv_img)
+         FIND(".debug_str",     debug_str_sz,    debug_str_img)
+         FIND(".debug_ranges",  debug_ranges_sz, debug_ranges_img)
+         FIND(".debug_loc",     debug_loc_sz,    debug_loc_img)
 
-         FIND(".debug",         dwarf1d_sz,    dwarf1d_img,     dummy_avma)
-         FIND(".line",          dwarf1l_sz,    dwarf1l_img,     dummy_avma)
-         FIND(".eh_frame",      ehframe_sz,    ehframe_img,     ehframe_avma)
+         FIND(".debug",         dwarf1d_sz,      dwarf1d_img)
+         FIND(".line",          dwarf1l_sz,      dwarf1l_img)
+         FIND(".eh_frame",      ehframe_sz,      ehframe_img)
 
-         FIND(".got",           si->got_size,  dummy_filea_img, si->got_start_avma)
-         FIND(".plt",           si->plt_size,  dummy_filea_img, si->plt_start_avma)
-         FIND(".opd",           si->opd_size,  opd_filea_img,   si->opd_start_avma)
+         FIND(".opd",           opd_sz_unused,   opd_img)
 
 #        undef FIND
       }
@@ -1132,74 +1666,132 @@
          crc = *(UInt *)(debuglink_img + crc_offset);
 
          /* See if we can find a matching debug file */
-         dimage = find_debug_file(si->filename, debuglink_img, crc, &n_dimage);
-         if (dimage != 0) {
-            ehdr = (ElfXX_Ehdr*)dimage;
+         dimage = find_debug_file( di, di->filename, debuglink_img,
+                                   crc, &n_dimage );
 
-            if (n_dimage >= sizeof(ElfXX_Ehdr) 
-                && ML_(is_elf_object_file(ehdr))
-                && ehdr->e_phoff + ehdr->e_phnum*sizeof(ElfXX_Phdr) <= n_dimage
-                && ehdr->e_shoff + ehdr->e_shnum*sizeof(ElfXX_Shdr) <= n_dimage)
-            {
-               Bool need_symtab = (NULL == symtab_img);
-               Bool need_stabs = (NULL == stab_img);
-               Bool need_dwarf2 = (NULL == debug_info_img);
-               Bool need_dwarf1 = (NULL == dwarf1d_img);
+         if (dimage != 0 
+             && n_dimage >= sizeof(ElfXX_Ehdr)
+             && ML_(is_elf_object_file)((void*)dimage, n_dimage)) {
 
-               for (i = 0; i < ehdr->e_phnum; i++) {
-                  ElfXX_Phdr *o_phdr = &((ElfXX_Phdr *)(dimage + ehdr->e_phoff))[i];
-                  if (o_phdr->p_type == PT_LOAD) {
-                     offset_dimage = si->text_start_avma - o_phdr->p_vaddr;
-                     break;
-                  }
+            /* Pull out and validate program header and section header info */
+            ElfXX_Ehdr* ehdr_dimg     = (ElfXX_Ehdr*)dimage;
+            ElfXX_Phdr* phdr_dimg     = (ElfXX_Phdr*)( ((UChar*)ehdr_dimg)
+                                                       + ehdr_dimg->e_phoff );
+            UWord       phdr_dnent    = ehdr_dimg->e_phnum;
+            UWord       phdr_dent_szB = ehdr_dimg->e_phentsize;
+            ElfXX_Shdr* shdr_dimg     = (ElfXX_Shdr*)( ((UChar*)ehdr_dimg)
+                                                       + ehdr_dimg->e_shoff );
+            UWord       shdr_dnent       = ehdr_dimg->e_shnum;
+            UWord       shdr_dent_szB    = ehdr_dimg->e_shentsize;
+            UChar*      shdr_strtab_dimg = NULL;
+
+            Bool need_symtab, need_stabs, need_dwarf2, need_dwarf1;
+
+            if (phdr_dnent == 0
+                || !contained_within(
+                       dimage, n_dimage,
+                       (Addr)phdr_dimg, phdr_dnent * phdr_dent_szB)) {
+               ML_(symerr)(di, True,
+                           "Missing or invalid ELF Program Header Table"
+                           " (debuginfo file)");
+               goto out;
+            }
+
+            if (shdr_dnent == 0
+                || !contained_within(
+                       dimage, n_dimage,
+                       (Addr)shdr_dimg, shdr_dnent * shdr_dent_szB)) {
+               ML_(symerr)(di, True,
+                           "Missing or invalid ELF Section Header Table"
+                           " (debuginfo file)");
+               goto out;
+            }
+
+            /* Also find the section header's string table, and validate. */
+            /* checked previously by is_elf_object_file: */
+            vg_assert( ehdr_dimg->e_shstrndx != SHN_UNDEF );
+
+            shdr_strtab_dimg
+               = (UChar*)( ((UChar*)ehdr_dimg)
+                           + shdr_dimg[ehdr_dimg->e_shstrndx].sh_offset);
+            if (!contained_within( 
+                    dimage, n_dimage,
+                    (Addr)shdr_strtab_dimg,
+                    1/*bogus, but we don't know the real size*/ )) {
+               ML_(symerr)(di, True, 
+                           "Invalid ELF Section Header String Table"
+                           " (debuginfo file)");
+               goto out;
+            }
+
+            need_symtab = (NULL == symtab_img);
+            need_stabs  = (NULL == stab_img);
+            need_dwarf2 = (NULL == debug_info_img);
+            need_dwarf1 = (NULL == dwarf1d_img);
+
+            for (i = 0; i < ehdr_dimg->e_phnum; i++) {
+               ElfXX_Phdr* phdr 
+                  = INDEX_BIS( (void*)(dimage + ehdr_dimg->e_phoff), 
+                                          i, phdr_ent_szB );
+               if (phdr->p_type == PT_LOAD) {
+                  //offset_dimage = di->text_avma - phdr->p_vaddr;
+                  // FIXME: update di->text_bias at this point?
+                  // or can we assume the SVMAs in the debuginfo
+                  // file (hence, the biases) are the same as
+                  // established from the main file?
+                  break;
                }
+            }
 
-               debug_offset = offset_dimage;
-               if (need_symtab)
-                  symtab_offset = offset_dimage;
+            /* Same deal as previous FIND, except only do it for those
+               sections for which we didn't find anything useful in
+               the main file. */
 
-               shdr = (ElfXX_Shdr*)(dimage + ehdr->e_shoff);
-               sh_strtab = (UChar*)(dimage + shdr[ehdr->e_shstrndx].sh_offset);
+            /* Find all interesting sections */
+            for (i = 0; i < ehdr_dimg->e_shnum; i++) {
 
-               /* Same deal as previous FIND, except simpler - doesn't
-                  look for avma, only oimage address. */
-
-               /* Find all interesting sections */
-               for (i = 0; i < ehdr->e_shnum; i++) {
-
-#                 define FIND(condition, sec_name, sec_size, sec_filea)	\
+#              define FIND(condition, sec_name, sec_size, sec_img) \
+               do { ElfXX_Shdr* shdr \
+                       = INDEX_BIS( shdr_dimg, i, shdr_dent_szB ); \
                   if (condition \
-                      && 0 == VG_(strcmp)(sec_name, sh_strtab + shdr[i].sh_name)) { \
+                      && 0 == VG_(strcmp)(sec_name, \
+                                          shdr_strtab_dimg + shdr->sh_name)) { \
                      Bool nobits; \
-                     if (0 != sec_filea) \
+                     if (0 != sec_img) \
                         VG_(core_panic)("repeated section!\n"); \
-                     sec_filea = (void*)(dimage + shdr[i].sh_offset); \
-                     sec_size  = shdr[i].sh_size; \
-                     nobits = shdr[i].sh_type == SHT_NOBITS; \
-                     TRACE_SYMTAB( "%18s: filea %p .. %p\n", \
-                                   sec_name, (UChar*)sec_filea, \
-                                             ((UChar*)sec_filea) + sec_size - 1); \
+                     sec_img  = (void*)(dimage + shdr->sh_offset); \
+                     sec_size = shdr->sh_size; \
+                     nobits   = shdr->sh_type == SHT_NOBITS; \
+                     TRACE_SYMTAB( "%18s: dimg %p .. %p\n", \
+                                   sec_name, \
+                                   (UChar*)sec_img, \
+                                   ((UChar*)sec_img) + sec_size - 1); \
                      /* SHT_NOBITS sections have zero size in the file. */ \
-                     if ( shdr[i].sh_offset + (nobits ? 0 : sec_size) > n_dimage ) { \
-                        ML_(symerr)("   section beyond image end?!"); \
+                     if ( shdr->sh_offset \
+                          + (nobits ? 0 : sec_size) > n_dimage ) { \
+                        ML_(symerr)(di, True, \
+                                    "   section beyond image end?!"); \
                         goto out; \
                      } \
-                  }
+                  } \
+               } while (0);
 
-                  /*   ??           NAME             SIZE           IMAGE addr */
-                  FIND(need_symtab, ".symtab",       symtab_sz,     symtab_img)
-                  FIND(need_symtab, ".strtab",       strtab_sz,     strtab_img)
-                  FIND(need_stabs,  ".stab",         stab_sz,       stab_img)
-                  FIND(need_stabs,  ".stabstr",      stabstr_sz,    stabstr_img)
-                  FIND(need_dwarf2, ".debug_line",   debug_line_sz, debug_line_img)
-                  FIND(need_dwarf2, ".debug_info",   debug_info_sz, debug_info_img)
-                  FIND(need_dwarf2, ".debug_abbrev", debug_abbv_sz, debug_abbv_img)
-                  FIND(need_dwarf2, ".debug_str",    debug_str_sz,  debug_str_img)
-                  FIND(need_dwarf1, ".debug",        dwarf1d_sz,    dwarf1d_img)
-                  FIND(need_dwarf1, ".line",         dwarf1l_sz,    dwarf1l_img)
+               /* NEEDED?        NAME             SIZE           IMAGE addr */
+               FIND(need_symtab, ".symtab",       symtab_sz,     symtab_img)
+               FIND(need_symtab, ".strtab",       strtab_sz,     strtab_img)
+               FIND(need_stabs,  ".stab",         stab_sz,       stab_img)
+               FIND(need_stabs,  ".stabstr",      stabstr_sz,    stabstr_img)
+               FIND(need_dwarf2, ".debug_line",   debug_line_sz, debug_line_img)
+               FIND(need_dwarf2, ".debug_info",   debug_info_sz, debug_info_img)
+               FIND(need_dwarf2, ".debug_abbrev", debug_abbv_sz, debug_abbv_img)
+               FIND(need_dwarf2, ".debug_str",    debug_str_sz,  debug_str_img)
+               FIND(need_dwarf2, ".debug_ranges", debug_ranges_sz, 
+                                                               debug_ranges_img)
+               FIND(need_dwarf2, ".debug_loc",    debug_loc_sz,  debug_loc_img)
+               FIND(need_dwarf1, ".debug",        dwarf1d_sz,    dwarf1d_img)
+               FIND(need_dwarf1, ".line",         dwarf1l_sz,    dwarf1l_img)
 
-#                 undef FIND
-               }
+#              undef FIND
             }
          }
       }
@@ -1210,28 +1802,30 @@
 
       /* Read symbols */
       {
-         void (*read_elf_symtab)(struct _SegInfo*,UChar*,ElfXX_Sym*,
-                                 UInt,OffT,UChar*,UInt,UChar*,OffT);
+         void (*read_elf_symtab)(struct _DebugInfo*,UChar*,
+                                 ElfXX_Sym*,SizeT,
+                                 UChar*,SizeT,
+                                 UChar*);
 #        if defined(VGP_ppc64_linux)
          read_elf_symtab = read_elf_symtab__ppc64_linux;
 #        else
          read_elf_symtab = read_elf_symtab__normal;
 #        endif
-         read_elf_symtab(si, "symbol table",
-                         symtab_img, symtab_sz, symtab_offset,
+         read_elf_symtab(di, "symbol table",
+                         symtab_img, symtab_sz,
                          strtab_img, strtab_sz, 
-                         opd_filea_img, opd_offset);
+                         opd_img);
 
-         read_elf_symtab(si, "dynamic symbol table",
-                         dynsym_img, dynsym_sz, dynsym_offset,
+         read_elf_symtab(di, "dynamic symbol table",
+                         dynsym_img, dynsym_sz,
                          dynstr_img, dynstr_sz, 
-                         opd_filea_img, opd_offset);
+                         opd_img);
       }
 
       /* Read .eh_frame (call-frame-info) if any */
       if (ehframe_img) {
-         ML_(read_callframe_info_dwarf3)
-            ( si, ehframe_img, ehframe_sz, ehframe_avma );
+         vg_assert(ehframe_sz == di->ehframe_size);
+         ML_(read_callframe_info_dwarf3)( di, ehframe_img );
       }
 
       /* Read the stabs and/or dwarf2 debug information, if any.  It
@@ -1239,7 +1833,7 @@
          we ignore it. */
 #     if !defined(VGP_amd64_linux)
       if (stab_img && stabstr_img) {
-         ML_(read_debuginfo_stabs) ( si, debug_offset, stab_img, stab_sz, 
+         ML_(read_debuginfo_stabs) ( di, di->text_bias, stab_img, stab_sz, 
                                          stabstr_img, stabstr_sz );
       }
 #     endif
@@ -1250,14 +1844,32 @@
          before using it. */
       if (debug_info_img && debug_abbv_img && debug_line_img
                                            /* && debug_str_img */) {
-         ML_(read_debuginfo_dwarf2) ( si, debug_offset, 
-                                      debug_info_img,   debug_info_sz,
-                                      debug_abbv_img,
-                                      debug_line_img,   debug_line_sz,
-                                      debug_str_img );
+
+         /* The old reader: line numbers and unwind info only */
+         ML_(read_debuginfo_dwarf3) ( di,
+                                      debug_info_img, debug_info_sz,
+                                      debug_abbv_img, debug_abbv_sz,
+                                      debug_line_img, debug_line_sz,
+                                      debug_str_img,  debug_str_sz );
+
+         /* The new reader: read the DIEs in .debug_info to acquire
+            information on variable types and locations.  But only if
+            the tool asks for it, or the user requests it on the
+            command line. */
+         if (VG_(needs).var_info /* the tool requires it */
+             || VG_(clo_read_var_info) /* the user asked for it */) {
+            ML_(new_dwarf3_reader)(
+               di, debug_info_img,   debug_info_sz,
+                   debug_abbv_img,   debug_abbv_sz,
+                   debug_line_img,   debug_line_sz,
+                   debug_str_img,    debug_str_sz,
+                   debug_ranges_img, debug_ranges_sz,
+                   debug_loc_img,    debug_loc_sz
+            );
+         }
       }
       if (dwarf1d_img && dwarf1l_img) {
-         ML_(read_debuginfo_dwarf1) ( si, dwarf1d_img, dwarf1d_sz, 
+         ML_(read_debuginfo_dwarf1) ( di, dwarf1d_img, dwarf1d_sz, 
                                           dwarf1l_img, dwarf1l_sz );
       }
    }
@@ -1265,6 +1877,7 @@
 
   out: {
    SysRes m_res;
+
    /* Last, but not least, heave the image(s) back overboard. */
    if (dimage) {
       m_res = VG_(am_munmap_valgrind) ( dimage, n_dimage );
diff --git a/coregrind/m_debuginfo/readstabs.c b/coregrind/m_debuginfo/readstabs.c
index 82cc2ed..fb24ac9 100644
--- a/coregrind/m_debuginfo/readstabs.c
+++ b/coregrind/m_debuginfo/readstabs.c
@@ -38,8 +38,10 @@
 #include "pub_core_libcbase.h"
 #include "pub_core_libcassert.h"
 #include "pub_core_libcprint.h"
-#include "pub_core_mallocfree.h"
 #include "pub_core_xarray.h"
+#include "priv_misc.h"             /* dinfo_zalloc/free/strdup */
+#include "priv_tytypes.h"
+#include "priv_d3basics.h"
 #include "priv_storage.h"
 #include "priv_readstabs.h"        /* self */
 
@@ -78,7 +80,7 @@
 /* Read stabs-format debug info.  This is all rather horrible because
    stabs is a underspecified, kludgy hack.
 */
-void ML_(read_debuginfo_stabs) ( SegInfo* si,    OffT debug_offset,
+void ML_(read_debuginfo_stabs) ( DebugInfo* di,  OffT debug_offset,
                                  UChar* stabC,   Int stab_sz, 
                                  UChar* stabstr, Int stabstr_sz )
 {
@@ -119,7 +121,7 @@
       Finding the instruction address range covered by an N_SLINE is
       complicated;  see the N_SLINE case below.
    */
-   file.name     = ML_(addStr)(si,"???", -1);
+   file.name     = ML_(addStr)(di,"???", -1);
 
    n_stab_entries = stab_sz/(int)sizeof(struct nlist);
 
@@ -176,11 +178,11 @@
                   qbuflen = 16;
                while ((qidx + qlen) >= qbuflen)
                   qbuflen *= 2;
-               n = VG_(arena_malloc)(VG_AR_SYMTAB, qbuflen);
+               n = ML_(dinfo_zalloc)(qbuflen);
                VG_(memcpy)(n, qbuf, qidx);
                
                if (qbuf != NULL)
-                  VG_(arena_free)(VG_AR_SYMTAB, qbuf);
+                  ML_(dinfo_free)(qbuf);
                qbuf = n;
             }
 
@@ -206,8 +208,8 @@
 
          if (qbuf != NULL) {
             i--;                        /* overstepped */
-            string = ML_(addStr)(si, qbuf, qidx);
-            VG_(arena_free)(VG_AR_SYMTAB, qbuf);
+            string = ML_(addStr)(di, qbuf, qidx);
+            ML_(dinfo_free)(qbuf);
             if (contdebug)
                VG_(printf)("made composite: \"%s\"\n", string);
          }
@@ -253,7 +255,7 @@
 
             if (line.addr != 0) {
                /* finish off previous line */
-               ML_(addLineInfo)(si, file.name, NULL, line.addr,
+               ML_(addLineInfo)(di, file.name, NULL, line.addr,
                                 addr, line.no + line.ovf * LINENO_OVERFLOW, i);
             }
 
@@ -264,11 +266,11 @@
             line.no = 0;
 
             if (len > 0 && nm[len-1] != '/') {
-               file.name = ML_(addStr)(si, nm, -1);
+               file.name = ML_(addStr)(di, nm, -1);
                if (debug)
                   VG_(printf)("new source: %s\n", file.name);
             } else if (len == 0)
-               file.name = ML_(addStr)(si, "?1\0", -1);
+               file.name = ML_(addStr)(di, "?1\0", -1);
 
             break;
          }
@@ -278,7 +280,7 @@
 
             if (line.addr != 0) {
                /* there was a previous */
-               ML_(addLineInfo)(si, file.name, NULL, line.addr,
+               ML_(addLineInfo)(di, file.name, NULL, line.addr,
                                 addr, line.no + line.ovf * LINENO_OVERFLOW, i);
             }
 
@@ -340,7 +342,7 @@
             }
 
             if (line.addr) {
-               ML_(addLineInfo)(si, file.name, NULL, line.addr,
+               ML_(addLineInfo)(di, file.name, NULL, line.addr,
                                 addr, line.no + line.ovf * LINENO_OVERFLOW, i);
                line.addr = 0;
             }
diff --git a/coregrind/m_debuginfo/readxcoff.c b/coregrind/m_debuginfo/readxcoff.c
index 797fe46..80846fe 100644
--- a/coregrind/m_debuginfo/readxcoff.c
+++ b/coregrind/m_debuginfo/readxcoff.c
@@ -49,15 +49,16 @@
 
 #include "pub_core_basics.h"
 #include "pub_core_vki.h"          /* struct vki_stat et al */
-#include "pub_core_debuginfo.h"
 #include "pub_core_libcbase.h"
 #include "pub_core_libcassert.h"
 #include "pub_core_libcprint.h"
-#include "pub_core_mallocfree.h"
 #include "pub_core_libcfile.h"     /* stat, open, close */
 #include "pub_core_aspacemgr.h"    /* for mmaping debuginfo files */
 #include "pub_core_options.h"      /* VG_(clo_trace_symtab) */
 #include "pub_core_xarray.h"
+#include "priv_misc.h"
+#include "priv_tytypes.h"
+#include "priv_d3basics.h"
 #include "priv_storage.h"
 #include "priv_readxcoff.h"        /* self */
 
@@ -92,7 +93,7 @@
 
 #define SHOW_AR_DETAILS 0  /* show details of .a file internals */
 
-#define SHOW  VG_(clo_trace_symtab)
+#define SHOW  di->trace_symtab
 
 /* A small stack of filenames is maintained for dealing
    with BINCL/EINCL symbol table entries. */
@@ -493,19 +494,12 @@
    return False;
 }
 
-static void* malloc_AR_SYMTAB ( SizeT nbytes ) {
-   return VG_(arena_malloc)(VG_AR_SYMTAB, nbytes);
-}
-static void free_AR_SYMTAB ( void* ptr ) {
-   return VG_(arena_free)(VG_AR_SYMTAB, ptr);
-}
-
 /* Read symbol and line number info for the given text section.  (This
    is the central routine for XCOFF reading.)  Returns NULL on
    success, or the text of an error message otherwise. */
 static 
 HChar* read_symbol_table ( 
-          /*MOD*/SegInfo* si,
+          /*MOD*/struct _DebugInfo* di,
 
           /* location of symbol table */
           UChar* oi_symtab, UWord oi_nent_symtab,
@@ -524,8 +518,8 @@
           Int sndata_1based_if_known,
 
           /* where the mapped data section is */
-          Addr data_avma, 
-          UWord data_alen,
+          /* Now in di->data_avma:   Addr data_avma, */
+          /* Now in di->data_size:   UWord data_alen, */
           UWord data_alen_from_auxhdr,
 
 	  /* where the mapped toc is (in the data section,
@@ -546,7 +540,7 @@
 
    /* If the TOC avma is obviously bogus, get rid of it */
    { 
-     UWord data_maxlen = data_alen;
+     UWord data_maxlen = di->data_size;
      if (data_maxlen < data_alen_from_auxhdr)
         data_maxlen = data_alen_from_auxhdr;
 
@@ -555,7 +549,8 @@
      //VG_(printf)("dxxx_avma %p\n", data_avma + data_maxlen);
 
      if (toc_avma != 0
-         && (toc_avma < data_avma || toc_avma >= data_avma + data_maxlen))
+         && (toc_avma < di->data_avma 
+             || toc_avma >= di->data_avma + data_maxlen))
         toc_avma = 0;
      //VG_(printf)("2toc_avma %p\n", toc_avma);
    }
@@ -574,7 +569,8 @@
       add the rest to 'syms'.
       ---------------------------------------------------------- */
 
-   syms = VG_(newXA)( malloc_AR_SYMTAB, free_AR_SYMTAB, sizeof(XCoffSym) );
+   syms = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free), 
+                      sizeof(XCoffSym) );
 
    if (SHOW && SHOW_SYMS_P1) {
       VG_(printf)("--- BEGIN Phase1 (find text symbol starts) ---\n");
@@ -648,8 +644,8 @@
             cand.last += text_bias;
             cand.name = name;
 
-            if (cand.last < si->text_start_avma 
-                || cand.first >= si->text_start_avma+si->text_size)
+            if (cand.last < di->text_avma 
+                || cand.first >= di->text_avma + di->text_size)
                continue;
             if (cand.last < cand.first)
                continue;
@@ -703,8 +699,8 @@
             cand.last += text_bias;
             cand.name = name;
 
-            if (cand.last < si->text_start_avma 
-                || cand.first >= si->text_start_avma+si->text_size)
+            if (cand.last < di->text_avma 
+                || cand.first >= di->text_avma + di->text_size)
                continue;
             if (cand.last < cand.first)
                continue;
@@ -1212,7 +1208,7 @@
                harmless enough. */
             if ((!si_fname_str) && !is_empty_Name(p4currsym->fname)) {
                si_dname_str = NULL;
-               si_fname_str = ML_(addStr)(si, p4currsym->fname.vec,
+               si_fname_str = ML_(addStr)(di, p4currsym->fname.vec,
                                               p4currsym->fname.len);
                UChar* lastslash = VG_(strrchr)(si_fname_str, '/');
                if (lastslash)
@@ -1228,7 +1224,7 @@
             }
             /* finally .. */
 	    if (line_no >= 0)
-               ML_(addLineInfo)(si, si_fname_str, si_dname_str,
+               ML_(addLineInfo)(di, si_fname_str, si_dname_str,
                                 line_first_avma, line_last_avma+1,
                                 line_no, i/*debugging only*/);
          }
@@ -1312,8 +1308,8 @@
          the actual text segment.  Discard any that don't. */
 
       Addr fndescr_0 = (Addr)fndescr[0];
-      if (fndescr_0 < si->text_start_avma 
-          || fndescr_0 >= si->text_start_avma+si->text_size)
+      if (fndescr_0 < si->text_avma 
+          || fndescr_0 >= si->text_avma+si->text_size)
          continue;
 
       /* Let's suppose that fndescr is the descriptor for a
@@ -1376,20 +1372,20 @@
 
    if (SHOW)
       VG_(printf)("Phase5: actual data segment: %p %p\n",
-                  data_avma, data_avma + data_alen);
+                  di->data_avma, di->data_avma + di->data_size);
 
    /* Skip obviously-missing data sections. */
-   if (data_avma != 0 && data_alen >= sizeof(UWord)) {
+   if (di->data_avma != 0 && di->data_size >= sizeof(UWord)) {
 
       /* set up for inspecting all the aligned words in the actual
          data section. */
 
-      Addr tmp = (Addr)data_avma;
+      Addr tmp = di->data_avma;
       while (tmp & (sizeof(UWord)-1))
          tmp++;
 
       UWord* first_data_word = (UWord*)tmp;
-      tmp = data_avma + data_alen - sizeof(UWord);
+      tmp = di->data_avma + di->data_size - sizeof(UWord);
       while (tmp & (sizeof(UWord)-1))
          tmp--;
       UWord* last_data_word = (UWord*)tmp;
@@ -1411,14 +1407,14 @@
             break; /* no space left for a 3-word descriptor */
 
          w = wP[0];
-         if (!(w >= si->text_start_avma 
-               && w < si->text_start_avma+si->text_size)) {
+         if (!(w >= di->text_avma 
+               && w < di->text_avma + di->text_size)) {
             wP++;
             continue; /* entry pointer is not to text segment */
          }
 
          w = wP[1];
-         if (!(w >= data_avma && w < data_avma + data_alen)) {
+         if (!(w >= di->data_avma && w < di->data_avma + di->data_size)) {
             wP++;
             if (SHOW && SHOW_SYMS_P5) {
                VG_(memset)(&key, 0, sizeof(key));
@@ -1479,7 +1475,7 @@
    }
 
    for (i = 0; i < nsyms; i++) {
-      DiSym     di;
+      DiSym     dis;
       XCoffSym* s = (XCoffSym*)VG_(indexXA)(syms, i);
       Addr  addr = s->first;
       UWord size = s->last + 1 - s->first;
@@ -1487,8 +1483,8 @@
 
       /* If everything worked right, the symbol should fall within the
          mapped text segment.  Hence .. */
-      Bool  sane = addr >= si->text_start_avma 
-                   && addr+size <= si->text_start_avma + si->text_size;
+      Bool sane = addr >= di->text_avma 
+                  && addr+size <= di->text_avma + di->text_size;
 
       if (SHOW && SHOW_SYMS_P6) {
          VG_(printf)("Phase6: %s %3d  0x%08lx-0x%08lx  0x%08lx  ", 
@@ -1520,17 +1516,18 @@
       /* Actually add the symbol (finallyatlast) */
       if (sane) {
          UInt nlen;
-         di.addr   = addr;
-         di.size   = size;
-         di.tocptr = s->r2known ? s->r2value : 0;
+         dis.addr   = addr;
+         dis.size   = size;
+         dis.tocptr = s->r2known ? s->r2value : 0;
+         dis.isText = True;
          vg_assert(!is_empty_Name(s->name));
          nlen = s->name.len;
          vg_assert(nlen > 0);
          if (s->name.vec[0] == '.')
-            di.name = ML_(addStr)(si, &s->name.vec[1], nlen-1 );
+            dis.name = ML_(addStr)(di, &s->name.vec[1], nlen-1 );
          else
-            di.name = ML_(addStr)(si, &s->name.vec[0], nlen-0 );
-         ML_(addSym)( si, &di );
+            dis.name = ML_(addStr)(di, &s->name.vec[0], nlen-0 );
+         ML_(addSym)( di, &dis );
          if (0 && s->r2known)
             VG_(printf)("r2 known for %s\n", s->name);
 
@@ -1549,7 +1546,8 @@
 }
 
 
-static void show_loader_section ( UChar* oi_start, UWord size )
+static void show_loader_section ( struct _DebugInfo* di,
+                                  UChar* oi_start, UWord size )
 {
    Int i, j;
    LDHDR* hdr = (LDHDR*)oi_start;
@@ -1670,7 +1668,7 @@
    [oimage .. oimage + n_oimage).
 
    The VMA of where the relevant text section really got loaded (the
-   "actual VMA", _avma) is [si->text_start_avma .. si->text_start_avma
+   "actual VMA", _avma) is [si->text_avma .. si->text_avma
    + si->text_size).
 
    The VMA of the associated data section really got loaded
@@ -1683,11 +1681,11 @@
    we get here.
 */
 static 
-Bool read_xcoff_mapped_object ( SegInfo* si,
-                                UChar* oimage, UWord n_oimage,
-                                Addr data_avma, UWord data_alen )
+Bool read_xcoff_mapped_object ( struct _DebugInfo* di,
+                                UChar* oimage, UWord n_oimage )
 {
-#define BAD(_msg)  do { ML_(symerr)(_msg); return False; } while (0)
+#define BAD(_msg)  do { ML_(symerr)(di, True/*serious*/,_msg); \
+                        return False; } while (0)
 
    Int i, j;
 
@@ -1949,7 +1947,7 @@
       .o files.  These have a stated text VMA of zero, and so their
          symbols start from zero and work upwards.  In that case the
          bias is precisely the offset where the text section is 
-         loaded (si->text_start_avma), that is, the actual text VMA.
+         loaded (si->text_avma), that is, the actual text VMA.
 
          Except -- cryptically -- /usr/include/sys/ldr.h says that the
          ld_info.ldinfo_textorg field is "start of loaded program
@@ -1973,15 +1971,15 @@
    if (text_svma_known) {
 #if 0
       if (text_svma == 0) {
-         text_bias = si->text_start_avma;
+         text_bias = di->text_avma;
          if (sntext_1based_if_known >= 1 
              && sntext_1based_if_known <= t_filehdr->f_nscns)
             text_bias += t_scnhdr[sntext_1based_if_known - 1].s_scnptr;
       } else {
-         text_bias = si->text_start_avma - VG_PGROUNDDN(text_svma);
+         text_bias = di->text_avma - VG_PGROUNDDN(text_svma);
       }
 #else
-      text_bias = si->text_start_avma - text_svma;
+      text_bias = di->text_avma - text_svma;
       if (sntext_1based_if_known >= 1 
           && sntext_1based_if_known <= t_filehdr->f_nscns)
          text_bias += t_scnhdr[sntext_1based_if_known - 1].s_scnptr;
@@ -1990,7 +1988,7 @@
       if (SHOW)
          VG_(printf)("   text section: stated vma 0x%lx, "
                      "actual vma 0x%lx, bias 0x%lx\n", 
-                     text_svma, si->text_start_avma, text_bias);
+                     text_svma, di->text_avma, text_bias);
    } else {
       text_bias = 0;
       if (SHOW)
@@ -1998,11 +1996,11 @@
    }
 
    if (data_svma_known) {
-      data_bias = data_avma - data_svma;
+      data_bias = di->data_avma - data_svma;
       if (SHOW)
          VG_(printf)("   data section: stated vma 0x%lx, "
                      "actual vma 0x%lx, bias 0x%lx\n", 
-                     data_svma, data_avma, data_bias);
+                     data_svma, di->data_avma, data_bias);
    } else {
       data_bias = 0;
       if (SHOW)
@@ -2027,7 +2025,7 @@
                      i, name_of_scnhdr_s_flags(t_scnhdr[i].s_flags) );
       switch (t_scnhdr[i].s_flags & 0xFFFF) {
          case STYP_LOADER:
-            show_loader_section( oimage + t_scnhdr[i].s_scnptr, 
+            show_loader_section( di, oimage + t_scnhdr[i].s_scnptr, 
                                  t_scnhdr[i].s_size );
             break;
          default:
@@ -2077,13 +2075,13 @@
    cursor = oimage;
    cursor += t_filehdr->f_symptr;
    HChar* badness = read_symbol_table( 
-                       si,
+                       di,
                        cursor, t_filehdr->f_nsyms, 
                        oi_strtab, oi_n_strtab,
                        oi_debug, oi_n_debug,
                        oi_lnos,  oi_nent_lnos,
                        sntext_1based_if_known, sndata_1based_if_known,
-                       data_avma, data_alen, data_alen_from_auxhdr,
+                       data_alen_from_auxhdr,
                        toc_avma,
                        text_bias, data_bias 
                     );
@@ -2137,9 +2135,8 @@
 
 /* Returns True on success, False if any kind of problem. */
 static
-Bool read_xcoff_o_or_a ( /*MOD*/SegInfo* si,
-                         HChar* a_name, HChar* o_name,
-                         Addr data_avma, UWord data_alen )
+Bool read_xcoff_o_or_a ( /*MOD*/struct _DebugInfo* di,
+                         HChar* a_name, HChar* o_name )
 {
    UChar* image   = NULL;
    Word   n_image = 0;
@@ -2156,7 +2153,7 @@
 
       sr = VG_(stat)( o_name, &stat_buf );
       if (sr.isError) {
-         ML_(symerr)("can't stat XCOFF object file");
+         ML_(symerr)(di, True, "can't stat XCOFF object file");
          return False;
       }
 
@@ -2164,13 +2161,13 @@
       if (SHOW && SHOW_AR_DETAILS)
          VG_(printf)("XCOFF object file size %ld\n", n_image);
       if (n_image <= 0) {
-         ML_(symerr)("implausible XCOFF object file size");
+         ML_(symerr)(di, True, "implausible XCOFF object file size");
          return False;
       }
 
       fd = VG_(open)( o_name, VKI_O_RDONLY, 0 );
       if (fd.isError) {
-         ML_(symerr)("can't open XCOFF object file");
+         ML_(symerr)(di, True, "can't open XCOFF object file");
          return False;
       }
 
@@ -2179,13 +2176,12 @@
       VG_(close)(fd.res);
 
       if (sr.isError) {
-         ML_(symerr)("can't mmap XCOFF object file");
+         ML_(symerr)(di, True, "can't mmap XCOFF object file");
          return False;
       }
 
       image = (UChar*)sr.res;
-      ok = read_xcoff_mapped_object( si, image, n_image, 
-                                         data_avma, data_alen );
+      ok = read_xcoff_mapped_object( di, image, n_image );
       VG_(am_munmap_valgrind)( (Addr)image, n_image);
 
       /* assert OK */
@@ -2199,7 +2195,7 @@
 
       sr = VG_(stat)( a_name, &stat_buf );
       if (sr.isError) {
-         ML_(symerr)("can't stat XCOFF archive file");
+         ML_(symerr)(di, True, "can't stat XCOFF archive file");
          return False;
       }
 
@@ -2207,13 +2203,13 @@
       if (SHOW && SHOW_AR_DETAILS)
          VG_(printf)("XCOFF archive file size %ld\n", n_image);
       if (n_image <= 0) {
-         ML_(symerr)("implausible XCOFF archive file size");
+         ML_(symerr)(di, True, "implausible XCOFF archive file size");
          return False;
       }
 
       fd = VG_(open)( a_name, VKI_O_RDONLY, 0 );
       if (fd.isError) {
-         ML_(symerr)("can't open XCOFF archive file");
+         ML_(symerr)(di, True, "can't open XCOFF archive file");
          return False;
       }
 
@@ -2222,7 +2218,7 @@
       VG_(close)(fd.res);
 
       if (sr.isError) {
-         ML_(symerr)("can't mmap XCOFF archive file");
+         ML_(symerr)(di, True, "can't mmap XCOFF archive file");
          return False;
       }
 
@@ -2233,7 +2229,7 @@
          peer at the archive's fixed header. */
 
       if (n_image < sizeof(FL_HDR)) {
-         ML_(symerr)("XCOFF archive too small for fixed header");
+         ML_(symerr)(di, True, "XCOFF archive too small for fixed header");
          goto done;
       }
 
@@ -2251,7 +2247,8 @@
             && s[6] == '>' && s[7] == '\n') {
            /* ok */
         } else {
-           ML_(symerr)("Is not XCOFF 'big'-variant .a format archive");
+           ML_(symerr)(di, True, 
+                       "Is not XCOFF 'big'-variant .a format archive");
            goto done;
         }
       }
@@ -2262,14 +2259,16 @@
       AR_HDR* mt_hdr = (AR_HDR*)mtabC;
 
       if (mtabC < image || mtabC + sizeof(AR_HDR) > image + n_image) {
-         ML_(symerr)("XCOFF archive member table header exceeds image");
+         ML_(symerr)(di, True, 
+                     "XCOFF archive member table header exceeds image");
          goto done;
       }
 
       /* should be: backquote newline */
       if (mt_hdr->_ar_name.ar_name[0] != 0x60 /* backquote */
           || mt_hdr->_ar_name.ar_name[1] != 0x0A /* \n */) {
-         ML_(symerr)("XCOFF archive member table header is invalid");
+         ML_(symerr)(di, True, 
+                     "XCOFF archive member table header is invalid");
          goto done;
       }
 
@@ -2284,7 +2283,7 @@
           || mtabC + sizeof(AR_HDR) 
                    + ascii_to_ULong(&mt_hdr->ar_size, 20) 
              > image + n_image) {
-         ML_(symerr)("XCOFF archive member table exceeds image");
+         ML_(symerr)(di, True, "XCOFF archive member table exceeds image");
          goto done;
       }
 
@@ -2321,7 +2320,8 @@
          /* Sanity check the selected member */
          UChar* o_hdrC = image + objoff;
          if (o_hdrC + sizeof(AR_HDR) >= image + n_image) {
-            ML_(symerr)("XCOFF archive member header exceeds image");
+            ML_(symerr)(di, True, 
+                        "XCOFF archive member header exceeds image");
             goto done;
          }
          AR_HDR* o_hdr  = (AR_HDR*)o_hdrC;
@@ -2336,12 +2336,14 @@
             VG_(printf)("member data = %p, size = %ld\n", o_data, o_size);
 
          if (!(o_data >= image && o_data + o_size <= image + n_image)) {
-            ML_(symerr)("XCOFF archive member exceeds image");
+            ML_(symerr)(di, True, 
+                        "XCOFF archive member exceeds image");
             goto done;
          }
 
          if (o_size < sizeof(FILHDR)) {
-            ML_(symerr)("XCOFF object file header is implausibly small (1)");
+            ML_(symerr)(di, True, 
+                        "XCOFF object file header is implausibly small (1)");
 	    goto done;
 	 }
 
@@ -2366,8 +2368,7 @@
          if (SHOW && SHOW_AR_DETAILS)
             VG_(printf)("\nimage: %p-%p   object: %p-%p\n\n", 
                         image, image+n_image-1, o_data, o_data+o_size-1);
-         ok = read_xcoff_mapped_object( si, o_data, o_size,
-                                        data_avma, data_alen );
+         ok = read_xcoff_mapped_object( di, o_data, o_size );
          goto done;
 
          vg_assert(0);
@@ -2385,7 +2386,7 @@
       }
 
       vg_assert(i == nmembers);
-      ML_(symerr)("can't find object in XCOFF archive file");
+      ML_(symerr)(di, True, "can't find object in XCOFF archive file");
 
      done:
       if (image) {
@@ -2398,68 +2399,74 @@
 }
 
 
-Bool ML_(read_xcoff_debug_info) ( struct _SegInfo* si,
-                                  Addr   data_avma,
-                                  SSizeT data_alen,
-                                  Bool   is_mainexe )
+/* Main entry point for XCOFF reading.  The following di fields must
+   be filled in by the caller:
+
+     filename
+     memname (optional)
+     text_avma, text_size
+     data_avma, data_size
+
+   and all other fields should be zeroed.
+*/
+Bool ML_(read_xcoff_debug_info) ( struct _DebugInfo* di,
+                                  Bool is_mainexe )
 {
    Bool ok;
 
    if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir)) {
-      if (si->memname) {
+      if (di->memname) {
          VG_(message)(Vg_DebugMsg, "Reading syms from %s(%s) (%p)",
-                      si->filename, si->memname, si->text_start_avma);
+                      di->filename, di->memname, di->text_avma);
       } else {
          VG_(message)(Vg_DebugMsg, "Reading syms from %s (%p)",
-                      si->filename, si->text_start_avma);
+                      di->filename, di->text_avma);
       }
    }
 
    if (SHOW) {
       VG_(printf)("------------------- BEGIN read xcoff ------------------\n");
-      VG_(printf)("---         file: %s\n",  si->filename);
-      VG_(printf)("---          mem: %s\n",  si->memname ? si->memname  
+      VG_(printf)("---         file: %s\n",  di->filename);
+      VG_(printf)("---          mem: %s\n",  di->memname ? di->memname  
                                                          : (UChar*)"(none)" );
-      VG_(printf)("--- t actual vma: %p\n",  si->text_start_avma);
-      VG_(printf)("--- t actual len: %ld\n", si->text_size);
-      VG_(printf)("--- d actual vma: %p\n",  data_avma);
-      VG_(printf)("--- d actual len: %ld\n", data_alen);
+      VG_(printf)("--- t actual vma: %p\n",  di->text_avma);
+      VG_(printf)("--- t actual len: %ld\n", di->text_size);
+      VG_(printf)("--- d actual vma: %p\n",  di->data_avma);
+      VG_(printf)("--- d actual len: %ld\n", di->data_size);
    }
 
-   if (si->memname) {
-      /* XCOFF .a file.  si->filename is its name, si->memname is the
+   if (di->memname) {
+      /* XCOFF .a file.  di->filename is its name, di->memname is the
          name of the required .o within it. */
-      ok = read_xcoff_o_or_a( si, si->filename, si->memname,
-                                  data_avma, (UWord)data_alen );
+      ok = read_xcoff_o_or_a( di, di->filename, di->memname );
    } else {
-      /* no archive member name, so si->filename is an XCOFF object */
-      ok = read_xcoff_o_or_a( si, NULL, si->filename,
-                                  data_avma, (UWord)data_alen );
+      /* no archive member name, so di->filename is an XCOFF object */
+      ok = read_xcoff_o_or_a( di, NULL, di->filename );
    }
 
-   si->soname = NULL;
+   di->soname = NULL;
    if (ok) {
       if (is_mainexe) {
-         si->soname = "NONE";
+         di->soname = "NONE";
       } else {
-         UChar* p = VG_(strrchr)(si->filename, '/');
-         p = p  ? p+1  : si->filename;
+         UChar* p = VG_(strrchr)(di->filename, '/');
+         p = p  ? p+1  : di->filename;
          /* p points at the main filename */
-         if (si->memname) {
+         if (di->memname) {
             /* set the soname to "archive.a(member.o)" */
-            Int nbytes = VG_(strlen)(p) + 1 + VG_(strlen)(si->memname) + 1 + 1;
-            UChar* so = malloc_AR_SYMTAB(nbytes);
+            Int nbytes = VG_(strlen)(p) + 1 + VG_(strlen)(di->memname) + 1 + 1;
+            UChar* so = ML_(dinfo_zalloc)(nbytes);
             vg_assert(so);
-            VG_(sprintf)(so, "%s(%s)", p, si->memname);
+            VG_(sprintf)(so, "%s(%s)", p, di->memname);
             vg_assert(VG_(strlen)(so) == nbytes-1);
-            si->soname = so;
+            di->soname = so;
          } else {
             /* no member name, hence soname = "archive.a" */
-            si->soname = VG_(arena_strdup)(VG_AR_SYMTAB, p);
+            di->soname = ML_(dinfo_strdup)(p);
          }
       }
       if (SHOW)
-         VG_(printf)("Setting soname to %s\n", si->soname);
+         VG_(printf)("Setting soname to %s\n", di->soname);
    }
 
    if (SHOW)
diff --git a/coregrind/m_debuginfo/storage.c b/coregrind/m_debuginfo/storage.c
index f6c5e0a..8edf1b4 100644
--- a/coregrind/m_debuginfo/storage.c
+++ b/coregrind/m_debuginfo/storage.c
@@ -42,9 +42,13 @@
 #include "pub_core_libcassert.h"
 #include "pub_core_libcbase.h"
 #include "pub_core_libcprint.h"
-#include "pub_core_mallocfree.h"
 #include "pub_core_xarray.h"
-#include "priv_storage.h"          /* self */
+#include "pub_core_oset.h"
+
+#include "priv_misc.h"         /* dinfo_zalloc/free/strdup */
+#include "priv_d3basics.h"     /* ML_(pp_GX) */
+#include "priv_tytypes.h"
+#include "priv_storage.h"      /* self */
 
 
 /*------------------------------------------------------------*/
@@ -52,13 +56,36 @@
 /*------------------------------------------------------------*/
 
 /* Show a non-fatal debug info reading error.  Use vg_panic if
-   terminal. */
-void ML_(symerr) ( HChar* msg )
+   terminal.  'serious' errors are shown regardless of the
+   verbosity setting. */
+void ML_(symerr) ( struct _DebugInfo* di, Bool serious, HChar* msg )
 {
-   if (VG_(clo_verbosity) > 1)
+   /* XML mode hides everything :-( */
+   if (VG_(clo_xml))
+      return;
+
+   if (serious) {
+
+      VG_(message)(Vg_DebugMsg, "WARNING: Serious error when "
+                                "reading debug info");
+      if (True || VG_(clo_verbosity) < 2) {
+         /* Need to show what the file name is, at verbosity levels 2
+            or below, since that won't already have been shown */
+         VG_(message)(Vg_DebugMsg, 
+                      "When reading debug info from %s:",
+                      (di && di->filename) ? di->filename : (UChar*)"???");
+      }
       VG_(message)(Vg_DebugMsg, "%s", msg);
+
+   } else { /* !serious */
+
+      if (VG_(clo_verbosity) >= 2)
+         VG_(message)(Vg_DebugMsg, "%s", msg);
+
+   }
 }
 
+
 /* Print a symbol. */
 void ML_(ppSym) ( Int idx, DiSym* sym )
 {
@@ -137,27 +164,30 @@
    a chunking memory allocator rather than reallocating, so the
    pointers are stable.
 */
-UChar* ML_(addStr) ( struct _SegInfo* si, UChar* str, Int len )
+UChar* ML_(addStr) ( struct _DebugInfo* di, UChar* str, Int len )
 {
    struct strchunk *chunk;
    Int    space_needed;
    UChar* p;
 
-   if (len == -1)
+   if (len == -1) {
       len = VG_(strlen)(str);
+   } else {
+      vg_assert(len >= 0);
+   }
 
    space_needed = 1 + len;
 
    // Allocate a new strtab chunk if necessary
-   if (si->strchunks == NULL || 
-       (si->strchunks->strtab_used 
+   if (di->strchunks == NULL || 
+       (di->strchunks->strtab_used 
         + space_needed) > SEGINFO_STRCHUNKSIZE) {
-      chunk = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*chunk));
+      chunk = ML_(dinfo_zalloc)(sizeof(*chunk));
       chunk->strtab_used = 0;
-      chunk->next = si->strchunks;
-      si->strchunks = chunk;
+      chunk->next = di->strchunks;
+      di->strchunks = chunk;
    }
-   chunk = si->strchunks;
+   chunk = di->strchunks;
 
    p = &chunk->strtab[chunk->strtab_used];
    VG_(memcpy)(p, str, len);
@@ -170,7 +200,7 @@
 
 /* Add a symbol to the symbol table. 
 */
-void ML_(addSym) ( struct _SegInfo* si, DiSym* sym )
+void ML_(addSym) ( struct _DebugInfo* di, DiSym* sym )
 {
    UInt   new_sz, i;
    DiSym* new_tab;
@@ -178,28 +208,28 @@
    /* Ignore zero-sized syms. */
    if (sym->size == 0) return;
 
-   if (si->symtab_used == si->symtab_size) {
-      new_sz = 2 * si->symtab_size;
+   if (di->symtab_used == di->symtab_size) {
+      new_sz = 2 * di->symtab_size;
       if (new_sz == 0) new_sz = 500;
-      new_tab = VG_(arena_malloc)(VG_AR_SYMTAB, new_sz * sizeof(DiSym) );
-      if (si->symtab != NULL) {
-         for (i = 0; i < si->symtab_used; i++)
-            new_tab[i] = si->symtab[i];
-         VG_(arena_free)(VG_AR_SYMTAB, si->symtab);
+      new_tab = ML_(dinfo_zalloc)( new_sz * sizeof(DiSym) );
+      if (di->symtab != NULL) {
+         for (i = 0; i < di->symtab_used; i++)
+            new_tab[i] = di->symtab[i];
+         ML_(dinfo_free)(di->symtab);
       }
-      si->symtab = new_tab;
-      si->symtab_size = new_sz;
+      di->symtab = new_tab;
+      di->symtab_size = new_sz;
    }
 
-   si->symtab[si->symtab_used] = *sym;
-   si->symtab_used++;
-   vg_assert(si->symtab_used <= si->symtab_size);
+   di->symtab[di->symtab_used] = *sym;
+   di->symtab_used++;
+   vg_assert(di->symtab_used <= di->symtab_size);
 }
 
 
 /* Add a location to the location table. 
 */
-static void addLoc ( struct _SegInfo* si, DiLoc* loc )
+static void addLoc ( struct _DebugInfo* di, DiLoc* loc )
 {
    UInt   new_sz, i;
    DiLoc* new_tab;
@@ -207,28 +237,28 @@
    /* Zero-sized locs should have been ignored earlier */
    vg_assert(loc->size > 0);
 
-   if (si->loctab_used == si->loctab_size) {
-      new_sz = 2 * si->loctab_size;
+   if (di->loctab_used == di->loctab_size) {
+      new_sz = 2 * di->loctab_size;
       if (new_sz == 0) new_sz = 500;
-      new_tab = VG_(arena_malloc)(VG_AR_SYMTAB, new_sz * sizeof(DiLoc) );
-      if (si->loctab != NULL) {
-         for (i = 0; i < si->loctab_used; i++)
-            new_tab[i] = si->loctab[i];
-         VG_(arena_free)(VG_AR_SYMTAB, si->loctab);
+      new_tab = ML_(dinfo_zalloc)( new_sz * sizeof(DiLoc) );
+      if (di->loctab != NULL) {
+         for (i = 0; i < di->loctab_used; i++)
+            new_tab[i] = di->loctab[i];
+         ML_(dinfo_free)(di->loctab);
       }
-      si->loctab = new_tab;
-      si->loctab_size = new_sz;
+      di->loctab = new_tab;
+      di->loctab_size = new_sz;
    }
 
-   si->loctab[si->loctab_used] = *loc;
-   si->loctab_used++;
-   vg_assert(si->loctab_used <= si->loctab_size);
+   di->loctab[di->loctab_used] = *loc;
+   di->loctab_used++;
+   vg_assert(di->loctab_used <= di->loctab_size);
 }
 
 
 /* Top-level place to call to add a source-location mapping entry.
 */
-void ML_(addLineInfo) ( struct _SegInfo* si,
+void ML_(addLineInfo) ( struct _DebugInfo* di,
                         UChar*   filename,
                         UChar*   dirname, /* NULL == directory is unknown */
                         Addr     this,
@@ -273,16 +303,18 @@
        size = 1;
    }
 
-   /* vg_assert(this < si->text_start_avma + si->size 
-                && next-1 >= si->text_start_avma); */
-   if (this >= si->text_start_avma + si->text_size 
-       || next-1 < si->text_start_avma) {
+   /* Rule out ones which are completely outside the r-x mapped area.
+      See "Comment_Regarding_Text_Range_Checks" elsewhere in this file
+      for background and rationale. */
+   vg_assert(di->have_rx_map && di->have_rw_map);
+   if (next-1 < di->rx_map_avma
+       || this >= di->rx_map_avma + di->rx_map_size ) {
        if (0)
           VG_(message)(Vg_DebugMsg, 
                        "warning: ignoring line info entry falling "
-                       "outside current SegInfo: %p %p %p %p",
-                       si->text_start_avma, 
-                       si->text_start_avma + si->text_size, 
+                       "outside current DebugInfo: %p %p %p %p",
+                       di->text_avma, 
+                       di->text_avma + di->text_size, 
                        this, next-1);
        return;
    }
@@ -314,13 +346,13 @@
 		       "addLoc: addr %p, size %d, line %d, file %s",
 		       this,size,lineno,filename);
 
-   addLoc ( si, &loc );
+   addLoc ( di, &loc );
 }
 
 
 /* Top-level place to call to add a CFI summary record.  The supplied
    DiCfSI is copied. */
-void ML_(addDiCfSI) ( struct _SegInfo* si, DiCfSI* cfsi )
+void ML_(addDiCfSI) ( struct _DebugInfo* di, DiCfSI* cfsi )
 {
    static const Bool debug = False;
    UInt    new_sz, i;
@@ -328,7 +360,7 @@
 
    if (debug) {
       VG_(printf)("adding DiCfSI: ");
-      ML_(ppDiCfSI)(si->cfsi_exprs, cfsi);
+      ML_(ppDiCfSI)(di->cfsi_exprs, cfsi);
    }
 
    /* sanity */
@@ -339,12 +371,13 @@
       broken. */
    vg_assert(cfsi->len < 5000000);
 
-   /* Rule out ones which are completely outside the segment.  These
-      probably indicate some kind of bug, but for the meantime ignore
-      them. */
-   if ( cfsi->base + cfsi->len - 1  <  si->text_start_avma
-        || si->text_start_avma + si->text_size - 1  <  cfsi->base ) {
-      static Int complaints = 3;
+   /* Rule out ones which are completely outside the r-x mapped area.
+      See "Comment_Regarding_Text_Range_Checks" elsewhere in this file
+      for background and rationale. */
+   vg_assert(di->have_rx_map && di->have_rw_map);
+   if (cfsi->base + cfsi->len - 1 < di->rx_map_avma
+       || cfsi->base >= di->rx_map_avma + di->rx_map_size) {
+      static Int complaints = 10;
       if (VG_(clo_trace_cfi) || complaints > 0) {
          complaints--;
          if (VG_(clo_verbosity) > 1) {
@@ -353,32 +386,32 @@
                "warning: DiCfSI %p .. %p outside segment %p .. %p",
                cfsi->base, 
                cfsi->base + cfsi->len - 1,
-               si->text_start_avma,
-               si->text_start_avma + si->text_size - 1 
+               di->text_avma,
+               di->text_avma + di->text_size - 1 
             );
          }
          if (VG_(clo_trace_cfi)) 
-            ML_(ppDiCfSI)(si->cfsi_exprs, cfsi);
+            ML_(ppDiCfSI)(di->cfsi_exprs, cfsi);
       }
       return;
    }
 
-   if (si->cfsi_used == si->cfsi_size) {
-      new_sz = 2 * si->cfsi_size;
+   if (di->cfsi_used == di->cfsi_size) {
+      new_sz = 2 * di->cfsi_size;
       if (new_sz == 0) new_sz = 20;
-      new_tab = VG_(arena_malloc)(VG_AR_SYMTAB, new_sz * sizeof(DiCfSI) );
-      if (si->cfsi != NULL) {
-         for (i = 0; i < si->cfsi_used; i++)
-            new_tab[i] = si->cfsi[i];
-         VG_(arena_free)(VG_AR_SYMTAB, si->cfsi);
+      new_tab = ML_(dinfo_zalloc)( new_sz * sizeof(DiCfSI) );
+      if (di->cfsi != NULL) {
+         for (i = 0; i < di->cfsi_used; i++)
+            new_tab[i] = di->cfsi[i];
+         ML_(dinfo_free)(di->cfsi);
       }
-      si->cfsi = new_tab;
-      si->cfsi_size = new_sz;
+      di->cfsi = new_tab;
+      di->cfsi_size = new_sz;
    }
 
-   si->cfsi[si->cfsi_used] = *cfsi;
-   si->cfsi_used++;
-   vg_assert(si->cfsi_used <= si->cfsi_size);
+   di->cfsi[di->cfsi_used] = *cfsi;
+   di->cfsi_used++;
+   vg_assert(di->cfsi_used <= di->cfsi_size);
 }
 
 
@@ -493,6 +526,397 @@
 }
 
 
+Word ML_(cmp_for_DiAddrRange_range) ( const void* keyV,
+                                      const void* elemV ) {
+   const Addr* key = (const Addr*)keyV;
+   const DiAddrRange* elem = (const DiAddrRange*)elemV;
+   if (0)
+      VG_(printf)("cmp_for_DiAddrRange_range: %p vs %p\n",
+                  *key, elem->aMin);
+   if ((*key) < elem->aMin) return -1;
+   if ((*key) > elem->aMax) return 1;
+   return 0;
+}
+
+static
+void show_scope ( OSet* /* of DiAddrRange */ scope, HChar* who )
+{
+   DiAddrRange* range;
+   VG_(printf)("Scope \"%s\" = {\n", who);
+   VG_(OSetGen_ResetIter)( scope );
+   while (True) {
+      range = VG_(OSetGen_Next)( scope );
+      if (!range) break;
+      VG_(printf)("   %p .. %p: %lu vars\n", range->aMin, range->aMax,
+                  range->vars ? VG_(sizeXA)(range->vars) : 0);
+   }
+   VG_(printf)("}\n");
+}
+
+/* Add the variable 'var' to 'scope' for the address range [aMin,aMax]
+   (inclusive of aMin and aMax).  Split existing ranges as required if
+   aMin or aMax or both don't match existing range boundaries, and add
+   'var' to all required ranges.  Take great care to preserve the
+   invariant that the ranges in 'scope' cover the entire address range
+   exactly once, with no overlaps and no holes. */
+static void add_var_to_arange ( 
+               /*MOD*/OSet* /* of DiAddrRange */ scope,
+               Addr aMin, 
+               Addr aMax,
+               DiVariable* var
+            )
+{
+   DiAddrRange *first, *last, *range;
+   /* These xx variables are for assertion checking only; they don't
+      contribute anything to the actual work of this function. */
+   DiAddrRange *xxRangep, *xxFirst, *xxLast;
+   UWord       xxIters;
+
+   vg_assert(aMin <= aMax);
+
+   if (0) VG_(printf)("add_var_to_arange: %p .. %p\n", aMin, aMax);
+   if (0) show_scope( scope, "add_var_to_arange(1)" );
+
+   /* See if the lower end of the range (aMin) falls exactly on an
+      existing range boundary.  If not, find the range it does fall
+      into, and split it (copying the variables in the process), so
+      that aMin does exactly fall on a range boundary. */
+   first = VG_(OSetGen_Lookup)( scope, &aMin );
+   /* It must be present, since the presented OSet must cover
+      the entire address range. */
+   vg_assert(first);
+   vg_assert(first->aMin <= first->aMax);
+   vg_assert(first->aMin <= aMin && aMin <= first->aMax);
+
+   /* Fast track common case, which is that the range specified for
+      the variable exactly coincides with one already-existing
+      range. */
+   if (first->aMin == aMin && first->aMax == aMax) {
+      vg_assert(first->vars);
+      VG_(addToXA)( first->vars, var );
+      return;
+   }
+
+   /* We have to get into splitting ranges, which is complex
+      and slow. */
+   if (first->aMin < aMin) {
+      DiAddrRange* nyu;
+      /* Ok.  We'll have to split 'first'. */
+      /* truncate the upper end of 'first' */
+      Addr tmp = first->aMax;
+      first->aMax = aMin-1;
+      vg_assert(first->aMin <= first->aMax);
+      /* create a new range */
+      nyu = VG_(OSetGen_AllocNode)( scope, sizeof(DiAddrRange) );
+      vg_assert(nyu);
+      nyu->aMin = aMin;
+      nyu->aMax = tmp;
+      vg_assert(nyu->aMin <= nyu->aMax);
+      /* copy vars into it */
+      vg_assert(first->vars);
+      nyu->vars = VG_(cloneXA)( first->vars );
+      vg_assert(nyu->vars);
+      VG_(OSetGen_Insert)( scope, nyu );
+      first = nyu;
+   }
+
+   vg_assert(first->aMin == aMin);
+
+   /* Now do exactly the same for the upper end (aMax): if it doesn't
+      fall on a boundary, cause it to do so by splitting the range it
+      does currently fall into. */
+   last = VG_(OSetGen_Lookup)( scope, &aMax );
+   vg_assert(last->aMin <= last->aMax);
+   vg_assert(last->aMin <= aMax && aMax <= last->aMax);
+
+   if (aMax < last->aMax) {
+      DiAddrRange* nyu;
+      /* We have to split 'last'. */
+      /* truncate the lower end of 'last' */
+      Addr tmp = last->aMin;
+      last->aMin = aMax+1;
+      vg_assert(last->aMin <= last->aMax);
+      /* create a new range */
+      nyu = VG_(OSetGen_AllocNode)( scope, sizeof(DiAddrRange) );
+      vg_assert(nyu);
+      nyu->aMin = tmp;
+      nyu->aMax = aMax;
+      vg_assert(nyu->aMin <= nyu->aMax);
+      /* copy vars into it */
+      vg_assert(last->vars);
+      nyu->vars = VG_(cloneXA)( last->vars );
+      vg_assert(nyu->vars);
+      VG_(OSetGen_Insert)( scope, nyu );
+      last = nyu;
+   }
+
+   vg_assert(aMax == last->aMax);
+
+   xxFirst = (DiAddrRange*)VG_(OSetGen_Lookup)(scope, &aMin);
+   xxLast  = (DiAddrRange*)VG_(OSetGen_Lookup)(scope, &aMax);
+   vg_assert(xxFirst);
+   vg_assert(xxLast);
+   vg_assert(xxFirst->aMin == aMin);
+   vg_assert(xxLast->aMax == aMax);
+   if (xxFirst != xxLast)
+      vg_assert(xxFirst->aMax < xxLast->aMin);
+
+   /* Great.  Now we merely need to iterate over the segments from
+      'first' to 'last' inclusive, and add 'var' to the variable set
+      of each of them. */
+   if (0) {
+      static UWord ctr = 0;
+      ctr++;
+      VG_(printf)("ctr = %lu\n", ctr);
+      if (ctr >= 33263) show_scope( scope, "add_var_to_arange(2)" );
+   }
+
+   xxIters = 0;
+   range = xxRangep = NULL;
+   VG_(OSetGen_ResetIterAt)( scope, &aMin );
+   while (True) {
+      xxRangep = range;
+      range    = VG_(OSetGen_Next)( scope );
+      if (!range) break;
+      if (range->aMin > aMax) break;
+      xxIters++;
+      if (0) VG_(printf)("have range %p %p\n", 
+                         range->aMin, range->aMax);
+
+      /* Sanity checks */
+      if (!xxRangep) {
+         /* This is the first in the range */
+         vg_assert(range->aMin == aMin);
+      } else {
+         vg_assert(xxRangep->aMax + 1 == range->aMin);
+      }
+
+      vg_assert(range->vars);
+      VG_(addToXA)( range->vars, var );
+   }
+   /* Done.  We should have seen at least one range. */
+   vg_assert(xxIters >= 1);
+   if (xxIters == 1) vg_assert(xxFirst == xxLast);
+   if (xxFirst == xxLast) vg_assert(xxIters == 1);
+   vg_assert(xxRangep);
+   vg_assert(xxRangep->aMax == aMax);
+   vg_assert(xxRangep == xxLast);
+}
+
+
+/* Top-level place to call to add a variable description (as extracted
+   from a DWARF3 .debug_info section. */
+void ML_(addVar)( struct _DebugInfo* di,
+                  Int    level,
+                  Addr   aMin,
+                  Addr   aMax,
+                  UChar* name, /* in di's .strchunks */
+                  Type*  type,
+                  GExpr* gexpr,
+                  GExpr* fbGX,
+                  UChar* fileName, /* where decl'd - may be NULL.
+                                      in di's .strchunks */
+                  Int    lineNo, /* where decl'd - may be zero */
+                  Bool   show )
+{
+   OSet* /* of DiAddrRange */ scope;
+   DiVariable var;
+   Bool       all;
+
+   if (0) {
+      VG_(printf)("  ML_(addVar): level %d  %p-%p  %s :: ",
+                  level, aMin, aMax, name );
+      ML_(pp_Type_C_ishly)( type );
+      VG_(printf)("\n  Var=");
+      ML_(pp_GX)(gexpr);
+      VG_(printf)("\n");
+      if (fbGX) {
+         VG_(printf)("  FrB=");
+         ML_(pp_GX)( fbGX );
+         VG_(printf)("\n");
+      } else {
+         VG_(printf)("  FrB=none\n");
+      }
+      VG_(printf)("\n");
+   }
+
+   vg_assert(level >= 0);
+   vg_assert(aMin <= aMax);
+   vg_assert(name);
+   vg_assert(type);
+   vg_assert(gexpr);
+
+   /* "Comment_Regarding_Text_Range_Checks" (is referred to elsewhere)
+      ----------------------------------------------------------------
+      Ignore any variables whose aMin .. aMax (that is, range of text
+      addresses for which they actually exist) falls outside the text
+      segment.  Is this indicative of a bug in the reader?  Maybe.
+      (LATER): instead of restricting strictly to the .text segment,
+      be a bit more relaxed, and accept any variable whose text range
+      falls inside the r-x mapped area.  This is useful because .text
+      is not always the only instruction-carrying segment: others are:
+      .init .plt __libc_freeres_fn and .fini.  This implicitly assumes
+      that those extra sections have the same bias as .text, but that
+      seems a reasonable assumption to me. */
+   /* This is assured us by top level steering logic in debuginfo.c,
+      and it is re-checked at the start of
+      ML_(read_elf_debug_info). */
+   vg_assert(di->have_rx_map && di->have_rw_map);
+   if (level > 0
+       && (aMax < di->rx_map_avma
+           || aMin >= di->rx_map_avma + di->rx_map_size)) {
+      if (VG_(clo_verbosity) >= 0) {
+         VG_(message)(Vg_DebugMsg, 
+            "warning: addVar: in range %p .. %p outside "
+            "segment %p .. %p (%s)",
+            aMin, aMax,
+            di->text_avma, di->text_avma + di->text_size -1,
+            name
+         );
+      }
+      return;
+   }
+
+   /* If the type's size is zero (which can mean unknown size), ignore
+      it.  We will never be able to actually relate a data address to
+      a data object with zero size, so there's no point in storing
+      info on it. */
+   if (ML_(sizeOfType)(type).b != True) {
+      static Int complaints = 10;
+      if (VG_(clo_verbosity) >= 2 && complaints > 0) {
+         VG_(message)(Vg_DebugMsg, 
+            "warning: addVar: unknown size (%s)",
+            name
+         );
+         complaints--;
+      }
+      return;
+   }
+
+   if (!di->varinfo) {
+      di->varinfo = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                                sizeof(OSet*) );
+   }
+
+   vg_assert(level < 256); /* arbitrary; stay sane */
+   /* Expand the top level array enough to map this level */
+   while ( VG_(sizeXA)(di->varinfo) <= level ) {
+      DiAddrRange* nyu;
+      scope = VG_(OSetGen_Create)( offsetof(DiAddrRange,aMin), 
+                                   ML_(cmp_for_DiAddrRange_range),
+                                   ML_(dinfo_zalloc), ML_(dinfo_free) );
+      vg_assert(scope);
+      if (0) VG_(printf)("create: scope = %p, adding at %ld\n",
+                         scope, VG_(sizeXA)(di->varinfo));
+      VG_(addToXA)( di->varinfo, &scope );
+      /* Add a single range covering the entire address space.  At
+         level 0 we require this doesn't get split.  At levels above 0
+         we require that any additions to it cause it to get split.
+         All of these invariants get checked both add_var_to_arange
+         and after reading is complete, in canonicaliseVarInfo. */
+      nyu = VG_(OSetGen_AllocNode)( scope, sizeof(DiAddrRange) );
+      vg_assert(nyu);
+      nyu->aMin = (Addr)0;
+      nyu->aMax = ~(Addr)0;
+      nyu->vars = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                              sizeof(DiVariable) );
+      vg_assert(nyu->vars);
+      VG_(OSetGen_Insert)( scope, nyu );
+   }
+
+   vg_assert( VG_(sizeXA)(di->varinfo) > level );
+   scope = *(OSet**)VG_(indexXA)( di->varinfo, level );
+   vg_assert(scope);
+
+   var.name     = name;
+   var.type     = type;
+   var.gexpr    = gexpr;
+   var.fbGX     = fbGX;
+   var.fileName = fileName;
+   var.lineNo   = lineNo;
+
+   all = aMin == (Addr)0 && aMax == ~(Addr)0;
+   vg_assert(level == 0 ? all : !all);
+
+   add_var_to_arange( /*MOD*/scope, aMin, aMax, &var );
+}
+
+
+/* This really just checks the constructed data structure, as there is
+   no canonicalisation to do. */
+static void canonicaliseVarInfo ( struct _DebugInfo* di )
+{
+   Word i, nInThisScope;
+
+   if (!di->varinfo)
+      return;
+
+   for (i = 0; i < VG_(sizeXA)(di->varinfo); i++) {
+
+      DiAddrRange *range, *rangep;
+      OSet* scope = *(OSet**)VG_(indexXA)(di->varinfo, i);
+      if (!scope) continue;
+
+      /* Deal with the global-scope case. */
+      if (i == 0) {
+         Addr zero = 0;
+         vg_assert(VG_(OSetGen_Size)( scope ) == 1);
+         range = VG_(OSetGen_Lookup)( scope, &zero );
+         vg_assert(range);
+         vg_assert(range->aMin == (Addr)0);
+         vg_assert(range->aMax == ~(Addr)0);
+         continue;
+      }
+
+      /* All the rest of this is for the local-scope case. */
+      /* iterate over all entries in 'scope' */
+      nInThisScope = 0;
+      range = rangep = NULL;
+      VG_(OSetGen_ResetIter)(scope);
+      while (True) {
+         range = VG_(OSetGen_Next)(scope);
+         if (!range) {
+           /* We just saw the last one.  There must have been at
+              least one entry in the range. */
+           vg_assert(rangep);
+           vg_assert(rangep->aMax == ~(Addr)0);
+           break;
+         }
+
+         vg_assert(range->aMin <= range->aMax);
+         vg_assert(range->vars);
+
+         if (!rangep) {
+           /* This is the first entry in the range. */
+           vg_assert(range->aMin == 0);
+         } else {
+           vg_assert(rangep->aMax + 1 == range->aMin);
+         }
+
+         rangep = range;
+         nInThisScope++;
+      } /* iterating over ranges in a given scope */
+
+      /* If there's only one entry in this (local) scope, it must
+         cover the entire address space (obviously), but it must not
+         contain any vars. */
+
+      vg_assert(nInThisScope > 0);
+      if (nInThisScope == 1) {
+         Addr zero = 0;
+         vg_assert(VG_(OSetGen_Size)( scope ) == 1);
+         range = VG_(OSetGen_Lookup)( scope, &zero );
+         vg_assert(range);
+         vg_assert(range->aMin == (Addr)0);
+         vg_assert(range->aMax == ~(Addr)0);
+         vg_assert(range->vars);
+         vg_assert(VG_(sizeXA)(range->vars) == 0);
+      }
+
+   } /* iterate over scopes */
+}
+
+
 /*------------------------------------------------------------*/
 /*--- Canonicalisers                                       ---*/
 /*------------------------------------------------------------*/
@@ -517,7 +941,7 @@
 /* Two symbols have the same address.  Which name do we prefer?
 
    The general rule is to prefer the shorter symbol name.  If the
-   symbol contains a '@', which means its versioned, then the length
+   symbol contains a '@', which means it is versioned, then the length
    up to the '@' is used for length comparison purposes (so
    "foo@GLIBC_2.4.2" is considered shorter than "foobar"), but if two
    symbols have the same length, the one with the version string is
@@ -528,12 +952,18 @@
    so we can misdescribe memcmp() as bcmp()).  This is hard to avoid.
    It's mentioned in the FAQ file.
  */
-static DiSym* prefersym ( struct _SegInfo* si, DiSym* a, DiSym* b )
+static DiSym* prefersym ( struct _DebugInfo* di, DiSym* a, DiSym* b )
 {
+   Int cmp;
    Int lena, lenb;		/* full length */
    Int vlena, vlenb;		/* length without version */
    const UChar *vpa, *vpb;
 
+   Bool preferA = False;
+   Bool preferB = False;
+
+   vg_assert(a->addr == b->addr);
+
    vlena = lena = VG_(strlen)(a->name);
    vlenb = lenb = VG_(strlen)(b->name);
 
@@ -545,40 +975,68 @@
    if (vpb)
       vlenb = vpb - b->name;
 
-   TRACE_SYMTAB("choosing between '%s' and '%s'\n", a->name, b->name);
-
    /* MPI hack: prefer PMPI_Foo over MPI_Foo */
    if (0==VG_(strncmp)(a->name, "MPI_", 4)
        && 0==VG_(strncmp)(b->name, "PMPI_", 5)
-       && 0==VG_(strcmp)(a->name, 1+b->name))
-      return b;
-   else
+       && 0==VG_(strcmp)(a->name, 1+b->name)) {
+      preferB = True; goto out;
+   } 
    if (0==VG_(strncmp)(b->name, "MPI_", 4)
        && 0==VG_(strncmp)(a->name, "PMPI_", 5)
-       && 0==VG_(strcmp)(b->name, 1+a->name))
-      return a;
+       && 0==VG_(strcmp)(b->name, 1+a->name)) {
+      preferA = True; goto out;
+   }
 
    /* Select the shortest unversioned name */
-   if (vlena < vlenb)
-      return a;
-   else if (vlenb < vlena)
-      return b;
+   if (vlena < vlenb) {
+      preferA = True; goto out;
+   } 
+   if (vlenb < vlena) {
+      preferB = True; goto out;
+   }
 
    /* Equal lengths; select the versioned name */
-   if (vpa && !vpb)
-      return a;
-   if (vpb && !vpa)
-      return b;
+   if (vpa && !vpb) {
+      preferA = True; goto out;
+   }
+   if (vpb && !vpa) {
+      preferB = True; goto out;
+   }
 
    /* Either both versioned or neither is versioned; select them
       alphabetically */
-   if (VG_(strcmp)(a->name, b->name) < 0)
+   cmp = VG_(strcmp)(a->name, b->name);
+   if (cmp < 0) {
+      preferA = True; goto out;
+   }
+   if (cmp > 0) {
+      preferB = True; goto out;
+   }
+   /* If we get here, they are the same (?!).  That's very odd.  In
+      this case we could choose either (arbitrarily), but might as
+      well choose the one with the lowest DiSym* address, so as to try
+      and make the comparison mechanism more stable (a la sorting
+      parlance).  Also, skip the diagnostic printing in this case. */
+   return a <= b  ? a  : b;
+
+   /*NOTREACHED*/
+   vg_assert(0);
+  out:
+   if (preferA && !preferB) {
+      TRACE_SYMTAB("sym at %p: prefer '%s' to '%s'\n",
+                   a->addr, a->name, b->name );
       return a;
-   else
+   }
+   if (preferB && !preferA) {
+      TRACE_SYMTAB("sym at %p: prefer '%s' to '%s'\n",
+                   b->addr, b->name, a->name );
       return b;
+   }
+   /*NOTREACHED*/
+   vg_assert(0);
 }
 
-static void canonicaliseSymtab ( struct _SegInfo* si )
+static void canonicaliseSymtab ( struct _DebugInfo* di )
 {
    Int   i, j, n_merged, n_truncated;
    Addr  s1, s2, e1, e2;
@@ -586,11 +1044,11 @@
 #  define SWAP(ty,aa,bb) \
       do { ty tt = (aa); (aa) = (bb); (bb) = tt; } while (0)
 
-   if (si->symtab_used == 0)
+   if (di->symtab_used == 0)
       return;
 
-   VG_(ssort)(si->symtab, si->symtab_used, 
-                          sizeof(*si->symtab), compare_DiSym);
+   VG_(ssort)(di->symtab, di->symtab_used, 
+                          sizeof(*di->symtab), compare_DiSym);
 
   cleanup_more:
  
@@ -598,51 +1056,51 @@
       using prefersym() (see it for details). */
    do {
       n_merged = 0;
-      j = si->symtab_used;
-      si->symtab_used = 0;
+      j = di->symtab_used;
+      di->symtab_used = 0;
       for (i = 0; i < j; i++) {
          if (i < j-1
-             && si->symtab[i].addr   == si->symtab[i+1].addr
-             && si->symtab[i].size   == si->symtab[i+1].size) {
+             && di->symtab[i].addr   == di->symtab[i+1].addr
+             && di->symtab[i].size   == di->symtab[i+1].size) {
             n_merged++;
             /* merge the two into one */
-	    si->symtab[si->symtab_used++] 
-               = *prefersym(si, &si->symtab[i], &si->symtab[i+1]);
+	    di->symtab[di->symtab_used++] 
+               = *prefersym(di, &di->symtab[i], &di->symtab[i+1]);
             i++;
          } else {
-            si->symtab[si->symtab_used++] = si->symtab[i];
+            di->symtab[di->symtab_used++] = di->symtab[i];
          }
       }
-      TRACE_SYMTAB( "%d merged\n", n_merged);
+      TRACE_SYMTAB( "canonicaliseSymtab: %d symbols merged\n", n_merged);
    }
    while (n_merged > 0);
 
    /* Detect and "fix" overlapping address ranges. */
    n_truncated = 0;
 
-   for (i = 0; i < ((Int)si->symtab_used) -1; i++) {
+   for (i = 0; i < ((Int)di->symtab_used) -1; i++) {
 
-      vg_assert(si->symtab[i].addr <= si->symtab[i+1].addr);
+      vg_assert(di->symtab[i].addr <= di->symtab[i+1].addr);
 
       /* Check for common (no overlap) case. */ 
-      if (si->symtab[i].addr + si->symtab[i].size 
-          <= si->symtab[i+1].addr)
+      if (di->symtab[i].addr + di->symtab[i].size 
+          <= di->symtab[i+1].addr)
          continue;
 
       /* There's an overlap.  Truncate one or the other. */
-      if (si->trace_symtab) {
+      if (di->trace_symtab) {
          VG_(printf)("overlapping address ranges in symbol table\n\t");
-         ML_(ppSym)( i, &si->symtab[i] );
+         ML_(ppSym)( i, &di->symtab[i] );
          VG_(printf)("\t");
-         ML_(ppSym)( i+1, &si->symtab[i+1] );
+         ML_(ppSym)( i+1, &di->symtab[i+1] );
          VG_(printf)("\n");
       }
 
       /* Truncate one or the other. */
-      s1 = si->symtab[i].addr;
-      s2 = si->symtab[i+1].addr;
-      e1 = s1 + si->symtab[i].size - 1;
-      e2 = s2 + si->symtab[i+1].size - 1;
+      s1 = di->symtab[i].addr;
+      s2 = di->symtab[i+1].addr;
+      e1 = s1 + di->symtab[i].size - 1;
+      e2 = s2 + di->symtab[i+1].size - 1;
       if (s1 < s2) {
          e1 = s2-1;
       } else {
@@ -657,19 +1115,19 @@
               up back at cleanup_more, which will take care of it. */
 	 }
       }
-      si->symtab[i].addr   = s1;
-      si->symtab[i+1].addr = s2;
-      si->symtab[i].size   = e1 - s1 + 1;
-      si->symtab[i+1].size = e2 - s2 + 1;
+      di->symtab[i].addr   = s1;
+      di->symtab[i+1].addr = s2;
+      di->symtab[i].size   = e1 - s1 + 1;
+      di->symtab[i+1].size = e2 - s2 + 1;
       vg_assert(s1 <= s2);
-      vg_assert(si->symtab[i].size > 0);
-      vg_assert(si->symtab[i+1].size > 0);
+      vg_assert(di->symtab[i].size > 0);
+      vg_assert(di->symtab[i+1].size > 0);
       /* It may be that the i+1 entry now needs to be moved further
          along to maintain the address order requirement. */
       j = i+1;
-      while (j < ((Int)si->symtab_used)-1 
-             && si->symtab[j].addr > si->symtab[j+1].addr) {
-         SWAP(DiSym,si->symtab[j],si->symtab[j+1]);
+      while (j < ((Int)di->symtab_used)-1 
+             && di->symtab[j].addr > di->symtab[j+1].addr) {
+         SWAP(DiSym,di->symtab[j],di->symtab[j+1]);
          j++;
       }
       n_truncated++;
@@ -678,14 +1136,14 @@
    if (n_truncated > 0) goto cleanup_more;
 
    /* Ensure relevant postconditions hold. */
-   for (i = 0; i < ((Int)si->symtab_used)-1; i++) {
+   for (i = 0; i < ((Int)di->symtab_used)-1; i++) {
       /* No zero-sized symbols. */
-      vg_assert(si->symtab[i].size > 0);
+      vg_assert(di->symtab[i].size > 0);
       /* In order. */
-      vg_assert(si->symtab[i].addr < si->symtab[i+1].addr);
+      vg_assert(di->symtab[i].addr < di->symtab[i+1].addr);
       /* No overlaps. */
-      vg_assert(si->symtab[i].addr + si->symtab[i].size - 1
-                < si->symtab[i+1].addr);
+      vg_assert(di->symtab[i].addr + di->symtab[i].size - 1
+                < di->symtab[i+1].addr);
    }
 #  undef SWAP
 }
@@ -705,34 +1163,34 @@
    return 0;
 }
 
-static void canonicaliseLoctab ( struct _SegInfo* si )
+static void canonicaliseLoctab ( struct _DebugInfo* di )
 {
    Int i, j;
 
 #  define SWAP(ty,aa,bb) \
       do { ty tt = (aa); (aa) = (bb); (bb) = tt; } while (0);
 
-   if (si->loctab_used == 0)
+   if (di->loctab_used == 0)
       return;
 
    /* Sort by start address. */
-   VG_(ssort)(si->loctab, si->loctab_used, 
-                          sizeof(*si->loctab), compare_DiLoc);
+   VG_(ssort)(di->loctab, di->loctab_used, 
+                          sizeof(*di->loctab), compare_DiLoc);
 
    /* If two adjacent entries overlap, truncate the first. */
-   for (i = 0; i < ((Int)si->loctab_used)-1; i++) {
-      vg_assert(si->loctab[i].size < 10000);
-      if (si->loctab[i].addr + si->loctab[i].size > si->loctab[i+1].addr) {
+   for (i = 0; i < ((Int)di->loctab_used)-1; i++) {
+      vg_assert(di->loctab[i].size < 10000);
+      if (di->loctab[i].addr + di->loctab[i].size > di->loctab[i+1].addr) {
          /* Do this in signed int32 because the actual .size fields
             are only 12 bits. */
-         Int new_size = si->loctab[i+1].addr - si->loctab[i].addr;
+         Int new_size = di->loctab[i+1].addr - di->loctab[i].addr;
          if (new_size < 0) {
-            si->loctab[i].size = 0;
+            di->loctab[i].size = 0;
          } else
          if (new_size > MAX_LOC_SIZE) {
-           si->loctab[i].size = MAX_LOC_SIZE;
+           di->loctab[i].size = MAX_LOC_SIZE;
          } else {
-           si->loctab[i].size = (UShort)new_size;
+           di->loctab[i].size = (UShort)new_size;
          }
       }
    }
@@ -740,29 +1198,29 @@
    /* Zap any zero-sized entries resulting from the truncation
       process. */
    j = 0;
-   for (i = 0; i < (Int)si->loctab_used; i++) {
-      if (si->loctab[i].size > 0) {
+   for (i = 0; i < (Int)di->loctab_used; i++) {
+      if (di->loctab[i].size > 0) {
          if (j != i)
-            si->loctab[j] = si->loctab[i];
+            di->loctab[j] = di->loctab[i];
          j++;
       }
    }
-   si->loctab_used = j;
+   di->loctab_used = j;
 
    /* Ensure relevant postconditions hold. */
-   for (i = 0; i < ((Int)si->loctab_used)-1; i++) {
+   for (i = 0; i < ((Int)di->loctab_used)-1; i++) {
       /* 
       VG_(printf)("%d   (%d) %d 0x%x\n", 
-                   i, si->loctab[i+1].confident, 
-                   si->loctab[i+1].size, si->loctab[i+1].addr );
+                   i, di->loctab[i+1].confident, 
+                   di->loctab[i+1].size, di->loctab[i+1].addr );
       */
       /* No zero-sized symbols. */
-      vg_assert(si->loctab[i].size > 0);
+      vg_assert(di->loctab[i].size > 0);
       /* In order. */
-      vg_assert(si->loctab[i].addr < si->loctab[i+1].addr);
+      vg_assert(di->loctab[i].addr < di->loctab[i+1].addr);
       /* No overlaps. */
-      vg_assert(si->loctab[i].addr + si->loctab[i].size - 1
-                < si->loctab[i+1].addr);
+      vg_assert(di->loctab[i].addr + di->loctab[i].size - 1
+                < di->loctab[i+1].addr);
    }
 #  undef SWAP
 }
@@ -788,100 +1246,101 @@
    return 0;
 }
 
-static void canonicaliseCFI ( struct _SegInfo* si )
+static void canonicaliseCFI ( struct _DebugInfo* di )
 {
    Int   i, j;
-   const Addr minAddr = 0;
-   const Addr maxAddr = ~minAddr;
+   const Addr minAvma = 0;
+   const Addr maxAvma = ~minAvma;
 
-   /* Note: take care in here.  si->cfsi can be NULL, in which
+   /* Note: take care in here.  di->cfsi can be NULL, in which
       case _used and _size fields will be zero. */
-   if (si->cfsi == NULL) {
-      vg_assert(si->cfsi_used == 0);
-      vg_assert(si->cfsi_size == 0);
+   if (di->cfsi == NULL) {
+      vg_assert(di->cfsi_used == 0);
+      vg_assert(di->cfsi_size == 0);
    }
 
-   /* Set cfsi_minaddr and cfsi_maxaddr to summarise the entire
+   /* Set cfsi_minavma and cfsi_maxavma to summarise the entire
       address range contained in cfsi[0 .. cfsi_used-1]. */
-   si->cfsi_minaddr = maxAddr; 
-   si->cfsi_maxaddr = minAddr;
-   for (i = 0; i < (Int)si->cfsi_used; i++) {
-      Addr here_min = si->cfsi[i].base;
-      Addr here_max = si->cfsi[i].base + si->cfsi[i].len - 1;
-      if (here_min < si->cfsi_minaddr)
-         si->cfsi_minaddr = here_min;
-      if (here_max > si->cfsi_maxaddr)
-         si->cfsi_maxaddr = here_max;
+   di->cfsi_minavma = maxAvma; 
+   di->cfsi_maxavma = minAvma;
+   for (i = 0; i < (Int)di->cfsi_used; i++) {
+      Addr here_min = di->cfsi[i].base;
+      Addr here_max = di->cfsi[i].base + di->cfsi[i].len - 1;
+      if (here_min < di->cfsi_minavma)
+         di->cfsi_minavma = here_min;
+      if (here_max > di->cfsi_maxavma)
+         di->cfsi_maxavma = here_max;
    }
 
-   if (si->trace_cfi)
+   if (di->trace_cfi)
       VG_(printf)("canonicaliseCfiSI: %d entries, %p .. %p\n", 
-                  si->cfsi_used,
-	          si->cfsi_minaddr, si->cfsi_maxaddr);
+                  di->cfsi_used,
+	          di->cfsi_minavma, di->cfsi_maxavma);
 
    /* Sort the cfsi array by base address. */
-   VG_(ssort)(si->cfsi, si->cfsi_used, sizeof(*si->cfsi), compare_DiCfSI);
+   VG_(ssort)(di->cfsi, di->cfsi_used, sizeof(*di->cfsi), compare_DiCfSI);
 
    /* If two adjacent entries overlap, truncate the first. */
-   for (i = 0; i < (Int)si->cfsi_used-1; i++) {
-      if (si->cfsi[i].base + si->cfsi[i].len > si->cfsi[i+1].base) {
-         Int new_len = si->cfsi[i+1].base - si->cfsi[i].base;
+   for (i = 0; i < (Int)di->cfsi_used-1; i++) {
+      if (di->cfsi[i].base + di->cfsi[i].len > di->cfsi[i+1].base) {
+         Int new_len = di->cfsi[i+1].base - di->cfsi[i].base;
          /* how could it be otherwise?  The entries are sorted by the
             .base field. */         
          vg_assert(new_len >= 0);
-	 vg_assert(new_len <= si->cfsi[i].len);
-         si->cfsi[i].len = new_len;
+	 vg_assert(new_len <= di->cfsi[i].len);
+         di->cfsi[i].len = new_len;
       }
    }
 
    /* Zap any zero-sized entries resulting from the truncation
       process. */
    j = 0;
-   for (i = 0; i < (Int)si->cfsi_used; i++) {
-      if (si->cfsi[i].len > 0) {
+   for (i = 0; i < (Int)di->cfsi_used; i++) {
+      if (di->cfsi[i].len > 0) {
          if (j != i)
-            si->cfsi[j] = si->cfsi[i];
+            di->cfsi[j] = di->cfsi[i];
          j++;
       }
    }
-   /* VG_(printf)("XXXXXXXXXXXXX %d %d\n", si->cfsi_used, j); */
-   si->cfsi_used = j;
+   /* VG_(printf)("XXXXXXXXXXXXX %d %d\n", di->cfsi_used, j); */
+   di->cfsi_used = j;
 
    /* Ensure relevant postconditions hold. */
-   for (i = 0; i < (Int)si->cfsi_used; i++) {
+   for (i = 0; i < (Int)di->cfsi_used; i++) {
       /* No zero-length ranges. */
-      vg_assert(si->cfsi[i].len > 0);
+      vg_assert(di->cfsi[i].len > 0);
       /* Makes sense w.r.t. summary address range */
-      vg_assert(si->cfsi[i].base >= si->cfsi_minaddr);
-      vg_assert(si->cfsi[i].base + si->cfsi[i].len - 1
-                <= si->cfsi_maxaddr);
+      vg_assert(di->cfsi[i].base >= di->cfsi_minavma);
+      vg_assert(di->cfsi[i].base + di->cfsi[i].len - 1
+                <= di->cfsi_maxavma);
 
-      if (i < si->cfsi_used - 1) {
+      if (i < di->cfsi_used - 1) {
          /*
-         if (!(si->cfsi[i].base < si->cfsi[i+1].base)) {
+         if (!(di->cfsi[i].base < di->cfsi[i+1].base)) {
             VG_(printf)("\nOOO cfsis:\n");
-            ML_(ppCfiSI)(&si->cfsi[i]);
-            ML_(ppCfiSI)(&si->cfsi[i+1]);
+            ML_(ppCfiSI)(&di->cfsi[i]);
+            ML_(ppCfiSI)(&di->cfsi[i+1]);
          }
          */
          /* In order. */
-         vg_assert(si->cfsi[i].base < si->cfsi[i+1].base);
+         vg_assert(di->cfsi[i].base < di->cfsi[i+1].base);
          /* No overlaps. */
-         vg_assert(si->cfsi[i].base + si->cfsi[i].len - 1
-                   < si->cfsi[i+1].base);
+         vg_assert(di->cfsi[i].base + di->cfsi[i].len - 1
+                   < di->cfsi[i+1].base);
       }
    }
 
 }
 
 
-/* Canonicalise the tables held by 'si', in preparation for use.  Call
+/* Canonicalise the tables held by 'di', in preparation for use.  Call
    this after finishing adding entries to these tables. */
-void ML_(canonicaliseTables) ( struct _SegInfo* si )
+void ML_(canonicaliseTables) ( struct _DebugInfo* di )
 {
-   canonicaliseSymtab ( si );
-   canonicaliseLoctab ( si );
-   canonicaliseCFI ( si );
+   canonicaliseSymtab ( di );
+   canonicaliseLoctab ( di );
+   canonicaliseCFI ( di );
+   canonicaliseVarInfo ( di );
 }
 
 
@@ -892,27 +1351,32 @@
 /* Find a symbol-table index containing the specified pointer, or -1
    if not found.  Binary search.  */
 
-Int ML_(search_one_symtab) ( struct _SegInfo* si, Addr ptr,
-                             Bool match_anywhere_in_fun )
+Int ML_(search_one_symtab) ( struct _DebugInfo* di, Addr ptr,
+                             Bool match_anywhere_in_sym,
+                             Bool findText )
 {
    Addr a_mid_lo, a_mid_hi;
-   Int  mid, size, 
+   Word mid, size, 
         lo = 0, 
-        hi = si->symtab_used-1;
+        hi = di->symtab_used-1;
    while (True) {
       /* current unsearched space is from lo to hi, inclusive. */
       if (lo > hi) return -1; /* not found */
       mid      = (lo + hi) / 2;
-      a_mid_lo = si->symtab[mid].addr;
-      size = ( match_anywhere_in_fun
-             ? si->symtab[mid].size
+      a_mid_lo = di->symtab[mid].addr;
+      size = ( match_anywhere_in_sym
+             ? di->symtab[mid].size
              : 1);
-      a_mid_hi = ((Addr)si->symtab[mid].addr) + size - 1;
+      a_mid_hi = ((Addr)di->symtab[mid].addr) + size - 1;
 
       if (ptr < a_mid_lo) { hi = mid-1; continue; } 
       if (ptr > a_mid_hi) { lo = mid+1; continue; }
       vg_assert(ptr >= a_mid_lo && ptr <= a_mid_hi);
-      return mid;
+      /* Found a symbol with the correct address range.  But is it
+         of the right kind (text vs data) ? */
+      if (  findText   &&   di->symtab[mid].isText  ) return mid;
+      if ( (!findText) && (!di->symtab[mid].isText) ) return mid;
+      return -1;
    }
 }
 
@@ -920,18 +1384,18 @@
 /* Find a location-table index containing the specified pointer, or -1
    if not found.  Binary search.  */
 
-Int ML_(search_one_loctab) ( struct _SegInfo* si, Addr ptr )
+Int ML_(search_one_loctab) ( struct _DebugInfo* di, Addr ptr )
 {
    Addr a_mid_lo, a_mid_hi;
-   Int  mid, 
+   Word mid, 
         lo = 0, 
-        hi = si->loctab_used-1;
+        hi = di->loctab_used-1;
    while (True) {
       /* current unsearched space is from lo to hi, inclusive. */
       if (lo > hi) return -1; /* not found */
       mid      = (lo + hi) / 2;
-      a_mid_lo = si->loctab[mid].addr;
-      a_mid_hi = ((Addr)si->loctab[mid].addr) + si->loctab[mid].size - 1;
+      a_mid_lo = di->loctab[mid].addr;
+      a_mid_hi = ((Addr)di->loctab[mid].addr) + di->loctab[mid].size - 1;
 
       if (ptr < a_mid_lo) { hi = mid-1; continue; } 
       if (ptr > a_mid_hi) { lo = mid+1; continue; }
@@ -944,18 +1408,18 @@
 /* Find a CFI-table index containing the specified pointer, or -1
    if not found.  Binary search.  */
 
-Int ML_(search_one_cfitab) ( struct _SegInfo* si, Addr ptr )
+Int ML_(search_one_cfitab) ( struct _DebugInfo* di, Addr ptr )
 {
    Addr a_mid_lo, a_mid_hi;
    Int  mid, size, 
         lo = 0, 
-        hi = si->cfsi_used-1;
+        hi = di->cfsi_used-1;
    while (True) {
       /* current unsearched space is from lo to hi, inclusive. */
       if (lo > hi) return -1; /* not found */
       mid      = (lo + hi) / 2;
-      a_mid_lo = si->cfsi[mid].base;
-      size     = si->cfsi[mid].len;
+      a_mid_lo = di->cfsi[mid].base;
+      size     = di->cfsi[mid].len;
       a_mid_hi = a_mid_lo + size - 1;
       vg_assert(a_mid_hi >= a_mid_lo);
       if (ptr < a_mid_lo) { hi = mid-1; continue; } 
diff --git a/coregrind/m_debuginfo/tytypes.c b/coregrind/m_debuginfo/tytypes.c
new file mode 100644
index 0000000..e43e0c1
--- /dev/null
+++ b/coregrind/m_debuginfo/tytypes.c
@@ -0,0 +1,584 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Representation of source level types.              tytypes.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 OpenWorks LLP
+      info@open-works.co.uk
+
+   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., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+
+   Neither the names of the U.S. Department of Energy nor the
+   University of California nor the names of its contributors may be
+   used to endorse or promote products derived from this software
+   without prior written permission.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_xarray.h"   /* to keep priv_tytypes.h happy */
+
+#include "priv_misc.h"         /* dinfo_zalloc/free/strdup */
+#include "priv_d3basics.h"     /* ML_(evaluate_Dwarf3_Expr) et al */
+#include "priv_tytypes.h"      /* self */
+
+
+TyAdmin* ML_(new_TyAdmin) ( UWord cuOff, TyAdmin* next ) {
+   TyAdmin* admin = ML_(dinfo_zalloc)( sizeof(TyAdmin) );
+   admin->cuOff = cuOff;
+   admin->next  = next;
+   return admin;
+}
+TyAtom* ML_(new_TyAtom) ( UChar* name, Long value ) {
+   TyAtom* atom = ML_(dinfo_zalloc)( sizeof(TyAtom) );
+   atom->name  = name;
+   atom->value = value;
+   return atom;
+}
+TyField* ML_(new_TyField) ( UChar* name,
+                            Type* typeR, D3Expr* loc ) {
+   TyField* field = ML_(dinfo_zalloc)( sizeof(TyField) );
+   field->name  = name;
+   field->typeR = typeR;
+   field->loc   = loc;
+   return field;
+}
+TyBounds* ML_(new_TyBounds) ( void ) {
+   TyBounds* bounds = ML_(dinfo_zalloc)( sizeof(TyBounds) );
+   bounds->magic = TyBounds_MAGIC;
+   return bounds;
+}
+D3Expr* ML_(new_D3Expr) ( UChar* bytes, UWord nbytes ) {
+   D3Expr* expr = ML_(dinfo_zalloc)( sizeof(D3Expr) );
+   expr->bytes = bytes;
+   expr->nbytes = nbytes;
+   return expr;
+}
+Type* ML_(new_Type) ( void ) {
+   Type* type = ML_(dinfo_zalloc)( sizeof(Type) );
+   return type;
+}
+
+static void delete_TyAtom ( TyAtom* atom ) {
+   /* .name is in DebugInfo.strchunks */
+   ML_(dinfo_free)(atom);
+}
+static void delete_TyField ( TyField* field ) {
+   /* .name is in DebugInfo.strchunks */
+   /* typeR and loc will be on the admin list; no need to free */
+   ML_(dinfo_free)(field);
+}
+static void delete_TyBounds ( TyBounds* bounds ) {
+   ML_(dinfo_free)(bounds);
+}
+static void delete_D3Expr ( D3Expr* expr ) {
+   /* .bytes is in DebugInfo.strchunks */
+   ML_(dinfo_free)(expr);
+}
+static void delete_Type ( Type* ty ) {
+   switch (ty->tag) {
+      case Ty_Base:
+         /* .name is in DebugInfo.strchunks */
+         break;
+      case Ty_PorR:
+         /* typeR will be on the admin list */
+         break;
+      case Ty_TyDef:
+         /* .name is in DebugInfo.strchunks */
+         /* typeR will be on the admin list */
+         break;
+      case Ty_StOrUn:
+         /* .name is in DebugInfo.strchunks */
+         /* Just dump the containing XArray.  The fields themselves
+            will be on the admin list. */
+         if (ty->Ty.StOrUn.fields)
+            VG_(deleteXA)(ty->Ty.StOrUn.fields);
+         break;
+      case Ty_Enum:
+         /* .name is in DebugInfo.strchunks */
+         if (ty->Ty.Enum.atomRs)
+            VG_(deleteXA)( ty->Ty.Enum.atomRs);
+         /* Just dump the containing XArray.  The atoms themselves
+            will be on the admin list. */
+         break;
+      case Ty_Array:
+         if (ty->Ty.Array.bounds)
+            VG_(deleteXA)( ty->Ty.Array.bounds);
+         /* Just dump the containing XArray.  The bounds themselves
+            will be on the admin list. */
+         break;
+      case Ty_Fn:
+         break;
+      case Ty_Qual:
+         /* typeR will be on the admin list */
+         break;
+      case Ty_Void:
+         break;
+      default:
+         vg_assert(0);
+   }
+}
+
+void ML_(delete_TyAdmin_and_payload) ( TyAdmin* ad ) {
+   vg_assert(ad->payload);
+   switch (ad->tag) {
+      case TyA_Type:   delete_Type(ad->payload);     break;
+      case TyA_Atom:   delete_TyAtom(ad->payload);   break;
+      case TyA_Expr:   delete_D3Expr(ad->payload);   break;
+      case TyA_Field:  delete_TyField(ad->payload);  break;
+      case TyA_Bounds: delete_TyBounds(ad->payload); break;
+      default:         vg_assert(0);
+   }
+   ML_(dinfo_free)(ad);
+}
+
+
+
+static void pp_XArray_of_pointersOrRefs ( XArray* xa ) {
+   Word i;
+   VG_(printf)("{");
+   for (i = 0; i < VG_(sizeXA)(xa); i++) {
+      void* ptr = *(void**) VG_(indexXA)(xa, i);
+      VG_(printf)("0x%05lx", ptr);
+      if (i+1 < VG_(sizeXA)(xa))
+         VG_(printf)(",");
+   }
+   VG_(printf)("}");
+}
+void ML_(pp_TyAtom) ( TyAtom* atom ) {
+   VG_(printf)("TyAtom(%lld,\"%s\")", atom->value, atom->name);
+}
+void ML_(pp_D3Expr) ( D3Expr* expr ) {
+   VG_(printf)("D3Expr(%p,%lu)", expr->bytes, expr->nbytes);
+}
+void ML_(pp_TyField) ( TyField* field ) {
+   VG_(printf)("TyField(0x%05lx,%p,\"%s\")",
+               field->typeR, field->loc,
+               field->name ? field->name : (UChar*)"");
+}
+void ML_(pp_TyBounds) ( TyBounds* bounds ) {
+   vg_assert(bounds->magic == TyBounds_MAGIC);
+   VG_(printf)("TyBounds[");
+   if (bounds->knownL)
+      VG_(printf)("%lld", bounds->boundL);
+   else
+      VG_(printf)("??");
+   VG_(printf)(",");
+   if (bounds->knownU)
+      VG_(printf)("%lld", bounds->boundU);
+   else
+      VG_(printf)("??");
+   VG_(printf)("]");
+}
+
+static void pp_TyBounds_C_ishly ( TyBounds* bounds ) {
+   vg_assert(bounds->magic == TyBounds_MAGIC);
+   if (bounds->knownL && bounds->knownU && bounds->boundL == 0) {
+      VG_(printf)("[%lld]", 1 + bounds->boundU);
+   }
+   else
+   if (bounds->knownL && (!bounds->knownU) && bounds->boundL == 0) {
+      VG_(printf)("[]");
+   }
+   else
+      ML_(pp_TyBounds)( bounds );
+}
+
+
+void ML_(pp_Type) ( Type* ty )
+{
+   if (!ty) {
+      VG_(printf)("**type=NULL**");
+      return;
+   }
+   switch (ty->tag) {
+      case Ty_Base:
+         VG_(printf)("Ty_Base(%d,%c,\"%s\")",
+                     ty->Ty.Base.szB, ty->Ty.Base.enc,
+                     ty->Ty.Base.name ? ty->Ty.Base.name
+                                        : (UChar*)"(null)" );
+         break;
+      case Ty_PorR:
+         VG_(printf)("Ty_PorR(%d,%c,0x%05lx)",
+                     ty->Ty.PorR.szB, 
+                     ty->Ty.PorR.isPtr ? 'P' : 'R',
+                     ty->Ty.PorR.typeR);
+         break;
+      case Ty_Enum:
+         VG_(printf)("Ty_Enum(%d,%p,\"%s\")",
+                     ty->Ty.Enum.szB, ty->Ty.Enum.atomRs,
+                     ty->Ty.Enum.name ? ty->Ty.Enum.name
+                                        : (UChar*)"" );
+         if (ty->Ty.Enum.atomRs)
+            pp_XArray_of_pointersOrRefs( ty->Ty.Enum.atomRs );
+         break;
+      case Ty_StOrUn:
+         if (ty->Ty.StOrUn.complete) {
+            VG_(printf)("Ty_StOrUn(%d,%c,%p,\"%s\")",
+                        ty->Ty.StOrUn.szB, 
+                        ty->Ty.StOrUn.isStruct ? 'S' : 'U',
+                        ty->Ty.StOrUn.fields,
+                        ty->Ty.StOrUn.name ? ty->Ty.StOrUn.name
+                                             : (UChar*)"" );
+            if (ty->Ty.StOrUn.fields)
+               pp_XArray_of_pointersOrRefs( ty->Ty.StOrUn.fields );
+         } else {
+            VG_(printf)("Ty_StOrUn(INCOMPLETE,\"%s\")",
+                        ty->Ty.StOrUn.name);
+         }
+         break;
+      case Ty_Array:
+         VG_(printf)("Ty_Array(0x%05lx,%p)",
+                     ty->Ty.Array.typeR, ty->Ty.Array.bounds);
+         if (ty->Ty.Array.bounds)
+            pp_XArray_of_pointersOrRefs( ty->Ty.Array.bounds );
+         break;
+      case Ty_TyDef:
+         VG_(printf)("Ty_TyDef(0x%05lx,\"%s\")",
+                     ty->Ty.TyDef.typeR,
+                     ty->Ty.TyDef.name ? ty->Ty.TyDef.name
+                                         : (UChar*)"" );
+         break;
+      case Ty_Fn:
+         VG_(printf)("Ty_Fn");
+         break;
+      case Ty_Qual:
+         VG_(printf)("Ty_Qual(%c,0x%05lx)", ty->Ty.Qual.qual,
+                     ty->Ty.Qual.typeR);
+         break;
+      case Ty_Void:
+         VG_(printf)("Ty_Void%s",
+                     ty->Ty.Void.isFake ? "(fake)" : "");
+         break;
+      default: VG_(printf)("pp_Type:???");
+         break;
+   }
+}
+void ML_(pp_TyAdmin) ( TyAdmin* admin ) {
+   if (admin->cuOff != -1UL) {
+      VG_(printf)("<%05lx,%p> ", admin->cuOff, admin->payload);
+   } else {
+      VG_(printf)("<ff..f,%p> ", admin->payload);
+   }
+   switch (admin->tag) {
+      case TyA_Type:   ML_(pp_Type)(admin->payload);     break;
+      case TyA_Atom:   ML_(pp_TyAtom)(admin->payload);   break;
+      case TyA_Expr:   ML_(pp_D3Expr)(admin->payload);   break;
+      case TyA_Field:  ML_(pp_TyField)(admin->payload);  break;
+      case TyA_Bounds: ML_(pp_TyBounds)(admin->payload); break;
+      default:         VG_(printf)("pp_TyAdmin:???");    break;
+   }
+}
+
+/* NOTE: this assumes that the types have all been 'resolved' (that
+   is, inter-type references expressed as .debug_info offsets have
+   been converted into pointers) */
+void ML_(pp_Type_C_ishly) ( Type* ty )
+{
+   if (!ty) {
+      VG_(printf)("**type=NULL**");
+      return;
+   }
+   switch (ty->tag) {
+      case Ty_Base:
+         if (!ty->Ty.Base.name) goto unhandled;
+         VG_(printf)("%s", ty->Ty.Base.name);
+         break;
+      case Ty_PorR:
+         ML_(pp_Type_C_ishly)(ty->Ty.PorR.typeR);
+         VG_(printf)("%s", ty->Ty.PorR.isPtr ? "*" : "&");
+         break;
+      case Ty_Enum:
+         if (!ty->Ty.Enum.name) goto unhandled;
+         VG_(printf)("enum %s", ty->Ty.Enum.name);
+         break;
+      case Ty_StOrUn:
+         if (!ty->Ty.StOrUn.name) goto unhandled;
+         VG_(printf)("%s %s",
+                     ty->Ty.StOrUn.isStruct ? "struct" : "union",
+                     ty->Ty.StOrUn.name);
+         break;
+      case Ty_Array:
+         ML_(pp_Type_C_ishly)(ty->Ty.Array.typeR);
+         if (ty->Ty.Array.bounds) {
+            Word    w;
+            XArray* xa = ty->Ty.Array.bounds;
+            for (w = 0; w < VG_(sizeXA)(xa); w++) {
+               pp_TyBounds_C_ishly( *(TyBounds**)VG_(indexXA)(xa, w) );
+            }
+         } else {
+            VG_(printf)("%s", "[??]");
+         }
+         break;
+      case Ty_TyDef:
+         if (!ty->Ty.TyDef.name) goto unhandled;
+         VG_(printf)("%s", ty->Ty.TyDef.name);
+         break;
+      case Ty_Fn:
+         VG_(printf)("%s", "<function_type>");
+         break;
+      case Ty_Qual:
+         switch (ty->Ty.Qual.qual) {
+            case 'C': VG_(printf)("const "); break;
+            case 'V': VG_(printf)("volatile "); break;
+            default: goto unhandled;
+         }
+         ML_(pp_Type_C_ishly)(ty->Ty.Qual.typeR);
+         break;
+      case Ty_Void:
+         VG_(printf)("%svoid",
+                     ty->Ty.Void.isFake ? "fake" : "");
+         break;
+      default: VG_(printf)("pp_Type_C_ishly:???");
+         break;
+   }
+   return;
+
+  unhandled:
+   ML_(pp_Type)(ty);
+}
+
+
+static MaybeUWord mk_MaybeUWord_Nothing ( void ) {
+   MaybeUWord muw;
+   muw.w = 0;
+   muw.b = False;
+   return muw;
+}
+static MaybeUWord mk_MaybeUWord_Just ( UWord w ) {
+   MaybeUWord muw;
+   muw.w = w;
+   muw.b = True;
+   return muw;
+}
+static MaybeUWord mul_MaybeUWord ( MaybeUWord muw1, MaybeUWord muw2 ) {
+   if (!muw1.b) { vg_assert(muw1.w == 0); return muw1; }
+   if (!muw2.b) { vg_assert(muw2.w == 0); return muw2; }
+   muw1.w *= muw2.w;
+   return muw1;
+}
+
+/* How big is this type?  (post-resolved only) */
+/* FIXME: check all pointers before dereferencing */
+MaybeUWord ML_(sizeOfType)( Type* ty )
+{
+   Word       i;
+   MaybeUWord eszB;
+   vg_assert(ty);
+   switch (ty->tag) {
+      case Ty_Base:
+         vg_assert(ty->Ty.Base.szB > 0);
+         return mk_MaybeUWord_Just( ty->Ty.Base.szB );
+      case Ty_Qual:
+         return ML_(sizeOfType)( ty->Ty.Qual.typeR );
+      case Ty_TyDef:
+         if (!ty->Ty.TyDef.typeR)
+            return mk_MaybeUWord_Nothing(); /*UNKNOWN*/
+         return ML_(sizeOfType)( ty->Ty.TyDef.typeR );
+      case Ty_PorR:
+         vg_assert(ty->Ty.PorR.szB == 4 || ty->Ty.PorR.szB == 8);
+         return mk_MaybeUWord_Just( ty->Ty.PorR.szB );
+      case Ty_StOrUn:
+         return ty->Ty.StOrUn.complete
+                   ? mk_MaybeUWord_Just( ty->Ty.StOrUn.szB )
+                   : mk_MaybeUWord_Nothing();
+      case Ty_Enum:
+         return mk_MaybeUWord_Just( ty->Ty.Enum.szB );
+      case Ty_Array:
+         if (!ty->Ty.Array.typeR)
+            return mk_MaybeUWord_Nothing(); /*UNKNOWN*/
+         eszB = ML_(sizeOfType)( ty->Ty.Array.typeR );
+         for (i = 0; i < VG_(sizeXA)( ty->Ty.Array.bounds ); i++) {
+            TyBounds* bo
+               = *(TyBounds**)VG_(indexXA)(ty->Ty.Array.bounds, i);
+            vg_assert(bo);
+            if (!(bo->knownL && bo->knownU))
+               return mk_MaybeUWord_Nothing(); /*UNKNOWN*/
+            eszB = mul_MaybeUWord( 
+                      eszB,
+                      mk_MaybeUWord_Just( bo->boundU - bo->boundL + 1 ));
+         }
+         return eszB;
+      default:
+         VG_(printf)("ML_(sizeOfType): unhandled: ");
+         ML_(pp_Type)(ty);
+         VG_(printf)("\n");
+         vg_assert(0);
+   }
+}
+
+
+static void copy_UWord_into_XA ( XArray* /* of UChar */ xa,
+                                 UWord uw ) {
+   UChar buf[32];
+   VG_(memset)(buf, 0, sizeof(buf));
+   VG_(sprintf)(buf, "%lu", uw);
+   VG_(addBytesToXA)( xa, buf, VG_(strlen)(buf));
+}
+
+/* Describe where in the type 'offset' falls.  Caller must
+   deallocate the resulting XArray. */
+XArray* /*UChar*/ ML_(describe_type)( /*OUT*/OffT* residual_offset,
+                                      Type* ty, OffT offset )
+{
+   XArray* xa = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
+                            sizeof(UChar) );
+   vg_assert(xa);
+
+   while (True) {
+      vg_assert(ty);
+
+      switch (ty->tag) {
+
+         /* These are all atomic types; there is nothing useful we can
+            do. */
+         case Ty_Enum:
+         case Ty_Fn:
+         case Ty_Void:
+         case Ty_PorR:
+         case Ty_Base:
+            goto done;
+
+         case Ty_StOrUn: {
+            Word       i;
+            GXResult   res;
+            MaybeUWord muw;
+            TyField    *field = NULL, *fields;
+            OffT       offMin = 0, offMax1 = 0;
+            if (!ty->Ty.StOrUn.isStruct) goto done;
+            fields = ty->Ty.StOrUn.fields;
+            if ((!fields) || VG_(sizeXA)(fields) == 0) goto done;
+            for (i = 0; i < VG_(sizeXA)( fields ); i++ ) {
+               field = *(TyField**)VG_(indexXA)( fields, i );
+               vg_assert(field);
+               vg_assert(field->loc);
+               /* Re data_bias in this call, we should really send in
+                  a legitimate value.  But the expression is expected
+                  to be a constant expression, evaluation of which
+                  will not need to use DW_OP_addr and hence we can
+                  avoid the trouble of plumbing the data bias through
+                  to this point (if, indeed, it has any meaning; from
+                  which DebugInfo would we take the data bias? */
+               res = ML_(evaluate_Dwarf3_Expr)(
+                       field->loc->bytes, field->loc->nbytes,
+                       NULL/*fbGX*/, NULL/*RegSummary*/,
+                       0/*data_bias*/,
+                       True/*push_initial_zero*/);
+               if (0) {
+                  VG_(printf)("QQQ ");
+                  ML_(pp_GXResult)(res);
+                  VG_(printf)("\n");
+               }
+               if (res.kind != GXR_Value)
+                  continue;
+               muw = ML_(sizeOfType)( field->typeR );
+               if (muw.b != True)
+                  goto done; /* size of field is unknown (?!) */
+               offMin  = res.word;
+               offMax1 = offMin + muw.w;
+               if (offMin == offMax1)
+                  continue;
+               vg_assert(offMin < offMax1);
+               if (offset >= offMin && offset < offMax1)
+                  break;
+            }
+            /* Did we find a suitable field? */
+            vg_assert(i >= 0 && i <= VG_(sizeXA)( fields ));
+            if (i == VG_(sizeXA)( fields ))
+               goto done; /* No.  Give up. */
+            /* Yes.  'field' is it. */
+            if (!field->name) goto done;
+            VG_(addBytesToXA)( xa, ".", 1 );
+            VG_(addBytesToXA)( xa, field->name,
+                               VG_(strlen)(field->name) );
+            offset -= offMin;
+            ty = field->typeR;
+            if (!ty) goto done;
+            /* keep going; look inside the field. */
+            break;
+         }
+
+         case Ty_Array: {
+            MaybeUWord muw;
+            TyBounds*  bounds;
+            UWord      size, eszB, ix;
+            /* Just deal with the simple, common C-case: 1-D array,
+               zero based, known size. */
+            if (!(ty->Ty.Array.typeR && ty->Ty.Array.bounds))
+               goto done;
+            if (VG_(sizeXA)( ty->Ty.Array.bounds ) != 1) goto done;
+            bounds = *(TyBounds**)VG_(indexXA)( ty->Ty.Array.bounds, 0 );
+            vg_assert(bounds);
+            vg_assert(bounds->magic == TyBounds_MAGIC);
+            if (!(bounds->knownL && bounds->knownU && bounds->boundL == 0
+                  && bounds->boundU >= bounds->boundL))
+               goto done;
+            size = bounds->boundU - bounds->boundL + 1;
+            vg_assert(size >= 1);
+            muw = ML_(sizeOfType)( ty->Ty.Array.typeR );
+            if (muw.b != True)
+               goto done; /* size of element type not known */
+            eszB = muw.w;
+            if (eszB == 0) goto done;
+            ix = offset / eszB;
+            VG_(addBytesToXA)( xa, "[", 1 );
+            copy_UWord_into_XA( xa, ix );
+            VG_(addBytesToXA)( xa, "]", 1 );
+            ty = ty->Ty.Array.typeR;
+            offset -= ix * eszB;
+            /* keep going; look inside the array element. */
+            break;
+         }
+
+         case Ty_Qual: {
+            if (!ty->Ty.Qual.typeR) goto done;
+            ty = ty->Ty.Qual.typeR;
+            break;
+         }
+
+         case Ty_TyDef: {
+            if (!ty->Ty.TyDef.typeR) goto done;
+            ty = ty->Ty.TyDef.typeR;
+            break;
+         }
+
+         default: {
+            VG_(printf)("ML_(describe_type): unhandled: ");
+            ML_(pp_Type)(ty);
+            VG_(printf)("\n");
+            vg_assert(0);
+         }
+
+      }
+   }
+
+  done:
+   *residual_offset = offset;
+   VG_(addBytesToXA)( xa, "\0", 1 );
+   return xa;
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                tytypes.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_errormgr.c b/coregrind/m_errormgr.c
index 1ddf3d5..535252f 100644
--- a/coregrind/m_errormgr.c
+++ b/coregrind/m_errormgr.c
@@ -119,9 +119,9 @@
    // NULL if unsuppressed; or ptr to suppression record.
    Supp* supp;
    Int count;
-   ThreadId tid;
 
    // The tool-specific part
+   ThreadId tid;           // Initialised by core
    ExeContext* where;      // Initialised by core
    ErrorKind ekind;        // Used by ALL.  Must be in the range (0..)
    Addr addr;              // Used frequently
@@ -129,6 +129,7 @@
    void* extra;            // For any tool-specific extras
 };
 
+
 ExeContext* VG_(get_error_where) ( Error* err )
 {
    return err->where;
diff --git a/coregrind/m_execontext.c b/coregrind/m_execontext.c
index 3a8c24c..51b3d82 100644
--- a/coregrind/m_execontext.c
+++ b/coregrind/m_execontext.c
@@ -307,6 +307,8 @@
       ips[0] = VG_(get_IP)(tid);
    } else {
       n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size),
+                                   NULL/*array to dump SP values in*/,
+                                   NULL/*array to dump FP values in*/,
                                    first_ip_delta );
    }
 
diff --git a/coregrind/m_libcassert.c b/coregrind/m_libcassert.c
index 9d43230..a3d9b3d 100644
--- a/coregrind/m_libcassert.c
+++ b/coregrind/m_libcassert.c
@@ -142,8 +142,13 @@
  
    stacktop = tst->os_state.valgrind_stack_init_SP;
  
-   VG_(get_StackTrace2)(0/*tid is unknown*/, 
-                        ips, BACKTRACE_DEPTH, ip, sp, fp, lr, sp, stacktop);
+   VG_(get_StackTrace_wrk)(
+      0/*tid is unknown*/, 
+      ips, BACKTRACE_DEPTH, 
+      NULL/*array to dump SP values in*/,
+      NULL/*array to dump FP values in*/,
+      ip, sp, fp, lr, sp, stacktop
+   );
    VG_(pp_StackTrace)  (ips, BACKTRACE_DEPTH);
  
    VG_(show_sched_status)();
diff --git a/coregrind/m_libcbase.c b/coregrind/m_libcbase.c
index fb41329..3643bce 100644
--- a/coregrind/m_libcbase.c
+++ b/coregrind/m_libcbase.c
@@ -519,23 +519,39 @@
    return dest;
 }
 
-void* VG_(memset) ( void *dest, Int c, SizeT sz )
+void* VG_(memset) ( void *destV, Int c, SizeT sz )
 {
-   Char *d = (Char *)dest;
-   while (sz >= 4) {
-      d[0] = c;
-      d[1] = c;
-      d[2] = c;
-      d[3] = c;
-      d += 4;
-      sz -= 4;
-   }
-   while (sz > 0) {
+   Int   c4;
+   Char* d = (Char*)destV;
+   while ((!VG_IS_4_ALIGNED(d)) && sz >= 1) {
       d[0] = c;
       d++;
       sz--;
    }
-   return dest;
+   if (sz == 0)
+      return destV;
+   c4 = c & 0xFF;
+   c4 |= (c4 << 8);
+   c4 |= (c4 << 16);
+   while (sz >= 16) {
+      ((Int*)d)[0] = c4;
+      ((Int*)d)[1] = c4;
+      ((Int*)d)[2] = c4;
+      ((Int*)d)[3] = c4;
+      d += 16;
+      sz -= 16;
+   }
+   while (sz >= 4) {
+      ((Int*)d)[0] = c4;
+      d += 4;
+      sz -= 4;
+   }
+   while (sz >= 1) {
+      d[0] = c;
+      d++;
+      sz--;
+   }
+   return destV;
 }
 
 Int VG_(memcmp) ( const void* s1, const void* s2, SizeT n )
@@ -563,13 +579,152 @@
    Misc useful functions
    ------------------------------------------------------------------ */
 
-/* Returns the base-2 logarithm of x.  Returns -1 if x is not a power of two. */
-Int VG_(log2) ( Int x ) 
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+/// begin Bentley-McIlroy style quicksort
+/// See "Engineering a Sort Function".  Jon L Bentley, M. Douglas
+/// McIlroy.  Software Practice and Experience Vol 23(11), Nov 1993.
+
+#define BM_MIN(a, b)                                     \
+   (a) < (b) ? a : b
+
+#define BM_SWAPINIT(a, es)                               \
+   swaptype =   ((a-(Char*)0) | es) % sizeof(Word)  ? 2  \
+              : es > (SizeT)sizeof(Word) ? 1             \
+              : 0
+
+#define BM_EXCH(a, b, t)                                 \
+   (t = a, a = b, b = t)
+
+#define BM_SWAP(a, b)                                    \
+   swaptype != 0                                         \
+      ? bm_swapfunc(a, b, es, swaptype)                  \
+      : (void)BM_EXCH(*(Word*)(a), *(Word*)(b), t)
+
+#define BM_VECSWAP(a, b, n)                              \
+   if (n > 0) bm_swapfunc(a, b, n, swaptype)
+
+#define BM_PVINIT(pv, pm)                                \
+   if (swaptype != 0)                                    \
+      pv = a, BM_SWAP(pv, pm);                           \
+   else                                                  \
+      pv = (Char*)&v, v = *(Word*)pm
+
+static Char* bm_med3 ( Char* a, Char* b, Char* c, 
+                       Int (*cmp)(void*,void*) ) {
+   return cmp(a, b) < 0
+          ? (cmp(b, c) < 0 ? b : cmp(a, c) < 0 ? c : a)
+          : (cmp(b, c) > 0 ? b : cmp(a, c) > 0 ? c : a);
+}
+
+static void bm_swapfunc ( Char* a, Char* b, SizeT n, Int swaptype )
+{
+   if (swaptype <= 1) {
+      Word t;
+      for ( ; n > 0; a += sizeof(Word), b += sizeof(Word),
+                                        n -= sizeof(Word))
+         BM_EXCH(*(Word*)a, *(Word*)b, t);
+   } else {
+      Char t;
+      for ( ; n > 0; a += 1, b += 1, n -= 1)
+         BM_EXCH(*a, *b, t);
+   }
+}
+
+static void bm_qsort ( Char* a, SizeT n, SizeT es,
+                       Int (*cmp)(void*,void*) )
+{
+   Char  *pa, *pb, *pc, *pd, *pl, *pm, *pn, *pv;
+   Int   r, swaptype;
+   Word  t, v;
+   SizeT s, s1, s2;
+  tailcall:
+   BM_SWAPINIT(a, es);
+   if (n < 7) {
+      for (pm = a + es; pm < a + n*es; pm += es)
+         for (pl = pm; pl > a && cmp(pl-es, pl) > 0; pl -= es)
+            BM_SWAP(pl, pl-es);
+      return;
+   }
+   pm = a + (n/2)*es;
+   if (n > 7) {
+      pl = a;
+      pn = a + (n-1)*es;
+      if (n > 40) {
+         s = (n/8)*es;
+         pl = bm_med3(pl, pl+s, pl+2*s, cmp);
+         pm = bm_med3(pm-s, pm, pm+s, cmp);
+         pn = bm_med3(pn-2*s, pn-s, pn, cmp);
+      }
+      pm = bm_med3(pl, pm, pn, cmp);
+   }
+   BM_PVINIT(pv, pm);
+   pa = pb = a;
+   pc = pd = a + (n-1)*es;
+   for (;;) {
+      while (pb <= pc && (r = cmp(pb, pv)) <= 0) {
+         if (r == 0) { BM_SWAP(pa, pb); pa += es; }
+         pb += es;
+      }
+      while (pc >= pb && (r = cmp(pc, pv)) >= 0) {
+         if (r == 0) { BM_SWAP(pc, pd); pd -= es; }
+         pc -= es;
+      }
+      if (pb > pc) break;
+      BM_SWAP(pb, pc);
+      pb += es;
+      pc -= es;
+   }
+   pn = a + n*es;
+   s = BM_MIN(pa-a,  pb-pa   ); BM_VECSWAP(a,  pb-s, s);
+   s = BM_MIN(pd-pc, pn-pd-es); BM_VECSWAP(pb, pn-s, s);
+   /* Now recurse.  Do the smaller partition first with an explicit
+      recursion, then do the larger partition using a tail call.
+      Except we can't rely on gcc to implement a tail call in any sane
+      way, so simply jump back to the start.  This guarantees stack
+      growth can never exceed O(log N) even in the worst case. */
+   s1 = pb-pa;
+   s2 = pd-pc;
+   if (s1 < s2) {
+      if (s1 > es) {
+         bm_qsort(a, s1/es, es, cmp);
+      }
+      if (s2 > es) {
+         /* bm_qsort(pn-s2, s2/es, es, cmp); */
+         a = pn-s2; n = s2/es; es = es; cmp = cmp;
+         goto tailcall;
+      }
+   } else {
+      if (s2 > es) {
+         bm_qsort(pn-s2, s2/es, es, cmp);
+      }
+      if (s1 > es) {
+         /* bm_qsort(a, s1/es, es, cmp); */
+         a = a; n = s1/es; es = es; cmp = cmp;
+         goto tailcall;
+      } 
+   }
+}
+
+#undef BM_MIN
+#undef BM_SWAPINIT
+#undef BM_EXCH
+#undef BM_SWAP
+#undef BM_VECSWAP
+#undef BM_PVINIT
+
+/// end Bentley-McIlroy style quicksort
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+
+/* Returns the base-2 logarithm of x.  Returns -1 if x is not a power
+   of two. */
+Int VG_(log2) ( UInt x ) 
 {
    Int i;
    /* Any more than 32 and we overflow anyway... */
    for (i = 0; i < 32; i++) {
-      if (1 << i == x) return i;
+      if ((1U << i) == x) return i;
    }
    return -1;
 }
@@ -579,110 +734,10 @@
 void VG_(ssort)( void* base, SizeT nmemb, SizeT size,
                  Int (*compar)(void*, void*) )
 {
-   Int   incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
-                      9841, 29524, 88573, 265720,
-                      797161, 2391484 };
-   Int   lo = 0;
-   Int   hi = nmemb-1;
-   Int   i, j, h, bigN, hp;
-
-   bigN = hi - lo + 1; if (bigN < 2) return;
-   hp = 0; while (hp < 14 && incs[hp] < bigN) hp++; hp--;
-
-   #define SORT \
-   for ( ; hp >= 0; hp--) { \
-      h = incs[hp]; \
-      for (i = lo + h; i <= hi; i++) { \
-         ASSIGN(v,0, a,i); \
-         j = i; \
-         while (COMPAR(a,(j-h), v,0) > 0) { \
-            ASSIGN(a,j, a,(j-h)); \
-            j = j - h; \
-            if (j <= (lo + h - 1)) break; \
-         } \
-         ASSIGN(a,j, v,0); \
-      } \
-   }
-
-   // Specialised cases
-   if (sizeof(ULong) == size) {
-
-      #define ASSIGN(dst, dsti, src, srci) \
-      (dst)[(dsti)] = (src)[(srci)];      
-      
-      #define COMPAR(dst, dsti, src, srci) \
-      compar( (void*)(& (dst)[(dsti)]), (void*)(& (src)[(srci)]) )
-
-      ULong* a = (ULong*)base;
-      ULong  v[1];
-
-      SORT;
-
-   } else if (sizeof(UInt) == size) {
-
-      UInt* a = (UInt*)base;
-      UInt  v[1];
-
-      SORT;
-
-   } else if (sizeof(UShort) == size) {
-      UShort* a = (UShort*)base;
-      UShort  v[1];
-
-      SORT;
-
-   } else if (sizeof(UChar) == size) {
-      UChar* a = (UChar*)base;
-      UChar  v[1];
-
-      SORT;
-
-      #undef ASSIGN
-      #undef COMPAR
-
-   } else if ( (4*sizeof(UWord)) == size ) {
-      /* special-case 4 word-elements.  This captures a lot of cases
-         from symbol table reading/canonicalisaton, because both DiLoc
-         and DiSym are 4 word structures. */
-      HChar* a = base;
-      HChar  v[size];
-
-      #define ASSIGN(dst, dsti, src, srci) \
-       do { UWord* dP = (UWord*)&dst[size*(dsti)]; \
-            UWord* sP = (UWord*)&src[size*(srci)]; \
-            dP[0] = sP[0]; \
-            dP[1] = sP[1]; \
-            dP[2] = sP[2]; \
-            dP[3] = sP[3]; \
-          } while (0)
-
-      #define COMPAR(dst, dsti, src, srci) \
-      compar( &dst[size*(dsti)], &src[size*(srci)] )
-
-      SORT;
-
-      #undef ASSIGN
-      #undef COMPAR
-
-   // General case
-   } else {
-      HChar* a = base;
-      HChar  v[size];      // will be at least 'size' bytes
-
-      #define ASSIGN(dst, dsti, src, srci) \
-      VG_(memcpy)( &dst[size*(dsti)], &src[size*(srci)], size );
-
-      #define COMPAR(dst, dsti, src, srci) \
-      compar( &dst[size*(dsti)], &src[size*(srci)] )
-
-      SORT;
-
-      #undef ASSIGN
-      #undef COMPAR
-   }
-   #undef SORT
+   bm_qsort(base,nmemb,size,compar);
 }
 
+
 // This random number generator is based on the one suggested in Kernighan
 // and Ritchie's "The C Programming Language".
 
diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c
index aae2be1..42bdc17 100644
--- a/coregrind/m_machine.c
+++ b/coregrind/m_machine.c
@@ -193,22 +193,23 @@
    }
 }
 
-static ThreadId thread_stack_iter = VG_INVALID_THREADID;
-
-void VG_(thread_stack_reset_iter)(void)
+void VG_(thread_stack_reset_iter)(/*OUT*/ThreadId* tid)
 {
-   thread_stack_iter = 1;
+   *tid = (ThreadId)(-1);
 }
 
-Bool VG_(thread_stack_next)(ThreadId* tid, Addr* stack_min, Addr* stack_max)
+Bool VG_(thread_stack_next)(/*MOD*/ThreadId* tid,
+                            /*OUT*/Addr* stack_min, 
+                            /*OUT*/Addr* stack_max)
 {
    ThreadId i;
-   for (i = thread_stack_iter; i < VG_N_THREADS; i++) {
+   for (i = (*tid)+1; i < VG_N_THREADS; i++) {
+      if (i == VG_INVALID_THREADID)
+         continue;
       if (VG_(threads)[i].status != VgTs_Empty) {
          *tid       = i;
          *stack_min = VG_(get_SP)(i);
          *stack_max = VG_(threads)[i].client_stack_highest_word;
-         thread_stack_iter = i + 1;
          return True;
       }
    }
diff --git a/coregrind/m_main.c b/coregrind/m_main.c
index 5bae548..5914a2f 100644
--- a/coregrind/m_main.c
+++ b/coregrind/m_main.c
@@ -172,6 +172,7 @@
 "    --trace-sched=no|yes      show thread scheduler details? [no]\n"
 "    --wait-for-gdb=yes|no     pause on startup to wait for gdb attach\n"
 "    --sym-offsets=yes|no      show syms in form 'name+offset' ? [no]\n"
+"    --read-var-info=yes|no    read variable type & location info? [no]\n"
 "    --command-line-only=no|yes  only use command line options [no]\n"
 "\n"
 "    --vex-iropt-verbosity             0 .. 9 [0]\n"
@@ -423,6 +424,7 @@
       else VG_STR_CLO (arg, "--db-command",       VG_(clo_db_command))
       else VG_STR_CLO (arg, "--sim-hints",        VG_(clo_sim_hints))
       else VG_BOOL_CLO(arg, "--sym-offsets",      VG_(clo_sym_offsets))
+      else VG_BOOL_CLO(arg, "--read-var-info",    VG_(clo_read_var_info))
 
       else VG_NUM_CLO (arg, "--dump-error",       VG_(clo_dump_error))
       else VG_NUM_CLO (arg, "--input-fd",         VG_(clo_input_fd))
diff --git a/coregrind/m_mallocfree.c b/coregrind/m_mallocfree.c
index d316cdb..fe61ff5 100644
--- a/coregrind/m_mallocfree.c
+++ b/coregrind/m_mallocfree.c
@@ -518,7 +518,7 @@
       // Initialise the non-client arenas
       arena_init ( VG_AR_CORE,      "core",     4,             1048576 );
       arena_init ( VG_AR_TOOL,      "tool",     4,             4194304 );
-      arena_init ( VG_AR_SYMTAB,    "symtab",   4,             1048576 );
+      arena_init ( VG_AR_DINFO,     "dinfo",    4,             1048576 );
       arena_init ( VG_AR_DEMANGLE,  "demangle", 4,               65536 );
       arena_init ( VG_AR_EXECTXT,   "exectxt",  4,             1048576 );
       arena_init ( VG_AR_ERRORS,    "errors",   4,               65536 );
diff --git a/coregrind/m_options.c b/coregrind/m_options.c
index 4d7dd51..af47dbf 100644
--- a/coregrind/m_options.c
+++ b/coregrind/m_options.c
@@ -80,6 +80,7 @@
 Int    VG_(clo_backtrace_size) = 12;
 Char*  VG_(clo_sim_hints)      = NULL;
 Bool   VG_(clo_sym_offsets)    = False;
+Bool   VG_(clo_read_var_info)  = False;
 Bool   VG_(clo_run_libc_freeres) = True;
 Bool   VG_(clo_track_fds)      = False;
 Bool   VG_(clo_show_below_main)= False;
diff --git a/coregrind/m_oset.c b/coregrind/m_oset.c
index e45b07b..d176398 100644
--- a/coregrind/m_oset.c
+++ b/coregrind/m_oset.c
@@ -113,7 +113,7 @@
    OSetCmp_t   cmp;        // compare a key and an element, or NULL
    OSetAlloc_t alloc;      // allocator
    OSetFree_t  free;       // deallocator
-   Int         nElems;     // number of elements in the tree
+   Word        nElems;     // number of elements in the tree
    AvlNode*    root;       // root node
 
    AvlNode*    nodeStack[STACK_MAX];   // Iterator node stack
@@ -175,8 +175,8 @@
 // Compare the first word of each element.  Inlining is *crucial*.
 static inline Word fast_cmp(void* k, AvlNode* n)
 {
-   Word w1 = *(Word*)k;
-   Word w2 = *(Word*)elem_of_node(n);
+   UWord w1 = *(UWord*)k;
+   UWord w2 = *(UWord*)elem_of_node(n);
    // In previous versions, we tried to do this faster by doing
    // "return w1 - w2".  But it didn't work reliably, because the
    // complete result of subtracting two N-bit numbers is an N+1-bit
@@ -189,7 +189,8 @@
 }
 
 // Compare a key and an element.  Inlining is *crucial*.
-static inline Word slow_cmp(const AvlTree* t, const void* k, const AvlNode* n)
+static 
+inline Word slow_cmp(const AvlTree* t, const void* k, const AvlNode* n)
 {
    return t->cmp(k, elem_of_node(n));
 }
@@ -314,7 +315,8 @@
 void VG_(OSetGen_Destroy)(AvlTree* t)
 {
    AvlNode* n = NULL;
-   Int i = 0, sz = 0;
+   Int i = 0;
+   Word sz = 0;
    
    vg_assert(t);
    stackClear(t);
@@ -460,8 +462,9 @@
 
    vg_assert(t);
 
-   // Initialise.  Even though OSetGen_AllocNode zeroes these fields, we should
-   // do it again in case a node is removed and then re-added to the tree.
+   // Initialise.  Even though OSetGen_AllocNode zeroes these fields, 
+   // we should do it again in case a node is removed and then 
+   // re-added to the tree.
    n          = node_of_elem(e);
    n->left    = 0;
    n->right   = 0;
@@ -478,9 +481,9 @@
    t->stackTop = 0;  // So the iterator can't get out of sync
 }
 
-void VG_(OSetWord_Insert)(AvlTree* t, Word val)
+void VG_(OSetWord_Insert)(AvlTree* t, UWord val)
 {
-   Word* node = VG_(OSetGen_AllocNode)(t, sizeof(Word));
+   Word* node = VG_(OSetGen_AllocNode)(t, sizeof(UWord));
    *node = val;
    VG_(OSetGen_Insert)(t, node);
 }
@@ -509,11 +512,11 @@
       // elem_of_node because it saves about 10% on lookup time.  This
       // shouldn't be very dangerous because each node will have been
       // checked on insertion.
-      Word w1 = *(Word*)k;
-      Word w2;
+      UWord w1 = *(UWord*)k;
+      UWord w2;
       while (True) {
          if (curr == NULL) return NULL;
-         w2 = *(Word*)elem_of_node_no_check(curr);
+         w2 = *(UWord*)elem_of_node_no_check(curr);
          if      (w1 < w2) curr = curr->left;
          else if (w1 > w2) curr = curr->right;
          else return curr;
@@ -551,7 +554,7 @@
    return (NULL != VG_(OSetGen_Lookup)(t, k));
 }
 
-Bool VG_(OSetWord_Contains)(AvlTree* t, Word val)
+Bool VG_(OSetWord_Contains)(AvlTree* t, UWord val)
 {
    return (NULL != VG_(OSetGen_Lookup)(t, &val));
 }
@@ -683,7 +686,8 @@
    return False;
 }
 
-// Remove and return the element matching the key 'k', or NULL if not present.
+// Remove and return the element matching the key 'k', or NULL 
+// if not present.
 void* VG_(OSetGen_Remove)(AvlTree* t, const void* k)
 {
    // Have to find the node first, then remove it.
@@ -698,7 +702,7 @@
    }
 }
 
-Bool VG_(OSetWord_Remove)(AvlTree* t, Word val)
+Bool VG_(OSetWord_Remove)(AvlTree* t, UWord val)
 {
    void* n = VG_(OSetGen_Remove)(t, &val);
    if (n) {
@@ -760,9 +764,9 @@
    return NULL;
 }
 
-Bool VG_(OSetWord_Next)(AvlTree* t, Word* val)
+Bool VG_(OSetWord_Next)(AvlTree* t, UWord* val)
 {
-   Word* n = VG_(OSetGen_Next)(t);
+   UWord* n = VG_(OSetGen_Next)(t);
    if (n) {
       *val = *n;
       return True;
@@ -771,17 +775,81 @@
    }
 }
 
+// set up 'oset' for iteration so that the first key subsequently
+// produced VG_(OSetGen_Next) is the smallest key in the map 
+// >= start_at.  Naturally ">=" is defined by the comparison 
+// function supplied to VG_(OSetGen_Create).
+void VG_(OSetGen_ResetIterAt)(AvlTree* oset, void* k)
+{
+   Int     i;
+   AvlNode *n, *t;
+   Word    cmpresS; /* signed */
+   UWord   cmpresU; /* unsigned */
+
+   vg_assert(oset);
+   stackClear(oset);
+
+   if (!oset->root)
+      return;
+
+   n = NULL;
+   // We need to do regular search and fill in the stack.
+   t = oset->root;
+
+   while (True) {
+      if (t == NULL) return;
+
+      if (oset->cmp) {
+         cmpresS = (Word)slow_cmp(oset, k, t);
+      } else {
+         /* this is believed to be correct, but really needs testing
+            before the assertion is removed. */
+         vg_assert(0);
+         cmpresS = fast_cmp(k, t);
+      }
+
+      /* Switch the sense of the comparison, since the comparison
+         order of args (k vs t) above is opposite to that of the
+         corresponding code in hg_wordfm.c. */
+      if (cmpresS < 0) { cmpresS = 1; } 
+      else if (cmpresS > 0) { cmpresS = -1; }
+
+      if (cmpresS == 0) {
+         // We found the exact key -- we are done.
+         // The iteration should start with this node.
+         stackPush(oset, t, 2);
+         // The stack now looks like {2, 2, ... ,2, 2}
+         return;
+      }
+      cmpresU = (UWord)cmpresS;
+      cmpresU >>=/*unsigned*/ (8 * sizeof(cmpresU) - 1);
+      vg_assert(cmpresU == 0 || cmpresU == 1);
+      if (!cmpresU) {
+         // Push this node only if we go to the left child.
+         stackPush(oset, t, 2);
+      }
+      t = cmpresU==0 ? t->left : t->right;
+   }
+   if (stackPop(oset, &n, &i)) {
+      // If we've pushed something to stack and did not find the exact key,
+      // we must fix the top element of stack.
+      vg_assert(i == 2);
+      stackPush(oset, n, 3);
+      // the stack looks like {2, 2, ..., 2, 3}
+   }
+}
+
 /*--------------------------------------------------------------------*/
 /*--- Miscellaneous operations                                     ---*/
 /*--------------------------------------------------------------------*/
 
-Int VG_(OSetGen_Size)(const AvlTree* t)
+Word VG_(OSetGen_Size)(const AvlTree* t)
 {
    vg_assert(t);
    return t->nElems;
 }
 
-Int VG_(OSetWord_Size)(AvlTree* t)
+Word VG_(OSetWord_Size)(AvlTree* t)
 {
    return VG_(OSetGen_Size)(t);
 }
diff --git a/coregrind/m_redir.c b/coregrind/m_redir.c
index 6cb380e..f478707 100644
--- a/coregrind/m_redir.c
+++ b/coregrind/m_redir.c
@@ -239,9 +239,9 @@
 typedef
    struct _TopSpec {
       struct _TopSpec* next; /* linked list */
-      SegInfo* seginfo;      /* symbols etc */
-      Spec*    specs;        /* specs pulled out of seginfo */
-      Bool     mark; /* transient temporary used during deletion */
+      DebugInfo* seginfo;    /* symbols etc */
+      Spec*      specs;      /* specs pulled out of seginfo */
+      Bool       mark; /* transient temporary used during deletion */
    }
    TopSpec;
 
@@ -280,9 +280,9 @@
 
 static void maybe_add_active ( Active /*by value; callee copies*/ );
 
-static void*  symtab_zalloc(SizeT);
-static void   symtab_free(void*);
-static HChar* symtab_strdup(HChar*);
+static void*  dinfo_zalloc(SizeT);
+static void   dinfo_free(void*);
+static HChar* dinfo_strdup(HChar*);
 static Bool   is_plausible_guest_addr(Addr);
 static Bool   is_aix5_glink_idiom(Addr);
 
@@ -302,19 +302,19 @@
         /* spec list and the owning TopSpec */
         Spec*    specs, 
         TopSpec* parent_spec,
-	/* seginfo and the owning TopSpec */
-        SegInfo* si,
+	/* debuginfo and the owning TopSpec */
+        DebugInfo* di,
         TopSpec* parent_sym 
      );
 
-/* Notify m_redir of the arrival of a new SegInfo.  This is fairly
+/* Notify m_redir of the arrival of a new DebugInfo.  This is fairly
    complex, but the net effect is to (1) add a new entry to the
    topspecs list, and (2) figure out what new binding are now active,
    and, as a result, add them to the actives mapping. */
 
 #define N_DEMANGLED 256
 
-void VG_(redir_notify_new_SegInfo)( SegInfo* newsi )
+void VG_(redir_notify_new_DebugInfo)( DebugInfo* newsi )
 {
    Bool         ok, isWrap;
    Int          i, nsyms;
@@ -327,6 +327,7 @@
    HChar        demangled_sopatt[N_DEMANGLED];
    HChar        demangled_fnpatt[N_DEMANGLED];
    Bool         check_ppcTOCs = False;
+   Bool         isText;
    const UChar* newsi_soname;
 
 #  if defined(VG_PLAT_USES_PPCTOC)
@@ -341,7 +342,7 @@
    for (ts = topSpecs; ts; ts = ts->next)
       vg_assert(ts->seginfo != newsi);
 
-   /* scan this SegInfo's symbol table, pulling out and demangling
+   /* scan this DebugInfo's symbol table, pulling out and demangling
       any specs found */
 
    specList = NULL; /* the spec list we're building up */
@@ -349,9 +350,12 @@
    nsyms = VG_(seginfo_syms_howmany)( newsi );
    for (i = 0; i < nsyms; i++) {
       VG_(seginfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc, 
-                                          NULL, &sym_name );
+                                          NULL, &sym_name, &isText );
       ok = VG_(maybe_Z_demangle)( sym_name, demangled_sopatt, N_DEMANGLED,
                                   demangled_fnpatt, N_DEMANGLED, &isWrap );
+      /* ignore data symbols */
+      if (!isText)
+         continue;
       if (!ok) {
          /* It's not a full-scale redirect, but perhaps it is a load-notify
             fn?  Let the load-notify department see it. */
@@ -365,10 +369,10 @@
             the following loop, and complain at that point. */
          continue;
       }
-      spec = symtab_zalloc(sizeof(Spec));
+      spec = dinfo_zalloc(sizeof(Spec));
       vg_assert(spec);
-      spec->from_sopatt = symtab_strdup(demangled_sopatt);
-      spec->from_fnpatt = symtab_strdup(demangled_fnpatt);
+      spec->from_sopatt = dinfo_strdup(demangled_sopatt);
+      spec->from_fnpatt = dinfo_strdup(demangled_fnpatt);
       vg_assert(spec->from_sopatt);
       vg_assert(spec->from_fnpatt);
       spec->to_addr = sym_addr;
@@ -384,9 +388,11 @@
    if (check_ppcTOCs) {
       for (i = 0; i < nsyms; i++) {
          VG_(seginfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc, 
-                                             NULL, &sym_name );
-         ok = VG_(maybe_Z_demangle)( sym_name, demangled_sopatt, N_DEMANGLED,
-                                     demangled_fnpatt, N_DEMANGLED, &isWrap );
+                                             NULL, &sym_name, &isText );
+         ok = isText
+              && VG_(maybe_Z_demangle)( 
+                    sym_name, demangled_sopatt, N_DEMANGLED,
+                    demangled_fnpatt, N_DEMANGLED, &isWrap );
          if (!ok)
             /* not a redirect.  Ignore. */
             continue;
@@ -410,9 +416,9 @@
       }
    }
 
-   /* Ok.  Now specList holds the list of specs from the SegInfo. 
+   /* Ok.  Now specList holds the list of specs from the DebugInfo. 
       Build a new TopSpec, but don't add it to topSpecs yet. */
-   newts = symtab_zalloc(sizeof(TopSpec));
+   newts = dinfo_zalloc(sizeof(TopSpec));
    vg_assert(newts);
    newts->next    = NULL; /* not significant */
    newts->seginfo = newsi;
@@ -458,7 +464,7 @@
    topSpecs = newts;
 
    if (VG_(clo_trace_redir))
-      show_redir_state("after VG_(redir_notify_new_SegInfo)");
+      show_redir_state("after VG_(redir_notify_new_DebugInfo)");
 }
 
 #undef N_DEMANGLED
@@ -475,12 +481,12 @@
         Spec*    specs, 
         TopSpec* parent_spec,
 	/* seginfo and the owning TopSpec */
-        SegInfo* si,
+        DebugInfo* di,
         TopSpec* parent_sym 
      )
 {
    Spec*  sp;
-   Bool   anyMark;
+   Bool   anyMark, isText;
    Active act;
    Int    nsyms, i;
    Addr   sym_addr;
@@ -493,7 +499,7 @@
    for (sp = specs; sp; sp = sp->next) {
       sp->done = False;
       sp->mark = VG_(string_match)( sp->from_sopatt, 
-                                    VG_(seginfo_soname)(si) );
+                                    VG_(seginfo_soname)(di) );
       anyMark = anyMark || sp->mark;
    }
 
@@ -503,9 +509,14 @@
 
    /* Iterate outermost over the symbols in the seginfo, in the hope
       of trashing the caches less. */
-   nsyms = VG_(seginfo_syms_howmany)( si );
+   nsyms = VG_(seginfo_syms_howmany)( di );
    for (i = 0; i < nsyms; i++) {
-      VG_(seginfo_syms_getidx)( si, i, &sym_addr, NULL, NULL, &sym_name );
+      VG_(seginfo_syms_getidx)( di, i,
+                                &sym_addr, NULL, NULL, &sym_name, &isText );
+
+      /* ignore data symbols */
+      if (!isText)
+         continue;
 
       /* On AIX, we cannot redirect calls to a so-called glink
          function for reasons which are not obvious - something to do
@@ -565,7 +576,7 @@
       VG_(printf)(
       "%swas not found whilst processing\n", v);
       VG_(printf)(
-      "%ssymbols from the object with soname: %s\n", v, VG_(seginfo_soname)(si));
+      "%ssymbols from the object with soname: %s\n", v, VG_(seginfo_soname)(di));
       VG_(printf)(
       "%s\n", v);
       VG_(printf)(
@@ -632,9 +643,9 @@
          paranoia (but, I believe, unnecessarily), discard 'to' as
          well. */
       VG_(discard_translations)( (Addr64)act.from_addr, 1,
-                                 "redir_new_SegInfo(from_addr)");
+                                 "redir_new_DebugInfo(from_addr)");
       VG_(discard_translations)( (Addr64)act.to_addr, 1,
-                                 "redir_new_SegInfo(to_addr)");
+                                 "redir_new_DebugInfo(to_addr)");
    }
    return;
 
@@ -647,11 +658,11 @@
 }
 
 
-/* Notify m_redir of the deletion of a SegInfo.  This is relatively
+/* Notify m_redir of the deletion of a DebugInfo.  This is relatively
    simple -- just get rid of all actives derived from it, and free up
    the associated list elements. */
 
-void VG_(redir_notify_delete_SegInfo)( SegInfo* delsi )
+void VG_(redir_notify_delete_DebugInfo)( DebugInfo* delsi )
 {
    TopSpec* ts;
    TopSpec* tsPrev;
@@ -675,12 +686,12 @@
      ts = ts->next;
    }
 
-   vg_assert(ts); /* else we don't have the deleted SegInfo */
+   vg_assert(ts); /* else we don't have the deleted DebugInfo */
    vg_assert(ts->seginfo == delsi);
 
    /* Traverse the actives, copying the addresses of those we intend
       to delete into tmpSet. */
-   tmpSet = VG_(OSetWord_Create)(symtab_zalloc, symtab_free);
+   tmpSet = VG_(OSetWord_Create)(dinfo_zalloc, dinfo_free);
 
    ts->mark = True;
 
@@ -710,9 +721,9 @@
          /* While we have our hands on both the 'from' and 'to'
             of this Active, do paranoid stuff with tt/tc. */
          VG_(discard_translations)( (Addr64)act->from_addr, 1,
-                                    "redir_del_SegInfo(from_addr)");
+                                    "redir_del_DebugInfo(from_addr)");
          VG_(discard_translations)( (Addr64)act->to_addr, 1,
-                                    "redir_del_SegInfo(to_addr)");
+                                    "redir_del_DebugInfo(to_addr)");
       }
    }
 
@@ -729,10 +740,10 @@
    /* The Actives set is now cleaned up.  Free up this TopSpec and
       everything hanging off it. */
    for (sp = ts->specs; sp; sp = sp_next) {
-      if (sp->from_sopatt) symtab_free(sp->from_sopatt);
-      if (sp->from_fnpatt) symtab_free(sp->from_fnpatt);
+      if (sp->from_sopatt) dinfo_free(sp->from_sopatt);
+      if (sp->from_fnpatt) dinfo_free(sp->from_fnpatt);
       sp_next = sp->next;
-      symtab_free(sp);
+      dinfo_free(sp);
    }
 
    if (tsPrev == NULL) {
@@ -741,10 +752,10 @@
    } else {
       tsPrev->next = ts->next;
    }
-   symtab_free(ts);
+   dinfo_free(ts);
 
    if (VG_(clo_trace_redir))
-      show_redir_state("after VG_(redir_notify_delete_SegInfo)");
+      show_redir_state("after VG_(redir_notify_delete_DebugInfo)");
 }
 
 
@@ -798,11 +809,11 @@
                                  Addr   to_addr,
                                  HChar* mandatory )
 {
-   Spec* spec = symtab_zalloc(sizeof(Spec));
+   Spec* spec = dinfo_zalloc(sizeof(Spec));
    vg_assert(spec);
 
    if (topSpecs == NULL) {
-      topSpecs = symtab_zalloc(sizeof(TopSpec));
+      topSpecs = dinfo_zalloc(sizeof(TopSpec));
       vg_assert(topSpecs);
       /* symtab_zalloc sets all fields to zero */
    }
@@ -828,19 +839,19 @@
 /* Initialise the redir system, and create the initial Spec list and
    for amd64-linux a couple of permanent active mappings.  The initial
    Specs are not converted into Actives yet, on the (checked)
-   assumption that no SegInfos have so far been created, and so when
+   assumption that no DebugInfos have so far been created, and so when
    they are created, that will happen. */
 
 void VG_(redir_initialise) ( void )
 {
-   // Assert that there are no SegInfos so far
+   // Assert that there are no DebugInfos so far
    vg_assert( VG_(next_seginfo)(NULL) == NULL );
 
    // Initialise active mapping.
    activeSet = VG_(OSetGen_Create)(offsetof(Active, from_addr),
                                    NULL,     // Use fast comparison
-                                   symtab_zalloc,
-                                   symtab_free);
+                                   dinfo_zalloc,
+                                   dinfo_free);
 
    // The rest of this function just adds initial Specs.   
 
@@ -937,23 +948,23 @@
 /*--- MISC HELPERS                                         ---*/
 /*------------------------------------------------------------*/
 
-static void* symtab_zalloc(SizeT n) {
+static void* dinfo_zalloc(SizeT n) {
    void* p;
    vg_assert(n > 0);
-   p = VG_(arena_malloc)(VG_AR_SYMTAB, n);
+   p = VG_(arena_malloc)(VG_AR_DINFO, n);
    tl_assert(p);
    VG_(memset)(p, 0, n);
    return p;
 }
 
-static void symtab_free(void* p) {
+static void dinfo_free(void* p) {
    tl_assert(p);
-   return VG_(arena_free)(VG_AR_SYMTAB, p);
+   return VG_(arena_free)(VG_AR_DINFO, p);
 }
 
-static HChar* symtab_strdup(HChar* str)
+static HChar* dinfo_strdup(HChar* str)
 {
-   return VG_(arena_strdup)(VG_AR_SYMTAB, str);
+   return VG_(arena_strdup)(VG_AR_DINFO, str);
 }
 
 /* Really this should be merged with translations_allowable_from_seg
diff --git a/coregrind/m_stacktrace.c b/coregrind/m_stacktrace.c
index 2e80e99..f1073e2 100644
--- a/coregrind/m_stacktrace.c
+++ b/coregrind/m_stacktrace.c
@@ -57,10 +57,11 @@
    first parameter, else send zero.  This helps generate better stack
    traces on ppc64-linux and has no effect on other platforms.
 */
-UInt VG_(get_StackTrace2) ( ThreadId tid_if_known,
-                            Addr* ips, UInt n_ips, 
-                            Addr ip, Addr sp, Addr fp, Addr lr,
-                            Addr fp_min, Addr fp_max_orig )
+UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
+                               /*OUT*/Addr* ips, UInt n_ips,
+                               /*OUT*/Addr* sps, /*OUT*/Addr* fps,
+                               Addr ip, Addr sp, Addr fp, Addr lr,
+                               Addr fp_min, Addr fp_max_orig )
 {
 #  if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) \
                                || defined(VGP_ppc32_aix5) \
@@ -92,7 +93,8 @@
    fp_max -= sizeof(Addr);
 
    if (debug)
-      VG_(printf)("n_ips=%d fp_min=%p fp_max_orig=%p, fp_max=%p ip=%p fp=%p\n",
+      VG_(printf)("n_ips=%d fp_min=%p fp_max_orig=%p, "
+                  "fp_max=%p ip=%p fp=%p\n",
 		  n_ips, fp_min, fp_max_orig, fp_max, ip, fp);
 
    /* Assertion broken before main() is reached in pthreaded programs;  the
@@ -116,6 +118,8 @@
 
    /* fp is %ebp.  sp is %esp.  ip is %eip. */
 
+   if (sps) sps[0] = sp;
+   if (fps) fps[0] = fp;
    ips[0] = ip;
    i = 1;
 
@@ -133,6 +137,11 @@
     * This most frequently happens at the end of a function when
     * a tail call occurs and we wind up using the CFI info for the
     * next function which is completely wrong.
+    *
+    * Note that VG_(get_data_description) (in m_debuginfo) has to take
+    * this same problem into account when unwinding the stack to
+    * examine local variable descriptions (as documented therein in
+    * comments).
     */
    while (True) {
 
@@ -155,6 +164,8 @@
          sp = fp + sizeof(Addr) /*saved %ebp*/ 
                  + sizeof(Addr) /*ra*/;
          fp = (((UWord*)fp)[0]);
+         if (sps) sps[i] = sp;
+         if (fps) fps[i] = fp;
          ips[i++] = ip;
          if (debug)
             VG_(printf)("     ipsF[%d]=0x%08lx\n", i-1, ips[i-1]);
@@ -165,6 +176,8 @@
       /* That didn't work out, so see if there is any CF info to hand
          which can be used. */
       if ( VG_(use_CF_info)( &ip, &sp, &fp, fp_min, fp_max ) ) {
+         if (sps) sps[i] = sp;
+         if (fps) fps[i] = fp;
          ips[i++] = ip;
          if (debug)
             VG_(printf)("     ipsC[%d]=0x%08lx\n", i-1, ips[i-1]);
@@ -183,6 +196,8 @@
    /* fp is %rbp.  sp is %rsp.  ip is %rip. */
 
    ips[0] = ip;
+   if (sps) sps[0] = sp;
+   if (fps) fps[0] = fp;
    i = 1;
 
    /* Loop unwinding the stack. Note that the IP value we get on
@@ -199,6 +214,11 @@
     * This most frequently happens at the end of a function when
     * a tail call occurs and we wind up using the CFI info for the
     * next function which is completely wrong.
+    *
+    * Note that VG_(get_data_description) (in m_debuginfo) has to take
+    * this same problem into account when unwinding the stack to
+    * examine local variable descriptions (as documented therein in
+    * comments).
     */
    while (True) {
 
@@ -211,6 +231,8 @@
       /* First off, see if there is any CFI info to hand which can
          be used. */
       if ( VG_(use_CF_info)( &ip, &sp, &fp, fp_min, fp_max ) ) {
+         if (sps) sps[i] = sp;
+         if (fps) fps[i] = fp;
          ips[i++] = ip;
          if (debug)
             VG_(printf)("     ipsC[%d]=%08p\n", i-1, ips[i-1]);
@@ -232,6 +254,8 @@
          sp = fp + sizeof(Addr) /*saved %rbp*/ 
                  + sizeof(Addr) /*ra*/;
          fp = (((UWord*)fp)[0]);
+         if (sps) sps[i] = sp;
+         if (fps) fps[i] = fp;
          ips[i++] = ip;
          if (debug)
             VG_(printf)("     ipsF[%d]=%08p\n", i-1, ips[i-1]);
@@ -253,6 +277,8 @@
       */
       if (fp_min <= sp && sp < fp_max) {
          ip = ((UWord*)sp)[0];
+         if (sps) sps[i] = sp;
+         if (fps) fps[i] = fp;
          ips[i++] = ip;
          if (debug)
             VG_(printf)("     ipsH[%d]=%08p\n", i-1, ips[i-1]);
@@ -315,6 +341,8 @@
 #     undef M_VG_ERRTXT
    }
 
+   if (sps) sps[0] = fp; /* NB. not sp */
+   if (fps) fps[0] = fp;
    ips[0] = ip;
    i = 1;
 
@@ -361,7 +389,8 @@
                used by the unwinding so far with 'redirs_used'. */
             if (ip == (Addr)&VG_(ppctoc_magic_redirect_return_stub)
                 && VG_(is_valid_tid)(tid_if_known)) {
-               Word hsp = VG_(threads)[tid_if_known].arch.vex.guest_REDIR_SP;
+               Word hsp = VG_(threads)[tid_if_known]
+                             .arch.vex.guest_REDIR_SP;
                hsp -= 2 * redirs_used;
                redirs_used ++;
                if (hsp >= 1 && hsp < redir_stack_size)
@@ -371,6 +400,8 @@
 #           endif
 
             fp = (((UWord*)fp)[0]);
+            if (sps) sps[i] = fp; /* NB. not sp */
+            if (fps) fps[i] = fp;
             ips[i++] = ip;
             if (debug)
                VG_(printf)("     ipsF[%d]=%08p\n", i-1, ips[i-1]);
@@ -390,7 +421,10 @@
    return n_found;
 }
 
-UInt VG_(get_StackTrace) ( ThreadId tid, StackTrace ips, UInt n_ips, 
+UInt VG_(get_StackTrace) ( ThreadId tid, 
+                           /*OUT*/StackTrace ips, UInt n_ips,
+                           /*OUT*/StackTrace sps,
+                           /*OUT*/StackTrace fps,
                            Word first_ip_delta )
 {
    /* thread in thread table */
@@ -436,11 +470,14 @@
    ip += first_ip_delta;
 
    if (0)
-      VG_(printf)("tid %d: stack_highest=0x%08lx ip=0x%08lx sp=0x%08lx fp=0x%08lx\n",
+      VG_(printf)("tid %d: stack_highest=0x%08lx ip=0x%08lx "
+                  "sp=0x%08lx fp=0x%08lx\n",
 		  tid, stack_highest_word, ip, sp, fp);
 
-   return VG_(get_StackTrace2)(tid, ips, n_ips, ip, sp, fp, lr, sp, 
-                                    stack_highest_word);
+   return VG_(get_StackTrace_wrk)(tid, ips, n_ips, 
+                                       sps, fps,
+                                       ip, sp, fp, lr, sp, 
+                                       stack_highest_word);
 }
 
 static void printIpDesc(UInt n, Addr ip)
@@ -476,8 +513,11 @@
 void VG_(get_and_pp_StackTrace) ( ThreadId tid, UInt n_ips )
 {
    Addr ips[n_ips];
-   UInt n_ips_obtained = VG_(get_StackTrace)(tid, ips, n_ips,
-                                             0/*first_ip_delta*/);
+   UInt n_ips_obtained 
+      = VG_(get_StackTrace)(tid, ips, n_ips,
+                            NULL/*array to dump SP values in*/,
+                            NULL/*array to dump FP values in*/,
+                            0/*first_ip_delta*/);
    VG_(pp_StackTrace)(ips, n_ips_obtained);
 }
 
diff --git a/coregrind/m_tooliface.c b/coregrind/m_tooliface.c
index 0b78202..a0aa2fd 100644
--- a/coregrind/m_tooliface.c
+++ b/coregrind/m_tooliface.c
@@ -91,7 +91,7 @@
    .client_requests      = False,
    .syscall_wrapper      = False,
    .sanity_checks        = False,
-   .data_syms	         = False,
+   .var_info	         = False,
    .malloc_replacement   = False,
    .xml_output           = False,
    .final_IR_tidy_pass   = False
@@ -162,7 +162,7 @@
 // These ones don't require any tool-supplied functions
 NEEDS(libc_freeres)
 NEEDS(core_errors)
-NEEDS(data_syms)
+NEEDS(var_info)
 NEEDS(xml_output)
 
 void VG_(needs_superblock_discards)(
diff --git a/coregrind/m_translate.c b/coregrind/m_translate.c
index 1af3ca2..5857dd9 100644
--- a/coregrind/m_translate.c
+++ b/coregrind/m_translate.c
@@ -1011,9 +1011,9 @@
    wrapped/redirected function to lead to our magic return stub, which
    restores LR and R2 from said stack and returns for real.
 
-   VG_(get_StackTrace2) understands that the LR value may point to the
-   return stub address, and that in that case it can get the real LR
-   value from the hidden stack instead. */
+   VG_(get_StackTrace_wrk) understands that the LR value may point to
+   the return stub address, and that in that case it can get the real
+   LR value from the hidden stack instead. */
 static 
 Bool mk_preamble__set_NRADDR_to_zero ( void* closureV, IRSB* bb )
 {
diff --git a/coregrind/m_xarray.c b/coregrind/m_xarray.c
index a0d5d19..8b3abf6 100644
--- a/coregrind/m_xarray.c
+++ b/coregrind/m_xarray.c
@@ -76,6 +76,32 @@
    return xa;
 }
 
+XArray* VG_(cloneXA)( XArray* xao )
+{
+   struct _XArray* xa = (struct _XArray*)xao;
+   struct _XArray* nyu;
+   vg_assert(xa);
+   vg_assert(xa->alloc);
+   vg_assert(xa->free);
+   vg_assert(xa->elemSzB >= 1);
+   nyu = xa->alloc( sizeof(struct _XArray) );
+   if (!nyu)
+      return NULL;
+   /* Copy everything verbatim ... */
+   *nyu = *xa;
+   /* ... except we have to clone the contents-array */
+   if (nyu->arr) {
+      nyu->arr = nyu->alloc( nyu->totsizeE * nyu->elemSzB );
+      if (!nyu->arr) {
+         nyu->free(nyu);
+         return NULL;
+      }
+      VG_(memcpy)( nyu->arr, xa->arr, nyu->totsizeE * nyu->elemSzB );
+   }
+   /* We're done! */
+   return nyu;
+}
+
 void VG_(deleteXA) ( XArray* xao )
 {
    struct _XArray* xa = (struct _XArray*)xao;
@@ -104,13 +130,8 @@
    return ((char*)xa->arr) + n * xa->elemSzB;
 }
 
-Int VG_(addToXA) ( XArray* xao, void* elem )
+static inline void ensureSpaceXA ( struct _XArray* xa )
 {
-   struct _XArray* xa = (struct _XArray*)xao;
-   vg_assert(xa);
-   vg_assert(elem);
-   vg_assert(xa->totsizeE >= 0);
-   vg_assert(xa->usedsizeE >= 0 && xa->usedsizeE <= xa->totsizeE);
    if (xa->usedsizeE == xa->totsizeE) {
       void* tmp;
       Word  newsz;
@@ -118,7 +139,18 @@
          vg_assert(!xa->arr);
       if (xa->totsizeE > 0)
          vg_assert(xa->arr);
-      newsz = xa->totsizeE==0 ? 2 : 2 * xa->totsizeE;
+      if (xa->totsizeE == 0) {
+         /* No point in having tiny (eg) 2-byte allocations for the
+            element array, since all allocs are rounded up to 8 anyway.
+            Hence increase the initial array size for tiny elements in
+            an attempt to avoid reallocations of size 2, 4, 8 if the
+            array does start to fill up. */
+         if (xa->elemSzB == 1) newsz = 8;
+         else if (xa->elemSzB == 2) newsz = 4;
+         else newsz = 2;
+      } else {
+         newsz = 2 * xa->totsizeE;
+      }
       if (0) 
          VG_(printf)("addToXA: increasing from %ld to %ld\n", 
                      xa->totsizeE, newsz);
@@ -131,6 +163,16 @@
       xa->arr = tmp;
       xa->totsizeE = newsz;
    }
+}
+
+Int VG_(addToXA) ( XArray* xao, void* elem )
+{
+   struct _XArray* xa = (struct _XArray*)xao;
+   vg_assert(xa);
+   vg_assert(elem);
+   vg_assert(xa->totsizeE >= 0);
+   vg_assert(xa->usedsizeE >= 0 && xa->usedsizeE <= xa->totsizeE);
+   ensureSpaceXA( xa );
    vg_assert(xa->usedsizeE < xa->totsizeE);
    vg_assert(xa->arr);
    VG_(memcpy)( ((UChar*)xa->arr) + xa->usedsizeE * xa->elemSzB,
@@ -140,6 +182,27 @@
    return xa->usedsizeE-1;
 }
 
+Int VG_(addBytesToXA) ( XArray* xao, void* bytesV, Int nbytes )
+{
+   Int r, i;
+   struct _XArray* xa = (struct _XArray*)xao;
+   vg_assert(xa);
+   vg_assert(xa->elemSzB == 1);
+   vg_assert(nbytes >= 0);
+   vg_assert(xa->totsizeE >= 0);
+   vg_assert(xa->usedsizeE >= 0 && xa->usedsizeE <= xa->totsizeE);
+   r = xa->usedsizeE;
+   for (i = 0; i < nbytes; i++) {
+      ensureSpaceXA( xa );
+      vg_assert(xa->usedsizeE < xa->totsizeE);
+      vg_assert(xa->arr);
+      * (((UChar*)xa->arr) + xa->usedsizeE) = ((UChar*)bytesV)[i];
+      xa->usedsizeE++;
+   }
+   xa->sorted = False;
+   return r;
+}
+
 void VG_(sortXA) ( XArray* xao )
 {
    struct _XArray* xa = (struct _XArray*)xao;
diff --git a/coregrind/pub_core_mallocfree.h b/coregrind/pub_core_mallocfree.h
index 0980622..f643862 100644
--- a/coregrind/pub_core_mallocfree.h
+++ b/coregrind/pub_core_mallocfree.h
@@ -42,7 +42,7 @@
 
       CORE      for the core's general use.
       TOOL      for the tool to use (and the only one it uses).
-      SYMTAB    for Valgrind's symbol table storage.
+      DINFO     for debug info (symbols, line #s, CFI, etc) storage.
       CLIENT    for the client's mallocs/frees, if the tool replaces glibc's
                     malloc() et al -- redzone size is chosen by the tool.
       DEMANGLE  for the C++ demangler.
@@ -59,7 +59,7 @@
 
 #define VG_AR_CORE         0
 #define VG_AR_TOOL         1
-#define VG_AR_SYMTAB       2
+#define VG_AR_DINFO        2
 #define VG_AR_CLIENT       3
 #define VG_AR_DEMANGLE     4
 #define VG_AR_EXECTXT      5
diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h
index c28d385..c8495e0 100644
--- a/coregrind/pub_core_options.h
+++ b/coregrind/pub_core_options.h
@@ -135,6 +135,8 @@
 extern Char* VG_(clo_sim_hints);
 /* Show symbols in the form 'name+offset' ?  Default: NO */
 extern Bool VG_(clo_sym_offsets);
+/* Read DWARF3 variable info even if tool doesn't ask for it? */
+extern Bool VG_(clo_read_var_info);
 
 /* Track open file descriptors? */
 extern Bool  VG_(clo_track_fds);
diff --git a/coregrind/pub_core_redir.h b/coregrind/pub_core_redir.h
index 1f84d21..e1bf487 100644
--- a/coregrind/pub_core_redir.h
+++ b/coregrind/pub_core_redir.h
@@ -48,12 +48,12 @@
 // Notifications - by which we are told of state changes
 //--------------------------------------------------------------------
 
-/* Notify the module of a new SegInfo (called from m_debuginfo). */
-extern void VG_(redir_notify_new_SegInfo)( SegInfo* );
+/* Notify the module of a new DebugInfo (called from m_debuginfo). */
+extern void VG_(redir_notify_new_DebugInfo)( DebugInfo* );
 
-/* Notify the module of the disappearance of a SegInfo (also called
+/* Notify the module of the disappearance of a DebugInfo (also called
    from m_debuginfo). */
-extern void VG_(redir_notify_delete_SegInfo)( SegInfo* );
+extern void VG_(redir_notify_delete_DebugInfo)( DebugInfo* );
 
 /* Initialise the module, and load initial "hardwired" redirects. */
 extern void VG_(redir_initialise)( void );
diff --git a/coregrind/pub_core_stacktrace.h b/coregrind/pub_core_stacktrace.h
index 1c4e63f..1c32cbd 100644
--- a/coregrind/pub_core_stacktrace.h
+++ b/coregrind/pub_core_stacktrace.h
@@ -38,15 +38,23 @@
 
 #include "pub_tool_stacktrace.h"
 
-// Variant that gives a little more control over the stack-walking.
+// Variant that gives a little more control over the stack-walking
+// (this is the "worker" function that actually does the walking).
 // If you know what the thread ID for this stack is, send that
 // as the first parameter, else send zero.  This helps generate
 // better stack traces on ppc64-linux and has no effect on other
 // platforms.
-extern UInt VG_(get_StackTrace2) ( ThreadId tid_if_known,
-                                   StackTrace ips, UInt n_ips, 
-                                   Addr ip, Addr sp, Addr fp, Addr lr,
-                                   Addr fp_min, Addr fp_max );
+//
+// The acquired IP values are placed in
+// ips[0 .. min(n_ips,return_value)].  If sps and fps are non-NULL,
+// the corresponding frame-pointer and stack-pointer values for each
+// frame are stored there.
+
+UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
+                               /*OUT*/Addr* ips, UInt n_ips,
+                               /*OUT*/Addr* sps, /*OUT*/Addr* fps,
+                               Addr ip, Addr sp, Addr fp, Addr lr,
+                               Addr fp_min, Addr fp_max_orig );
 
 #endif   // __PUB_CORE_STACKTRACE_H
 
diff --git a/coregrind/pub_core_tooliface.h b/coregrind/pub_core_tooliface.h
index b46f5ca..aee88d3 100644
--- a/coregrind/pub_core_tooliface.h
+++ b/coregrind/pub_core_tooliface.h
@@ -88,7 +88,7 @@
       Bool client_requests;
       Bool syscall_wrapper;
       Bool sanity_checks;
-      Bool data_syms;
+      Bool var_info;
       Bool malloc_replacement;
       Bool xml_output;
       Bool final_IR_tidy_pass;
diff --git a/docs/internals/xml-output.txt b/docs/internals/xml-output.txt
index 03cea95..7b5d502 100644
--- a/docs/internals/xml-output.txt
+++ b/docs/internals/xml-output.txt
@@ -106,8 +106,19 @@
     PROTOCOL
   </valgrindoutput>
 
-Valgrind versions 3.0.0 and 3.0.1 emit protocol version 1.  Version
-3.1.0 emits protocol version 2.
+Valgrind versions 3.0.0 and 3.0.1 emit protocol version 1.  Versions
+3.1.X and 3.2.X emit protocol version 2.
+
+
+PROTOCOL for version 3
+----------------------
+Changes in 3.3.X (tentative): (jrs, 1 March 2008)
+
+* There may be more than one <logfilequalifier> clause, depending on
+  how this pans out.  (AshleyP perhaps to investigate)
+
+* Some errors may have two <auxwhat> blocks, rather than just one
+  (resulting from merge of the DATASYMS branch)
 
 
 PROTOCOL for version 2
diff --git a/exp-drd/drd_error.c b/exp-drd/drd_error.c
index 9d70164..05bf4a4 100644
--- a/exp-drd/drd_error.c
+++ b/exp-drd/drd_error.c
@@ -68,7 +68,7 @@
 void describe_addr(Addr const a, SizeT const len, AddrInfo* const ai)
 {
    Addr       stack_min, stack_max;
-   SegInfo*   sg;
+   DebugInfo* sg;
 
    /* Perhaps it's on a thread's stack? */
    ai->stack_tid = thread_lookup_stackaddr(a, &stack_min, &stack_max);
@@ -89,7 +89,7 @@
       int i, n;
 
       ai->akind   = eSegment;
-      ai->seginfo = sg;
+      ai->debuginfo = sg;
       ai->name[0] = 0;
       ai->size = 1;
       ai->rwoffset = 0;
@@ -103,9 +103,10 @@
          HChar* name;
          Char filename[256];
          Int linenum;
+         Bool isText;
 
-         VG_(seginfo_syms_getidx)(sg, i, &addr, &tocptr, &size, &name);
-         if (addr <= a && a < addr + size)
+         VG_(seginfo_syms_getidx)(sg, i, &addr, &tocptr, &size, &name, &isText);
+         if (isText && addr <= a && a < addr + size)
          {
             ai->size     = size;
             ai->rwoffset = a - addr;
@@ -131,10 +132,8 @@
       {
          Char filename[512];
          Char soname[512];
-         Char sect_kind_name[16];
-
-         VG_(seginfo_sect_kind_name)(a, sect_kind_name,
-                                     sizeof(sect_kind_name));
+         VgSectKind kind = VG_(seginfo_sect_kind)(NULL, 0, a);
+         const HChar* sect_kind_name = VG_(pp_SectKind)(kind);
          VG_(strncpy)(filename, VG_(seginfo_filename)(sg), sizeof(filename));
          filename[sizeof(filename) - 1] = 0;
          make_path_relative(filename);
diff --git a/exp-drd/drd_error.h b/exp-drd/drd_error.h
index 6607366..1ef243e 100644
--- a/exp-drd/drd_error.h
+++ b/exp-drd/drd_error.h
@@ -71,7 +71,7 @@
    OffT        rwoffset;      //   ALL
    ExeContext* lastchange;    //   Mallocd
    DrdThreadId stack_tid;     //   Stack
-   SegInfo*    seginfo;       //   Segment
+   DebugInfo*  debuginfo;     //   Segment
    Char        name[256];     //   Segment
    Char        descr[256];    //   Segment
 }
diff --git a/exp-drd/drd_main.c b/exp-drd/drd_main.c
index a2e0d12..fce6142 100644
--- a/exp-drd/drd_main.c
+++ b/exp-drd/drd_main.c
@@ -584,7 +584,8 @@
       switch (st->tag)
       {
       case Ist_IMark:
-         instrument = VG_(seginfo_sect_kind)(st->Ist.IMark.addr) != Vg_SectPLT;
+         instrument = VG_(seginfo_sect_kind)(NULL, 0, st->Ist.IMark.addr)
+                      != Vg_SectPLT;
          break;
 
       case Ist_MBE:
@@ -811,7 +812,7 @@
    VG_(track_pre_thread_ll_exit)   (drd_thread_finished);
 
    // Other stuff.
-   VG_(needs_data_syms)();
+   VG_(needs_var_info)();
 
    drd_register_malloc_wrappers(drd_start_using_mem, drd_stop_using_mem);
 
diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c
index 664c449..b0a0cff 100644
--- a/helgrind/hg_main.c
+++ b/helgrind/hg_main.c
@@ -48,6 +48,7 @@
 #include "pub_tool_options.h"
 #include "pub_tool_xarray.h"
 #include "pub_tool_stacktrace.h"
+#include "pub_tool_debuginfo.h"  /* VG_(get_data_description) */
 
 #include "helgrind.h"
 
@@ -7828,6 +7829,8 @@
             SVal  old_state;
             ExeContext* mb_lastlock;
             Thread* thr;
+            Char  descr1[96];
+            Char  descr2[96];
          } Race;
          struct {
             Thread* thr;  /* doing the freeing */
@@ -7917,6 +7920,20 @@
    // FIXME: tid vs thr
    tl_assert(isWrite == False || isWrite == True);
    tl_assert(szB == 8 || szB == 4 || szB == 2 || szB == 1);
+
+   tl_assert(sizeof(xe.XE.Race.descr1) == sizeof(xe.XE.Race.descr2));
+   xe.XE.Race.descr1[0] = xe.XE.Race.descr2[0] = 0;
+   if (VG_(get_data_description)(
+             &xe.XE.Race.descr1[0],
+             &xe.XE.Race.descr2[0],
+             sizeof(xe.XE.Race.descr1)-1,
+             data_addr )) {
+      tl_assert( xe.XE.Race.descr1
+                    [ sizeof(xe.XE.Race.descr1)-1 ] == 0);
+      tl_assert( xe.XE.Race.descr2
+                    [ sizeof(xe.XE.Race.descr2)-1 ] == 0);
+   }
+
    VG_(maybe_record_error)( map_threads_reverse_lookup_SLOW(thr),
                             XE_Race, data_addr, NULL, &xe );
 }
@@ -8452,6 +8469,12 @@
                       old_state, old_buf, new_state, new_buf);
       }
 
+      /* If we have a better description of the address, show it. */
+      if (xe->XE.Race.descr1[0] != 0)
+         VG_(message)(Vg_UserMsg, "  %s", &xe->XE.Race.descr1);
+      if (xe->XE.Race.descr2[0] != 0)
+         VG_(message)(Vg_UserMsg, "  %s", &xe->XE.Race.descr2);
+
       break; /* case XE_Race */
    } /* case XE_Race */
 
@@ -8801,7 +8824,7 @@
                                    hg_cli__realloc,
                                    HG_CLI__MALLOC_REDZONE_SZB );
 
-   VG_(needs_data_syms)();
+   VG_(needs_var_info)();
 
    //VG_(needs_xml_output)          ();
 
diff --git a/helgrind/tests/Makefile.am b/helgrind/tests/Makefile.am
index dac727c..8c23bc1 100644
--- a/helgrind/tests/Makefile.am
+++ b/helgrind/tests/Makefile.am
@@ -74,6 +74,7 @@
 		tc20_verifywrap.stderr.exp-glibc25-amd64 \
 		tc20_verifywrap.stderr.exp-glibc25-x86 \
 	tc21_pthonce.vgtest tc21_pthonce.stdout.exp \
+		tc21_pthonce.stderr.exp-glibc23-amd64 \
 		tc21_pthonce.stderr.exp-glibc25-amd64 \
 		tc21_pthonce.stderr.exp-glibc25-x86 \
 	tc22_exit_w_lock.vgtest tc22_exit_w_lock.stdout.exp \
diff --git a/helgrind/tests/hg03_inherit.stderr.exp-glibc25-amd64 b/helgrind/tests/hg03_inherit.stderr.exp-glibc25-amd64
index 3cf4b5e..46c6e53 100644
--- a/helgrind/tests/hg03_inherit.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/hg03_inherit.stderr.exp-glibc25-amd64
@@ -17,5 +17,7 @@
   New state: shared-modified by threads #1, #3
   Reason:    this thread, #3, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside shared[1],
+  declared at hg03_inherit.c:11, in frame #0 of thread 3
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/hg03_inherit.stderr.exp-glibc25-x86 b/helgrind/tests/hg03_inherit.stderr.exp-glibc25-x86
index a7e4407..078708e 100644
--- a/helgrind/tests/hg03_inherit.stderr.exp-glibc25-x86
+++ b/helgrind/tests/hg03_inherit.stderr.exp-glibc25-x86
@@ -16,5 +16,7 @@
   New state: shared-modified by threads #1, #3
   Reason:    this thread, #3, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside shared[1],
+  declared at hg03_inherit.c:11, in frame #0 of thread 3
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/hg04_race.stderr.exp-glibc25-amd64 b/helgrind/tests/hg04_race.stderr.exp-glibc25-amd64
index 6ec1ac3..75afd8b 100644
--- a/helgrind/tests/hg04_race.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/hg04_race.stderr.exp-glibc25-amd64
@@ -22,5 +22,7 @@
   New state: shared-modified by threads #2, #3
   Reason:    this thread, #3, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside local var "shared"
+  declared at hg04_race.c:6, in frame #0 of thread 2
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/hg04_race.stderr.exp-glibc25-x86 b/helgrind/tests/hg04_race.stderr.exp-glibc25-x86
index 3d28bd8..b9474a5 100644
--- a/helgrind/tests/hg04_race.stderr.exp-glibc25-x86
+++ b/helgrind/tests/hg04_race.stderr.exp-glibc25-x86
@@ -20,5 +20,7 @@
   New state: shared-modified by threads #2, #3
   Reason:    this thread, #3, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside local var "shared"
+  declared at hg04_race.c:6, in frame #0 of thread 2
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/hg05_race2.stderr.exp-glibc25-amd64 b/helgrind/tests/hg05_race2.stderr.exp-glibc25-amd64
index b6dc2e9..81bbbaa 100644
--- a/helgrind/tests/hg05_race2.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/hg05_race2.stderr.exp-glibc25-amd64
@@ -22,5 +22,7 @@
   New state: shared-modified by threads #2, #3
   Reason:    this thread, #3, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside foo.poot[5].plop[11],
+  declared at hg05_race2.c:24, in frame #4 of thread 1
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/hg05_race2.stderr.exp-glibc25-x86 b/helgrind/tests/hg05_race2.stderr.exp-glibc25-x86
index 285efeb..3662d24 100644
--- a/helgrind/tests/hg05_race2.stderr.exp-glibc25-x86
+++ b/helgrind/tests/hg05_race2.stderr.exp-glibc25-x86
@@ -20,5 +20,7 @@
   New state: shared-modified by threads #2, #3
   Reason:    this thread, #3, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside foo.poot[5].plop[11],
+  declared at hg05_race2.c:24, in frame #3 of thread 1
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc01_simple_race.stderr.exp-glibc25-amd64 b/helgrind/tests/tc01_simple_race.stderr.exp-glibc25-amd64
index a5b31c3..47265d2 100644
--- a/helgrind/tests/tc01_simple_race.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/tc01_simple_race.stderr.exp-glibc25-amd64
@@ -14,5 +14,7 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside global var "x"
+  declared at tc01_simple_race.c:9
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc01_simple_race.stderr.exp-glibc25-x86 b/helgrind/tests/tc01_simple_race.stderr.exp-glibc25-x86
index ba37641..475d44d 100644
--- a/helgrind/tests/tc01_simple_race.stderr.exp-glibc25-x86
+++ b/helgrind/tests/tc01_simple_race.stderr.exp-glibc25-x86
@@ -13,5 +13,7 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside global var "x"
+  declared at tc01_simple_race.c:9
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc05_simple_race.stderr.exp-glibc25-amd64 b/helgrind/tests/tc05_simple_race.stderr.exp-glibc25-amd64
index 8c15d1a..50ecb4a 100644
--- a/helgrind/tests/tc05_simple_race.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/tc05_simple_race.stderr.exp-glibc25-amd64
@@ -14,5 +14,7 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside global var "y"
+  declared at tc05_simple_race.c:10
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc05_simple_race.stderr.exp-glibc25-x86 b/helgrind/tests/tc05_simple_race.stderr.exp-glibc25-x86
index fb6dccf..df63ed6 100644
--- a/helgrind/tests/tc05_simple_race.stderr.exp-glibc25-x86
+++ b/helgrind/tests/tc05_simple_race.stderr.exp-glibc25-x86
@@ -13,5 +13,7 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside global var "y"
+  declared at tc05_simple_race.c:10
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc06_two_races.stderr.exp-glibc25-amd64 b/helgrind/tests/tc06_two_races.stderr.exp-glibc25-amd64
index db74a70..e95ae9a 100644
--- a/helgrind/tests/tc06_two_races.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/tc06_two_races.stderr.exp-glibc25-amd64
@@ -14,6 +14,8 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside global var "unprot1"
+  declared at tc06_two_races.c:9
 
 Possible data race during write of size 4 at 0x........
    at 0x........: main (tc06_two_races.c:35)
@@ -21,5 +23,7 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside global var "unprot2"
+  declared at tc06_two_races.c:9
 
 ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc06_two_races.stderr.exp-glibc25-x86 b/helgrind/tests/tc06_two_races.stderr.exp-glibc25-x86
index 34874fc..31f778c 100644
--- a/helgrind/tests/tc06_two_races.stderr.exp-glibc25-x86
+++ b/helgrind/tests/tc06_two_races.stderr.exp-glibc25-x86
@@ -13,6 +13,8 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside global var "unprot1"
+  declared at tc06_two_races.c:9
 
 Possible data race during write of size 4 at 0x........
    at 0x........: main (tc06_two_races.c:35)
@@ -20,5 +22,7 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside global var "unprot2"
+  declared at tc06_two_races.c:9
 
 ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc16_byterace.stderr.exp-glibc25-amd64 b/helgrind/tests/tc16_byterace.stderr.exp-glibc25-amd64
index e4286bc..74255e3 100644
--- a/helgrind/tests/tc16_byterace.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/tc16_byterace.stderr.exp-glibc25-amd64
@@ -14,5 +14,7 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside bytes[4],
+  a global variable declared at tc16_byterace.c:7
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc16_byterace.stderr.exp-glibc25-x86 b/helgrind/tests/tc16_byterace.stderr.exp-glibc25-x86
index d7ffb0a..b5430de 100644
--- a/helgrind/tests/tc16_byterace.stderr.exp-glibc25-x86
+++ b/helgrind/tests/tc16_byterace.stderr.exp-glibc25-x86
@@ -13,5 +13,7 @@
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside bytes[4],
+  a global variable declared at tc16_byterace.c:7
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc17_sembar.stderr.exp-glibc25-amd64 b/helgrind/tests/tc17_sembar.stderr.exp-glibc25-amd64
index 3600221..48b0c29 100644
--- a/helgrind/tests/tc17_sembar.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/tc17_sembar.stderr.exp-glibc25-amd64
@@ -41,6 +41,8 @@
    at 0x........: pthread_mutex_init (hg_intercepts.c:...)
    by 0x........: gomp_barrier_init (tc17_sembar.c:46)
    by 0x........: main (tc17_sembar.c:192)
+  Location 0x........ is 0 bytes inside bar.arrived,
+  declared at tc17_sembar.c:136, in frame #0 of thread 2
 done, result is 88, should be 88
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc17_sembar.stderr.exp-glibc25-x86 b/helgrind/tests/tc17_sembar.stderr.exp-glibc25-x86
index 7675dbb..2167fa4 100644
--- a/helgrind/tests/tc17_sembar.stderr.exp-glibc25-x86
+++ b/helgrind/tests/tc17_sembar.stderr.exp-glibc25-x86
@@ -37,6 +37,8 @@
    at 0x........: pthread_mutex_init (hg_intercepts.c:...)
    by 0x........: gomp_barrier_init (tc17_sembar.c:46)
    by 0x........: main (tc17_sembar.c:192)
+  Location 0x........ is 0 bytes inside bar.arrived,
+  declared at tc17_sembar.c:136, in frame #0 of thread 2
 done, result is 88, should be 88
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc20_verifywrap.stderr.exp-glibc23-amd64 b/helgrind/tests/tc20_verifywrap.stderr.exp-glibc23-amd64
index df85c25..f55e67c 100644
--- a/helgrind/tests/tc20_verifywrap.stderr.exp-glibc23-amd64
+++ b/helgrind/tests/tc20_verifywrap.stderr.exp-glibc23-amd64
@@ -19,6 +19,8 @@
   Old state: owned exclusively by thread #2
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no locks at all
+  Location 0x........ is 0 bytes inside global var "unprotected"
+  declared at tc20_verifywrap.c:27
 
 Thread #1's call to pthread_join failed
    with error code 35 (EDEADLK: Resource deadlock would occur)
diff --git a/helgrind/tests/tc20_verifywrap.stderr.exp-glibc25-amd64 b/helgrind/tests/tc20_verifywrap.stderr.exp-glibc25-amd64
index 0b399de..e5f3070 100644
--- a/helgrind/tests/tc20_verifywrap.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/tc20_verifywrap.stderr.exp-glibc25-amd64
@@ -19,6 +19,8 @@
   Old state: owned exclusively by thread #2
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no locks at all
+  Location 0x........ is 0 bytes inside global var "unprotected"
+  declared at tc20_verifywrap.c:27
 
 Thread #1's call to pthread_join failed
    with error code 35 (EDEADLK: Resource deadlock would occur)
diff --git a/helgrind/tests/tc20_verifywrap.stderr.exp-glibc25-x86 b/helgrind/tests/tc20_verifywrap.stderr.exp-glibc25-x86
index c44e998..c1d8677 100644
--- a/helgrind/tests/tc20_verifywrap.stderr.exp-glibc25-x86
+++ b/helgrind/tests/tc20_verifywrap.stderr.exp-glibc25-x86
@@ -18,6 +18,8 @@
   Old state: owned exclusively by thread #2
   New state: shared-modified by threads #1, #2
   Reason:    this thread, #1, holds no locks at all
+  Location 0x........ is 0 bytes inside global var "unprotected"
+  declared at tc20_verifywrap.c:27
 
 Thread #1's call to pthread_join failed
    with error code 35 (EDEADLK: Resource deadlock would occur)
diff --git a/helgrind/tests/tc21_pthonce.stderr.exp-glibc23-amd64 b/helgrind/tests/tc21_pthonce.stderr.exp-glibc23-amd64
new file mode 100644
index 0000000..72cc0d8
--- /dev/null
+++ b/helgrind/tests/tc21_pthonce.stderr.exp-glibc23-amd64
@@ -0,0 +1,28 @@
+
+Thread #2 was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: ...
+   by 0x........: pthread_create@GLIBC_ (in /lib/libpthread...)
+   by 0x........: pthread_create@* (hg_intercepts.c:...)
+   by 0x........: main (tc21_pthonce.c:86)
+
+Thread #3 was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: ...
+   by 0x........: pthread_create@GLIBC_ (in /lib/libpthread...)
+   by 0x........: pthread_create@* (hg_intercepts.c:...)
+   by 0x........: main (tc21_pthonce.c:86)
+
+Possible data race during write of size 4 at 0x........
+   at 0x........: child (tc21_pthonce.c:74)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   by 0x........: ...
+   by 0x........: ...
+  Old state: shared-readonly by threads #2, #3
+  New state: shared-modified by threads #2, #3
+  Reason:    this thread, #3, holds no consistent locks
+  Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside local var "unprotected2"
+  declared at tc21_pthonce.c:51, in frame #0 of thread 3
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc21_pthonce.stderr.exp-glibc25-amd64 b/helgrind/tests/tc21_pthonce.stderr.exp-glibc25-amd64
index 4d0fd44..2bb2de0 100644
--- a/helgrind/tests/tc21_pthonce.stderr.exp-glibc25-amd64
+++ b/helgrind/tests/tc21_pthonce.stderr.exp-glibc25-amd64
@@ -22,5 +22,7 @@
   New state: shared-modified by threads #2, #3
   Reason:    this thread, #3, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside local var "unprotected2"
+  declared at tc21_pthonce.c:51, in frame #0 of thread 2
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/helgrind/tests/tc21_pthonce.stderr.exp-glibc25-x86 b/helgrind/tests/tc21_pthonce.stderr.exp-glibc25-x86
index f0b58a3..bd08213 100644
--- a/helgrind/tests/tc21_pthonce.stderr.exp-glibc25-x86
+++ b/helgrind/tests/tc21_pthonce.stderr.exp-glibc25-x86
@@ -20,5 +20,7 @@
   New state: shared-modified by threads #2, #3
   Reason:    this thread, #3, holds no consistent locks
   Location 0x........ has never been protected by any lock
+  Location 0x........ is 0 bytes inside local var "unprotected2"
+  declared at tc21_pthonce.c:51, in frame #0 of thread 2
 
 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/include/pub_tool_debuginfo.h b/include/pub_tool_debuginfo.h
index 1c03ce0..a6849ab 100644
--- a/include/pub_tool_debuginfo.h
+++ b/include/pub_tool_debuginfo.h
@@ -74,9 +74,27 @@
    entry points within it. */
 extern Bool VG_(get_fnname_if_entry) ( Addr a, Char* fnname, Int n_fnname );
 
+/* Looks up data_addr in the collection of data symbols, and if found
+   puts its name (or as much as will fit) into dname[0 .. n_dname-1],
+   which is guaranteed to be zero terminated.  Also data_addr's offset
+   from the symbol start is put into *offset. */
+extern Bool VG_(get_datasym_and_offset)( Addr data_addr,
+                                         /*OUT*/Char* dname, Int n_dname,
+                                         /*OUT*/OffT* offset );
+
+/* Try to form some description of data_addr by looking at the DWARF3
+   debug info we have.  This considers all global variables, and all
+   frames in the stacks of all threads.  Result (or as much as will
+   fit) is put into into dname{1,2}[0 .. n_dname-1] and is guaranteed
+   to be zero terminated. */
+extern Bool VG_(get_data_description)( /*OUT*/Char* dname1,
+                                       /*OUT*/Char* dname2,
+                                       Int  n_dname,
+                                       Addr data_addr );
+
 /* Succeeds if the address is within a shared object or the main executable.
    It doesn't matter if debug info is present or not. */
-extern Bool VG_(get_objname)  ( Addr a, Char* objname,  Int n_objname  );
+extern Bool VG_(get_objname)  ( Addr a, Char* objname, Int n_objname );
 
 /* Puts into 'buf' info about the code address %eip:  the address, function
    name (if known) and filename/line number (if known), like this:
@@ -87,42 +105,44 @@
 */
 extern Char* VG_(describe_IP)(Addr eip, Char* buf, Int n_buf);
 
-
 /*====================================================================*/
 /*=== Obtaining segment information                                ===*/
 /*====================================================================*/
 
 /* A way to get information about what segments are mapped */
-typedef struct _SegInfo SegInfo;
+typedef struct _DebugInfo DebugInfo;
 
-/* Returns NULL if the SegInfo isn't found.  It doesn't matter if debug info
-   is present or not. */
-extern       SegInfo* VG_(find_seginfo)      ( Addr a );
+/* Returns NULL if the DebugInfo isn't found.  It doesn't matter if
+   debug info is present or not. */
+extern       DebugInfo* VG_(find_seginfo)      ( Addr a );
 
-/* Fish bits out of SegInfos. */
-extern       Addr     VG_(seginfo_start)     ( const SegInfo *si );
-extern       SizeT    VG_(seginfo_size)      ( const SegInfo *si );
-extern const UChar*   VG_(seginfo_soname)    ( const SegInfo *si );
-extern const UChar*   VG_(seginfo_filename)  ( const SegInfo *si );
-extern       ULong    VG_(seginfo_sym_offset)( const SegInfo *si );
+/* Fish bits out of DebugInfos. */
+extern       Addr     VG_(seginfo_get_text_avma)( const DebugInfo *di );
+extern       SizeT    VG_(seginfo_get_text_size)( const DebugInfo *di );
+extern const UChar*   VG_(seginfo_soname)       ( const DebugInfo *di );
+extern const UChar*   VG_(seginfo_filename)     ( const DebugInfo *di );
+extern       ULong    VG_(seginfo_get_text_bias)( const DebugInfo *di );
 
 /* Function for traversing the seginfo list.  When called with NULL it
    returns the first element; otherwise it returns the given element's
    successor. */
-extern const SegInfo* VG_(next_seginfo)      ( const SegInfo *si );
+extern const DebugInfo* VG_(next_seginfo)    ( const DebugInfo *di );
 
-/* Functions for traversing all the symbols in a SegInfo.  _howmany
+/* Functions for traversing all the symbols in a DebugInfo.  _howmany
    tells how many there are.  _getidx retrieves the n'th, for n in 0
    .. _howmany-1.  You may not modify the function name thereby
    acquired; if you want to do so, first strdup it. */
-extern Int  VG_(seginfo_syms_howmany) ( const SegInfo *si );
-extern void VG_(seginfo_syms_getidx)  ( const SegInfo *si, 
+extern Int  VG_(seginfo_syms_howmany) ( const DebugInfo *di );
+extern void VG_(seginfo_syms_getidx)  ( const DebugInfo *di, 
                                         Int idx,
-                                        /*OUT*/Addr*   addr,
+                                        /*OUT*/Addr*   avma,
                                         /*OUT*/Addr*   tocptr,
                                         /*OUT*/UInt*   size,
-                                        /*OUT*/HChar** name );
+                                        /*OUT*/HChar** name,
+                                        /*OUT*/Bool*   isText );
 
+/* A simple enumeration to describe the 'kind' of various kinds of
+   segments that arise from the mapping of object files. */
 typedef
    enum {
       Vg_SectUnknown,
@@ -130,13 +150,23 @@
       Vg_SectData,
       Vg_SectBSS,
       Vg_SectGOT,
-      Vg_SectPLT
+      Vg_SectPLT,
+      Vg_SectOPD
    }
    VgSectKind;
 
-extern VgSectKind VG_(seginfo_sect_kind)(Addr);
+/* Convert a VgSectKind to a string, which must be copied if you want
+   to change it. */
+extern
+const HChar* VG_(pp_SectKind)( VgSectKind kind );
 
-extern Char* VG_(seginfo_sect_kind_name)(Addr a, Char* buf, UInt n_buf);
+/* Given an address 'a', make a guess of which section of which object
+   it comes from.  If name is non-NULL, then the last n_name-1
+   characters of the object's name is put in name[0 .. n_name-2], and
+   name[n_name-1] is set to zero (guaranteed zero terminated). */
+extern 
+VgSectKind VG_(seginfo_sect_kind)( /*OUT*/UChar* name, SizeT n_name, 
+                                   Addr a);
 
 
 #endif   // __PUB_TOOL_DEBUGINFO_H
diff --git a/include/pub_tool_libcbase.h b/include/pub_tool_libcbase.h
index 309ad4d..5b8cdad 100644
--- a/include/pub_tool_libcbase.h
+++ b/include/pub_tool_libcbase.h
@@ -135,8 +135,9 @@
 extern void VG_(ssort)( void* base, SizeT nmemb, SizeT size,
                         Int (*compar)(void*, void*) );
 
-/* Returns the base-2 logarithm of x.  Returns -1 if x is not a power of two. */
-extern Int VG_(log2) ( Int x );
+/* Returns the base-2 logarithm of x.  Returns -1 if x is not a power
+   of two. */
+extern Int VG_(log2) ( UInt x );
 
 // A pseudo-random number generator returning a random UInt.  If pSeed
 // is NULL, it uses its own seed, which starts at zero.  If pSeed is
diff --git a/include/pub_tool_machine.h b/include/pub_tool_machine.h
index e261593..c8556b5 100644
--- a/include/pub_tool_machine.h
+++ b/include/pub_tool_machine.h
@@ -94,11 +94,13 @@
 // doing leak checking.
 extern void VG_(apply_to_GP_regs)(void (*f)(UWord val));
 
-// This iterator lets you inspect each live thread's stack bounds.  The
-// params are all 'out' params.  Returns False at the end.
-extern void VG_(thread_stack_reset_iter) ( void );
-extern Bool VG_(thread_stack_next)       ( ThreadId* tid, Addr* stack_min,
-                                                          Addr* stack_max );
+// This iterator lets you inspect each live thread's stack bounds.
+// Returns False at the end.  'tid' is the iterator and you can only
+// safely change it by making calls to these functions.
+extern void VG_(thread_stack_reset_iter) ( /*OUT*/ThreadId* tid );
+extern Bool VG_(thread_stack_next)       ( /*MOD*/ThreadId* tid,
+                                           /*OUT*/Addr* stack_min, 
+                                           /*OUT*/Addr* stack_max );
 
 // Returns .client_stack_highest_word for the given thread
 extern Addr VG_(thread_get_stack_max) ( ThreadId tid );
diff --git a/include/pub_tool_oset.h b/include/pub_tool_oset.h
index 44b1a19..a068d3e 100644
--- a/include/pub_tool_oset.h
+++ b/include/pub_tool_oset.h
@@ -39,9 +39,9 @@
 // It has two interfaces.  
 //
 // - The "OSetWord_" interface provides an easier-to-use interface for the
-//   case where you just want to store Word-sized values.  The user provides
-//   the allocation and deallocation functions, and possibly a comparison
-//   function.
+//   case where you just want to store UWord-sized values.  The user
+//   provides the allocation and deallocation functions, and possibly a 
+//   comparison function.
 //
 // - The "OSetGen_" interface provides a totally generic interface, which
 //   allows any kind of structure to be put into the set.  The user provides
@@ -81,7 +81,7 @@
 typedef void  (*OSetFree_t)        ( void* p );
 
 /*--------------------------------------------------------------------*/
-/*--- Creating and destroying OSets (Word)                         ---*/
+/*--- Creating and destroying OSets (UWord)                        ---*/
 /*--------------------------------------------------------------------*/
 
 // * Create: allocates and initialises the OSet.  Arguments:
@@ -102,7 +102,7 @@
 extern void  VG_(OSetWord_Destroy)      ( OSet* os );
 
 /*--------------------------------------------------------------------*/
-/*--- Operations on OSets (Word)                                   ---*/
+/*--- Operations on OSets (UWord)                                  ---*/
 /*--------------------------------------------------------------------*/
 
 // In everything that follows, the parameter 'key' is always the *address*
@@ -124,8 +124,8 @@
 // 
 // * Next: Copies the next value according to the OSet's iterator into &val,
 //   advances the iterator by one, and returns True;  the elements are
-//   visited in order.  Or, returns False if the iterator has reached the
-//   set's end.
+//   visited in increasing order of unsigned words (UWord).  Or, returns
+//   False if the iterator has reached the set's end.
 //   
 //   You can thus iterate in order through a set like this:
 //
@@ -141,12 +141,12 @@
 //   they will return False if VG_(OSetWord_Next)() is called without an
 //   intervening call to VG_(OSetWord_ResetIter)().
 
-extern Int   VG_(OSetWord_Size)         ( OSet* os );
-extern void  VG_(OSetWord_Insert)       ( OSet* os, Word val );
-extern Bool  VG_(OSetWord_Contains)     ( OSet* os, Word val );
-extern Bool  VG_(OSetWord_Remove)       ( OSet* os, Word val );
+extern Word  VG_(OSetWord_Size)         ( OSet* os );
+extern void  VG_(OSetWord_Insert)       ( OSet* os, UWord val );
+extern Bool  VG_(OSetWord_Contains)     ( OSet* os, UWord val );
+extern Bool  VG_(OSetWord_Remove)       ( OSet* os, UWord val );
 extern void  VG_(OSetWord_ResetIter)    ( OSet* os );
-extern Bool  VG_(OSetWord_Next)         ( OSet* os, Word* val );
+extern Bool  VG_(OSetWord_Next)         ( OSet* os, /*OUT*/UWord* val );
 
 
 /*--------------------------------------------------------------------*/
@@ -234,15 +234,22 @@
 //   they will return NULL if VG_(OSetGen_Next)() is called without an
 //   intervening call to VG_(OSetGen_ResetIter)().
 
-extern Int   VG_(OSetGen_Size)         ( const OSet* os );
+extern Word  VG_(OSetGen_Size)         ( const OSet* os );
 extern void  VG_(OSetGen_Insert)       ( OSet* os, void* elem );
 extern Bool  VG_(OSetGen_Contains)     ( const OSet* os, const void* key  );
 extern void* VG_(OSetGen_Lookup)       ( const OSet* os, const void* key  );
-extern void* VG_(OSetGen_LookupWithCmp)( OSet* os, const void* key, OSetCmp_t cmp );
+extern void* VG_(OSetGen_LookupWithCmp)( OSet* os,
+                                         const void* key, OSetCmp_t cmp );
 extern void* VG_(OSetGen_Remove)       ( OSet* os, const void* key  );
 extern void  VG_(OSetGen_ResetIter)    ( OSet* os );
 extern void* VG_(OSetGen_Next)         ( OSet* os );
 
+// set up 'oset' for iteration so that the first key subsequently
+// produced VG_(OSetGen_Next) is the smallest key in the map 
+// >= start_at.  Naturally ">=" is defined by the comparison 
+// function supplied to VG_(OSetGen_Create).
+extern void VG_(OSetGen_ResetIterAt) ( OSet* oset, void* key );
+
 #endif   // __PUB_TOOL_OSET_H
 
 /*--------------------------------------------------------------------*/
diff --git a/include/pub_tool_stacktrace.h b/include/pub_tool_stacktrace.h
index 5387676..9fda821 100644
--- a/include/pub_tool_stacktrace.h
+++ b/include/pub_tool_stacktrace.h
@@ -34,13 +34,20 @@
 // The basic stack trace type:  just an array of code addresses.
 typedef Addr* StackTrace;
 
-// Walks the stack to get instruction pointers from the top stack frames for
-// thread 'tid'.  Maximum of 'n_ips' addresses put into 'ips';  0 is the top
-// of the stack, 1 is its caller, etc.  Everything from ips[n_ips] onwards
-// is undefined and should not be read.  The initial IP value to 
-// use is adjusted by first_ip_delta before the stack is unwound.
-// A safe value to pass is zero.
-extern UInt VG_(get_StackTrace) ( ThreadId tid, StackTrace ips, UInt n_ips,
+// Walks the stack to get instruction pointers from the top stack frames 
+// for thread 'tid'.  Maximum of 'n_ips' addresses put into 'ips';
+// 0 is the top of the stack, 1 is its caller, etc.  Everything from
+// ips[return_value] onwards is undefined and should not be read.
+// The initial IP value to use is adjusted by first_ip_delta before
+// the stack is unwound. A safe value to pass is zero.
+//
+// If sps and fps are non-NULL, the corresponding frame-pointer and
+// stack-pointer values for each frame are stored there.
+
+extern UInt VG_(get_StackTrace) ( ThreadId tid, 
+                                  /*OUT*/StackTrace ips, UInt n_ips,
+                                  /*OUT*/StackTrace sps,
+                                  /*OUT*/StackTrace fps,
                                   Word first_ip_delta );
 
 // Apply a function to every element in the StackTrace.  The parameter 'n'
diff --git a/include/pub_tool_tooliface.h b/include/pub_tool_tooliface.h
index 0c0dafa..f75c74a 100644
--- a/include/pub_tool_tooliface.h
+++ b/include/pub_tool_tooliface.h
@@ -416,8 +416,8 @@
    Bool(*expensive_sanity_check)(void)
 );
 
-/* Do we need to see data symbols? */
-extern void VG_(needs_data_syms) ( void );
+/* Do we need to see variable type and location information? */
+extern void VG_(needs_var_info) ( void );
 
 /* Does the tool replace malloc() and friends with its own versions?
    This has to be combined with the use of a vgpreload_<tool>.so module
diff --git a/include/pub_tool_xarray.h b/include/pub_tool_xarray.h
index 6862982..b223fea 100644
--- a/include/pub_tool_xarray.h
+++ b/include/pub_tool_xarray.h
@@ -66,6 +66,11 @@
    invalidated if the array is later sortXA'd. */
 extern Int VG_(addToXA) ( XArray*, void* elem );
 
+/* Add a sequence of bytes to an XArray of bytes.  Asserts if nbytes
+   is negative or the array's element size is not 1.  Returns the
+   index at which the first byte was added. */
+extern Int VG_(addBytesToXA) ( XArray* xao, void* bytesV, Int nbytes );
+
 /* Sort an XArray using its comparison function, if set; else bomb.
    Probably not a stable sort w.r.t. equal elements module cmpFn. */
 extern void VG_(sortXA) ( XArray* );
@@ -94,6 +99,11 @@
    than n elements in the array. */
 extern void VG_(dropTailXA) ( XArray*, Word );
 
+/* Make a new, completely independent copy of the given XArray, using
+   the existing allocation function to allocate the new space.
+   Returns NULL if the allocation function didn't manage to allocate
+   space (but did return NULL rather than merely abort.) */
+extern XArray* VG_(cloneXA)( XArray* xa );
 
 #endif   // __PUB_TOOL_XARRAY_H
 
diff --git a/massif/ms_main.c b/massif/ms_main.c
index 7ad47c2..ba56c30 100644
--- a/massif/ms_main.c
+++ b/massif/ms_main.c
@@ -813,7 +813,9 @@
 
       // Ask for more IPs than clo_depth suggests we need.
       n_ips = VG_(get_StackTrace)( tid, ips, clo_depth + overestimate,
-                                        0/*first_ip_delta*/ );
+                                   NULL/*array to dump SP values in*/,
+                                   NULL/*array to dump FP values in*/,
+                                   0/*first_ip_delta*/ );
       tl_assert(n_ips > 0);
 
       // If the original stack trace is smaller than asked-for, redo=False.
diff --git a/memcheck/mc_leakcheck.c b/memcheck/mc_leakcheck.c
index 7dbb1d5..c838d3c 100644
--- a/memcheck/mc_leakcheck.c
+++ b/memcheck/mc_leakcheck.c
@@ -215,7 +215,9 @@
 {
    MC_Chunk* mc1 = *(MC_Chunk**)n1;
    MC_Chunk* mc2 = *(MC_Chunk**)n2;
-   return (mc1->data < mc2->data ? -1 : 1);
+   if (mc1->data < mc2->data) return -1;
+   if (mc1->data > mc2->data) return  1;
+   return 0;
 }
 
 /* If ptr is pointing to a heap-allocated block which hasn't been seen
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index 4f704a2..8c1c2c2 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -44,6 +44,7 @@
 #include "pub_tool_tooliface.h"
 #include "pub_tool_threadstate.h"
 #include "pub_tool_oset.h"
+#include "pub_tool_debuginfo.h"     // VG_(get_dataname_and_offset)
 
 #include "mc_include.h"
 #include "memcheck.h"   /* for client requests */
@@ -2625,10 +2626,13 @@
 /* The classification of a faulting address. */
 typedef 
    enum { 
-      Addr_Undescribed,   // as-yet unclassified
-      Addr_Unknown,       // classification yielded nothing useful
-      Addr_Stack,          
-      Addr_Block,
+      Addr_Undescribed, // as-yet unclassified
+      Addr_Unknown,     // classification yielded nothing useful
+      Addr_Block,       // in malloc'd/free'd block
+      Addr_Stack,       // on a thread's stack       
+      Addr_DataSym,     // in a global data sym
+      Addr_Variable,    // variable described by the debug info
+      Addr_SectKind     // last-ditch classification attempt
    }
    AddrTag;
 
@@ -2657,6 +2661,27 @@
          ExeContext* lastchange;
       } Block;
 
+      // In a global .data symbol.  This holds the first 63 chars of
+      // the variable's (zero terminated), plus an offset.
+      struct {
+         Char name[128];
+         OffT offset;
+      } DataSym;
+
+      // Is described by Dwarf debug info.  Arbitrary strings.  Must
+      // be the same length.
+      struct {
+         Char descr1[96];
+         Char descr2[96];
+      } Variable;
+
+      // Could only narrow it down to be the PLT/GOT/etc of a given
+      // object.  Better than nothing, perhaps.
+      struct {
+         Char       objname[128];
+         VgSectKind kind;
+      } SectKind;
+
       // Classification yielded nothing useful.
       struct { } Unknown;
 
@@ -2833,6 +2858,36 @@
          break;
       }
 
+      case Addr_DataSym:
+         VG_(message)(Vg_UserMsg, 
+                      "%sAddress 0x%llx is %llu bytes "
+                        "inside data symbol \"%t\"%s", 
+                      xpre, 
+                      (ULong)a, 
+                      (ULong)ai->Addr.DataSym.offset,
+                      ai->Addr.DataSym.name, 
+                      xpost);
+         break;
+
+      case Addr_Variable:
+         if (ai->Addr.Variable.descr1[0] != '\0')
+            VG_(message)(Vg_UserMsg, "%s%s%s",
+                         xpre, ai->Addr.Variable.descr1, xpost);
+         if (ai->Addr.Variable.descr2[0] != '\0')
+            VG_(message)(Vg_UserMsg, "%s%s%s",
+                         xpre, ai->Addr.Variable.descr2, xpost);
+         break;
+
+      case Addr_SectKind:
+         VG_(message)(Vg_UserMsg, 
+                      "%sAddress 0x%llx is in the %t segment of %t%s",
+                      xpre, 
+                      (ULong)a, 
+                      VG_(pp_SectKind)(ai->Addr.SectKind.kind),
+                      ai->Addr.SectKind.objname, 
+                      xpost);
+         break;
+
       default:
          VG_(tool_panic)("mc_pp_AddrInfo");
    }
@@ -3321,26 +3376,18 @@
 
 /* Describe an address as best you can, for error messages,
    putting the result in ai. */
-static void describe_addr ( Addr a, AddrInfo* ai )
+static void describe_addr ( Addr a, /*OUT*/AddrInfo* ai )
 {
-   MC_Chunk* mc;
-   ThreadId  tid;
-   Addr      stack_min, stack_max;
+   MC_Chunk*  mc;
+   ThreadId   tid;
+   Addr       stack_min, stack_max;
+   VgSectKind sect;
 
    tl_assert(Addr_Undescribed == ai->tag);
 
    /* Perhaps it's a user-def'd block? */
-   if (client_perm_maybe_describe( a, ai ))
+   if (client_perm_maybe_describe( a, ai )) {
       return;
-
-   /* Perhaps it's on a thread's stack? */
-   VG_(thread_stack_reset_iter)();
-   while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) {
-      if (stack_min <= a && a <= stack_max) {
-         ai->tag            = Addr_Stack;
-         ai->Addr.Stack.tid = tid;
-         return;
-      }
    }
    /* Search for a recently freed block which might bracket it. */
    mc = MC_(get_freed_list_head)();
@@ -3369,6 +3416,61 @@
          return;
       }
    }
+   /* Perhaps the variable type/location data describes it? */
+   tl_assert(sizeof(ai->Addr.Variable.descr1) 
+             == sizeof(ai->Addr.Variable.descr2));
+   VG_(memset)( &ai->Addr.Variable.descr1, 
+                0, sizeof(ai->Addr.Variable.descr1));
+   VG_(memset)( &ai->Addr.Variable.descr2, 
+                0, sizeof(ai->Addr.Variable.descr2));
+   if (VG_(get_data_description)(
+             &ai->Addr.Variable.descr1[0],
+             &ai->Addr.Variable.descr2[0],
+             sizeof(ai->Addr.Variable.descr1)-1, 
+             a )) {
+      ai->tag = Addr_Variable;
+      tl_assert( ai->Addr.Variable.descr1
+                    [ sizeof(ai->Addr.Variable.descr1)-1 ] == 0);
+      tl_assert( ai->Addr.Variable.descr2
+                    [ sizeof(ai->Addr.Variable.descr2)-1 ] == 0);
+      return;
+   }
+   /* Have a look at the low level data symbols - perhaps it's in
+      there. */
+   VG_(memset)( &ai->Addr.DataSym.name,
+                0, sizeof(ai->Addr.DataSym.name));
+   if (VG_(get_datasym_and_offset)(
+             a, &ai->Addr.DataSym.name[0],
+             sizeof(ai->Addr.DataSym.name)-1,
+             &ai->Addr.DataSym.offset )) {
+      ai->tag = Addr_DataSym;
+      tl_assert( ai->Addr.DataSym.name
+                    [ sizeof(ai->Addr.DataSym.name)-1 ] == 0);
+      return;
+   }
+   /* Perhaps it's on a thread's stack? */
+   VG_(thread_stack_reset_iter)(&tid);
+   while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) {
+      if (stack_min - VG_STACK_REDZONE_SZB <= a && a <= stack_max) {
+         ai->tag            = Addr_Stack;
+         ai->Addr.Stack.tid = tid;
+         return;
+      }
+   }
+   /* last ditch attempt at classification */
+   tl_assert( sizeof(ai->Addr.SectKind.objname) > 4 );
+   VG_(memset)( &ai->Addr.SectKind.objname, 
+                0, sizeof(ai->Addr.SectKind.objname));
+   VG_(strcpy)( ai->Addr.SectKind.objname, "???" );
+   sect = VG_(seginfo_sect_kind)( &ai->Addr.SectKind.objname[0],
+                                  sizeof(ai->Addr.SectKind.objname)-1, a);
+   if (sect != Vg_SectUnknown) {
+      ai->tag = Addr_SectKind;
+      ai->Addr.SectKind.kind = sect;
+      tl_assert( ai->Addr.SectKind.objname
+                    [ sizeof(ai->Addr.SectKind.objname)-1 ] == 0);
+      return;
+   }
    /* Clueless ... */
    ai->tag = Addr_Unknown;
    return;
@@ -3388,26 +3490,31 @@
    case Err_Overlap:
    case Err_RegParam:
    // For Err_Leaks the returned size does not matter -- they are always
-   // shown with VG_(unique_error)() so they 'extra' not copied.  But we make it
-   // consistent with the others.
+   // shown with VG_(unique_error)() so they 'extra' not copied.  But
+   // we make it consistent with the others.
    case Err_Leak:
       return sizeof(MC_Error);
 
    // These ones always involve a memory address.
    case Err_Addr:
-      describe_addr ( VG_(get_error_address)(err), &extra->Err.Addr.ai );
+      describe_addr ( VG_(get_error_address)(err),
+                      &extra->Err.Addr.ai );
       return sizeof(MC_Error);
    case Err_MemParam:
-      describe_addr ( VG_(get_error_address)(err), &extra->Err.MemParam.ai );
+      describe_addr ( VG_(get_error_address)(err),
+                      &extra->Err.MemParam.ai );
       return sizeof(MC_Error);
    case Err_Jump:
-      describe_addr ( VG_(get_error_address)(err), &extra->Err.Jump.ai );
+      describe_addr ( VG_(get_error_address)(err),
+                      &extra->Err.Jump.ai );
       return sizeof(MC_Error);
    case Err_User:
-      describe_addr ( VG_(get_error_address)(err), &extra->Err.User.ai );
+      describe_addr ( VG_(get_error_address)(err),
+                      &extra->Err.User.ai );
       return sizeof(MC_Error);
    case Err_Free:
-      describe_addr ( VG_(get_error_address)(err), &extra->Err.Free.ai );
+      describe_addr ( VG_(get_error_address)(err),
+                      &extra->Err.Free.ai );
       return sizeof(MC_Error);
    case Err_IllegalMempool:
       describe_addr ( VG_(get_error_address)(err),
diff --git a/memcheck/mc_malloc_wrappers.c b/memcheck/mc_malloc_wrappers.c
index 8218561..06f568b 100644
--- a/memcheck/mc_malloc_wrappers.c
+++ b/memcheck/mc_malloc_wrappers.c
@@ -508,7 +508,9 @@
 {
    MC_Chunk* mc1 = *(MC_Chunk**)n1;
    MC_Chunk* mc2 = *(MC_Chunk**)n2;
-   return (mc1->data < mc2->data ? -1 : 1);
+   if (mc1->data < mc2->data) return -1;
+   if (mc1->data > mc2->data) return  1;
+   return 0;
 }
 
 static void 
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index b593d24..fbaa886 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -19,7 +19,8 @@
 DIST_SUBDIRS = ${VG_ARCH_ALL} .
 
 noinst_SCRIPTS = filter_allocs filter_leak_check_size \
-		 filter_stderr filter_stderr_backtrace filter_xml
+		 filter_stderr filter_stderr_backtrace filter_xml \
+		 filter_varinfo3
 
 EXTRA_DIST = $(noinst_SCRIPTS) \
 	addressable.stderr.exp addressable.stdout.exp addressable.vgtest \
@@ -129,6 +130,13 @@
 	toobig-allocs.stderr.exp toobig-allocs.vgtest \
 	trivialleak.stderr.exp trivialleak.vgtest \
 	metadata.stderr.exp metadata.stdout.exp metadata.vgtest \
+	varinfo1.vgtest varinfo1.stdout.exp varinfo1.stderr.exp \
+	varinfo2.vgtest varinfo2.stdout.exp varinfo2.stderr.exp \
+	varinfo3.vgtest varinfo3.stdout.exp varinfo3.stderr.exp \
+	varinfo4.vgtest varinfo4.stdout.exp varinfo4.stderr.exp \
+	varinfo5.vgtest varinfo5.stdout.exp varinfo5.stderr.exp \
+	varinfo6.vgtest varinfo6.stdout.exp \
+	varinfo6.stderr.exp-glibc25-amd64 varinfo6.stderr.exp-glibc25-x86 \
 	vcpu_bz2.stdout.exp vcpu_bz2.stderr.exp vcpu_bz2.vgtest \
 	vcpu_fbench.stdout.exp vcpu_fbench.stderr.exp vcpu_fbench.vgtest \
 	vcpu_fnfns.stdout.exp vcpu_fnfns.stderr.exp vcpu_fnfns.vgtest \
@@ -181,6 +189,8 @@
 	supp_unknown supp1 supp2 suppfree \
 	trivialleak \
 	mismatches new_override metadata \
+	varinfo1 varinfo2 varinfo3 varinfo4 \
+	varinfo5 varinfo5so.so varinfo6 \
 	vcpu_bz2 vcpu_fbench vcpu_fnfns \
 	xml1 \
 	wrap1 wrap2 wrap3 wrap4 wrap5 wrap6 wrap7 wrap7so.so wrap8 \
@@ -211,6 +221,14 @@
 supp_unknown_SOURCES	= badjump.c
 supp1_SOURCES		= supp.c
 supp2_SOURCES		= supp.c
+# To make it a bit more realistic, have some optimisation enabled
+# for the varinfo tests.  We still expect sane results.
+varinfo1_CFLAGS		= -O -g
+varinfo2_CFLAGS		= -O -g
+varinfo3_CFLAGS		= -O -g
+varinfo4_CFLAGS		= -O -g
+varinfo5_CFLAGS		= -O -g
+varinfo6_CFLAGS		= -O -g
 
 # C++ tests
 mismatches_SOURCES	= mismatches.cpp
@@ -270,6 +288,39 @@
 endif
 endif
 
+# Build shared object for varinfo5
+varinfo5_SOURCES        = varinfo5.c
+varinfo5_DEPENDENCIES   = varinfo5so.so
+if VGP_PPC64_AIX5 
+ varinfo5_LDADD         = `pwd`/varinfo5so.so
+ varinfo5_LDFLAGS       = $(AM_FLAG_M3264_PRI)
+else
+if VGP_PPC32_AIX5
+ varinfo5_LDADD         = `pwd`/varinfo5so.so
+ varinfo5_LDFLAGS       = $(AM_FLAG_M3264_PRI) -Wl,-G -Wl,-bnogc
+else
+ varinfo5_LDADD         = varinfo5so.so
+ varinfo5_LDFLAGS       = $(AM_FLAG_M3264_PRI) \
+				-Wl,-rpath,$(top_builddir)/memcheck/tests
+endif
+endif
+
+varinfo5so_so_SOURCES   = varinfo5so.c
+varinfo5so_so_LDADD     = 
+varinfo5so_so_DEPENDENCIES = 
+varinfo5so_so_CFLAGS    = -fpic $(AM_FLAG_M3264_PRI) -g -O
+if VGP_PPC64_AIX5
+ varinfo5so_so_LDFLAGS  = -fpic $(AM_FLAG_M3264_PRI) -shared
+else
+if VGP_PPC32_AIX5
+ varinfo5so_so_LDFLAGS  = -fpic $(AM_FLAG_M3264_PRI) -shared \
+				-Wl,-G -Wl,-bnogc
+else
+ varinfo5so_so_LDFLAGS  = -fpic $(AM_FLAG_M3264_PRI) -shared \
+				-Wl,-soname -Wl,varinfo5so.so
+endif
+endif
+
 # Valgrind unit self-tests
 #hello_LDFLAGS		= -Wl,-defsym,valt_load_address=0x50000000 \
 #			  -Wl,-T,$(top_builddir)/valt_load_address.lds
diff --git a/memcheck/tests/filter_varinfo3 b/memcheck/tests/filter_varinfo3
new file mode 100755
index 0000000..334fc3f
--- /dev/null
+++ b/memcheck/tests/filter_varinfo3
@@ -0,0 +1,7 @@
+#! /bin/sh
+
+# See comment at top of varinfo3.c for why these hacks are necessary
+
+./filter_stderr |
+sed "s/static_local_def\.[0-9]*/static_local_def\.XXXX/g" |
+sed "s/static_local_undef\.[0-9]*/static_local_undef\.XXXX/g"
diff --git a/memcheck/tests/oset_test.c b/memcheck/tests/oset_test.c
index ee99beb..49b8000 100644
--- a/memcheck/tests/oset_test.c
+++ b/memcheck/tests/oset_test.c
@@ -369,8 +369,8 @@
    // Create a dynamic OSet of Blocks.  This one uses slow (custom)
    // comparisons.
    OSet* oset = VG_(OSetGen_Create)(offsetof(Block, first),
-                                  blockCmp,
-                                  (void*)malloc, free);
+                                    blockCmp,
+                                    malloc, free);
 
    // Try some operations on an empty OSet to ensure they don't screw up.
    vg_assert( ! VG_(OSetGen_Contains)(oset, &v) );
diff --git a/memcheck/tests/varinfo1.c b/memcheck/tests/varinfo1.c
new file mode 100644
index 0000000..752d698
--- /dev/null
+++ b/memcheck/tests/varinfo1.c
@@ -0,0 +1,58 @@
+
+/* Basic check of variable location identification, in a zero-biased
+   executable. */
+
+/* Relevant compile flags are:
+
+   -Wall -g -I$prefix/include/valgrind
+
+   eg -Wall -g -I`pwd`/Inst/include/valgrind
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "memcheck/memcheck.h"
+
+/* Cause memcheck to complain about the address "a" and so to print
+   its best guess as to what "a" actually is.  a must be
+   addressible. */
+
+void croak ( void* aV )
+{
+  char* a = (char*)aV;
+  char* undefp = malloc(1);
+  char saved = *a;
+  assert(undefp);
+  *a = *undefp;
+  VALGRIND_CHECK_MEM_IS_DEFINED(a, 1);
+  *a = saved;
+  free(undefp);
+}
+
+#include <stdio.h>
+
+int global_u1;
+
+int global_i1 = 17;
+
+char global_u2[10];
+
+char global_i2[10] = { 1,2,3,4,5,6,7,8,9,10 };
+
+
+int main ( void )
+{
+  int local;
+  char* onheap = malloc(3);
+  assert(onheap);
+  croak(onheap+1);
+  free(onheap);
+
+  croak( &global_u1 );
+  croak( &global_i1 );
+  croak( &global_u2[3] );
+  croak( &global_i2[7] );
+  croak( &local );
+  return 0;
+}
diff --git a/memcheck/tests/varinfo1.stderr.exp b/memcheck/tests/varinfo1.stderr.exp
new file mode 100644
index 0000000..bc17c61
--- /dev/null
+++ b/memcheck/tests/varinfo1.stderr.exp
@@ -0,0 +1,43 @@
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo1.c:28)
+   by 0x........: main (varinfo1.c:49)
+ Address 0x........ is 1 bytes inside a block of size 3 alloc'd
+   at 0x........: malloc (vg_replace_malloc.c:...)
+   by 0x........: main (varinfo1.c:47)
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo1.c:28)
+   by 0x........: main (varinfo1.c:52)
+ Location 0x........ is 0 bytes inside global var "global_u1"
+ declared at varinfo1.c:35
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo1.c:28)
+   by 0x........: main (varinfo1.c:53)
+ Location 0x........ is 0 bytes inside global var "global_i1"
+ declared at varinfo1.c:37
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo1.c:28)
+   by 0x........: main (varinfo1.c:54)
+ Location 0x........ is 0 bytes inside global_u2[3],
+ a global variable declared at varinfo1.c:39
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo1.c:28)
+   by 0x........: main (varinfo1.c:55)
+ Location 0x........ is 0 bytes inside global_i2[7],
+ a global variable declared at varinfo1.c:41
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo1.c:28)
+   by 0x........: main (varinfo1.c:56)
+ Location 0x........ is 0 bytes inside local var "local"
+ declared at varinfo1.c:46, in frame #1 of thread 1
+
+ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 7 allocs, 7 frees, 9 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/varinfo1.stdout.exp b/memcheck/tests/varinfo1.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/memcheck/tests/varinfo1.stdout.exp
diff --git a/memcheck/tests/varinfo1.vgtest b/memcheck/tests/varinfo1.vgtest
new file mode 100644
index 0000000..f1ff6fb
--- /dev/null
+++ b/memcheck/tests/varinfo1.vgtest
@@ -0,0 +1,2 @@
+prog: varinfo1
+vgopts: --read-var-info=yes
diff --git a/memcheck/tests/varinfo2.c b/memcheck/tests/varinfo2.c
new file mode 100644
index 0000000..7ccea80
--- /dev/null
+++ b/memcheck/tests/varinfo2.c
@@ -0,0 +1,53 @@
+
+/* Check for correct handling of nested scopes in a zero-biased
+   executable. */
+
+/* Relevant compile flags are:
+
+   -Wall -g -I$prefix/include/valgrind
+
+   eg -Wall -g -I`pwd`/Inst/include/valgrind
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "memcheck/memcheck.h"
+
+/* Cause memcheck to complain about the address "a" and so to print
+   its best guess as to what "a" actually is.  a must be
+   addressible. */
+
+void croak ( void* aV )
+{
+  char* a = (char*)aV;
+  char* undefp = malloc(1);
+  char saved = *a;
+  assert(undefp);
+  *a = *undefp;
+  VALGRIND_CHECK_MEM_IS_DEFINED(a, 1);
+  *a = saved;
+  free(undefp);
+}
+
+#include <stdio.h>
+
+void foo ( void )
+{
+  int var;
+  var = 1;
+  { char var[10];
+    var[6] = 4;
+    croak( &var[7] );
+    { struct { double foo; float bar; } var;
+      croak ( 2 + (char*)&var.bar );
+    }
+  }
+  croak( 1 + (char*)&var );
+}
+
+int main ( void )
+{
+  foo();
+  return 0;
+}
diff --git a/memcheck/tests/varinfo2.stderr.exp b/memcheck/tests/varinfo2.stderr.exp
new file mode 100644
index 0000000..90a0352
--- /dev/null
+++ b/memcheck/tests/varinfo2.stderr.exp
@@ -0,0 +1,27 @@
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo2.c:28)
+   by 0x........: foo (varinfo2.c:41)
+   by 0x........: main (varinfo2.c:51)
+ Location 0x........ is 0 bytes inside var[7],
+ declared at varinfo2.c:39, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo2.c:28)
+   by 0x........: foo (varinfo2.c:43)
+   by 0x........: main (varinfo2.c:51)
+ Location 0x........ is 2 bytes inside var.bar,
+ declared at varinfo2.c:42, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo2.c:28)
+   by 0x........: foo (varinfo2.c:46)
+   by 0x........: main (varinfo2.c:51)
+ Location 0x........ is 1 byte inside local var "var"
+ declared at varinfo2.c:37, in frame #1 of thread 1
+
+ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 3 allocs, 3 frees, 3 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/varinfo2.stdout.exp b/memcheck/tests/varinfo2.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/memcheck/tests/varinfo2.stdout.exp
diff --git a/memcheck/tests/varinfo2.vgtest b/memcheck/tests/varinfo2.vgtest
new file mode 100644
index 0000000..61ef4dd
--- /dev/null
+++ b/memcheck/tests/varinfo2.vgtest
@@ -0,0 +1,2 @@
+prog: varinfo2
+vgopts: --read-var-info=yes
diff --git a/memcheck/tests/varinfo3.c b/memcheck/tests/varinfo3.c
new file mode 100644
index 0000000..2523d91
--- /dev/null
+++ b/memcheck/tests/varinfo3.c
@@ -0,0 +1,68 @@
+
+/* Check for correct handling of static vs non-static, local vs
+   non-local variables in a zero-biased executable. */
+/* Relevant compile flags are:
+
+   -Wall -g -I$prefix/include/valgrind
+
+   eg -Wall -g -I`pwd`/Inst/include/valgrind
+*/
+/* Unfortunately 2008 Feb 26, requires its own filter_varinfo3, since
+   nonstatic_local_{un}def are not handled properly and so end up with
+   compiler-dependent names, eg static_local_def.2919 and
+   static_local_undef.2921.  So filter off the .nnnn part. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "memcheck/memcheck.h"
+/* Cause memcheck to complain about the address "a" and so to print
+   its best guess as to what "a" actually is.  a must be
+   addressible. */
+void croak ( void* aV )
+{
+  char* a = (char*)aV;
+  char* undefp = malloc(1);
+  char saved = *a;
+  assert(undefp);
+  *a = *undefp;
+  VALGRIND_CHECK_MEM_IS_DEFINED(a, 1);
+  *a = saved;
+  free(undefp);
+}
+
+#include <stdio.h>
+
+static char static_global_def[10]    = {0,0,0,0,0, 0,0,0,0,0};
+       char nonstatic_global_def[10] = {0,0,0,0,0, 0,0,0,0,0};
+static char static_global_undef[10];
+       char nonstatic_global_undef[10];
+
+void bar ( char* p1, char* p2, char* p3, char* p4 )
+{
+   croak(p1);
+   croak(p2);
+   croak(p3);
+   croak(p4);
+}
+
+void foo ( void )
+{
+   static char static_local_def[10]    = {0,0,0,0,0, 0,0,0,0,0};
+          char nonstatic_local_def[10] = {0,0,0,0,0, 0,0,0,0,0};
+   static char static_local_undef[10];
+          char nonstatic_local_undef[10];
+   croak ( 1 + (char*)&static_global_def );
+   croak ( 2 + (char*)&nonstatic_global_def );
+   croak ( 3 + (char*)&static_global_undef );
+   croak ( 4 + (char*)&nonstatic_global_undef );
+   bar( 5 + (char*)&static_local_def,
+        6 + (char*)&nonstatic_local_def,
+        7 + (char*)&static_local_undef,
+        8 + (char*)&nonstatic_local_undef );
+}
+
+int main ( void )
+{
+  foo();
+  return 0;
+}
diff --git a/memcheck/tests/varinfo3.stderr.exp b/memcheck/tests/varinfo3.stderr.exp
new file mode 100644
index 0000000..57f9cc5
--- /dev/null
+++ b/memcheck/tests/varinfo3.stderr.exp
@@ -0,0 +1,64 @@
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo3.c:28)
+   by 0x........: foo (varinfo3.c:54)
+   by 0x........: main (varinfo3.c:66)
+ Location 0x........ is 0 bytes inside static_global_def[1],
+ declared at varinfo3.c:35, in frame #0 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo3.c:28)
+   by 0x........: foo (varinfo3.c:55)
+   by 0x........: main (varinfo3.c:66)
+ Location 0x........ is 0 bytes inside nonstatic_global_def[2],
+ a global variable declared at varinfo3.c:36
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo3.c:28)
+   by 0x........: foo (varinfo3.c:56)
+   by 0x........: main (varinfo3.c:66)
+ Location 0x........ is 0 bytes inside static_global_undef[3],
+ declared at varinfo3.c:37, in frame #0 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo3.c:28)
+   by 0x........: foo (varinfo3.c:57)
+   by 0x........: main (varinfo3.c:66)
+ Location 0x........ is 0 bytes inside nonstatic_global_undef[4],
+ a global variable declared at varinfo3.c:38
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo3.c:28)
+   by 0x........: bar (varinfo3.c:42)
+   by 0x........: foo (varinfo3.c:58)
+   by 0x........: main (varinfo3.c:66)
+ Address 0x........ is 5 bytes inside data symbol "static_local_def.XXXX"
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo3.c:28)
+   by 0x........: bar (varinfo3.c:43)
+   by 0x........: foo (varinfo3.c:58)
+   by 0x........: main (varinfo3.c:66)
+ Location 0x........ is 0 bytes inside nonstatic_local_def[6],
+ declared at varinfo3.c:51, in frame #2 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo3.c:28)
+   by 0x........: bar (varinfo3.c:44)
+   by 0x........: foo (varinfo3.c:58)
+   by 0x........: main (varinfo3.c:66)
+ Address 0x........ is 7 bytes inside data symbol "static_local_undef.XXXX"
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo3.c:28)
+   by 0x........: bar (varinfo3.c:45)
+   by 0x........: foo (varinfo3.c:58)
+   by 0x........: main (varinfo3.c:66)
+ Location 0x........ is 0 bytes inside nonstatic_local_undef[8],
+ declared at varinfo3.c:53, in frame #2 of thread 1
+
+ERROR SUMMARY: 8 errors from 8 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 8 allocs, 8 frees, 8 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/varinfo3.stdout.exp b/memcheck/tests/varinfo3.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/memcheck/tests/varinfo3.stdout.exp
diff --git a/memcheck/tests/varinfo3.vgtest b/memcheck/tests/varinfo3.vgtest
new file mode 100644
index 0000000..2b827cf
--- /dev/null
+++ b/memcheck/tests/varinfo3.vgtest
@@ -0,0 +1,3 @@
+prog: varinfo3
+vgopts: --read-var-info=yes
+stderr_filter: filter_varinfo3
diff --git a/memcheck/tests/varinfo4.c b/memcheck/tests/varinfo4.c
new file mode 100644
index 0000000..499e087
--- /dev/null
+++ b/memcheck/tests/varinfo4.c
@@ -0,0 +1,58 @@
+
+/* A small demo of providing descriptions of structured types in error
+   messages. */
+
+/* Relevant compile flags are:
+
+   -Wall -g -I$prefix/include/valgrind
+
+   eg -Wall -g -I`pwd`/Inst/include/valgrind
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "memcheck/memcheck.h"
+
+/* Cause memcheck to complain about the address "a" and so to print
+   its best guess as to what "a" actually is.  a must be
+   addressible. */
+
+void croak ( void* aV )
+{
+  char* a = (char*)aV;
+  char* undefp = malloc(1);
+  char saved = *a;
+  assert(undefp);
+  *a = *undefp;
+  VALGRIND_CHECK_MEM_IS_DEFINED(a, 1);
+  *a = saved;
+  free(undefp);
+}
+
+#include <stdio.h>
+#include <string.h>
+
+typedef struct { short c1; char* c2[3]; } XX;
+
+typedef
+   struct _str { int bing; int bong; XX xyzzy[77]; }
+   Str;
+
+__attribute__((noinline))
+int blah ( int x, int y )
+{
+  Str a[10];
+  memset(a, 0, sizeof(a));
+  croak(1 + (char*)(&a[3].xyzzy[x*y].c1));
+  croak( (char*)(&a[5].bong) );
+  croak( 1 + (char*)(&a[3].xyzzy[x*y].c2[2]) );
+  memset(a, 0, sizeof(a));
+  return a[3].xyzzy[x*y].c1;
+}
+
+int main ( void )
+{
+  printf("answer is %d\n", blah(3,7) );
+  return 0;
+}
diff --git a/memcheck/tests/varinfo4.stderr.exp b/memcheck/tests/varinfo4.stderr.exp
new file mode 100644
index 0000000..7f1226f
--- /dev/null
+++ b/memcheck/tests/varinfo4.stderr.exp
@@ -0,0 +1,27 @@
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo4.c:28)
+   by 0x........: blah (varinfo4.c:47)
+   by 0x........: main (varinfo4.c:56)
+ Location 0x........ is 1 byte inside a[3].xyzzy[21].c1,
+ declared at varinfo4.c:45, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo4.c:28)
+   by 0x........: blah (varinfo4.c:48)
+   by 0x........: main (varinfo4.c:56)
+ Location 0x........ is 0 bytes inside a[5].bong,
+ declared at varinfo4.c:45, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo4.c:28)
+   by 0x........: blah (varinfo4.c:49)
+   by 0x........: main (varinfo4.c:56)
+ Location 0x........ is 1 byte inside a[3].xyzzy[21].c2[2],
+ declared at varinfo4.c:45, in frame #1 of thread 1
+
+ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 3 allocs, 3 frees, 3 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/varinfo4.stdout.exp b/memcheck/tests/varinfo4.stdout.exp
new file mode 100644
index 0000000..b7089fd
--- /dev/null
+++ b/memcheck/tests/varinfo4.stdout.exp
@@ -0,0 +1 @@
+answer is 0
diff --git a/memcheck/tests/varinfo4.vgtest b/memcheck/tests/varinfo4.vgtest
new file mode 100644
index 0000000..cd72fb2
--- /dev/null
+++ b/memcheck/tests/varinfo4.vgtest
@@ -0,0 +1,2 @@
+prog: varinfo4
+vgopts: --read-var-info=yes
diff --git a/memcheck/tests/varinfo5.c b/memcheck/tests/varinfo5.c
new file mode 100644
index 0000000..97e3516
--- /dev/null
+++ b/memcheck/tests/varinfo5.c
@@ -0,0 +1,7 @@
+
+extern void varinfo5_main ( void );
+
+int main ( void ) {
+  varinfo5_main();
+  return 0;
+}
diff --git a/memcheck/tests/varinfo5.stderr.exp b/memcheck/tests/varinfo5.stderr.exp
new file mode 100644
index 0000000..291ab0c
--- /dev/null
+++ b/memcheck/tests/varinfo5.stderr.exp
@@ -0,0 +1,186 @@
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: varinfo1_main (varinfo5so.c:52)
+   by 0x........: varinfo5_main (varinfo5so.c:154)
+   by 0x........: main (varinfo5.c:5)
+ Address 0x........ is 1 bytes inside a block of size 3 alloc'd
+   at 0x........: malloc (vg_replace_malloc.c:...)
+   by 0x........: varinfo1_main (varinfo5so.c:50)
+   by 0x........: varinfo5_main (varinfo5so.c:154)
+   by 0x........: main (varinfo5.c:5)
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: varinfo1_main (varinfo5so.c:55)
+   by 0x........: varinfo5_main (varinfo5so.c:154)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside global var "global_u1"
+ declared at varinfo5so.c:38
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: varinfo1_main (varinfo5so.c:56)
+   by 0x........: varinfo5_main (varinfo5so.c:154)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside global var "global_i1"
+ declared at varinfo5so.c:40
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: varinfo1_main (varinfo5so.c:57)
+   by 0x........: varinfo5_main (varinfo5so.c:154)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside global_u2[3],
+ a global variable declared at varinfo5so.c:42
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: varinfo1_main (varinfo5so.c:58)
+   by 0x........: varinfo5_main (varinfo5so.c:154)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside global_i2[7],
+ a global variable declared at varinfo5so.c:44
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: varinfo1_main (varinfo5so.c:59)
+   by 0x........: varinfo5_main (varinfo5so.c:154)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside local var "local"
+ declared at varinfo5so.c:49, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: foo2 (varinfo5so.c:71)
+   by 0x........: varinfo2_main (varinfo5so.c:81)
+   by 0x........: varinfo5_main (varinfo5so.c:155)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside var[7],
+ declared at varinfo5so.c:69, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: foo2 (varinfo5so.c:73)
+   by 0x........: varinfo2_main (varinfo5so.c:81)
+   by 0x........: varinfo5_main (varinfo5so.c:155)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 2 bytes inside var.bar,
+ declared at varinfo5so.c:72, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: foo2 (varinfo5so.c:76)
+   by 0x........: varinfo2_main (varinfo5so.c:81)
+   by 0x........: varinfo5_main (varinfo5so.c:155)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 1 byte inside local var "var"
+ declared at varinfo5so.c:67, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: foo3 (varinfo5so.c:106)
+   by 0x........: varinfo3_main (varinfo5so.c:118)
+   by 0x........: varinfo5_main (varinfo5so.c:156)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside static_global_def[1],
+ declared at varinfo5so.c:87, in frame #0 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: foo3 (varinfo5so.c:107)
+   by 0x........: varinfo3_main (varinfo5so.c:118)
+   by 0x........: varinfo5_main (varinfo5so.c:156)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside nonstatic_global_def[2],
+ a global variable declared at varinfo5so.c:88
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: foo3 (varinfo5so.c:108)
+   by 0x........: varinfo3_main (varinfo5so.c:118)
+   by 0x........: varinfo5_main (varinfo5so.c:156)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside static_global_undef[3],
+ declared at varinfo5so.c:89, in frame #0 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: foo3 (varinfo5so.c:109)
+   by 0x........: varinfo3_main (varinfo5so.c:118)
+   by 0x........: varinfo5_main (varinfo5so.c:156)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside nonstatic_global_undef[4],
+ a global variable declared at varinfo5so.c:90
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: bar3 (varinfo5so.c:94)
+   by 0x........: foo3 (varinfo5so.c:110)
+   by 0x........: varinfo3_main (varinfo5so.c:118)
+   by 0x........: varinfo5_main (varinfo5so.c:156)
+   by 0x........: main (varinfo5.c:5)
+ Address 0x........ is 5 bytes inside data symbol "static_local_def.XXXX"
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: bar3 (varinfo5so.c:95)
+   by 0x........: foo3 (varinfo5so.c:110)
+   by 0x........: varinfo3_main (varinfo5so.c:118)
+   by 0x........: varinfo5_main (varinfo5so.c:156)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside nonstatic_local_def[6],
+ declared at varinfo5so.c:103, in frame #2 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: bar3 (varinfo5so.c:96)
+   by 0x........: foo3 (varinfo5so.c:110)
+   by 0x........: varinfo3_main (varinfo5so.c:118)
+   by 0x........: varinfo5_main (varinfo5so.c:156)
+   by 0x........: main (varinfo5.c:5)
+ Address 0x........ is 7 bytes inside data symbol "static_local_undef.XXXX"
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: bar3 (varinfo5so.c:97)
+   by 0x........: foo3 (varinfo5so.c:110)
+   by 0x........: varinfo3_main (varinfo5so.c:118)
+   by 0x........: varinfo5_main (varinfo5so.c:156)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside nonstatic_local_undef[8],
+ declared at varinfo5so.c:105, in frame #2 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: blah4 (varinfo5so.c:137)
+   by 0x........: varinfo4_main (varinfo5so.c:146)
+   by 0x........: varinfo5_main (varinfo5so.c:157)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 1 byte inside a[3].xyzzy[21].c1,
+ declared at varinfo5so.c:135, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: blah4 (varinfo5so.c:138)
+   by 0x........: varinfo4_main (varinfo5so.c:146)
+   by 0x........: varinfo5_main (varinfo5so.c:157)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 0 bytes inside a[5].bong,
+ declared at varinfo5so.c:135, in frame #1 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo5so.c:29)
+   by 0x........: blah4 (varinfo5so.c:139)
+   by 0x........: varinfo4_main (varinfo5so.c:146)
+   by 0x........: varinfo5_main (varinfo5so.c:157)
+   by 0x........: main (varinfo5.c:5)
+ Location 0x........ is 1 byte inside a[3].xyzzy[21].c2[2],
+ declared at varinfo5so.c:135, in frame #1 of thread 1
+answer is 0
+
+ERROR SUMMARY: 20 errors from 20 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 21 allocs, 21 frees, 23 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/varinfo5.stdout.exp b/memcheck/tests/varinfo5.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/memcheck/tests/varinfo5.stdout.exp
diff --git a/memcheck/tests/varinfo5.vgtest b/memcheck/tests/varinfo5.vgtest
new file mode 100644
index 0000000..5fda7c4
--- /dev/null
+++ b/memcheck/tests/varinfo5.vgtest
@@ -0,0 +1,3 @@
+prog: varinfo5
+vgopts: --read-var-info=yes
+stderr_filter: filter_varinfo3
diff --git a/memcheck/tests/varinfo5so.c b/memcheck/tests/varinfo5so.c
new file mode 100644
index 0000000..fab8174
--- /dev/null
+++ b/memcheck/tests/varinfo5so.c
@@ -0,0 +1,158 @@
+
+/* A concatenation of varinfo1 .. varinfo4 in a shared object.  This
+   is to check for correct functionality in a non-zero-biased ELF
+   executable. */
+
+/* Relevant compile flags are:
+
+   -Wall -g -I$prefix/include/valgrind
+
+   eg -Wall -g -I`pwd`/Inst/include/valgrind
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "memcheck/memcheck.h"
+
+/* Cause memcheck to complain about the address "a" and so to print
+   its best guess as to what "a" actually is.  a must be
+   addressible. */
+__attribute__((noinline))
+void croak ( void* aV )
+{
+  char* a = (char*)aV;
+  char* undefp = malloc(1);
+  char saved = *a;
+  assert(undefp);
+  *a = *undefp;
+  VALGRIND_CHECK_MEM_IS_DEFINED(a, 1);
+  *a = saved;
+  free(undefp);
+}
+
+#include <stdio.h>
+
+/* ------------ varinfo1 ------------ */
+
+int global_u1;
+
+int global_i1 = 17;
+
+char global_u2[10];
+
+char global_i2[10] = { 1,2,3,4,5,6,7,8,9,10 };
+
+__attribute__((noinline))
+static int varinfo1_main ( void )
+{
+  int local;
+  char* onheap = malloc(3);
+  assert(onheap);
+  croak(onheap+1);
+  free(onheap);
+
+  croak( &global_u1 );
+  croak( &global_i1 );
+  croak( &global_u2[3] );
+  croak( &global_i2[7] );
+  croak( &local );
+  return 0;
+}
+
+/* ------------ varinfo2 ------------ */
+__attribute__((noinline))
+static void foo2 ( void )
+{
+  int var;
+  var = 1;
+  { char var[10];
+    var[6] = 4;
+    croak( &var[7] );
+    { struct { double foo; float bar; } var;
+      croak ( 2 + (char*)&var.bar );
+    }
+  }
+  croak( 1 + (char*)&var );
+}
+__attribute__((noinline))
+static int varinfo2_main ( void )
+{
+  foo2();
+  return 0;
+}
+
+/* ------------ varinfo3 ------------ */
+
+static char static_global_def[10]    = {0,0,0,0,0, 0,0,0,0,0};
+       char nonstatic_global_def[10] = {0,0,0,0,0, 0,0,0,0,0};
+static char static_global_undef[10];
+       char nonstatic_global_undef[10];
+__attribute__((noinline))
+static void bar3 ( char* p1, char* p2, char* p3, char* p4 )
+{
+   croak(p1);
+   croak(p2);
+   croak(p3);
+   croak(p4);
+}
+__attribute__((noinline))
+static void foo3 ( void )
+{
+   static char static_local_def[10]    = {0,0,0,0,0, 0,0,0,0,0};
+          char nonstatic_local_def[10] = {0,0,0,0,0, 0,0,0,0,0};
+   static char static_local_undef[10];
+          char nonstatic_local_undef[10];
+   croak ( 1 + (char*)&static_global_def );
+   croak ( 2 + (char*)&nonstatic_global_def );
+   croak ( 3 + (char*)&static_global_undef );
+   croak ( 4 + (char*)&nonstatic_global_undef );
+   bar3( 5 + (char*)&static_local_def,
+         6 + (char*)&nonstatic_local_def,
+         7 + (char*)&static_local_undef,
+         8 + (char*)&nonstatic_local_undef );
+}
+__attribute__((noinline))
+static int varinfo3_main ( void )
+{
+  foo3();
+  return 0;
+}
+
+/* ------------ varinfo4 ------------ */
+
+#include <string.h>
+
+typedef struct { short c1; char* c2[3]; } XX;
+
+typedef
+   struct _str { int bing; int bong; XX xyzzy[77]; }
+   Str;
+
+__attribute__((noinline))
+static int blah4 ( int x, int y )
+{
+  Str a[10];
+  memset(a, 0, sizeof(a));
+  croak(1 + (char*)(&a[3].xyzzy[x*y].c1));
+  croak( (char*)(&a[5].bong) );
+  croak( 1 + (char*)(&a[3].xyzzy[x*y].c2[2]) );
+  memset(a, 0, sizeof(a));
+  return a[3].xyzzy[x*y].c1;
+}
+__attribute__((noinline))
+static int varinfo4_main ( void )
+{
+  fprintf(stderr, "answer is %d\n", blah4(3,7) );
+  return 0;
+}
+
+/* ------------ varinfo5 ------------ */
+
+void varinfo5_main ( void )
+{
+   varinfo1_main();
+   varinfo2_main();
+   varinfo3_main();
+   varinfo4_main();
+}
diff --git a/memcheck/tests/varinfo6.c b/memcheck/tests/varinfo6.c
new file mode 100644
index 0000000..01f8d6b
--- /dev/null
+++ b/memcheck/tests/varinfo6.c
@@ -0,0 +1,6571 @@
+
+/* Test the variable identification machinery in a non-toy sized
+   program.  Also, the croak() call in BZ2_decompress causes Valgrind
+   to try to describe a local variable (i) that has at least a dozen
+   independent live ranges (hence, is really that many independent
+   variables).  Hence it tests the machinery's ability to correctly
+   handle a variable which has multiple live ranges and hence multiple
+   non-overlapping areas in which it actually exists.
+*/
+
+/* Relevant compile flags are:
+
+   -Wall -g -I$prefix/include/valgrind
+
+   eg -Wall -g -I`pwd`/Inst/include/valgrind
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "memcheck/memcheck.h"
+
+/* Cause memcheck to complain about the address "a" and so to print
+   its best guess as to what "a" actually is.  a must be
+   addressible. */
+
+void croak ( void* aV )
+{
+  char* a = (char*)aV;
+  char* undefp = malloc(1);
+  char saved = *a;
+  assert(undefp);
+  *a = *undefp;
+  VALGRIND_CHECK_MEM_IS_DEFINED(a, 1);
+  *a = saved;
+  free(undefp);
+}
+
+// This benchmark is basically bzip2 (mashed to be a single file)
+// compressing and decompressing some data.  It tests Valgrind's handling of
+// realistic and "difficult" (ie. lots of branches and memory accesses)
+// integer code.  Execution is spread out over quite a few basic blocks; 
+// --profile-flags indicates that to get to the top 90%th percentile of
+// dynamic BB counts requires considering the top 51 basic blocks
+
+// This program can be used both as part of the performance test
+// suite, in which case we want it to run for quite a while,
+// and as part of the regression (correctness) test suite, in
+// which case we want it to run quickly and be verbose.
+// So it does the latter iff given a command line arg.
+
+// Licensing: the code within is mostly taken from bzip2, which has a BSD
+// license.  There is a little code from VEX, which is licensed under GPLv2
+// And it's all written by Julian Seward.
+
+#define BZ_NO_STDIO
+
+
+/*-------------------------------------------------------------*/
+/*--- Private header file for the library.                  ---*/
+/*---                                       bzlib_private.h ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+
+#ifndef _BZLIB_PRIVATE_H
+#define _BZLIB_PRIVATE_H
+
+#include <stdlib.h>
+
+#ifndef BZ_NO_STDIO
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#endif
+
+
+/*-------------------------------------------------------------*/
+/*--- Public header file for the library.                   ---*/
+/*---                                               bzlib.h ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+
+#ifndef _BZLIB_H
+#define _BZLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BZ_RUN               0
+#define BZ_FLUSH             1
+#define BZ_FINISH            2
+
+#define BZ_OK                0
+#define BZ_RUN_OK            1
+#define BZ_FLUSH_OK          2
+#define BZ_FINISH_OK         3
+#define BZ_STREAM_END        4
+#define BZ_SEQUENCE_ERROR    (-1)
+#define BZ_PARAM_ERROR       (-2)
+#define BZ_MEM_ERROR         (-3)
+#define BZ_DATA_ERROR        (-4)
+#define BZ_DATA_ERROR_MAGIC  (-5)
+#define BZ_IO_ERROR          (-6)
+#define BZ_UNEXPECTED_EOF    (-7)
+#define BZ_OUTBUFF_FULL      (-8)
+#define BZ_CONFIG_ERROR      (-9)
+
+typedef 
+   struct {
+      char *next_in;
+      unsigned int avail_in;
+      unsigned int total_in_lo32;
+      unsigned int total_in_hi32;
+
+      char *next_out;
+      unsigned int avail_out;
+      unsigned int total_out_lo32;
+      unsigned int total_out_hi32;
+
+      void *state;
+
+      void *(*bzalloc)(void *,int,int);
+      void (*bzfree)(void *,void *);
+      void *opaque;
+   } 
+   bz_stream;
+
+
+#ifndef BZ_IMPORT
+#define BZ_EXPORT
+#endif
+
+#ifndef BZ_NO_STDIO
+/* Need a definitition for FILE */
+#include <stdio.h>
+#endif
+
+#ifdef _WIN32
+#   include <windows.h>
+#   ifdef small
+      /* windows.h define small to char */
+#      undef small
+#   endif
+#   ifdef BZ_EXPORT
+#   define BZ_API(func) WINAPI func
+#   define BZ_EXTERN extern
+#   else
+   /* import windows dll dynamically */
+#   define BZ_API(func) (WINAPI * func)
+#   define BZ_EXTERN
+#   endif
+#else
+#   define BZ_API(func) func
+#   define BZ_EXTERN extern
+#endif
+
+
+/*-- Core (low-level) library functions --*/
+
+BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( 
+      bz_stream* strm, 
+      int        blockSize100k, 
+      int        verbosity, 
+      int        workFactor 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzCompress) ( 
+      bz_stream* strm, 
+      int action 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( 
+      bz_stream* strm 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( 
+      bz_stream *strm, 
+      int       verbosity, 
+      int       small
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( 
+      bz_stream* strm 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( 
+      bz_stream *strm 
+   );
+
+
+
+/*-- High(er) level library functions --*/
+
+#ifndef BZ_NO_STDIO
+#define BZ_MAX_UNUSED 5000
+
+typedef void BZFILE;
+
+BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( 
+      int*  bzerror,   
+      FILE* f, 
+      int   verbosity, 
+      int   small,
+      void* unused,    
+      int   nUnused 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( 
+      int*    bzerror, 
+      BZFILE* b 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( 
+      int*    bzerror, 
+      BZFILE* b, 
+      void**  unused,  
+      int*    nUnused 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzRead) ( 
+      int*    bzerror, 
+      BZFILE* b, 
+      void*   buf, 
+      int     len 
+   );
+
+BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( 
+      int*  bzerror,      
+      FILE* f, 
+      int   blockSize100k, 
+      int   verbosity, 
+      int   workFactor 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzWrite) ( 
+      int*    bzerror, 
+      BZFILE* b, 
+      void*   buf, 
+      int     len 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( 
+      int*          bzerror, 
+      BZFILE*       b, 
+      int           abandon, 
+      unsigned int* nbytes_in, 
+      unsigned int* nbytes_out 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( 
+      int*          bzerror, 
+      BZFILE*       b, 
+      int           abandon, 
+      unsigned int* nbytes_in_lo32, 
+      unsigned int* nbytes_in_hi32, 
+      unsigned int* nbytes_out_lo32, 
+      unsigned int* nbytes_out_hi32
+   );
+#endif
+
+
+/*-- Utility functions --*/
+
+BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( 
+      char*         dest, 
+      unsigned int* destLen,
+      char*         source, 
+      unsigned int  sourceLen,
+      int           blockSize100k, 
+      int           verbosity, 
+      int           workFactor 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( 
+      char*         dest, 
+      unsigned int* destLen,
+      char*         source, 
+      unsigned int  sourceLen,
+      int           small, 
+      int           verbosity 
+   );
+
+
+/*--
+   Code contributed by Yoshioka Tsuneo
+   (QWF00133@niftyserve.or.jp/tsuneo-y@is.aist-nara.ac.jp),
+   to support better zlib compatibility.
+   This code is not _officially_ part of libbzip2 (yet);
+   I haven't tested it, documented it, or considered the
+   threading-safeness of it.
+   If this code breaks, please contact both Yoshioka and me.
+--*/
+
+BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
+      void
+   );
+
+#ifndef BZ_NO_STDIO
+BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
+      const char *path,
+      const char *mode
+   );
+
+BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
+      int        fd,
+      const char *mode
+   );
+         
+BZ_EXTERN int BZ_API(BZ2_bzread) (
+      BZFILE* b, 
+      void* buf, 
+      int len 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzwrite) (
+      BZFILE* b, 
+      void*   buf, 
+      int     len 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzflush) (
+      BZFILE* b
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzclose) (
+      BZFILE* b
+   );
+
+BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
+      BZFILE *b, 
+      int    *errnum
+   );
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*-------------------------------------------------------------*/
+/*--- end                                           bzlib.h ---*/
+/*-------------------------------------------------------------*/
+
+
+
+
+/*-- General stuff. --*/
+
+#define BZ_VERSION  "1.0.3, 17-Oct-2004"
+
+typedef char            Char;
+typedef unsigned char   Bool;
+typedef unsigned char   UChar;
+typedef int             Int32;
+typedef unsigned int    UInt32;
+typedef short           Int16;
+typedef unsigned short  UInt16;
+
+#define True  ((Bool)1)
+#define False ((Bool)0)
+
+#ifndef __GNUC__
+#define __inline__  /* */
+#endif 
+
+#ifndef BZ_NO_STDIO
+extern void BZ2_bz__AssertH__fail ( int errcode );
+#define AssertH(cond,errcode) \
+   { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); }
+#if BZ_DEBUG
+#define AssertD(cond,msg) \
+   { if (!(cond)) {       \
+      fprintf ( stderr,   \
+        "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\
+      exit(1); \
+   }}
+#else
+#define AssertD(cond,msg) /* */
+#endif
+#define VPrintf0(zf) \
+   fprintf(stderr,zf)
+#define VPrintf1(zf,za1) \
+   fprintf(stderr,zf,za1)
+#define VPrintf2(zf,za1,za2) \
+   fprintf(stderr,zf,za1,za2)
+#define VPrintf3(zf,za1,za2,za3) \
+   fprintf(stderr,zf,za1,za2,za3)
+#define VPrintf4(zf,za1,za2,za3,za4) \
+   fprintf(stderr,zf,za1,za2,za3,za4)
+#define VPrintf5(zf,za1,za2,za3,za4,za5) \
+   fprintf(stderr,zf,za1,za2,za3,za4,za5)
+#else
+extern void bz_internal_error ( int errcode );
+#define AssertH(cond,errcode) \
+   { if (!(cond)) bz_internal_error ( errcode ); }
+#define AssertD(cond,msg) /* */
+#define VPrintf0(zf) \
+   vex_printf(zf)
+#define VPrintf1(zf,za1) \
+   vex_printf(zf,za1)
+#define VPrintf2(zf,za1,za2) \
+   vex_printf(zf,za1,za2)
+#define VPrintf3(zf,za1,za2,za3) \
+   vex_printf(zf,za1,za2,za3)
+#define VPrintf4(zf,za1,za2,za3,za4) \
+   vex_printf(zf,za1,za2,za3,za4)
+#define VPrintf5(zf,za1,za2,za3,za4,za5) \
+   vex_printf(zf,za1,za2,za3,za4,za5)
+#endif
+
+
+#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1)
+#define BZFREE(ppp)  (strm->bzfree)(strm->opaque,(ppp))
+
+
+/*-- Header bytes. --*/
+
+#define BZ_HDR_B 0x42   /* 'B' */
+#define BZ_HDR_Z 0x5a   /* 'Z' */
+#define BZ_HDR_h 0x68   /* 'h' */
+#define BZ_HDR_0 0x30   /* '0' */
+  
+/*-- Constants for the back end. --*/
+
+#define BZ_MAX_ALPHA_SIZE 258
+#define BZ_MAX_CODE_LEN    23
+
+#define BZ_RUNA 0
+#define BZ_RUNB 1
+
+#define BZ_N_GROUPS 6
+#define BZ_G_SIZE   50
+#define BZ_N_ITERS  4
+
+#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE))
+
+
+
+/*-- Stuff for randomising repetitive blocks. --*/
+
+extern Int32 BZ2_rNums[512];
+
+#define BZ_RAND_DECLS                          \
+   Int32 rNToGo;                               \
+   Int32 rTPos                                 \
+
+#define BZ_RAND_INIT_MASK                      \
+   s->rNToGo = 0;                              \
+   s->rTPos  = 0                               \
+
+#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0)
+
+#define BZ_RAND_UPD_MASK                       \
+   if (s->rNToGo == 0) {                       \
+      s->rNToGo = BZ2_rNums[s->rTPos];         \
+      s->rTPos++;                              \
+      if (s->rTPos == 512) s->rTPos = 0;       \
+   }                                           \
+   s->rNToGo--;
+
+
+
+/*-- Stuff for doing CRCs. --*/
+
+extern UInt32 BZ2_crc32Table[256];
+
+#define BZ_INITIALISE_CRC(crcVar)              \
+{                                              \
+   crcVar = 0xffffffffL;                       \
+}
+
+#define BZ_FINALISE_CRC(crcVar)                \
+{                                              \
+   crcVar = ~(crcVar);                         \
+}
+
+#define BZ_UPDATE_CRC(crcVar,cha)              \
+{                                              \
+   crcVar = (crcVar << 8) ^                    \
+            BZ2_crc32Table[(crcVar >> 24) ^    \
+                           ((UChar)cha)];      \
+}
+
+
+
+/*-- States and modes for compression. --*/
+
+#define BZ_M_IDLE      1
+#define BZ_M_RUNNING   2
+#define BZ_M_FLUSHING  3
+#define BZ_M_FINISHING 4
+
+#define BZ_S_OUTPUT    1
+#define BZ_S_INPUT     2
+
+#define BZ_N_RADIX 2
+#define BZ_N_QSORT 12
+#define BZ_N_SHELL 18
+#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2)
+
+
+
+
+/*-- Structure holding all the compression-side stuff. --*/
+
+typedef
+   struct {
+      /* pointer back to the struct bz_stream */
+      bz_stream* strm;
+
+      /* mode this stream is in, and whether inputting */
+      /* or outputting data */
+      Int32    mode;
+      Int32    state;
+
+      /* remembers avail_in when flush/finish requested */
+      UInt32   avail_in_expect;
+
+      /* for doing the block sorting */
+      UInt32*  arr1;
+      UInt32*  arr2;
+      UInt32*  ftab;
+      Int32    origPtr;
+
+      /* aliases for arr1 and arr2 */
+      UInt32*  ptr;
+      UChar*   block;
+      UInt16*  mtfv;
+      UChar*   zbits;
+
+      /* for deciding when to use the fallback sorting algorithm */
+      Int32    workFactor;
+
+      /* run-length-encoding of the input */
+      UInt32   state_in_ch;
+      Int32    state_in_len;
+      BZ_RAND_DECLS;
+
+      /* input and output limits and current posns */
+      Int32    nblock;
+      Int32    nblockMAX;
+      Int32    numZ;
+      Int32    state_out_pos;
+
+      /* map of bytes used in block */
+      Int32    nInUse;
+      Bool     inUse[256];
+      UChar    unseqToSeq[256];
+
+      /* the buffer for bit stream creation */
+      UInt32   bsBuff;
+      Int32    bsLive;
+
+      /* block and combined CRCs */
+      UInt32   blockCRC;
+      UInt32   combinedCRC;
+
+      /* misc administratium */
+      Int32    verbosity;
+      Int32    blockNo;
+      Int32    blockSize100k;
+
+      /* stuff for coding the MTF values */
+      Int32    nMTF;
+      Int32    mtfFreq    [BZ_MAX_ALPHA_SIZE];
+      UChar    selector   [BZ_MAX_SELECTORS];
+      UChar    selectorMtf[BZ_MAX_SELECTORS];
+
+      UChar    len     [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    code    [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    rfreq   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      /* second dimension: only 3 needed; 4 makes index calculations faster */
+      UInt32   len_pack[BZ_MAX_ALPHA_SIZE][4];
+
+   }
+   EState;
+
+
+
+/*-- externs for compression. --*/
+
+extern void 
+BZ2_blockSort ( EState* );
+
+extern void 
+BZ2_compressBlock ( EState*, Bool );
+
+extern void 
+BZ2_bsInitWrite ( EState* );
+
+extern void 
+BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 );
+
+extern void 
+BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 );
+
+
+
+/*-- states for decompression. --*/
+
+#define BZ_X_IDLE        1
+#define BZ_X_OUTPUT      2
+
+#define BZ_X_MAGIC_1     10
+#define BZ_X_MAGIC_2     11
+#define BZ_X_MAGIC_3     12
+#define BZ_X_MAGIC_4     13
+#define BZ_X_BLKHDR_1    14
+#define BZ_X_BLKHDR_2    15
+#define BZ_X_BLKHDR_3    16
+#define BZ_X_BLKHDR_4    17
+#define BZ_X_BLKHDR_5    18
+#define BZ_X_BLKHDR_6    19
+#define BZ_X_BCRC_1      20
+#define BZ_X_BCRC_2      21
+#define BZ_X_BCRC_3      22
+#define BZ_X_BCRC_4      23
+#define BZ_X_RANDBIT     24
+#define BZ_X_ORIGPTR_1   25
+#define BZ_X_ORIGPTR_2   26
+#define BZ_X_ORIGPTR_3   27
+#define BZ_X_MAPPING_1   28
+#define BZ_X_MAPPING_2   29
+#define BZ_X_SELECTOR_1  30
+#define BZ_X_SELECTOR_2  31
+#define BZ_X_SELECTOR_3  32
+#define BZ_X_CODING_1    33
+#define BZ_X_CODING_2    34
+#define BZ_X_CODING_3    35
+#define BZ_X_MTF_1       36
+#define BZ_X_MTF_2       37
+#define BZ_X_MTF_3       38
+#define BZ_X_MTF_4       39
+#define BZ_X_MTF_5       40
+#define BZ_X_MTF_6       41
+#define BZ_X_ENDHDR_2    42
+#define BZ_X_ENDHDR_3    43
+#define BZ_X_ENDHDR_4    44
+#define BZ_X_ENDHDR_5    45
+#define BZ_X_ENDHDR_6    46
+#define BZ_X_CCRC_1      47
+#define BZ_X_CCRC_2      48
+#define BZ_X_CCRC_3      49
+#define BZ_X_CCRC_4      50
+
+
+
+/*-- Constants for the fast MTF decoder. --*/
+
+#define MTFA_SIZE 4096
+#define MTFL_SIZE 16
+
+
+
+/*-- Structure holding all the decompression-side stuff. --*/
+
+typedef
+   struct {
+      /* pointer back to the struct bz_stream */
+      bz_stream* strm;
+
+      /* state indicator for this stream */
+      Int32    state;
+
+      /* for doing the final run-length decoding */
+      UChar    state_out_ch;
+      Int32    state_out_len;
+      Bool     blockRandomised;
+      BZ_RAND_DECLS;
+
+      /* the buffer for bit stream reading */
+      UInt32   bsBuff;
+      Int32    bsLive;
+
+      /* misc administratium */
+      Int32    blockSize100k;
+      Bool     smallDecompress;
+      Int32    currBlockNo;
+      Int32    verbosity;
+
+      /* for undoing the Burrows-Wheeler transform */
+      Int32    origPtr;
+      UInt32   tPos;
+      Int32    k0;
+      Int32    unzftab[256];
+      Int32    nblock_used;
+      Int32    cftab[257];
+      Int32    cftabCopy[257];
+
+      /* for undoing the Burrows-Wheeler transform (FAST) */
+      UInt32   *tt;
+
+      /* for undoing the Burrows-Wheeler transform (SMALL) */
+      UInt16   *ll16;
+      UChar    *ll4;
+
+      /* stored and calculated CRCs */
+      UInt32   storedBlockCRC;
+      UInt32   storedCombinedCRC;
+      UInt32   calculatedBlockCRC;
+      UInt32   calculatedCombinedCRC;
+
+      /* map of bytes used in block */
+      Int32    nInUse;
+      Bool     inUse[256];
+      Bool     inUse16[16];
+      UChar    seqToUnseq[256];
+
+      /* for decoding the MTF values */
+      UChar    mtfa   [MTFA_SIZE];
+      Int32    mtfbase[256 / MTFL_SIZE];
+      UChar    selector   [BZ_MAX_SELECTORS];
+      UChar    selectorMtf[BZ_MAX_SELECTORS];
+      UChar    len  [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+
+      Int32    limit  [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    base   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    perm   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    minLens[BZ_N_GROUPS];
+
+      /* save area for scalars in the main decompress code */
+      Int32    save_i;
+      Int32    save_j;
+      Int32    save_t;
+      Int32    save_alphaSize;
+      Int32    save_nGroups;
+      Int32    save_nSelectors;
+      Int32    save_EOB;
+      Int32    save_groupNo;
+      Int32    save_groupPos;
+      Int32    save_nextSym;
+      Int32    save_nblockMAX;
+      Int32    save_nblock;
+      Int32    save_es;
+      Int32    save_N;
+      Int32    save_curr;
+      Int32    save_zt;
+      Int32    save_zn; 
+      Int32    save_zvec;
+      Int32    save_zj;
+      Int32    save_gSel;
+      Int32    save_gMinlen;
+      Int32*   save_gLimit;
+      Int32*   save_gBase;
+      Int32*   save_gPerm;
+
+   }
+   DState;
+
+
+
+/*-- Macros for decompression. --*/
+
+#define BZ_GET_FAST(cccc)                     \
+    s->tPos = s->tt[s->tPos];                 \
+    cccc = (UChar)(s->tPos & 0xff);           \
+    s->tPos >>= 8;
+
+#define BZ_GET_FAST_C(cccc)                   \
+    c_tPos = c_tt[c_tPos];                    \
+    cccc = (UChar)(c_tPos & 0xff);            \
+    c_tPos >>= 8;
+
+#define SET_LL4(i,n)                                          \
+   { if (((i) & 0x1) == 0)                                    \
+        s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else    \
+        s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4);  \
+   }
+
+#define GET_LL4(i)                             \
+   ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF)
+
+#define SET_LL(i,n)                          \
+   { s->ll16[i] = (UInt16)(n & 0x0000ffff);  \
+     SET_LL4(i, n >> 16);                    \
+   }
+
+#define GET_LL(i) \
+   (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16))
+
+#define BZ_GET_SMALL(cccc)                            \
+      cccc = BZ2_indexIntoF ( s->tPos, s->cftab );    \
+      s->tPos = GET_LL(s->tPos);
+
+
+/*-- externs for decompression. --*/
+
+extern Int32 
+BZ2_indexIntoF ( Int32, Int32* );
+
+extern Int32 
+BZ2_decompress ( DState* );
+
+extern void 
+BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*,
+                           Int32,  Int32, Int32 );
+
+
+#endif
+
+
+/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/
+
+#ifdef BZ_NO_STDIO
+#ifndef NULL
+#define NULL 0
+#endif
+#endif
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                   bzlib_private.h ---*/
+/*-------------------------------------------------------------*/
+
+
+/* Something which has the same size as void* on the host.  That is,
+   it is 32 bits on a 32-bit host and 64 bits on a 64-bit host, and so
+   it can safely be coerced to and from a pointer type on the host
+   machine. */
+typedef  unsigned long HWord;
+typedef  char          HChar;
+typedef  signed int    Int;
+typedef  unsigned int  UInt;
+
+typedef    signed long long int   Long;
+typedef  unsigned long long int   ULong;
+
+
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+
+static HWord (*serviceFn)(HWord,HWord) = 0;
+
+#if 0
+static char* my_strcpy ( char* dest, const char* src )
+{
+   char* dest_orig = dest;
+   while (*src) *dest++ = *src++;
+   *dest = 0;
+   return dest_orig;
+}
+
+static void* my_memcpy ( void *dest, const void *src, int sz )
+{
+   const char *s = (const char *)src;
+   char *d = (char *)dest;
+
+   while (sz--)
+      *d++ = *s++;
+
+   return dest;
+}
+
+static void* my_memmove( void *dst, const void *src, unsigned int len )
+{
+    register char *d;
+    register char *s;
+    if ( dst > src ) {
+        d = (char *)dst + len - 1;
+        s = (char *)src + len - 1;
+        while ( len >= 4 ) {
+            *d-- = *s--;
+            *d-- = *s--;
+            *d-- = *s--;
+            *d-- = *s--;
+            len -= 4;
+        }
+        while ( len-- ) {
+            *d-- = *s--;
+        }
+    } else if ( dst < src ) {
+        d = (char *)dst;
+        s = (char *)src;
+        while ( len >= 4 ) {
+            *d++ = *s++;
+            *d++ = *s++;
+            *d++ = *s++;
+            *d++ = *s++;
+            len -= 4;
+        }
+        while ( len-- ) {
+            *d++ = *s++;
+        }
+    }
+    return dst;
+}
+#endif
+
+char* my_strcat ( char* dest, const char* src )
+{
+   char* dest_orig = dest;
+   while (*dest) dest++;
+   while (*src) *dest++ = *src++;
+   *dest = 0;
+   return dest_orig;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+
+static void vex_log_bytes ( char* p, int n )
+{
+   int i;
+   for (i = 0; i < n; i++)
+      (*serviceFn)( 1, (int)p[i] );
+}
+
+/*---------------------------------------------------------*/
+/*--- vex_printf                                        ---*/
+/*---------------------------------------------------------*/
+
+/* This should be the only <...> include in the entire VEX library.
+   New code for vex_util.c should go above this point. */
+#include <stdarg.h>
+
+static HChar vex_toupper ( HChar c )
+{
+   if (c >= 'a' && c <= 'z')
+      return c + ('A' - 'a');
+   else
+      return c;
+}
+
+static Int vex_strlen ( const HChar* str )
+{
+   Int i = 0;
+   while (str[i] != 0) i++;
+   return i;
+}
+
+Bool vex_streq ( const HChar* s1, const HChar* s2 )
+{
+   while (True) {
+      if (*s1 == 0 && *s2 == 0)
+         return True;
+      if (*s1 != *s2)
+         return False;
+      s1++;
+      s2++;
+   }
+}
+
+/* Some flags.  */
+#define VG_MSG_SIGNED    1 /* The value is signed. */
+#define VG_MSG_ZJUSTIFY  2 /* Must justify with '0'. */
+#define VG_MSG_LJUSTIFY  4 /* Must justify on the left. */
+#define VG_MSG_PAREN     8 /* Parenthesize if present (for %y) */
+#define VG_MSG_COMMA    16 /* Add commas to numbers (for %d, %u) */
+
+/* Copy a string into the buffer. */
+static UInt
+myvprintf_str ( void(*send)(HChar), Int flags, Int width, HChar* str, 
+                Bool capitalise )
+{
+#  define MAYBE_TOUPPER(ch) (capitalise ? vex_toupper(ch) : (ch))
+   UInt ret = 0;
+   Int i, extra;
+   Int len = vex_strlen(str);
+
+   if (width == 0) {
+      ret += len;
+      for (i = 0; i < len; i++)
+         send(MAYBE_TOUPPER(str[i]));
+      return ret;
+   }
+
+   if (len > width) {
+      ret += width;
+      for (i = 0; i < width; i++)
+         send(MAYBE_TOUPPER(str[i]));
+      return ret;
+   }
+
+   extra = width - len;
+   if (flags & VG_MSG_LJUSTIFY) {
+      ret += extra;
+      for (i = 0; i < extra; i++)
+         send(' ');
+   }
+   ret += len;
+   for (i = 0; i < len; i++)
+      send(MAYBE_TOUPPER(str[i]));
+   if (!(flags & VG_MSG_LJUSTIFY)) {
+      ret += extra;
+      for (i = 0; i < extra; i++)
+         send(' ');
+   }
+
+#  undef MAYBE_TOUPPER
+
+   return ret;
+}
+
+/* Write P into the buffer according to these args:
+ *  If SIGN is true, p is a signed.
+ *  BASE is the base.
+ *  If WITH_ZERO is true, '0' must be added.
+ *  WIDTH is the width of the field.
+ */
+static UInt
+myvprintf_int64 ( void(*send)(HChar), Int flags, Int base, Int width, ULong pL)
+{
+   HChar buf[40];
+   Int   ind = 0;
+   Int   i, nc = 0;
+   Bool  neg = False;
+   HChar *digits = "0123456789ABCDEF";
+   UInt  ret = 0;
+   UInt  p = (UInt)pL;
+
+   if (base < 2 || base > 16)
+      return ret;
+ 
+   if ((flags & VG_MSG_SIGNED) && (Int)p < 0) {
+      p   = - (Int)p;
+      neg = True;
+   }
+
+   if (p == 0)
+      buf[ind++] = '0';
+   else {
+      while (p > 0) {
+         if ((flags & VG_MSG_COMMA) && 10 == base &&
+             0 == (ind-nc) % 3 && 0 != ind) 
+         {
+            buf[ind++] = ',';
+            nc++;
+         }
+         buf[ind++] = digits[p % base];
+         p /= base;
+      }
+   }
+
+   if (neg)
+      buf[ind++] = '-';
+
+   if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) {
+      for(; ind < width; ind++) {
+	//vassert(ind < 39);
+         buf[ind] = ((flags & VG_MSG_ZJUSTIFY) ? '0': ' ');
+      }
+   }
+
+   /* Reverse copy to buffer.  */
+   ret += ind;
+   for (i = ind -1; i >= 0; i--) {
+      send(buf[i]);
+   }
+   if (width > 0 && (flags & VG_MSG_LJUSTIFY)) {
+      for(; ind < width; ind++) {
+	 ret++;
+         send(' ');  // Never pad with zeroes on RHS -- changes the value!
+      }
+   }
+   return ret;
+}
+
+
+/* A simple vprintf().  */
+static 
+UInt vprintf_wrk ( void(*send)(HChar), const HChar *format, va_list vargs )
+{
+   UInt ret = 0;
+   int i;
+   int flags;
+   int width;
+   Bool is_long;
+
+   /* We assume that vargs has already been initialised by the 
+      caller, using va_start, and that the caller will similarly
+      clean up with va_end.
+   */
+
+   for (i = 0; format[i] != 0; i++) {
+      if (format[i] != '%') {
+         send(format[i]);
+	 ret++;
+         continue;
+      }
+      i++;
+      /* A '%' has been found.  Ignore a trailing %. */
+      if (format[i] == 0)
+         break;
+      if (format[i] == '%') {
+         /* `%%' is replaced by `%'. */
+         send('%');
+	 ret++;
+         continue;
+      }
+      flags = 0;
+      is_long = False;
+      width = 0; /* length of the field. */
+      if (format[i] == '(') {
+	 flags |= VG_MSG_PAREN;
+	 i++;
+      }
+      /* If ',' follows '%', commas will be inserted. */
+      if (format[i] == ',') {
+         flags |= VG_MSG_COMMA;
+         i++;
+      }
+      /* If '-' follows '%', justify on the left. */
+      if (format[i] == '-') {
+         flags |= VG_MSG_LJUSTIFY;
+         i++;
+      }
+      /* If '0' follows '%', pads will be inserted. */
+      if (format[i] == '0') {
+         flags |= VG_MSG_ZJUSTIFY;
+         i++;
+      }
+      /* Compute the field length. */
+      while (format[i] >= '0' && format[i] <= '9') {
+         width *= 10;
+         width += format[i++] - '0';
+      }
+      while (format[i] == 'l') {
+         i++;
+         is_long = True;
+      }
+
+      switch (format[i]) {
+         case 'd': /* %d */
+            flags |= VG_MSG_SIGNED;
+            if (is_long)
+               ret += myvprintf_int64(send, flags, 10, width, 
+				      (ULong)(va_arg (vargs, Long)));
+            else
+               ret += myvprintf_int64(send, flags, 10, width, 
+				      (ULong)(va_arg (vargs, Int)));
+            break;
+         case 'u': /* %u */
+            if (is_long)
+               ret += myvprintf_int64(send, flags, 10, width, 
+				      (ULong)(va_arg (vargs, ULong)));
+            else
+               ret += myvprintf_int64(send, flags, 10, width, 
+				      (ULong)(va_arg (vargs, UInt)));
+            break;
+         case 'p': /* %p */
+	    ret += 2;
+            send('0');
+            send('x');
+            ret += myvprintf_int64(send, flags, 16, width, 
+				   (ULong)((HWord)va_arg (vargs, void *)));
+            break;
+         case 'x': /* %x */
+            if (is_long)
+               ret += myvprintf_int64(send, flags, 16, width, 
+				      (ULong)(va_arg (vargs, ULong)));
+            else
+               ret += myvprintf_int64(send, flags, 16, width, 
+				      (ULong)(va_arg (vargs, UInt)));
+            break;
+         case 'c': /* %c */
+	    ret++;
+            send((va_arg (vargs, int)));
+            break;
+         case 's': case 'S': { /* %s */
+            char *str = va_arg (vargs, char *);
+            if (str == (char*) 0) str = "(null)";
+            ret += myvprintf_str(send, flags, width, str, 
+                                 (format[i]=='S'));
+            break;
+	 }
+#        if 0
+	 case 'y': { /* %y - print symbol */
+	    Char buf[100];
+	    Char *cp = buf;
+	    Addr a = va_arg(vargs, Addr);
+
+	    if (flags & VG_MSG_PAREN)
+	       *cp++ = '(';
+	    if (VG_(get_fnname_w_offset)(a, cp, sizeof(buf)-4)) {
+	       if (flags & VG_MSG_PAREN) {
+		  cp += VG_(strlen)(cp);
+		  *cp++ = ')';
+		  *cp = '\0';
+	       }
+	       ret += myvprintf_str(send, flags, width, buf, 0);
+	    }
+	    break;
+	 }
+#        endif
+         default:
+            break;
+      }
+   }
+   return ret;
+}
+
+
+/* A general replacement for printf().  Note that only low-level 
+   debugging info should be sent via here.  The official route is to
+   to use vg_message().  This interface is deprecated.
+*/
+static HChar myprintf_buf[1000];
+static Int   n_myprintf_buf;
+
+static void add_to_myprintf_buf ( HChar c )
+{
+   if (c == '\n' || n_myprintf_buf >= 1000-10 /*paranoia*/ ) {
+      (*vex_log_bytes)( myprintf_buf, vex_strlen(myprintf_buf) );
+      n_myprintf_buf = 0;
+      myprintf_buf[n_myprintf_buf] = 0;      
+   }
+   myprintf_buf[n_myprintf_buf++] = c;
+   myprintf_buf[n_myprintf_buf] = 0;
+}
+
+static UInt vex_printf ( const char *format, ... )
+{
+   UInt ret;
+   va_list vargs;
+   va_start(vargs,format);
+   
+   n_myprintf_buf = 0;
+   myprintf_buf[n_myprintf_buf] = 0;      
+   ret = vprintf_wrk ( add_to_myprintf_buf, format, vargs );
+
+   if (n_myprintf_buf > 0) {
+      (*vex_log_bytes)( myprintf_buf, n_myprintf_buf );
+   }
+
+   va_end(vargs);
+
+   return ret;
+}
+
+/*---------------------------------------------------------------*/
+/*--- end                                          vex_util.c ---*/
+/*---------------------------------------------------------------*/
+
+
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+
+
+/*-------------------------------------------------------------*/
+/*--- Decompression machinery                               ---*/
+/*---                                          decompress.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+
+
+
+/*---------------------------------------------------*/
+static
+void makeMaps_d ( DState* s )
+{
+   Int32 i;
+   s->nInUse = 0;
+   for (i = 0; i < 256; i++)
+      if (s->inUse[i]) {
+         s->seqToUnseq[s->nInUse] = i;
+         s->nInUse++;
+      }
+}
+
+
+/*---------------------------------------------------*/
+#define RETURN(rrr)                               \
+   { retVal = rrr; goto save_state_and_return; };
+
+#define GET_BITS(lll,vvv,nnn)                     \
+   case lll: s->state = lll;                      \
+   while (True) {                                 \
+      if (s->bsLive >= nnn) {                     \
+         UInt32 v;                                \
+         v = (s->bsBuff >>                        \
+             (s->bsLive-nnn)) & ((1 << nnn)-1);   \
+         s->bsLive -= nnn;                        \
+         vvv = v;                                 \
+         break;                                   \
+      }                                           \
+      if (s->strm->avail_in == 0) RETURN(BZ_OK);  \
+      s->bsBuff                                   \
+         = (s->bsBuff << 8) |                     \
+           ((UInt32)                              \
+              (*((UChar*)(s->strm->next_in))));   \
+      s->bsLive += 8;                             \
+      s->strm->next_in++;                         \
+      s->strm->avail_in--;                        \
+      s->strm->total_in_lo32++;                   \
+      if (s->strm->total_in_lo32 == 0)            \
+         s->strm->total_in_hi32++;                \
+   }
+
+#define GET_UCHAR(lll,uuu)                        \
+   GET_BITS(lll,uuu,8)
+
+#define GET_BIT(lll,uuu)                          \
+   GET_BITS(lll,uuu,1)
+
+/*---------------------------------------------------*/
+#define GET_MTF_VAL(label1,label2,lval)           \
+{                                                 \
+   if (groupPos == 0) {                           \
+      groupNo++;                                  \
+      if (groupNo >= nSelectors)                  \
+         RETURN(BZ_DATA_ERROR);                   \
+      groupPos = BZ_G_SIZE;                       \
+      gSel = s->selector[groupNo];                \
+      gMinlen = s->minLens[gSel];                 \
+      gLimit = &(s->limit[gSel][0]);              \
+      gPerm = &(s->perm[gSel][0]);                \
+      gBase = &(s->base[gSel][0]);                \
+   }                                              \
+   groupPos--;                                    \
+   zn = gMinlen;                                  \
+   GET_BITS(label1, zvec, zn);                    \
+   while (1) {                                    \
+      if (zn > 20 /* the longest code */)         \
+         RETURN(BZ_DATA_ERROR);                   \
+      if (zvec <= gLimit[zn]) break;              \
+      zn++;                                       \
+      GET_BIT(label2, zj);                        \
+      zvec = (zvec << 1) | zj;                    \
+   };                                             \
+   if (zvec - gBase[zn] < 0                       \
+       || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
+      RETURN(BZ_DATA_ERROR);                      \
+   lval = gPerm[zvec - gBase[zn]];                \
+}
+
+
+
+/*---------------------------------------------------*/
+Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab )
+{
+   Int32 nb, na, mid;
+   nb = 0;
+   na = 256;
+   do {
+      mid = (nb + na) >> 1;
+      if (indx >= cftab[mid]) nb = mid; else na = mid;
+   }
+   while (na - nb != 1);
+   return nb;
+}
+
+/*---------------------------------------------------*/
+Int32 BZ2_decompress ( DState* s )
+{
+   UChar      uc;
+   Int32      retVal;
+   Int32      minLen, maxLen;
+   bz_stream* strm = s->strm;
+
+   /* stuff that needs to be saved/restored */
+   Int32  i;
+   Int32  j;
+   Int32  t;
+   Int32  alphaSize;
+   Int32  nGroups;
+   Int32  nSelectors;
+   Int32  EOB;
+   Int32  groupNo;
+   Int32  groupPos;
+   Int32  nextSym;
+   Int32  nblockMAX;
+   Int32  nblock;
+   Int32  es;
+   Int32  N;
+   Int32  curr;
+   Int32  zt;
+   Int32  zn; 
+   Int32  zvec;
+   Int32  zj;
+   Int32  gSel;
+   Int32  gMinlen;
+   Int32* gLimit;
+   Int32* gBase;
+   Int32* gPerm;
+
+   if (s->state == BZ_X_MAGIC_1) {
+      /*initialise the save area*/
+      s->save_i           = 0;
+      s->save_j           = 0;
+      s->save_t           = 0;
+      s->save_alphaSize   = 0;
+      s->save_nGroups     = 0;
+      s->save_nSelectors  = 0;
+      s->save_EOB         = 0;
+      s->save_groupNo     = 0;
+      s->save_groupPos    = 0;
+      s->save_nextSym     = 0;
+      s->save_nblockMAX   = 0;
+      s->save_nblock      = 0;
+      s->save_es          = 0;
+      s->save_N           = 0;
+      s->save_curr        = 0;
+      s->save_zt          = 0;
+      s->save_zn          = 0;
+      s->save_zvec        = 0;
+      s->save_zj          = 0;
+      s->save_gSel        = 0;
+      s->save_gMinlen     = 0;
+      s->save_gLimit      = NULL;
+      s->save_gBase       = NULL;
+      s->save_gPerm       = NULL;
+   }
+
+   /*restore from the save area*/
+   i           = s->save_i;
+   j           = s->save_j;
+   t           = s->save_t;
+   alphaSize   = s->save_alphaSize;
+   nGroups     = s->save_nGroups;
+   nSelectors  = s->save_nSelectors;
+   EOB         = s->save_EOB;
+   groupNo     = s->save_groupNo;
+   groupPos    = s->save_groupPos;
+   nextSym     = s->save_nextSym;
+   nblockMAX   = s->save_nblockMAX;
+   nblock      = s->save_nblock;
+   es          = s->save_es;
+   N           = s->save_N;
+   curr        = s->save_curr;
+   zt          = s->save_zt;
+   zn          = s->save_zn; 
+   zvec        = s->save_zvec;
+   zj          = s->save_zj;
+   gSel        = s->save_gSel;
+   gMinlen     = s->save_gMinlen;
+   gLimit      = s->save_gLimit;
+   gBase       = s->save_gBase;
+   gPerm       = s->save_gPerm;
+
+   retVal = BZ_OK;
+
+   switch (s->state) {
+
+      GET_UCHAR(BZ_X_MAGIC_1, uc);
+      if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_UCHAR(BZ_X_MAGIC_2, uc);
+      if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_UCHAR(BZ_X_MAGIC_3, uc)
+      if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8)
+      if (s->blockSize100k < (BZ_HDR_0 + 1) || 
+          s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
+      s->blockSize100k -= BZ_HDR_0;
+
+      if (s->smallDecompress) {
+         s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
+         s->ll4  = BZALLOC( 
+                      ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) 
+                   );
+         if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
+      } else {
+         s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
+         if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
+      }
+
+      GET_UCHAR(BZ_X_BLKHDR_1, uc);
+
+      if (uc == 0x17) goto endhdr_2;
+      if (uc != 0x31) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_2, uc);
+      if (uc != 0x41) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_3, uc);
+      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_4, uc);
+      if (uc != 0x26) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_5, uc);
+      if (uc != 0x53) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_6, uc);
+      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
+
+      s->currBlockNo++;
+      if (s->verbosity >= 2)
+         VPrintf1 ( "\n    [%d: huff+mtf ", s->currBlockNo );
+ 
+      s->storedBlockCRC = 0;
+      GET_UCHAR(BZ_X_BCRC_1, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_2, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_3, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_4, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+
+      GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);
+
+      s->origPtr = 0;
+      GET_UCHAR(BZ_X_ORIGPTR_1, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+      GET_UCHAR(BZ_X_ORIGPTR_2, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+      GET_UCHAR(BZ_X_ORIGPTR_3, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+
+      if (s->origPtr < 0)
+         RETURN(BZ_DATA_ERROR);
+      if (s->origPtr > 10 + 100000*s->blockSize100k) 
+         RETURN(BZ_DATA_ERROR);
+
+      /*--- Receive the mapping table ---*/
+      for (i = 0; i < 16; i++) {
+         GET_BIT(BZ_X_MAPPING_1, uc);
+         if (uc == 1) 
+            s->inUse16[i] = True; else 
+            s->inUse16[i] = False;
+      }
+
+      for (i = 0; i < 256; i++) s->inUse[i] = False;
+
+      for (i = 0; i < 16; i++)
+         if (s->inUse16[i])
+            for (j = 0; j < 16; j++) {
+               GET_BIT(BZ_X_MAPPING_2, uc);
+               if (uc == 1) s->inUse[i * 16 + j] = True;
+            }
+      makeMaps_d ( s );
+      if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
+      alphaSize = s->nInUse+2;
+
+      /*--- Now the selectors ---*/
+      GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
+      if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
+      GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
+      if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
+      for (i = 0; i < nSelectors; i++) {
+         j = 0;
+         while (True) {
+            GET_BIT(BZ_X_SELECTOR_3, uc);
+            if (uc == 0) break;
+croak( 2 + (char*)&i );
+            j++;
+            if (j >= nGroups) RETURN(BZ_DATA_ERROR);
+         }
+         s->selectorMtf[i] = j;
+      }
+
+      /*--- Undo the MTF values for the selectors. ---*/
+      {
+         UChar pos[BZ_N_GROUPS], tmp, v;
+         for (v = 0; v < nGroups; v++) pos[v] = v;
+   
+         for (i = 0; i < nSelectors; i++) {
+            v = s->selectorMtf[i];
+            tmp = pos[v];
+            while (v > 0) { pos[v] = pos[v-1]; v--; }
+            pos[0] = tmp;
+            s->selector[i] = tmp;
+         }
+      }
+
+      /*--- Now the coding tables ---*/
+      for (t = 0; t < nGroups; t++) {
+         GET_BITS(BZ_X_CODING_1, curr, 5);
+         for (i = 0; i < alphaSize; i++) {
+            while (True) {
+               if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
+               GET_BIT(BZ_X_CODING_2, uc);
+               if (uc == 0) break;
+               GET_BIT(BZ_X_CODING_3, uc);
+               if (uc == 0) curr++; else curr--;
+            }
+            s->len[t][i] = curr;
+         }
+      }
+
+      /*--- Create the Huffman decoding tables ---*/
+      for (t = 0; t < nGroups; t++) {
+         minLen = 32;
+         maxLen = 0;
+         for (i = 0; i < alphaSize; i++) {
+            if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+            if (s->len[t][i] < minLen) minLen = s->len[t][i];
+         }
+         BZ2_hbCreateDecodeTables ( 
+            &(s->limit[t][0]), 
+            &(s->base[t][0]), 
+            &(s->perm[t][0]), 
+            &(s->len[t][0]),
+            minLen, maxLen, alphaSize
+         );
+         s->minLens[t] = minLen;
+      }
+
+      /*--- Now the MTF values ---*/
+
+      EOB      = s->nInUse+1;
+      nblockMAX = 100000 * s->blockSize100k;
+      groupNo  = -1;
+      groupPos = 0;
+
+      for (i = 0; i <= 255; i++) s->unzftab[i] = 0;
+
+      /*-- MTF init --*/
+      {
+         Int32 ii, jj, kk;
+         kk = MTFA_SIZE-1;
+         for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
+            for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
+               s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
+               kk--;
+            }
+            s->mtfbase[ii] = kk + 1;
+         }
+      }
+      /*-- end MTF init --*/
+
+      nblock = 0;
+      GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);
+
+      while (True) {
+
+         if (nextSym == EOB) break;
+
+         if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {
+
+            es = -1;
+            N = 1;
+            do {
+               if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
+               if (nextSym == BZ_RUNB) es = es + (1+1) * N;
+               N = N * 2;
+               GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
+            }
+               while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);
+
+            es++;
+            uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
+            s->unzftab[uc] += es;
+
+            if (s->smallDecompress)
+               while (es > 0) {
+                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+                  s->ll16[nblock] = (UInt16)uc;
+                  nblock++;
+                  es--;
+               }
+            else
+               while (es > 0) {
+                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+                  s->tt[nblock] = (UInt32)uc;
+                  nblock++;
+                  es--;
+               };
+
+            continue;
+
+         } else {
+
+            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+
+            /*-- uc = MTF ( nextSym-1 ) --*/
+            {
+               Int32 ii, jj, kk, pp, lno, off;
+               UInt32 nn;
+               nn = (UInt32)(nextSym - 1);
+
+               if (nn < MTFL_SIZE) {
+                  /* avoid general-case expense */
+                  pp = s->mtfbase[0];
+                  uc = s->mtfa[pp+nn];
+                  while (nn > 3) {
+                     Int32 z = pp+nn;
+                     s->mtfa[(z)  ] = s->mtfa[(z)-1];
+                     s->mtfa[(z)-1] = s->mtfa[(z)-2];
+                     s->mtfa[(z)-2] = s->mtfa[(z)-3];
+                     s->mtfa[(z)-3] = s->mtfa[(z)-4];
+                     nn -= 4;
+                  }
+                  while (nn > 0) { 
+                     s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; 
+                  };
+                  s->mtfa[pp] = uc;
+               } else { 
+                  /* general case */
+                  lno = nn / MTFL_SIZE;
+                  off = nn % MTFL_SIZE;
+                  pp = s->mtfbase[lno] + off;
+                  uc = s->mtfa[pp];
+                  while (pp > s->mtfbase[lno]) { 
+                     s->mtfa[pp] = s->mtfa[pp-1]; pp--; 
+                  };
+                  s->mtfbase[lno]++;
+                  while (lno > 0) {
+                     s->mtfbase[lno]--;
+                     s->mtfa[s->mtfbase[lno]] 
+                        = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
+                     lno--;
+                  }
+                  s->mtfbase[0]--;
+                  s->mtfa[s->mtfbase[0]] = uc;
+                  if (s->mtfbase[0] == 0) {
+                     kk = MTFA_SIZE-1;
+                     for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
+                        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
+                           s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
+                           kk--;
+                        }
+                        s->mtfbase[ii] = kk + 1;
+                     }
+                  }
+               }
+            }
+            /*-- end uc = MTF ( nextSym-1 ) --*/
+
+            s->unzftab[s->seqToUnseq[uc]]++;
+            if (s->smallDecompress)
+               s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
+               s->tt[nblock]   = (UInt32)(s->seqToUnseq[uc]);
+            nblock++;
+
+            GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
+            continue;
+         }
+      }
+
+      /* Now we know what nblock is, we can do a better sanity
+         check on s->origPtr.
+      */
+      if (s->origPtr < 0 || s->origPtr >= nblock)
+         RETURN(BZ_DATA_ERROR);
+
+      /*-- Set up cftab to facilitate generation of T^(-1) --*/
+      s->cftab[0] = 0;
+      for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
+      for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
+      for (i = 0; i <= 256; i++) {
+         if (s->cftab[i] < 0 || s->cftab[i] > nblock) {
+            /* s->cftab[i] can legitimately be == nblock */
+            RETURN(BZ_DATA_ERROR);
+         }
+      }
+
+      s->state_out_len = 0;
+      s->state_out_ch  = 0;
+      BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
+      s->state = BZ_X_OUTPUT;
+      if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );
+
+      if (s->smallDecompress) {
+
+         /*-- Make a copy of cftab, used in generation of T --*/
+         for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];
+
+         /*-- compute the T vector --*/
+         for (i = 0; i < nblock; i++) {
+            uc = (UChar)(s->ll16[i]);
+            SET_LL(i, s->cftabCopy[uc]);
+            s->cftabCopy[uc]++;
+         }
+
+         /*-- Compute T^(-1) by pointer reversal on T --*/
+         i = s->origPtr;
+         j = GET_LL(i);
+         do {
+            Int32 tmp = GET_LL(j);
+            SET_LL(j, i);
+            i = j;
+            j = tmp;
+         }
+            while (i != s->origPtr);
+
+         s->tPos = s->origPtr;
+         s->nblock_used = 0;
+         if (s->blockRandomised) {
+            BZ_RAND_INIT_MASK;
+            BZ_GET_SMALL(s->k0); s->nblock_used++;
+            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
+         } else {
+            BZ_GET_SMALL(s->k0); s->nblock_used++;
+         }
+
+      } else {
+
+         /*-- compute the T^(-1) vector --*/
+         for (i = 0; i < nblock; i++) {
+            uc = (UChar)(s->tt[i] & 0xff);
+            s->tt[s->cftab[uc]] |= (i << 8);
+            s->cftab[uc]++;
+         }
+
+         s->tPos = s->tt[s->origPtr] >> 8;
+         s->nblock_used = 0;
+         if (s->blockRandomised) {
+            BZ_RAND_INIT_MASK;
+            BZ_GET_FAST(s->k0); s->nblock_used++;
+            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
+         } else {
+            BZ_GET_FAST(s->k0); s->nblock_used++;
+         }
+
+      }
+
+      RETURN(BZ_OK);
+
+
+
+    endhdr_2:
+
+      GET_UCHAR(BZ_X_ENDHDR_2, uc);
+      if (uc != 0x72) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_3, uc);
+      if (uc != 0x45) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_4, uc);
+      if (uc != 0x38) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_5, uc);
+      if (uc != 0x50) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_6, uc);
+      if (uc != 0x90) RETURN(BZ_DATA_ERROR);
+
+      s->storedCombinedCRC = 0;
+      GET_UCHAR(BZ_X_CCRC_1, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_2, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_3, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_4, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+
+      s->state = BZ_X_IDLE;
+      RETURN(BZ_STREAM_END);
+
+      default: AssertH ( False, 4001 );
+   }
+
+   AssertH ( False, 4002 );
+
+   save_state_and_return:
+
+   s->save_i           = i;
+   s->save_j           = j;
+   s->save_t           = t;
+   s->save_alphaSize   = alphaSize;
+   s->save_nGroups     = nGroups;
+   s->save_nSelectors  = nSelectors;
+   s->save_EOB         = EOB;
+   s->save_groupNo     = groupNo;
+   s->save_groupPos    = groupPos;
+   s->save_nextSym     = nextSym;
+   s->save_nblockMAX   = nblockMAX;
+   s->save_nblock      = nblock;
+   s->save_es          = es;
+   s->save_N           = N;
+   s->save_curr        = curr;
+   s->save_zt          = zt;
+   s->save_zn          = zn;
+   s->save_zvec        = zvec;
+   s->save_zj          = zj;
+   s->save_gSel        = gSel;
+   s->save_gMinlen     = gMinlen;
+   s->save_gLimit      = gLimit;
+   s->save_gBase       = gBase;
+   s->save_gPerm       = gPerm;
+
+   return retVal;   
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                      decompress.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Block sorting machinery                               ---*/
+/*---                                           blocksort.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+
+  To get some idea how the block sorting algorithms in this file 
+  work, read my paper 
+     On the Performance of BWT Sorting Algorithms
+  in Proceedings of the IEEE Data Compression Conference 2000,
+  Snowbird, Utah, USA, 27-30 March 2000.  The main sort in this
+  file implements the algorithm called  cache  in the paper.
+--*/
+
+
+
+/*---------------------------------------------*/
+/*--- Fallback O(N log(N)^2) sorting        ---*/
+/*--- algorithm, for repetitive blocks      ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static 
+void fallbackSimpleSort ( UInt32* fmap, 
+                          UInt32* eclass, 
+                          Int32   lo, 
+                          Int32   hi )
+{
+   Int32 i, j, tmp;
+   UInt32 ec_tmp;
+
+   if (lo == hi) return;
+
+   if (hi - lo > 3) {
+      for ( i = hi-4; i >= lo; i-- ) {
+         tmp = fmap[i];
+         ec_tmp = eclass[tmp];
+         for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 )
+            fmap[j-4] = fmap[j];
+         fmap[j-4] = tmp;
+      }
+   }
+
+   for ( i = hi-1; i >= lo; i-- ) {
+      tmp = fmap[i];
+      ec_tmp = eclass[tmp];
+      for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ )
+         fmap[j-1] = fmap[j];
+      fmap[j-1] = tmp;
+   }
+}
+
+
+/*---------------------------------------------*/
+#define fswap(zz1, zz2) \
+   { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }
+
+#define fvswap(zzp1, zzp2, zzn)       \
+{                                     \
+   Int32 yyp1 = (zzp1);               \
+   Int32 yyp2 = (zzp2);               \
+   Int32 yyn  = (zzn);                \
+   while (yyn > 0) {                  \
+      fswap(fmap[yyp1], fmap[yyp2]);  \
+      yyp1++; yyp2++; yyn--;          \
+   }                                  \
+}
+
+
+#define fmin(a,b) ((a) < (b)) ? (a) : (b)
+
+#define fpush(lz,hz) { stackLo[sp] = lz; \
+                       stackHi[sp] = hz; \
+                       sp++; }
+
+#define fpop(lz,hz) { sp--;              \
+                      lz = stackLo[sp];  \
+                      hz = stackHi[sp]; }
+
+#define FALLBACK_QSORT_SMALL_THRESH 10
+#define FALLBACK_QSORT_STACK_SIZE   100
+
+
+static
+void fallbackQSort3 ( UInt32* fmap, 
+                      UInt32* eclass,
+                      Int32   loSt, 
+                      Int32   hiSt )
+{
+   Int32 unLo, unHi, ltLo, gtHi, n, m;
+   Int32 sp, lo, hi;
+   UInt32 med, r, r3;
+   Int32 stackLo[FALLBACK_QSORT_STACK_SIZE];
+   Int32 stackHi[FALLBACK_QSORT_STACK_SIZE];
+
+   r = 0;
+
+   sp = 0;
+   fpush ( loSt, hiSt );
+
+   while (sp > 0) {
+
+      AssertH ( sp < FALLBACK_QSORT_STACK_SIZE, 1004 );
+
+      fpop ( lo, hi );
+      if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) {
+         fallbackSimpleSort ( fmap, eclass, lo, hi );
+         continue;
+      }
+
+      /* Random partitioning.  Median of 3 sometimes fails to
+         avoid bad cases.  Median of 9 seems to help but 
+         looks rather expensive.  This too seems to work but
+         is cheaper.  Guidance for the magic constants 
+         7621 and 32768 is taken from Sedgewick's algorithms
+         book, chapter 35.
+      */
+      r = ((r * 7621) + 1) % 32768;
+      r3 = r % 3;
+      if (r3 == 0) med = eclass[fmap[lo]]; else
+      if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else
+                   med = eclass[fmap[hi]];
+
+      unLo = ltLo = lo;
+      unHi = gtHi = hi;
+
+      while (1) {
+         while (1) {
+            if (unLo > unHi) break;
+            n = (Int32)eclass[fmap[unLo]] - (Int32)med;
+            if (n == 0) { 
+               fswap(fmap[unLo], fmap[ltLo]); 
+               ltLo++; unLo++; 
+               continue; 
+            };
+            if (n > 0) break;
+            unLo++;
+         }
+         while (1) {
+            if (unLo > unHi) break;
+            n = (Int32)eclass[fmap[unHi]] - (Int32)med;
+            if (n == 0) { 
+               fswap(fmap[unHi], fmap[gtHi]); 
+               gtHi--; unHi--; 
+               continue; 
+            };
+            if (n < 0) break;
+            unHi--;
+         }
+         if (unLo > unHi) break;
+         fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--;
+      }
+
+      AssertD ( unHi == unLo-1, "fallbackQSort3(2)" );
+
+      if (gtHi < ltLo) continue;
+
+      n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n);
+      m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m);
+
+      n = lo + unLo - ltLo - 1;
+      m = hi - (gtHi - unHi) + 1;
+
+      if (n - lo > hi - m) {
+         fpush ( lo, n );
+         fpush ( m, hi );
+      } else {
+         fpush ( m, hi );
+         fpush ( lo, n );
+      }
+   }
+}
+
+#undef fmin
+#undef fpush
+#undef fpop
+#undef fswap
+#undef fvswap
+#undef FALLBACK_QSORT_SMALL_THRESH
+#undef FALLBACK_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+      nblock > 0
+      eclass exists for [0 .. nblock-1]
+      ((UChar*)eclass) [0 .. nblock-1] holds block
+      ptr exists for [0 .. nblock-1]
+
+   Post:
+      ((UChar*)eclass) [0 .. nblock-1] holds block
+      All other areas of eclass destroyed
+      fmap [0 .. nblock-1] holds sorted order
+      bhtab [ 0 .. 2+(nblock/32) ] destroyed
+*/
+
+#define       SET_BH(zz)  bhtab[(zz) >> 5] |= (1 << ((zz) & 31))
+#define     CLEAR_BH(zz)  bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31))
+#define     ISSET_BH(zz)  (bhtab[(zz) >> 5] & (1 << ((zz) & 31)))
+#define      WORD_BH(zz)  bhtab[(zz) >> 5]
+#define UNALIGNED_BH(zz)  ((zz) & 0x01f)
+
+static
+void fallbackSort ( UInt32* fmap, 
+                    UInt32* eclass, 
+                    UInt32* bhtab,
+                    Int32   nblock,
+                    Int32   verb )
+{
+   Int32 ftab[257];
+   Int32 ftabCopy[256];
+   Int32 H, i, j, k, l, r, cc, cc1;
+   Int32 nNotDone;
+   Int32 nBhtab;
+   UChar* eclass8 = (UChar*)eclass;
+
+   /*--
+      Initial 1-char radix sort to generate
+      initial fmap and initial BH bits.
+   --*/
+   if (verb >= 4)
+      VPrintf0 ( "        bucket sorting ...\n" );
+   for (i = 0; i < 257;    i++) ftab[i] = 0;
+   for (i = 0; i < nblock; i++) ftab[eclass8[i]]++;
+   for (i = 0; i < 256;    i++) ftabCopy[i] = ftab[i];
+   for (i = 1; i < 257;    i++) ftab[i] += ftab[i-1];
+
+   for (i = 0; i < nblock; i++) {
+      j = eclass8[i];
+      k = ftab[j] - 1;
+      ftab[j] = k;
+      fmap[k] = i;
+   }
+
+   nBhtab = 2 + (nblock / 32);
+   for (i = 0; i < nBhtab; i++) bhtab[i] = 0;
+   for (i = 0; i < 256; i++) SET_BH(ftab[i]);
+
+   /*--
+      Inductively refine the buckets.  Kind-of an
+      "exponential radix sort" (!), inspired by the
+      Manber-Myers suffix array construction algorithm.
+   --*/
+
+   /*-- set sentinel bits for block-end detection --*/
+   for (i = 0; i < 32; i++) { 
+      SET_BH(nblock + 2*i);
+      CLEAR_BH(nblock + 2*i + 1);
+   }
+
+   /*-- the log(N) loop --*/
+   H = 1;
+   while (1) {
+
+      if (verb >= 4) 
+         VPrintf1 ( "        depth %6d has ", H );
+
+      j = 0;
+      for (i = 0; i < nblock; i++) {
+         if (ISSET_BH(i)) j = i;
+         k = fmap[i] - H; if (k < 0) k += nblock;
+         eclass[k] = j;
+      }
+
+      nNotDone = 0;
+      r = -1;
+      while (1) {
+
+	 /*-- find the next non-singleton bucket --*/
+         k = r + 1;
+         while (ISSET_BH(k) && UNALIGNED_BH(k)) k++;
+         if (ISSET_BH(k)) {
+            while (WORD_BH(k) == 0xffffffff) k += 32;
+            while (ISSET_BH(k)) k++;
+         }
+         l = k - 1;
+         if (l >= nblock) break;
+         while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++;
+         if (!ISSET_BH(k)) {
+            while (WORD_BH(k) == 0x00000000) k += 32;
+            while (!ISSET_BH(k)) k++;
+         }
+         r = k - 1;
+         if (r >= nblock) break;
+
+         /*-- now [l, r] bracket current bucket --*/
+         if (r > l) {
+            nNotDone += (r - l + 1);
+            fallbackQSort3 ( fmap, eclass, l, r );
+
+            /*-- scan bucket and generate header bits-- */
+            cc = -1;
+            for (i = l; i <= r; i++) {
+               cc1 = eclass[fmap[i]];
+               if (cc != cc1) { SET_BH(i); cc = cc1; };
+            }
+         }
+      }
+
+      if (verb >= 4) 
+         VPrintf1 ( "%6d unresolved strings\n", nNotDone );
+
+      H *= 2;
+      if (H > nblock || nNotDone == 0) break;
+   }
+
+   /*-- 
+      Reconstruct the original block in
+      eclass8 [0 .. nblock-1], since the
+      previous phase destroyed it.
+   --*/
+   if (verb >= 4)
+      VPrintf0 ( "        reconstructing block ...\n" );
+   j = 0;
+   for (i = 0; i < nblock; i++) {
+      while (ftabCopy[j] == 0) j++;
+      ftabCopy[j]--;
+      eclass8[fmap[i]] = (UChar)j;
+   }
+   AssertH ( j < 256, 1005 );
+}
+
+#undef       SET_BH
+#undef     CLEAR_BH
+#undef     ISSET_BH
+#undef      WORD_BH
+#undef UNALIGNED_BH
+
+
+/*---------------------------------------------*/
+/*--- The main, O(N^2 log(N)) sorting       ---*/
+/*--- algorithm.  Faster for "normal"       ---*/
+/*--- non-repetitive blocks.                ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+Bool mainGtU ( UInt32  i1, 
+               UInt32  i2,
+               UChar*  block, 
+               UInt16* quadrant,
+               UInt32  nblock,
+               Int32*  budget )
+{
+   Int32  k;
+   UChar  c1, c2;
+   UInt16 s1, s2;
+
+   AssertD ( i1 != i2, "mainGtU" );
+   /* 1 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 2 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 3 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 4 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 5 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 6 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 7 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 8 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 9 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 10 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 11 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 12 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+
+   k = nblock + 8;
+
+   do {
+      /* 1 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 2 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 3 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 4 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 5 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 6 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 7 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 8 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+
+      if (i1 >= nblock) i1 -= nblock;
+      if (i2 >= nblock) i2 -= nblock;
+
+      k -= 8;
+      (*budget)--;
+   }
+      while (k >= 0);
+
+   return False;
+}
+
+
+/*---------------------------------------------*/
+/*--
+   Knuth's increments seem to work better
+   than Incerpi-Sedgewick here.  Possibly
+   because the number of elems to sort is
+   usually small, typically <= 20.
+--*/
+static
+Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+                   9841, 29524, 88573, 265720,
+                   797161, 2391484 };
+
+static
+void mainSimpleSort ( UInt32* ptr,
+                      UChar*  block,
+                      UInt16* quadrant,
+                      Int32   nblock,
+                      Int32   lo, 
+                      Int32   hi, 
+                      Int32   d,
+                      Int32*  budget )
+{
+   Int32 i, j, h, bigN, hp;
+   UInt32 v;
+
+   bigN = hi - lo + 1;
+   if (bigN < 2) return;
+
+   hp = 0;
+   while (incs[hp] < bigN) hp++;
+   hp--;
+
+   for (; hp >= 0; hp--) {
+      h = incs[hp];
+
+      i = lo + h;
+      while (True) {
+
+         /*-- copy 1 --*/
+         if (i > hi) break;
+         v = ptr[i];
+         j = i;
+         while ( mainGtU ( 
+                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
+                 ) ) {
+            ptr[j] = ptr[j-h];
+            j = j - h;
+            if (j <= (lo + h - 1)) break;
+         }
+         ptr[j] = v;
+         i++;
+
+         /*-- copy 2 --*/
+         if (i > hi) break;
+         v = ptr[i];
+         j = i;
+         while ( mainGtU ( 
+                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
+                 ) ) {
+            ptr[j] = ptr[j-h];
+            j = j - h;
+            if (j <= (lo + h - 1)) break;
+         }
+         ptr[j] = v;
+         i++;
+
+         /*-- copy 3 --*/
+         if (i > hi) break;
+         v = ptr[i];
+         j = i;
+         while ( mainGtU ( 
+                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
+                 ) ) {
+            ptr[j] = ptr[j-h];
+            j = j - h;
+            if (j <= (lo + h - 1)) break;
+         }
+         ptr[j] = v;
+         i++;
+
+         if (*budget < 0) return;
+      }
+   }
+}
+
+
+/*---------------------------------------------*/
+/*--
+   The following is an implementation of
+   an elegant 3-way quicksort for strings,
+   described in a paper "Fast Algorithms for
+   Sorting and Searching Strings", by Robert
+   Sedgewick and Jon L. Bentley.
+--*/
+
+#define mswap(zz1, zz2) \
+   { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }
+
+#define mvswap(zzp1, zzp2, zzn)       \
+{                                     \
+   Int32 yyp1 = (zzp1);               \
+   Int32 yyp2 = (zzp2);               \
+   Int32 yyn  = (zzn);                \
+   while (yyn > 0) {                  \
+      mswap(ptr[yyp1], ptr[yyp2]);    \
+      yyp1++; yyp2++; yyn--;          \
+   }                                  \
+}
+
+static 
+UChar mmed3 ( UChar a, UChar b, UChar c )
+{
+   UChar t;
+   if (a > b) { t = a; a = b; b = t; };
+   if (b > c) { 
+      b = c;
+      if (a > b) b = a;
+   }
+   return b;
+}
+
+#define mmin(a,b) ((a) < (b)) ? (a) : (b)
+
+#define mpush(lz,hz,dz) { stackLo[sp] = lz; \
+                          stackHi[sp] = hz; \
+                          stackD [sp] = dz; \
+                          sp++; }
+
+#define mpop(lz,hz,dz) { sp--;             \
+                         lz = stackLo[sp]; \
+                         hz = stackHi[sp]; \
+                         dz = stackD [sp]; }
+
+
+#define mnextsize(az) (nextHi[az]-nextLo[az])
+
+#define mnextswap(az,bz)                                        \
+   { Int32 tz;                                                  \
+     tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \
+     tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \
+     tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; }
+
+
+#define MAIN_QSORT_SMALL_THRESH 20
+#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT)
+#define MAIN_QSORT_STACK_SIZE 100
+
+static
+void mainQSort3 ( UInt32* ptr,
+                  UChar*  block,
+                  UInt16* quadrant,
+                  Int32   nblock,
+                  Int32   loSt, 
+                  Int32   hiSt, 
+                  Int32   dSt,
+                  Int32*  budget )
+{
+   Int32 unLo, unHi, ltLo, gtHi, n, m, med;
+   Int32 sp, lo, hi, d;
+
+   Int32 stackLo[MAIN_QSORT_STACK_SIZE];
+   Int32 stackHi[MAIN_QSORT_STACK_SIZE];
+   Int32 stackD [MAIN_QSORT_STACK_SIZE];
+
+   Int32 nextLo[3];
+   Int32 nextHi[3];
+   Int32 nextD [3];
+
+   sp = 0;
+   mpush ( loSt, hiSt, dSt );
+
+   while (sp > 0) {
+
+      AssertH ( sp < MAIN_QSORT_STACK_SIZE, 1001 );
+
+      mpop ( lo, hi, d );
+      if (hi - lo < MAIN_QSORT_SMALL_THRESH || 
+          d > MAIN_QSORT_DEPTH_THRESH) {
+         mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget );
+         if (*budget < 0) return;
+         continue;
+      }
+
+      med = (Int32) 
+            mmed3 ( block[ptr[ lo         ]+d],
+                    block[ptr[ hi         ]+d],
+                    block[ptr[ (lo+hi)>>1 ]+d] );
+
+      unLo = ltLo = lo;
+      unHi = gtHi = hi;
+
+      while (True) {
+         while (True) {
+            if (unLo > unHi) break;
+            n = ((Int32)block[ptr[unLo]+d]) - med;
+            if (n == 0) { 
+               mswap(ptr[unLo], ptr[ltLo]); 
+               ltLo++; unLo++; continue; 
+            };
+            if (n >  0) break;
+            unLo++;
+         }
+         while (True) {
+            if (unLo > unHi) break;
+            n = ((Int32)block[ptr[unHi]+d]) - med;
+            if (n == 0) { 
+               mswap(ptr[unHi], ptr[gtHi]); 
+               gtHi--; unHi--; continue; 
+            };
+            if (n <  0) break;
+            unHi--;
+         }
+         if (unLo > unHi) break;
+         mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--;
+      }
+
+      AssertD ( unHi == unLo-1, "mainQSort3(2)" );
+
+      if (gtHi < ltLo) {
+         mpush(lo, hi, d+1 );
+         continue;
+      }
+
+      n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n);
+      m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m);
+
+      n = lo + unLo - ltLo - 1;
+      m = hi - (gtHi - unHi) + 1;
+
+      nextLo[0] = lo;  nextHi[0] = n;   nextD[0] = d;
+      nextLo[1] = m;   nextHi[1] = hi;  nextD[1] = d;
+      nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1;
+
+      if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
+      if (mnextsize(1) < mnextsize(2)) mnextswap(1,2);
+      if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
+
+      AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" );
+      AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" );
+
+      mpush (nextLo[0], nextHi[0], nextD[0]);
+      mpush (nextLo[1], nextHi[1], nextD[1]);
+      mpush (nextLo[2], nextHi[2], nextD[2]);
+   }
+}
+
+#undef mswap
+#undef mvswap
+#undef mpush
+#undef mpop
+#undef mmin
+#undef mnextsize
+#undef mnextswap
+#undef MAIN_QSORT_SMALL_THRESH
+#undef MAIN_QSORT_DEPTH_THRESH
+#undef MAIN_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+      nblock > N_OVERSHOOT
+      block32 exists for [0 .. nblock-1 +N_OVERSHOOT]
+      ((UChar*)block32) [0 .. nblock-1] holds block
+      ptr exists for [0 .. nblock-1]
+
+   Post:
+      ((UChar*)block32) [0 .. nblock-1] holds block
+      All other areas of block32 destroyed
+      ftab [0 .. 65536 ] destroyed
+      ptr [0 .. nblock-1] holds sorted order
+      if (*budget < 0), sorting was abandoned
+*/
+
+#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8])
+#define SETMASK (1 << 21)
+#define CLEARMASK (~(SETMASK))
+
+/*static*/ __attribute__((noinline))
+void mainSort ( UInt32* ptr, 
+                UChar*  block,
+                UInt16* quadrant, 
+                UInt32* ftab,
+                Int32   nblock,
+                Int32   verb,
+                Int32*  budget )
+{
+   Int32  i, j, k, ss, sb;
+   Int32  runningOrder[256];
+   Bool   bigDone[256];
+   Int32  copyStart[256];
+   Int32  copyEnd  [256];
+   UChar  c1;
+   Int32  numQSorted;
+   UInt16 s;
+   if (verb >= 4) VPrintf0 ( "        main sort initialise ...\n" );
+
+   /*-- set up the 2-byte frequency table --*/
+   for (i = 65536; i >= 0; i--) ftab[i] = 0;
+
+   j = block[0] << 8;
+   i = nblock-1;
+   for (; i >= 3; i -= 4) {
+      quadrant[i] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i]) << 8);
+      ftab[j]++;
+      quadrant[i-1] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i-1]) << 8);
+      ftab[j]++;
+      quadrant[i-2] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i-2]) << 8);
+      ftab[j]++;
+      quadrant[i-3] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i-3]) << 8);
+      ftab[j]++;
+   }
+   for (; i >= 0; i--) {
+      quadrant[i] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i]) << 8);
+      ftab[j]++;
+   }
+
+   /*-- (emphasises close relationship of block & quadrant) --*/
+   for (i = 0; i < BZ_N_OVERSHOOT; i++) {
+      block   [nblock+i] = block[i];
+      quadrant[nblock+i] = 0;
+   }
+
+   if (verb >= 4) VPrintf0 ( "        bucket sorting ...\n" );
+
+   /*-- Complete the initial radix sort --*/
+   for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1];
+
+   s = block[0] << 8;
+   i = nblock-1;
+   for (; i >= 3; i -= 4) {
+      s = (s >> 8) | (block[i] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i;
+      s = (s >> 8) | (block[i-1] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i-1;
+      s = (s >> 8) | (block[i-2] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i-2;
+      s = (s >> 8) | (block[i-3] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i-3;
+   }
+   for (; i >= 0; i--) {
+      s = (s >> 8) | (block[i] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i;
+   }
+
+   /*--
+      Now ftab contains the first loc of every small bucket.
+      Calculate the running order, from smallest to largest
+      big bucket.
+   --*/
+   for (i = 0; i <= 255; i++) {
+      bigDone     [i] = False;
+      runningOrder[i] = i;
+   }
+
+   {
+      Int32 vv;
+      Int32 h = 1;
+      do h = 3 * h + 1; while (h <= 256);
+      do {
+         h = h / 3;
+         for (i = h; i <= 255; i++) {
+            vv = runningOrder[i];
+            j = i;
+            while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) {
+               runningOrder[j] = runningOrder[j-h];
+               j = j - h;
+               if (j <= (h - 1)) goto zero;
+            }
+            zero:
+            runningOrder[j] = vv;
+         }
+      } while (h != 1);
+   }
+
+   /*--
+      The main sorting loop.
+   --*/
+
+   numQSorted = 0;
+
+   for (i = 0; i <= 255; i++) {
+
+      /*--
+         Process big buckets, starting with the least full.
+         Basically this is a 3-step process in which we call
+         mainQSort3 to sort the small buckets [ss, j], but
+         also make a big effort to avoid the calls if we can.
+      --*/
+      ss = runningOrder[i];
+
+      /*--
+         Step 1:
+         Complete the big bucket [ss] by quicksorting
+         any unsorted small buckets [ss, j], for j != ss.  
+         Hopefully previous pointer-scanning phases have already
+         completed many of the small buckets [ss, j], so
+         we don't have to sort them at all.
+      --*/
+      for (j = 0; j <= 255; j++) {
+         if (j != ss) {
+            sb = (ss << 8) + j;
+            if ( ! (ftab[sb] & SETMASK) ) {
+               Int32 lo = ftab[sb]   & CLEARMASK;
+               Int32 hi = (ftab[sb+1] & CLEARMASK) - 1;
+               if (hi > lo) {
+                  if (verb >= 4)
+                     VPrintf4 ( "        qsort [0x%x, 0x%x]   "
+                                "done %d   this %d\n",
+                                ss, j, numQSorted, hi - lo + 1 );
+                  mainQSort3 ( 
+                     ptr, block, quadrant, nblock, 
+                     lo, hi, BZ_N_RADIX, budget 
+                  );   
+                  numQSorted += (hi - lo + 1);
+                  if (*budget < 0) return;
+               }
+            }
+            ftab[sb] |= SETMASK;
+         }
+      }
+
+      AssertH ( !bigDone[ss], 1006 );
+
+      /*--
+         Step 2:
+         Now scan this big bucket [ss] so as to synthesise the
+         sorted order for small buckets [t, ss] for all t,
+         including, magically, the bucket [ss,ss] too.
+         This will avoid doing Real Work in subsequent Step 1's.
+      --*/
+      {
+         for (j = 0; j <= 255; j++) {
+            copyStart[j] =  ftab[(j << 8) + ss]     & CLEARMASK;
+            copyEnd  [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1;
+         }
+         for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) {
+            k = ptr[j]-1; if (k < 0) k += nblock;
+            c1 = block[k];
+croak( 2 + (char*)budget ); /* should identify decl in calling frame */
+            if (!bigDone[c1])
+               ptr[ copyStart[c1]++ ] = k;
+         }
+         for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) {
+            k = ptr[j]-1; if (k < 0) k += nblock;
+            c1 = block[k];
+            if (!bigDone[c1]) 
+               ptr[ copyEnd[c1]-- ] = k;
+         }
+      }
+
+      AssertH ( (copyStart[ss]-1 == copyEnd[ss])
+                || 
+                /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1.
+                   Necessity for this case is demonstrated by compressing 
+                   a sequence of approximately 48.5 million of character 
+                   251; 1.0.0/1.0.1 will then die here. */
+                (copyStart[ss] == 0 && copyEnd[ss] == nblock-1),
+                1007 )
+
+      for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK;
+
+      /*--
+         Step 3:
+         The [ss] big bucket is now done.  Record this fact,
+         and update the quadrant descriptors.  Remember to
+         update quadrants in the overshoot area too, if
+         necessary.  The "if (i < 255)" test merely skips
+         this updating for the last bucket processed, since
+         updating for the last bucket is pointless.
+
+         The quadrant array provides a way to incrementally
+         cache sort orderings, as they appear, so as to 
+         make subsequent comparisons in fullGtU() complete
+         faster.  For repetitive blocks this makes a big
+         difference (but not big enough to be able to avoid
+         the fallback sorting mechanism, exponential radix sort).
+
+         The precise meaning is: at all times:
+
+            for 0 <= i < nblock and 0 <= j <= nblock
+
+            if block[i] != block[j], 
+
+               then the relative values of quadrant[i] and 
+                    quadrant[j] are meaningless.
+
+               else {
+                  if quadrant[i] < quadrant[j]
+                     then the string starting at i lexicographically
+                     precedes the string starting at j
+
+                  else if quadrant[i] > quadrant[j]
+                     then the string starting at j lexicographically
+                     precedes the string starting at i
+
+                  else
+                     the relative ordering of the strings starting
+                     at i and j has not yet been determined.
+               }
+      --*/
+      bigDone[ss] = True;
+
+      if (i < 255) {
+         Int32 bbStart  = ftab[ss << 8] & CLEARMASK;
+         Int32 bbSize   = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart;
+         Int32 shifts   = 0;
+
+         while ((bbSize >> shifts) > 65534) shifts++;
+
+         for (j = bbSize-1; j >= 0; j--) {
+            Int32 a2update     = ptr[bbStart + j];
+            UInt16 qVal        = (UInt16)(j >> shifts);
+            quadrant[a2update] = qVal;
+            if (a2update < BZ_N_OVERSHOOT)
+               quadrant[a2update + nblock] = qVal;
+         }
+         AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 );
+      }
+
+   }
+
+   if (verb >= 4)
+      VPrintf3 ( "        %d pointers, %d sorted, %d scanned\n",
+                 nblock, numQSorted, nblock - numQSorted );
+}
+
+#undef BIGFREQ
+#undef SETMASK
+#undef CLEARMASK
+
+
+/*---------------------------------------------*/
+/* Pre:
+      nblock > 0
+      arr2 exists for [0 .. nblock-1 +N_OVERSHOOT]
+      ((UChar*)arr2)  [0 .. nblock-1] holds block
+      arr1 exists for [0 .. nblock-1]
+
+   Post:
+      ((UChar*)arr2) [0 .. nblock-1] holds block
+      All other areas of block destroyed
+      ftab [ 0 .. 65536 ] destroyed
+      arr1 [0 .. nblock-1] holds sorted order
+*/
+__attribute__((noinline))
+void BZ2_blockSort ( EState* s )
+{
+   UInt32* ptr    = s->ptr; 
+   UChar*  block  = s->block;
+   UInt32* ftab   = s->ftab;
+   Int32   nblock = s->nblock;
+   Int32   verb   = s->verbosity;
+   Int32   wfact  = s->workFactor;
+   UInt16* quadrant;
+   Int32   budget;
+   Int32   budgetInit;
+   Int32   i;
+
+   if (nblock < /* 10000 */1000 ) {
+      fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
+   } else {
+      /* Calculate the location for quadrant, remembering to get
+         the alignment right.  Assumes that &(block[0]) is at least
+         2-byte aligned -- this should be ok since block is really
+         the first section of arr2.
+      */
+      i = nblock+BZ_N_OVERSHOOT;
+      if (i & 1) i++;
+      quadrant = (UInt16*)(&(block[i]));
+
+      /* (wfact-1) / 3 puts the default-factor-30
+         transition point at very roughly the same place as 
+         with v0.1 and v0.9.0.  
+         Not that it particularly matters any more, since the
+         resulting compressed stream is now the same regardless
+         of whether or not we use the main sort or fallback sort.
+      */
+      if (wfact < 1  ) wfact = 1;
+      if (wfact > 100) wfact = 100;
+      budgetInit = nblock * ((wfact-1) / 3);
+      budget = budgetInit;
+
+      mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget );
+      if (0 && verb >= 3) 
+         VPrintf3 ( "      %d work, %d block, ratio %5.2f\n",
+                    budgetInit - budget,
+                    nblock, 
+                    (float)(budgetInit - budget) /
+                    (float)(nblock==0 ? 1 : nblock) ); 
+      if (budget < 0) {
+         if (verb >= 2) 
+            VPrintf0 ( "    too repetitive; using fallback"
+                       " sorting algorithm\n" );
+         fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
+      }
+   }
+
+   s->origPtr = -1;
+   for (i = 0; i < s->nblock; i++)
+      if (ptr[i] == 0)
+         { s->origPtr = i; break; };
+
+   AssertH( s->origPtr != -1, 1003 );
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                       blocksort.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Huffman coding low-level stuff                        ---*/
+/*---                                             huffman.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+
+
+/*---------------------------------------------------*/
+#define WEIGHTOF(zz0)  ((zz0) & 0xffffff00)
+#define DEPTHOF(zz1)   ((zz1) & 0x000000ff)
+#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3))
+
+#define ADDWEIGHTS(zw1,zw2)                           \
+   (WEIGHTOF(zw1)+WEIGHTOF(zw2)) |                    \
+   (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2)))
+
+#define UPHEAP(z)                                     \
+{                                                     \
+   Int32 zz, tmp;                                     \
+   zz = z; tmp = heap[zz];                            \
+   while (weight[tmp] < weight[heap[zz >> 1]]) {      \
+      heap[zz] = heap[zz >> 1];                       \
+      zz >>= 1;                                       \
+   }                                                  \
+   heap[zz] = tmp;                                    \
+}
+
+#define DOWNHEAP(z)                                   \
+{                                                     \
+   Int32 zz, yy, tmp;                                 \
+   zz = z; tmp = heap[zz];                            \
+   while (True) {                                     \
+      yy = zz << 1;                                   \
+      if (yy > nHeap) break;                          \
+      if (yy < nHeap &&                               \
+          weight[heap[yy+1]] < weight[heap[yy]])      \
+         yy++;                                        \
+      if (weight[tmp] < weight[heap[yy]]) break;      \
+      heap[zz] = heap[yy];                            \
+      zz = yy;                                        \
+   }                                                  \
+   heap[zz] = tmp;                                    \
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbMakeCodeLengths ( UChar *len, 
+                             Int32 *freq,
+                             Int32 alphaSize,
+                             Int32 maxLen )
+{
+   /*--
+      Nodes and heap entries run from 1.  Entry 0
+      for both the heap and nodes is a sentinel.
+   --*/
+   Int32 nNodes, nHeap, n1, n2, i, j, k;
+   Bool  tooLong;
+
+   Int32 heap   [ BZ_MAX_ALPHA_SIZE + 2 ];
+   Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ];
+   Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; 
+
+   for (i = 0; i < alphaSize; i++)
+      weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+
+   while (True) {
+
+      nNodes = alphaSize;
+      nHeap = 0;
+
+      heap[0] = 0;
+      weight[0] = 0;
+      parent[0] = -2;
+
+      for (i = 1; i <= alphaSize; i++) {
+         parent[i] = -1;
+         nHeap++;
+         heap[nHeap] = i;
+         UPHEAP(nHeap);
+      }
+
+      AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 );
+   
+      while (nHeap > 1) {
+         n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
+         n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
+         nNodes++;
+         parent[n1] = parent[n2] = nNodes;
+         weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]);
+         parent[nNodes] = -1;
+         nHeap++;
+         heap[nHeap] = nNodes;
+         UPHEAP(nHeap);
+      }
+
+      AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 );
+
+      tooLong = False;
+      for (i = 1; i <= alphaSize; i++) {
+         j = 0;
+         k = i;
+         while (parent[k] >= 0) { k = parent[k]; j++; }
+         len[i-1] = j;
+         if (j > maxLen) tooLong = True;
+      }
+      
+      if (! tooLong) break;
+
+      /* 17 Oct 04: keep-going condition for the following loop used
+         to be 'i < alphaSize', which missed the last element,
+         theoretically leading to the possibility of the compressor
+         looping.  However, this count-scaling step is only needed if
+         one of the generated Huffman code words is longer than
+         maxLen, which up to and including version 1.0.2 was 20 bits,
+         which is extremely unlikely.  In version 1.0.3 maxLen was
+         changed to 17 bits, which has minimal effect on compression
+         ratio, but does mean this scaling step is used from time to
+         time, enough to verify that it works.
+
+         This means that bzip2-1.0.3 and later will only produce
+         Huffman codes with a maximum length of 17 bits.  However, in
+         order to preserve backwards compatibility with bitstreams
+         produced by versions pre-1.0.3, the decompressor must still
+         handle lengths of up to 20. */
+
+      for (i = 1; i <= alphaSize; i++) {
+         j = weight[i] >> 8;
+         j = 1 + (j / 2);
+         weight[i] = j << 8;
+      }
+   }
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbAssignCodes ( Int32 *code,
+                         UChar *length,
+                         Int32 minLen,
+                         Int32 maxLen,
+                         Int32 alphaSize )
+{
+   Int32 n, vec, i;
+
+   vec = 0;
+   for (n = minLen; n <= maxLen; n++) {
+      for (i = 0; i < alphaSize; i++)
+         if (length[i] == n) { code[i] = vec; vec++; };
+      vec <<= 1;
+   }
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbCreateDecodeTables ( Int32 *limit,
+                                Int32 *base,
+                                Int32 *perm,
+                                UChar *length,
+                                Int32 minLen,
+                                Int32 maxLen,
+                                Int32 alphaSize )
+{
+   Int32 pp, i, j, vec;
+
+   pp = 0;
+   for (i = minLen; i <= maxLen; i++)
+      for (j = 0; j < alphaSize; j++)
+         if (length[j] == i) { perm[pp] = j; pp++; };
+
+   for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0;
+   for (i = 0; i < alphaSize; i++) base[length[i]+1]++;
+
+   for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1];
+
+   for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0;
+   vec = 0;
+
+   for (i = minLen; i <= maxLen; i++) {
+      vec += (base[i+1] - base[i]);
+      limit[i] = vec-1;
+      vec <<= 1;
+   }
+   for (i = minLen + 1; i <= maxLen; i++)
+      base[i] = ((limit[i-1] + 1) << 1) - base[i];
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                         huffman.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Compression machinery (not incl block sorting)        ---*/
+/*---                                            compress.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+/*--
+   CHANGES
+   ~~~~~~~
+   0.9.0 -- original version.
+
+   0.9.0a/b -- no changes in this file.
+
+   0.9.0c
+      * changed setting of nGroups in sendMTFValues() so as to 
+        do a bit better on small files
+--*/
+
+
+
+/*---------------------------------------------------*/
+/*--- Bit stream I/O                              ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+void BZ2_bsInitWrite ( EState* s )
+{
+   s->bsLive = 0;
+   s->bsBuff = 0;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsFinishWrite ( EState* s )
+{
+   while (s->bsLive > 0) {
+      s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24);
+      s->numZ++;
+      s->bsBuff <<= 8;
+      s->bsLive -= 8;
+   }
+}
+
+
+/*---------------------------------------------------*/
+#define bsNEEDW(nz)                           \
+{                                             \
+   while (s->bsLive >= 8) {                   \
+      s->zbits[s->numZ]                       \
+         = (UChar)(s->bsBuff >> 24);          \
+      s->numZ++;                              \
+      s->bsBuff <<= 8;                        \
+      s->bsLive -= 8;                         \
+   }                                          \
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsW ( EState* s, Int32 n, UInt32 v )
+{
+   bsNEEDW ( n );
+   s->bsBuff |= (v << (32 - s->bsLive - n));
+   s->bsLive += n;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutUInt32 ( EState* s, UInt32 u )
+{
+   bsW ( s, 8, (u >> 24) & 0xffL );
+   bsW ( s, 8, (u >> 16) & 0xffL );
+   bsW ( s, 8, (u >>  8) & 0xffL );
+   bsW ( s, 8,  u        & 0xffL );
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutUChar ( EState* s, UChar c )
+{
+   bsW( s, 8, (UInt32)c );
+}
+
+
+/*---------------------------------------------------*/
+/*--- The back end proper                         ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+static
+void makeMaps_e ( EState* s )
+{
+   Int32 i;
+   s->nInUse = 0;
+   for (i = 0; i < 256; i++)
+      if (s->inUse[i]) {
+         s->unseqToSeq[i] = s->nInUse;
+         s->nInUse++;
+      }
+}
+
+
+/*---------------------------------------------------*/
+static
+void generateMTFValues ( EState* s )
+{
+   UChar   yy[256];
+   Int32   i, j;
+   Int32   zPend;
+   Int32   wr;
+   Int32   EOB;
+
+   /* 
+      After sorting (eg, here),
+         s->arr1 [ 0 .. s->nblock-1 ] holds sorted order,
+         and
+         ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] 
+         holds the original block data.
+
+      The first thing to do is generate the MTF values,
+      and put them in
+         ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ].
+      Because there are strictly fewer or equal MTF values
+      than block values, ptr values in this area are overwritten
+      with MTF values only when they are no longer needed.
+
+      The final compressed bitstream is generated into the
+      area starting at
+         (UChar*) (&((UChar*)s->arr2)[s->nblock])
+
+      These storage aliases are set up in bzCompressInit(),
+      except for the last one, which is arranged in 
+      compressBlock().
+   */
+   UInt32* ptr   = s->ptr;
+   UChar* block  = s->block;
+   UInt16* mtfv  = s->mtfv;
+
+   makeMaps_e ( s );
+   EOB = s->nInUse+1;
+
+   for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0;
+
+   wr = 0;
+   zPend = 0;
+   for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i;
+
+   for (i = 0; i < s->nblock; i++) {
+      UChar ll_i;
+      AssertD ( wr <= i, "generateMTFValues(1)" );
+      j = ptr[i]-1; if (j < 0) j += s->nblock;
+      ll_i = s->unseqToSeq[block[j]];
+      AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" );
+
+      if (yy[0] == ll_i) { 
+         zPend++;
+      } else {
+
+         if (zPend > 0) {
+            zPend--;
+            while (True) {
+               if (zPend & 1) {
+                  mtfv[wr] = BZ_RUNB; wr++; 
+                  s->mtfFreq[BZ_RUNB]++; 
+               } else {
+                  mtfv[wr] = BZ_RUNA; wr++; 
+                  s->mtfFreq[BZ_RUNA]++; 
+               }
+               if (zPend < 2) break;
+               zPend = (zPend - 2) / 2;
+            };
+            zPend = 0;
+         }
+         {
+            register UChar  rtmp;
+            register UChar* ryy_j;
+            register UChar  rll_i;
+            rtmp  = yy[1];
+            yy[1] = yy[0];
+            ryy_j = &(yy[1]);
+            rll_i = ll_i;
+            while ( rll_i != rtmp ) {
+               register UChar rtmp2;
+               ryy_j++;
+               rtmp2  = rtmp;
+               rtmp   = *ryy_j;
+               *ryy_j = rtmp2;
+            };
+            yy[0] = rtmp;
+            j = ryy_j - &(yy[0]);
+            mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++;
+         }
+
+      }
+   }
+
+   if (zPend > 0) {
+      zPend--;
+      while (True) {
+         if (zPend & 1) {
+            mtfv[wr] = BZ_RUNB; wr++; 
+            s->mtfFreq[BZ_RUNB]++; 
+         } else {
+            mtfv[wr] = BZ_RUNA; wr++; 
+            s->mtfFreq[BZ_RUNA]++; 
+         }
+         if (zPend < 2) break;
+         zPend = (zPend - 2) / 2;
+      };
+      zPend = 0;
+   }
+
+   mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++;
+
+   s->nMTF = wr;
+}
+
+
+/*---------------------------------------------------*/
+#define BZ_LESSER_ICOST  0
+#define BZ_GREATER_ICOST 15
+
+static
+void sendMTFValues ( EState* s )
+{
+   Int32 v, t, i, j, gs, ge, totc, bt, bc, iter;
+   Int32 nSelectors, alphaSize, minLen, maxLen, selCtr;
+   Int32 nGroups, nBytes;
+
+   /*--
+   UChar  len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+   is a global since the decoder also needs it.
+
+   Int32  code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+   Int32  rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+   are also globals only used in this proc.
+   Made global to keep stack frame size small.
+   --*/
+
+
+   UInt16 cost[BZ_N_GROUPS];
+   Int32  fave[BZ_N_GROUPS];
+
+   UInt16* mtfv = s->mtfv;
+
+   if (s->verbosity >= 3)
+      VPrintf3( "      %d in block, %d after MTF & 1-2 coding, "
+                "%d+2 syms in use\n", 
+                s->nblock, s->nMTF, s->nInUse );
+
+   alphaSize = s->nInUse+2;
+   for (t = 0; t < BZ_N_GROUPS; t++)
+      for (v = 0; v < alphaSize; v++)
+         s->len[t][v] = BZ_GREATER_ICOST;
+
+   /*--- Decide how many coding tables to use ---*/
+   AssertH ( s->nMTF > 0, 3001 );
+   if (s->nMTF < 200)  nGroups = 2; else
+   if (s->nMTF < 600)  nGroups = 3; else
+   if (s->nMTF < 1200) nGroups = 4; else
+   if (s->nMTF < 2400) nGroups = 5; else
+                       nGroups = 6;
+
+   /*--- Generate an initial set of coding tables ---*/
+   { 
+      Int32 nPart, remF, tFreq, aFreq;
+
+      nPart = nGroups;
+      remF  = s->nMTF;
+      gs = 0;
+      while (nPart > 0) {
+         tFreq = remF / nPart;
+         ge = gs-1;
+         aFreq = 0;
+         while (aFreq < tFreq && ge < alphaSize-1) {
+            ge++;
+            aFreq += s->mtfFreq[ge];
+         }
+
+         if (ge > gs 
+             && nPart != nGroups && nPart != 1 
+             && ((nGroups-nPart) % 2 == 1)) {
+            aFreq -= s->mtfFreq[ge];
+            ge--;
+         }
+
+         if (0 && s->verbosity >= 3)
+            VPrintf5( "      initial group %d, [%d .. %d], "
+                      "has %d syms (%4.1f%%)\n",
+                      nPart, gs, ge, aFreq, 
+                      (100.0 * (float)aFreq) / (float)(s->nMTF) );
+ 
+         for (v = 0; v < alphaSize; v++)
+            if (v >= gs && v <= ge) 
+               s->len[nPart-1][v] = BZ_LESSER_ICOST; else
+               s->len[nPart-1][v] = BZ_GREATER_ICOST;
+ 
+         nPart--;
+         gs = ge+1;
+         remF -= aFreq;
+      }
+   }
+
+   /*--- 
+      Iterate up to BZ_N_ITERS times to improve the tables.
+   ---*/
+   for (iter = 0; iter < BZ_N_ITERS; iter++) {
+
+      for (t = 0; t < nGroups; t++) fave[t] = 0;
+
+      for (t = 0; t < nGroups; t++)
+         for (v = 0; v < alphaSize; v++)
+            s->rfreq[t][v] = 0;
+
+      /*---
+        Set up an auxiliary length table which is used to fast-track
+	the common case (nGroups == 6). 
+      ---*/
+      if (nGroups == 6) {
+         for (v = 0; v < alphaSize; v++) {
+            s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v];
+            s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v];
+            s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v];
+	 }
+      }
+
+      nSelectors = 0;
+      totc = 0;
+      gs = 0;
+      while (True) {
+
+         /*--- Set group start & end marks. --*/
+         if (gs >= s->nMTF) break;
+         ge = gs + BZ_G_SIZE - 1; 
+         if (ge >= s->nMTF) ge = s->nMTF-1;
+
+         /*-- 
+            Calculate the cost of this group as coded
+            by each of the coding tables.
+         --*/
+         for (t = 0; t < nGroups; t++) cost[t] = 0;
+
+         if (nGroups == 6 && 50 == ge-gs+1) {
+            /*--- fast track the common case ---*/
+            register UInt32 cost01, cost23, cost45;
+            register UInt16 icv;
+            cost01 = cost23 = cost45 = 0;
+
+#           define BZ_ITER(nn)                \
+               icv = mtfv[gs+(nn)];           \
+               cost01 += s->len_pack[icv][0]; \
+               cost23 += s->len_pack[icv][1]; \
+               cost45 += s->len_pack[icv][2]; \
+
+            BZ_ITER(0);  BZ_ITER(1);  BZ_ITER(2);  BZ_ITER(3);  BZ_ITER(4);
+            BZ_ITER(5);  BZ_ITER(6);  BZ_ITER(7);  BZ_ITER(8);  BZ_ITER(9);
+            BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14);
+            BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19);
+            BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24);
+            BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29);
+            BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34);
+            BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39);
+            BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44);
+            BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49);
+
+#           undef BZ_ITER
+
+            cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
+            cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
+            cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;
+
+         } else {
+	    /*--- slow version which correctly handles all situations ---*/
+            for (i = gs; i <= ge; i++) { 
+               UInt16 icv = mtfv[i];
+               for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv];
+            }
+         }
+ 
+         /*-- 
+            Find the coding table which is best for this group,
+            and record its identity in the selector table.
+         --*/
+         bc = 999999999; bt = -1;
+         for (t = 0; t < nGroups; t++)
+            if (cost[t] < bc) { bc = cost[t]; bt = t; };
+         totc += bc;
+         fave[bt]++;
+         s->selector[nSelectors] = bt;
+         nSelectors++;
+
+         /*-- 
+            Increment the symbol frequencies for the selected table.
+          --*/
+         if (nGroups == 6 && 50 == ge-gs+1) {
+            /*--- fast track the common case ---*/
+
+#           define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++
+
+            BZ_ITUR(0);  BZ_ITUR(1);  BZ_ITUR(2);  BZ_ITUR(3);  BZ_ITUR(4);
+            BZ_ITUR(5);  BZ_ITUR(6);  BZ_ITUR(7);  BZ_ITUR(8);  BZ_ITUR(9);
+            BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14);
+            BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19);
+            BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24);
+            BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29);
+            BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34);
+            BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39);
+            BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44);
+            BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49);
+
+#           undef BZ_ITUR
+
+         } else {
+	    /*--- slow version which correctly handles all situations ---*/
+            for (i = gs; i <= ge; i++)
+               s->rfreq[bt][ mtfv[i] ]++;
+         }
+
+         gs = ge+1;
+      }
+      if (s->verbosity >= 3) {
+         VPrintf2 ( "      pass %d: size is %d, grp uses are ", 
+                   iter+1, totc/8 );
+         for (t = 0; t < nGroups; t++)
+            VPrintf1 ( "%d ", fave[t] );
+         VPrintf0 ( "\n" );
+      }
+
+      /*--
+        Recompute the tables based on the accumulated frequencies.
+      --*/
+      /* maxLen was changed from 20 to 17 in bzip2-1.0.3.  See 
+         comment in huffman.c for details. */
+      for (t = 0; t < nGroups; t++)
+         BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), 
+                                 alphaSize, 17 /*20*/ );
+   }
+
+
+   AssertH( nGroups < 8, 3002 );
+   AssertH( nSelectors < 32768 &&
+            nSelectors <= (2 + (900000 / BZ_G_SIZE)),
+            3003 );
+
+
+   /*--- Compute MTF values for the selectors. ---*/
+   {
+      UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp;
+      for (i = 0; i < nGroups; i++) pos[i] = i;
+      for (i = 0; i < nSelectors; i++) {
+         ll_i = s->selector[i];
+         j = 0;
+         tmp = pos[j];
+         while ( ll_i != tmp ) {
+            j++;
+            tmp2 = tmp;
+            tmp = pos[j];
+            pos[j] = tmp2;
+         };
+         pos[0] = tmp;
+         s->selectorMtf[i] = j;
+      }
+   };
+
+   /*--- Assign actual codes for the tables. --*/
+   for (t = 0; t < nGroups; t++) {
+      minLen = 32;
+      maxLen = 0;
+      for (i = 0; i < alphaSize; i++) {
+         if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+         if (s->len[t][i] < minLen) minLen = s->len[t][i];
+      }
+      AssertH ( !(maxLen > 17 /*20*/ ), 3004 );
+      AssertH ( !(minLen < 1),  3005 );
+      BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), 
+                          minLen, maxLen, alphaSize );
+   }
+
+   /*--- Transmit the mapping table. ---*/
+   { 
+      Bool inUse16[16];
+      for (i = 0; i < 16; i++) {
+          inUse16[i] = False;
+          for (j = 0; j < 16; j++)
+             if (s->inUse[i * 16 + j]) inUse16[i] = True;
+      }
+     
+      nBytes = s->numZ;
+      for (i = 0; i < 16; i++)
+         if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0);
+
+      for (i = 0; i < 16; i++)
+         if (inUse16[i])
+            for (j = 0; j < 16; j++) {
+               if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0);
+            }
+
+      if (s->verbosity >= 3) 
+         VPrintf1( "      bytes: mapping %d, ", s->numZ-nBytes );
+   }
+
+   /*--- Now the selectors. ---*/
+   nBytes = s->numZ;
+   bsW ( s, 3, nGroups );
+   bsW ( s, 15, nSelectors );
+   for (i = 0; i < nSelectors; i++) { 
+      for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1);
+      bsW(s,1,0);
+   }
+   if (s->verbosity >= 3)
+      VPrintf1( "selectors %d, ", s->numZ-nBytes );
+
+   /*--- Now the coding tables. ---*/
+   nBytes = s->numZ;
+
+   for (t = 0; t < nGroups; t++) {
+      Int32 curr = s->len[t][0];
+      bsW ( s, 5, curr );
+      for (i = 0; i < alphaSize; i++) {
+         while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ };
+         while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ };
+         bsW ( s, 1, 0 );
+      }
+   }
+
+   if (s->verbosity >= 3)
+      VPrintf1 ( "code lengths %d, ", s->numZ-nBytes );
+
+   /*--- And finally, the block data proper ---*/
+   nBytes = s->numZ;
+   selCtr = 0;
+   gs = 0;
+   while (True) {
+      if (gs >= s->nMTF) break;
+      ge = gs + BZ_G_SIZE - 1; 
+      if (ge >= s->nMTF) ge = s->nMTF-1;
+      AssertH ( s->selector[selCtr] < nGroups, 3006 );
+
+      if (nGroups == 6 && 50 == ge-gs+1) {
+            /*--- fast track the common case ---*/
+            UInt16 mtfv_i;
+            UChar* s_len_sel_selCtr 
+               = &(s->len[s->selector[selCtr]][0]);
+            Int32* s_code_sel_selCtr
+               = &(s->code[s->selector[selCtr]][0]);
+
+#           define BZ_ITAH(nn)                      \
+               mtfv_i = mtfv[gs+(nn)];              \
+               bsW ( s,                             \
+                     s_len_sel_selCtr[mtfv_i],      \
+                     s_code_sel_selCtr[mtfv_i] )
+
+            BZ_ITAH(0);  BZ_ITAH(1);  BZ_ITAH(2);  BZ_ITAH(3);  BZ_ITAH(4);
+            BZ_ITAH(5);  BZ_ITAH(6);  BZ_ITAH(7);  BZ_ITAH(8);  BZ_ITAH(9);
+            BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
+            BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
+            BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
+            BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
+            BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
+            BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
+            BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
+            BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);
+
+#           undef BZ_ITAH
+
+      } else {
+	 /*--- slow version which correctly handles all situations ---*/
+         for (i = gs; i <= ge; i++) {
+            bsW ( s, 
+                  s->len  [s->selector[selCtr]] [mtfv[i]],
+                  s->code [s->selector[selCtr]] [mtfv[i]] );
+         }
+      }
+
+
+      gs = ge+1;
+      selCtr++;
+   }
+   AssertH( selCtr == nSelectors, 3007 );
+
+   if (s->verbosity >= 3)
+      VPrintf1( "codes %d\n", s->numZ-nBytes );
+}
+
+
+/*---------------------------------------------------*/
+__attribute__((noinline))
+void BZ2_compressBlock ( EState* s, Bool is_last_block )
+{
+   if (s->nblock > 0) {
+
+      BZ_FINALISE_CRC ( s->blockCRC );
+      s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
+      s->combinedCRC ^= s->blockCRC;
+      if (s->blockNo > 1) s->numZ = 0;
+
+      if (s->verbosity >= 2)
+         VPrintf4( "    block %d: crc = 0x%08x, "
+                   "combined CRC = 0x%08x, size = %d\n",
+                   s->blockNo, s->blockCRC, s->combinedCRC, s->nblock );
+
+      BZ2_blockSort ( s );
+   }
+
+   s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]);
+
+   /*-- If this is the first block, create the stream header. --*/
+   if (s->blockNo == 1) {
+      BZ2_bsInitWrite ( s );
+      bsPutUChar ( s, BZ_HDR_B );
+      bsPutUChar ( s, BZ_HDR_Z );
+      bsPutUChar ( s, BZ_HDR_h );
+      bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) );
+   }
+
+   if (s->nblock > 0) {
+
+      bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 );
+      bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 );
+      bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 );
+
+      /*-- Now the block's CRC, so it is in a known place. --*/
+      bsPutUInt32 ( s, s->blockCRC );
+
+      /*-- 
+         Now a single bit indicating (non-)randomisation. 
+         As of version 0.9.5, we use a better sorting algorithm
+         which makes randomisation unnecessary.  So always set
+         the randomised bit to 'no'.  Of course, the decoder
+         still needs to be able to handle randomised blocks
+         so as to maintain backwards compatibility with
+         older versions of bzip2.
+      --*/
+      bsW(s,1,0);
+
+      bsW ( s, 24, s->origPtr );
+      generateMTFValues ( s );
+      sendMTFValues ( s );
+   }
+
+
+   /*-- If this is the last block, add the stream trailer. --*/
+   if (is_last_block) {
+
+      bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 );
+      bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 );
+      bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 );
+      bsPutUInt32 ( s, s->combinedCRC );
+      if (s->verbosity >= 2)
+         VPrintf1( "    final combined CRC = 0x%08x\n   ", s->combinedCRC );
+      bsFinishWrite ( s );
+   }
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                        compress.c ---*/
+/*-------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------*/
+/*--- Table for randomising repetitive blocks               ---*/
+/*---                                           randtable.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+
+
+
+/*---------------------------------------------*/
+Int32 BZ2_rNums[512] = { 
+   619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 
+   985, 724, 205, 454, 863, 491, 741, 242, 949, 214, 
+   733, 859, 335, 708, 621, 574, 73, 654, 730, 472, 
+   419, 436, 278, 496, 867, 210, 399, 680, 480, 51, 
+   878, 465, 811, 169, 869, 675, 611, 697, 867, 561, 
+   862, 687, 507, 283, 482, 129, 807, 591, 733, 623, 
+   150, 238, 59, 379, 684, 877, 625, 169, 643, 105, 
+   170, 607, 520, 932, 727, 476, 693, 425, 174, 647, 
+   73, 122, 335, 530, 442, 853, 695, 249, 445, 515, 
+   909, 545, 703, 919, 874, 474, 882, 500, 594, 612, 
+   641, 801, 220, 162, 819, 984, 589, 513, 495, 799, 
+   161, 604, 958, 533, 221, 400, 386, 867, 600, 782, 
+   382, 596, 414, 171, 516, 375, 682, 485, 911, 276, 
+   98, 553, 163, 354, 666, 933, 424, 341, 533, 870, 
+   227, 730, 475, 186, 263, 647, 537, 686, 600, 224, 
+   469, 68, 770, 919, 190, 373, 294, 822, 808, 206, 
+   184, 943, 795, 384, 383, 461, 404, 758, 839, 887, 
+   715, 67, 618, 276, 204, 918, 873, 777, 604, 560, 
+   951, 160, 578, 722, 79, 804, 96, 409, 713, 940, 
+   652, 934, 970, 447, 318, 353, 859, 672, 112, 785, 
+   645, 863, 803, 350, 139, 93, 354, 99, 820, 908, 
+   609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 
+   653, 282, 762, 623, 680, 81, 927, 626, 789, 125, 
+   411, 521, 938, 300, 821, 78, 343, 175, 128, 250, 
+   170, 774, 972, 275, 999, 639, 495, 78, 352, 126, 
+   857, 956, 358, 619, 580, 124, 737, 594, 701, 612, 
+   669, 112, 134, 694, 363, 992, 809, 743, 168, 974, 
+   944, 375, 748, 52, 600, 747, 642, 182, 862, 81, 
+   344, 805, 988, 739, 511, 655, 814, 334, 249, 515, 
+   897, 955, 664, 981, 649, 113, 974, 459, 893, 228, 
+   433, 837, 553, 268, 926, 240, 102, 654, 459, 51, 
+   686, 754, 806, 760, 493, 403, 415, 394, 687, 700, 
+   946, 670, 656, 610, 738, 392, 760, 799, 887, 653, 
+   978, 321, 576, 617, 626, 502, 894, 679, 243, 440, 
+   680, 879, 194, 572, 640, 724, 926, 56, 204, 700, 
+   707, 151, 457, 449, 797, 195, 791, 558, 945, 679, 
+   297, 59, 87, 824, 713, 663, 412, 693, 342, 606, 
+   134, 108, 571, 364, 631, 212, 174, 643, 304, 329, 
+   343, 97, 430, 751, 497, 314, 983, 374, 822, 928, 
+   140, 206, 73, 263, 980, 736, 876, 478, 430, 305, 
+   170, 514, 364, 692, 829, 82, 855, 953, 676, 246, 
+   369, 970, 294, 750, 807, 827, 150, 790, 288, 923, 
+   804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 
+   896, 831, 547, 261, 524, 462, 293, 465, 502, 56, 
+   661, 821, 976, 991, 658, 869, 905, 758, 745, 193, 
+   768, 550, 608, 933, 378, 286, 215, 979, 792, 961, 
+   61, 688, 793, 644, 986, 403, 106, 366, 905, 644, 
+   372, 567, 466, 434, 645, 210, 389, 550, 919, 135, 
+   780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 
+   920, 176, 193, 713, 857, 265, 203, 50, 668, 108, 
+   645, 990, 626, 197, 510, 357, 358, 850, 858, 364, 
+   936, 638
+};
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                       randtable.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Table for doing CRCs                                  ---*/
+/*---                                            crctable.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+
+
+
+
+/*--
+  I think this is an implementation of the AUTODIN-II,
+  Ethernet & FDDI 32-bit CRC standard.  Vaguely derived
+  from code by Rob Warnock, in Section 51 of the
+  comp.compression FAQ.
+--*/
+
+UInt32 BZ2_crc32Table[256] = {
+
+   /*-- Ugly, innit? --*/
+
+   0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
+   0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
+   0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
+   0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
+   0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
+   0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
+   0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
+   0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
+   0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
+   0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
+   0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
+   0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
+   0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
+   0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
+   0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
+   0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
+   0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
+   0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
+   0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
+   0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
+   0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
+   0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
+   0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
+   0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
+   0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
+   0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
+   0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
+   0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
+   0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
+   0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
+   0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
+   0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
+   0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
+   0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
+   0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
+   0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
+   0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
+   0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
+   0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
+   0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
+   0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
+   0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
+   0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
+   0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
+   0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
+   0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
+   0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
+   0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
+   0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
+   0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
+   0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
+   0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
+   0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
+   0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
+   0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
+   0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
+   0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
+   0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
+   0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
+   0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
+   0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
+   0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
+   0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
+   0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
+};
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                        crctable.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Library top-level functions.                          ---*/
+/*---                                               bzlib.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2004 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  jseward@bzip.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+/*--
+   CHANGES
+   ~~~~~~~
+   0.9.0 -- original version.
+
+   0.9.0a/b -- no changes in this file.
+
+   0.9.0c
+      * made zero-length BZ_FLUSH work correctly in bzCompress().
+      * fixed bzWrite/bzRead to ignore zero-length requests.
+      * fixed bzread to correctly handle read requests after EOF.
+      * wrong parameter order in call to bzDecompressInit in
+        bzBuffToBuffDecompress.  Fixed.
+--*/
+
+
+
+/*---------------------------------------------------*/
+/*--- Compression stuff                           ---*/
+/*---------------------------------------------------*/
+
+
+/*---------------------------------------------------*/
+void BZ2_bz__AssertH__fail ( int errcode )
+{
+   vex_printf("BZ2_bz__AssertH__fail(%d) called, exiting\n", errcode);
+   (*serviceFn)(0,0);
+}
+
+void bz_internal_error ( int errcode )
+{
+   vex_printf("bz_internal_error called, exiting\n", errcode);
+   (*serviceFn)(0,0);
+}
+
+/*---------------------------------------------------*/
+static
+int bz_config_ok ( void )
+{
+   if (sizeof(int)   != 4) return 0;
+   if (sizeof(short) != 2) return 0;
+   if (sizeof(char)  != 1) return 0;
+   return 1;
+}
+
+
+/*---------------------------------------------------*/
+static
+void* default_bzalloc ( void* opaque, Int32 items, Int32 size )
+{
+   void* v = (void*) (*serviceFn)(2, items * size );
+   return v;
+}
+
+static
+void default_bzfree ( void* opaque, void* addr )
+{
+   if (addr != NULL) (*serviceFn)( 3, (HWord)addr );
+}
+
+
+/*---------------------------------------------------*/
+static
+void prepare_new_block ( EState* s )
+{
+   Int32 i;
+   s->nblock = 0;
+   s->numZ = 0;
+   s->state_out_pos = 0;
+   BZ_INITIALISE_CRC ( s->blockCRC );
+   for (i = 0; i < 256; i++) s->inUse[i] = False;
+   s->blockNo++;
+}
+
+
+/*---------------------------------------------------*/
+static
+void init_RL ( EState* s )
+{
+   s->state_in_ch  = 256;
+   s->state_in_len = 0;
+}
+
+
+static
+Bool isempty_RL ( EState* s )
+{
+   if (s->state_in_ch < 256 && s->state_in_len > 0)
+      return False; else
+      return True;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompressInit) 
+                    ( bz_stream* strm, 
+                     int        blockSize100k,
+                     int        verbosity,
+                     int        workFactor )
+{
+   Int32   n;
+   EState* s;
+
+   if (!bz_config_ok()) return BZ_CONFIG_ERROR;
+
+   if (strm == NULL || 
+       blockSize100k < 1 || blockSize100k > 9 ||
+       workFactor < 0 || workFactor > 250)
+     return BZ_PARAM_ERROR;
+
+   if (workFactor == 0) workFactor = 30;
+   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
+   if (strm->bzfree == NULL) strm->bzfree = default_bzfree;
+
+   s = BZALLOC( sizeof(EState) );
+   if (s == NULL) return BZ_MEM_ERROR;
+   s->strm = strm;
+
+   s->arr1 = NULL;
+   s->arr2 = NULL;
+   s->ftab = NULL;
+
+   n       = 100000 * blockSize100k;
+   s->arr1 = BZALLOC( n                  * sizeof(UInt32) );
+   s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) );
+   s->ftab = BZALLOC( 65537              * sizeof(UInt32) );
+
+   if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) {
+      if (s->arr1 != NULL) BZFREE(s->arr1);
+      if (s->arr2 != NULL) BZFREE(s->arr2);
+      if (s->ftab != NULL) BZFREE(s->ftab);
+      if (s       != NULL) BZFREE(s);
+      return BZ_MEM_ERROR;
+   }
+
+   s->blockNo           = 0;
+   s->state             = BZ_S_INPUT;
+   s->mode              = BZ_M_RUNNING;
+   s->combinedCRC       = 0;
+   s->blockSize100k     = blockSize100k;
+   s->nblockMAX         = 100000 * blockSize100k - 19;
+   s->verbosity         = verbosity;
+   s->workFactor        = workFactor;
+
+   s->block             = (UChar*)s->arr2;
+   s->mtfv              = (UInt16*)s->arr1;
+   s->zbits             = NULL;
+   s->ptr               = (UInt32*)s->arr1;
+
+   strm->state          = s;
+   strm->total_in_lo32  = 0;
+   strm->total_in_hi32  = 0;
+   strm->total_out_lo32 = 0;
+   strm->total_out_hi32 = 0;
+   init_RL ( s );
+   prepare_new_block ( s );
+   return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+static
+void add_pair_to_block ( EState* s )
+{
+   Int32 i;
+   UChar ch = (UChar)(s->state_in_ch);
+   for (i = 0; i < s->state_in_len; i++) {
+      BZ_UPDATE_CRC( s->blockCRC, ch );
+   }
+   s->inUse[s->state_in_ch] = True;
+   switch (s->state_in_len) {
+      case 1:
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         break;
+      case 2:
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         break;
+      case 3:
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         break;
+      default:
+         s->inUse[s->state_in_len-4] = True;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = ((UChar)(s->state_in_len-4));
+         s->nblock++;
+         break;
+   }
+}
+
+
+/*---------------------------------------------------*/
+static
+void flush_RL ( EState* s )
+{
+   if (s->state_in_ch < 256) add_pair_to_block ( s );
+   init_RL ( s );
+}
+
+
+/*---------------------------------------------------*/
+#define ADD_CHAR_TO_BLOCK(zs,zchh0)               \
+{                                                 \
+   UInt32 zchh = (UInt32)(zchh0);                 \
+   /*-- fast track the common case --*/           \
+   if (zchh != zs->state_in_ch &&                 \
+       zs->state_in_len == 1) {                   \
+      UChar ch = (UChar)(zs->state_in_ch);        \
+      BZ_UPDATE_CRC( zs->blockCRC, ch );          \
+      zs->inUse[zs->state_in_ch] = True;          \
+      zs->block[zs->nblock] = (UChar)ch;          \
+      zs->nblock++;                               \
+      zs->state_in_ch = zchh;                     \
+   }                                              \
+   else                                           \
+   /*-- general, uncommon cases --*/              \
+   if (zchh != zs->state_in_ch ||                 \
+      zs->state_in_len == 255) {                  \
+      if (zs->state_in_ch < 256)                  \
+         add_pair_to_block ( zs );                \
+      zs->state_in_ch = zchh;                     \
+      zs->state_in_len = 1;                       \
+   } else {                                       \
+      zs->state_in_len++;                         \
+   }                                              \
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool copy_input_until_stop ( EState* s )
+{
+   Bool progress_in = False;
+
+   if (s->mode == BZ_M_RUNNING) {
+
+      /*-- fast track the common case --*/
+      while (True) {
+         /*-- block full? --*/
+         if (s->nblock >= s->nblockMAX) break;
+         /*-- no input? --*/
+         if (s->strm->avail_in == 0) break;
+         progress_in = True;
+         ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); 
+         s->strm->next_in++;
+         s->strm->avail_in--;
+         s->strm->total_in_lo32++;
+         if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++;
+      }
+
+   } else {
+
+      /*-- general, uncommon case --*/
+      while (True) {
+         /*-- block full? --*/
+         if (s->nblock >= s->nblockMAX) break;
+         /*-- no input? --*/
+         if (s->strm->avail_in == 0) break;
+         /*-- flush/finish end? --*/
+         if (s->avail_in_expect == 0) break;
+         progress_in = True;
+         ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); 
+         s->strm->next_in++;
+         s->strm->avail_in--;
+         s->strm->total_in_lo32++;
+         if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++;
+         s->avail_in_expect--;
+      }
+   }
+   return progress_in;
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool copy_output_until_stop ( EState* s )
+{
+   Bool progress_out = False;
+
+   while (True) {
+
+      /*-- no output space? --*/
+      if (s->strm->avail_out == 0) break;
+
+      /*-- block done? --*/
+      if (s->state_out_pos >= s->numZ) break;
+
+      progress_out = True;
+      *(s->strm->next_out) = s->zbits[s->state_out_pos];
+      s->state_out_pos++;
+      s->strm->avail_out--;
+      s->strm->next_out++;
+      s->strm->total_out_lo32++;
+      if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+   }
+
+   return progress_out;
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool handle_compress ( bz_stream* strm )
+{
+   Bool progress_in  = False;
+   Bool progress_out = False;
+   EState* s = strm->state;
+   
+   while (True) {
+
+      if (s->state == BZ_S_OUTPUT) {
+         progress_out |= copy_output_until_stop ( s );
+         if (s->state_out_pos < s->numZ) break;
+         if (s->mode == BZ_M_FINISHING && 
+             s->avail_in_expect == 0 &&
+             isempty_RL(s)) break;
+         prepare_new_block ( s );
+         s->state = BZ_S_INPUT;
+         if (s->mode == BZ_M_FLUSHING && 
+             s->avail_in_expect == 0 &&
+             isempty_RL(s)) break;
+      }
+
+      if (s->state == BZ_S_INPUT) {
+         progress_in |= copy_input_until_stop ( s );
+         if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) {
+            flush_RL ( s );
+            BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) );
+            s->state = BZ_S_OUTPUT;
+         }
+         else
+         if (s->nblock >= s->nblockMAX) {
+            BZ2_compressBlock ( s, False );
+            s->state = BZ_S_OUTPUT;
+         }
+         else
+         if (s->strm->avail_in == 0) {
+            break;
+         }
+      }
+
+   }
+
+   return progress_in || progress_out;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action )
+{
+   Bool progress;
+   EState* s;
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   s = strm->state;
+   if (s == NULL) return BZ_PARAM_ERROR;
+   if (s->strm != strm) return BZ_PARAM_ERROR;
+
+   preswitch:
+   switch (s->mode) {
+
+      case BZ_M_IDLE:
+         return BZ_SEQUENCE_ERROR;
+
+      case BZ_M_RUNNING:
+         if (action == BZ_RUN) {
+            progress = handle_compress ( strm );
+            return progress ? BZ_RUN_OK : BZ_PARAM_ERROR;
+         } 
+         else
+	 if (action == BZ_FLUSH) {
+            s->avail_in_expect = strm->avail_in;
+            s->mode = BZ_M_FLUSHING;
+            goto preswitch;
+         }
+         else
+         if (action == BZ_FINISH) {
+            s->avail_in_expect = strm->avail_in;
+            s->mode = BZ_M_FINISHING;
+            goto preswitch;
+         }
+         else 
+            return BZ_PARAM_ERROR;
+
+      case BZ_M_FLUSHING:
+         if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR;
+         if (s->avail_in_expect != s->strm->avail_in) 
+            return BZ_SEQUENCE_ERROR;
+         progress = handle_compress ( strm );
+         if (s->avail_in_expect > 0 || !isempty_RL(s) ||
+             s->state_out_pos < s->numZ) return BZ_FLUSH_OK;
+         s->mode = BZ_M_RUNNING;
+         return BZ_RUN_OK;
+
+      case BZ_M_FINISHING:
+         if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR;
+         if (s->avail_in_expect != s->strm->avail_in) 
+            return BZ_SEQUENCE_ERROR;
+         progress = handle_compress ( strm );
+         if (!progress) return BZ_SEQUENCE_ERROR;
+         if (s->avail_in_expect > 0 || !isempty_RL(s) ||
+             s->state_out_pos < s->numZ) return BZ_FINISH_OK;
+         s->mode = BZ_M_IDLE;
+         return BZ_STREAM_END;
+   }
+   return BZ_OK; /*--not reached--*/
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompressEnd)  ( bz_stream *strm )
+{
+   EState* s;
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   s = strm->state;
+   if (s == NULL) return BZ_PARAM_ERROR;
+   if (s->strm != strm) return BZ_PARAM_ERROR;
+
+   if (s->arr1 != NULL) BZFREE(s->arr1);
+   if (s->arr2 != NULL) BZFREE(s->arr2);
+   if (s->ftab != NULL) BZFREE(s->ftab);
+   BZFREE(strm->state);
+
+   strm->state = NULL;   
+
+   return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+/*--- Decompression stuff                         ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompressInit) 
+                     ( bz_stream* strm, 
+                       int        verbosity,
+                       int        small )
+{
+   DState* s;
+
+   if (!bz_config_ok()) return BZ_CONFIG_ERROR;
+
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   if (small != 0 && small != 1) return BZ_PARAM_ERROR;
+   if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR;
+
+   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
+   if (strm->bzfree == NULL) strm->bzfree = default_bzfree;
+
+   s = BZALLOC( sizeof(DState) );
+   if (s == NULL) return BZ_MEM_ERROR;
+   s->strm                  = strm;
+   strm->state              = s;
+   s->state                 = BZ_X_MAGIC_1;
+   s->bsLive                = 0;
+   s->bsBuff                = 0;
+   s->calculatedCombinedCRC = 0;
+   strm->total_in_lo32      = 0;
+   strm->total_in_hi32      = 0;
+   strm->total_out_lo32     = 0;
+   strm->total_out_hi32     = 0;
+   s->smallDecompress       = (Bool)small;
+   s->ll4                   = NULL;
+   s->ll16                  = NULL;
+   s->tt                    = NULL;
+   s->currBlockNo           = 0;
+   s->verbosity             = verbosity;
+
+   return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+/* Return  True iff data corruption is discovered.
+   Returns False if there is no problem.
+*/
+static
+Bool unRLE_obuf_to_output_FAST ( DState* s )
+{
+   UChar k1;
+
+   if (s->blockRandomised) {
+
+      while (True) {
+         /* try to finish existing run */
+         while (True) {
+            if (s->strm->avail_out == 0) return False;
+            if (s->state_out_len == 0) break;
+            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+            s->state_out_len--;
+            s->strm->next_out++;
+            s->strm->avail_out--;
+            s->strm->total_out_lo32++;
+            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+         }
+
+         /* can a new run be started? */
+         if (s->nblock_used == s->save_nblock+1) return False;
+               
+         /* Only caused by corrupt data stream? */
+         if (s->nblock_used > s->save_nblock+1)
+            return True;
+   
+         s->state_out_len = 1;
+         s->state_out_ch = s->k0;
+         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 2;
+         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 3;
+         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         s->state_out_len = ((Int32)k1) + 4;
+         BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; 
+         s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
+      }
+
+   } else {
+
+      /* restore */
+      UInt32        c_calculatedBlockCRC = s->calculatedBlockCRC;
+      UChar         c_state_out_ch       = s->state_out_ch;
+      Int32         c_state_out_len      = s->state_out_len;
+      Int32         c_nblock_used        = s->nblock_used;
+      Int32         c_k0                 = s->k0;
+      UInt32*       c_tt                 = s->tt;
+      UInt32        c_tPos               = s->tPos;
+      char*         cs_next_out          = s->strm->next_out;
+      unsigned int  cs_avail_out         = s->strm->avail_out;
+      /* end restore */
+
+      UInt32       avail_out_INIT = cs_avail_out;
+      Int32        s_save_nblockPP = s->save_nblock+1;
+      unsigned int total_out_lo32_old;
+
+      while (True) {
+
+         /* try to finish existing run */
+         if (c_state_out_len > 0) {
+            while (True) {
+               if (cs_avail_out == 0) goto return_notr;
+               if (c_state_out_len == 1) break;
+               *( (UChar*)(cs_next_out) ) = c_state_out_ch;
+               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
+               c_state_out_len--;
+               cs_next_out++;
+               cs_avail_out--;
+            }
+            s_state_out_len_eq_one:
+            {
+               if (cs_avail_out == 0) { 
+                  c_state_out_len = 1; goto return_notr;
+               };
+               *( (UChar*)(cs_next_out) ) = c_state_out_ch;
+               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
+               cs_next_out++;
+               cs_avail_out--;
+            }
+         }   
+         /* Only caused by corrupt data stream? */
+         if (c_nblock_used > s_save_nblockPP)
+            return True;
+
+         /* can a new run be started? */
+         if (c_nblock_used == s_save_nblockPP) {
+            c_state_out_len = 0; goto return_notr;
+         };   
+         c_state_out_ch = c_k0;
+         BZ_GET_FAST_C(k1); c_nblock_used++;
+         if (k1 != c_k0) { 
+            c_k0 = k1; goto s_state_out_len_eq_one; 
+         };
+         if (c_nblock_used == s_save_nblockPP) 
+            goto s_state_out_len_eq_one;
+   
+         c_state_out_len = 2;
+         BZ_GET_FAST_C(k1); c_nblock_used++;
+         if (c_nblock_used == s_save_nblockPP) continue;
+         if (k1 != c_k0) { c_k0 = k1; continue; };
+   
+         c_state_out_len = 3;
+         BZ_GET_FAST_C(k1); c_nblock_used++;
+         if (c_nblock_used == s_save_nblockPP) continue;
+         if (k1 != c_k0) { c_k0 = k1; continue; };
+   
+         BZ_GET_FAST_C(k1); c_nblock_used++;
+         c_state_out_len = ((Int32)k1) + 4;
+         BZ_GET_FAST_C(c_k0); c_nblock_used++;
+      }
+
+      return_notr:
+      total_out_lo32_old = s->strm->total_out_lo32;
+      s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out);
+      if (s->strm->total_out_lo32 < total_out_lo32_old)
+         s->strm->total_out_hi32++;
+
+      /* save */
+      s->calculatedBlockCRC = c_calculatedBlockCRC;
+      s->state_out_ch       = c_state_out_ch;
+      s->state_out_len      = c_state_out_len;
+      s->nblock_used        = c_nblock_used;
+      s->k0                 = c_k0;
+      s->tt                 = c_tt;
+      s->tPos               = c_tPos;
+      s->strm->next_out     = cs_next_out;
+      s->strm->avail_out    = cs_avail_out;
+      /* end save */
+   }
+   return False;
+}
+
+
+
+/*---------------------------------------------------*/
+/* Return  True iff data corruption is discovered.
+   Returns False if there is no problem.
+*/
+static
+Bool unRLE_obuf_to_output_SMALL ( DState* s )
+{
+   UChar k1;
+
+   if (s->blockRandomised) {
+
+      while (True) {
+         /* try to finish existing run */
+         while (True) {
+            if (s->strm->avail_out == 0) return False;
+            if (s->state_out_len == 0) break;
+            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+            s->state_out_len--;
+            s->strm->next_out++;
+            s->strm->avail_out--;
+            s->strm->total_out_lo32++;
+            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+         }
+   
+         /* can a new run be started? */
+         if (s->nblock_used == s->save_nblock+1) return False;
+
+         /* Only caused by corrupt data stream? */
+         if (s->nblock_used > s->save_nblock+1)
+            return True;
+   
+         s->state_out_len = 1;
+         s->state_out_ch = s->k0;
+         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 2;
+         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 3;
+         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         s->state_out_len = ((Int32)k1) + 4;
+         BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; 
+         s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
+      }
+
+   } else {
+
+      while (True) {
+         /* try to finish existing run */
+         while (True) {
+            if (s->strm->avail_out == 0) return False;
+            if (s->state_out_len == 0) break;
+            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+            s->state_out_len--;
+            s->strm->next_out++;
+            s->strm->avail_out--;
+            s->strm->total_out_lo32++;
+            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+         }
+   
+         /* can a new run be started? */
+         if (s->nblock_used == s->save_nblock+1) return False;
+
+         /* Only caused by corrupt data stream? */
+         if (s->nblock_used > s->save_nblock+1)
+            return True;
+   
+         s->state_out_len = 1;
+         s->state_out_ch = s->k0;
+         BZ_GET_SMALL(k1); s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 2;
+         BZ_GET_SMALL(k1); s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 3;
+         BZ_GET_SMALL(k1); s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         BZ_GET_SMALL(k1); s->nblock_used++;
+         s->state_out_len = ((Int32)k1) + 4;
+         BZ_GET_SMALL(s->k0); s->nblock_used++;
+      }
+
+   }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompress) ( bz_stream *strm )
+{
+   Bool    corrupt;
+   DState* s;
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   s = strm->state;
+   if (s == NULL) return BZ_PARAM_ERROR;
+   if (s->strm != strm) return BZ_PARAM_ERROR;
+
+   while (True) {
+      if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR;
+      if (s->state == BZ_X_OUTPUT) {
+         if (s->smallDecompress)
+            corrupt = unRLE_obuf_to_output_SMALL ( s ); else
+            corrupt = unRLE_obuf_to_output_FAST  ( s );
+         if (corrupt) return BZ_DATA_ERROR;
+         if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) {
+            BZ_FINALISE_CRC ( s->calculatedBlockCRC );
+            if (s->verbosity >= 3) 
+               VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, 
+                          s->calculatedBlockCRC );
+            if (s->verbosity >= 2) VPrintf0 ( "]" );
+            if (s->calculatedBlockCRC != s->storedBlockCRC)
+               return BZ_DATA_ERROR;
+            s->calculatedCombinedCRC 
+               = (s->calculatedCombinedCRC << 1) | 
+                    (s->calculatedCombinedCRC >> 31);
+            s->calculatedCombinedCRC ^= s->calculatedBlockCRC;
+            s->state = BZ_X_BLKHDR_1;
+         } else {
+            return BZ_OK;
+         }
+      }
+      if (s->state >= BZ_X_MAGIC_1) {
+         Int32 r = BZ2_decompress ( s );
+         if (r == BZ_STREAM_END) {
+            if (s->verbosity >= 3)
+               VPrintf2 ( "\n    combined CRCs: stored = 0x%08x, computed = 0x%08x", 
+                          s->storedCombinedCRC, s->calculatedCombinedCRC );
+            if (s->calculatedCombinedCRC != s->storedCombinedCRC)
+               return BZ_DATA_ERROR;
+            return r;
+         }
+         if (s->state != BZ_X_OUTPUT) return r;
+      }
+   }
+
+   AssertH ( 0, 6001 );
+
+   return 0;  /*NOTREACHED*/
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompressEnd)  ( bz_stream *strm )
+{
+   DState* s;
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   s = strm->state;
+   if (s == NULL) return BZ_PARAM_ERROR;
+   if (s->strm != strm) return BZ_PARAM_ERROR;
+
+   if (s->tt   != NULL) BZFREE(s->tt);
+   if (s->ll16 != NULL) BZFREE(s->ll16);
+   if (s->ll4  != NULL) BZFREE(s->ll4);
+
+   BZFREE(strm->state);
+   strm->state = NULL;
+
+   return BZ_OK;
+}
+
+
+#ifndef BZ_NO_STDIO
+/*---------------------------------------------------*/
+/*--- File I/O stuff                              ---*/
+/*---------------------------------------------------*/
+
+#define BZ_SETERR(eee)                    \
+{                                         \
+   if (bzerror != NULL) *bzerror = eee;   \
+   if (bzf != NULL) bzf->lastErr = eee;   \
+}
+
+typedef 
+   struct {
+      FILE*     handle;
+      Char      buf[BZ_MAX_UNUSED];
+      Int32     bufN;
+      Bool      writing;
+      bz_stream strm;
+      Int32     lastErr;
+      Bool      initialisedOk;
+   }
+   bzFile;
+
+
+/*---------------------------------------------*/
+static Bool myfeof ( FILE* f )
+{
+   Int32 c = fgetc ( f );
+   if (c == EOF) return True;
+   ungetc ( c, f );
+   return False;
+}
+
+
+/*---------------------------------------------------*/
+BZFILE* BZ_API(BZ2_bzWriteOpen) 
+                    ( int*  bzerror,      
+                      FILE* f, 
+                      int   blockSize100k, 
+                      int   verbosity,
+                      int   workFactor )
+{
+   Int32   ret;
+   bzFile* bzf = NULL;
+
+   BZ_SETERR(BZ_OK);
+
+   if (f == NULL ||
+       (blockSize100k < 1 || blockSize100k > 9) ||
+       (workFactor < 0 || workFactor > 250) ||
+       (verbosity < 0 || verbosity > 4))
+      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };
+
+   if (ferror(f))
+      { BZ_SETERR(BZ_IO_ERROR); return NULL; };
+
+   bzf = malloc ( sizeof(bzFile) );
+   if (bzf == NULL)
+      { BZ_SETERR(BZ_MEM_ERROR); return NULL; };
+
+   BZ_SETERR(BZ_OK);
+   bzf->initialisedOk = False;
+   bzf->bufN          = 0;
+   bzf->handle        = f;
+   bzf->writing       = True;
+   bzf->strm.bzalloc  = NULL;
+   bzf->strm.bzfree   = NULL;
+   bzf->strm.opaque   = NULL;
+
+   if (workFactor == 0) workFactor = 30;
+   ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, 
+                              verbosity, workFactor );
+   if (ret != BZ_OK)
+      { BZ_SETERR(ret); free(bzf); return NULL; };
+
+   bzf->strm.avail_in = 0;
+   bzf->initialisedOk = True;
+   return bzf;   
+}
+
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzWrite)
+             ( int*    bzerror, 
+               BZFILE* b, 
+               void*   buf, 
+               int     len )
+{
+   Int32 n, n2, ret;
+   bzFile* bzf = (bzFile*)b;
+
+   BZ_SETERR(BZ_OK);
+   if (bzf == NULL || buf == NULL || len < 0)
+      { BZ_SETERR(BZ_PARAM_ERROR); return; };
+   if (!(bzf->writing))
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+   if (ferror(bzf->handle))
+      { BZ_SETERR(BZ_IO_ERROR); return; };
+
+   if (len == 0)
+      { BZ_SETERR(BZ_OK); return; };
+
+   bzf->strm.avail_in = len;
+   bzf->strm.next_in  = buf;
+
+   while (True) {
+      bzf->strm.avail_out = BZ_MAX_UNUSED;
+      bzf->strm.next_out = bzf->buf;
+      ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN );
+      if (ret != BZ_RUN_OK)
+         { BZ_SETERR(ret); return; };
+
+      if (bzf->strm.avail_out < BZ_MAX_UNUSED) {
+         n = BZ_MAX_UNUSED - bzf->strm.avail_out;
+         n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), 
+                       n, bzf->handle );
+         if (n != n2 || ferror(bzf->handle))
+            { BZ_SETERR(BZ_IO_ERROR); return; };
+      }
+
+      if (bzf->strm.avail_in == 0)
+         { BZ_SETERR(BZ_OK); return; };
+   }
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzWriteClose)
+                  ( int*          bzerror, 
+                    BZFILE*       b, 
+                    int           abandon,
+                    unsigned int* nbytes_in,
+                    unsigned int* nbytes_out )
+{
+   BZ2_bzWriteClose64 ( bzerror, b, abandon, 
+                        nbytes_in, NULL, nbytes_out, NULL );
+}
+
+
+void BZ_API(BZ2_bzWriteClose64)
+                  ( int*          bzerror, 
+                    BZFILE*       b, 
+                    int           abandon,
+                    unsigned int* nbytes_in_lo32,
+                    unsigned int* nbytes_in_hi32,
+                    unsigned int* nbytes_out_lo32,
+                    unsigned int* nbytes_out_hi32 )
+{
+   Int32   n, n2, ret;
+   bzFile* bzf = (bzFile*)b;
+
+   if (bzf == NULL)
+      { BZ_SETERR(BZ_OK); return; };
+   if (!(bzf->writing))
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+   if (ferror(bzf->handle))
+      { BZ_SETERR(BZ_IO_ERROR); return; };
+
+   if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0;
+   if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0;
+   if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0;
+   if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0;
+
+   if ((!abandon) && bzf->lastErr == BZ_OK) {
+      while (True) {
+         bzf->strm.avail_out = BZ_MAX_UNUSED;
+         bzf->strm.next_out = bzf->buf;
+         ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH );
+         if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END)
+            { BZ_SETERR(ret); return; };
+
+         if (bzf->strm.avail_out < BZ_MAX_UNUSED) {
+            n = BZ_MAX_UNUSED - bzf->strm.avail_out;
+            n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), 
+                          n, bzf->handle );
+            if (n != n2 || ferror(bzf->handle))
+               { BZ_SETERR(BZ_IO_ERROR); return; };
+         }
+
+         if (ret == BZ_STREAM_END) break;
+      }
+   }
+
+   if ( !abandon && !ferror ( bzf->handle ) ) {
+      fflush ( bzf->handle );
+      if (ferror(bzf->handle))
+         { BZ_SETERR(BZ_IO_ERROR); return; };
+   }
+
+   if (nbytes_in_lo32 != NULL)
+      *nbytes_in_lo32 = bzf->strm.total_in_lo32;
+   if (nbytes_in_hi32 != NULL)
+      *nbytes_in_hi32 = bzf->strm.total_in_hi32;
+   if (nbytes_out_lo32 != NULL)
+      *nbytes_out_lo32 = bzf->strm.total_out_lo32;
+   if (nbytes_out_hi32 != NULL)
+      *nbytes_out_hi32 = bzf->strm.total_out_hi32;
+
+   BZ_SETERR(BZ_OK);
+   BZ2_bzCompressEnd ( &(bzf->strm) );
+   free ( bzf );
+}
+
+
+/*---------------------------------------------------*/
+BZFILE* BZ_API(BZ2_bzReadOpen) 
+                   ( int*  bzerror, 
+                     FILE* f, 
+                     int   verbosity,
+                     int   small,
+                     void* unused,
+                     int   nUnused )
+{
+   bzFile* bzf = NULL;
+   int     ret;
+
+   BZ_SETERR(BZ_OK);
+
+   if (f == NULL || 
+       (small != 0 && small != 1) ||
+       (verbosity < 0 || verbosity > 4) ||
+       (unused == NULL && nUnused != 0) ||
+       (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED)))
+      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };
+
+   if (ferror(f))
+      { BZ_SETERR(BZ_IO_ERROR); return NULL; };
+
+   bzf = malloc ( sizeof(bzFile) );
+   if (bzf == NULL) 
+      { BZ_SETERR(BZ_MEM_ERROR); return NULL; };
+
+   BZ_SETERR(BZ_OK);
+
+   bzf->initialisedOk = False;
+   bzf->handle        = f;
+   bzf->bufN          = 0;
+   bzf->writing       = False;
+   bzf->strm.bzalloc  = NULL;
+   bzf->strm.bzfree   = NULL;
+   bzf->strm.opaque   = NULL;
+   
+   while (nUnused > 0) {
+      bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++;
+      unused = ((void*)( 1 + ((UChar*)(unused))  ));
+      nUnused--;
+   }
+
+   ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small );
+   if (ret != BZ_OK)
+      { BZ_SETERR(ret); free(bzf); return NULL; };
+
+   bzf->strm.avail_in = bzf->bufN;
+   bzf->strm.next_in  = bzf->buf;
+
+   bzf->initialisedOk = True;
+   return bzf;   
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b )
+{
+   bzFile* bzf = (bzFile*)b;
+
+   BZ_SETERR(BZ_OK);
+   if (bzf == NULL)
+      { BZ_SETERR(BZ_OK); return; };
+
+   if (bzf->writing)
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+
+   if (bzf->initialisedOk)
+      (void)BZ2_bzDecompressEnd ( &(bzf->strm) );
+   free ( bzf );
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzRead) 
+           ( int*    bzerror, 
+             BZFILE* b, 
+             void*   buf, 
+             int     len )
+{
+   Int32   n, ret;
+   bzFile* bzf = (bzFile*)b;
+
+   BZ_SETERR(BZ_OK);
+
+   if (bzf == NULL || buf == NULL || len < 0)
+      { BZ_SETERR(BZ_PARAM_ERROR); return 0; };
+
+   if (bzf->writing)
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; };
+
+   if (len == 0)
+      { BZ_SETERR(BZ_OK); return 0; };
+
+   bzf->strm.avail_out = len;
+   bzf->strm.next_out = buf;
+
+   while (True) {
+
+      if (ferror(bzf->handle)) 
+         { BZ_SETERR(BZ_IO_ERROR); return 0; };
+
+      if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) {
+         n = fread ( bzf->buf, sizeof(UChar), 
+                     BZ_MAX_UNUSED, bzf->handle );
+         if (ferror(bzf->handle))
+            { BZ_SETERR(BZ_IO_ERROR); return 0; };
+         bzf->bufN = n;
+         bzf->strm.avail_in = bzf->bufN;
+         bzf->strm.next_in = bzf->buf;
+      }
+
+      ret = BZ2_bzDecompress ( &(bzf->strm) );
+
+      if (ret != BZ_OK && ret != BZ_STREAM_END)
+         { BZ_SETERR(ret); return 0; };
+
+      if (ret == BZ_OK && myfeof(bzf->handle) && 
+          bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0)
+         { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; };
+
+      if (ret == BZ_STREAM_END)
+         { BZ_SETERR(BZ_STREAM_END);
+           return len - bzf->strm.avail_out; };
+      if (bzf->strm.avail_out == 0)
+         { BZ_SETERR(BZ_OK); return len; };
+      
+   }
+
+   return 0; /*not reached*/
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzReadGetUnused) 
+                     ( int*    bzerror, 
+                       BZFILE* b, 
+                       void**  unused, 
+                       int*    nUnused )
+{
+   bzFile* bzf = (bzFile*)b;
+   if (bzf == NULL)
+      { BZ_SETERR(BZ_PARAM_ERROR); return; };
+   if (bzf->lastErr != BZ_STREAM_END)
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+   if (unused == NULL || nUnused == NULL)
+      { BZ_SETERR(BZ_PARAM_ERROR); return; };
+
+   BZ_SETERR(BZ_OK);
+   *nUnused = bzf->strm.avail_in;
+   *unused = bzf->strm.next_in;
+}
+#endif
+
+
+/*---------------------------------------------------*/
+/*--- Misc convenience stuff                      ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzBuffToBuffCompress) 
+                         ( char*         dest, 
+                           unsigned int* destLen,
+                           char*         source, 
+                           unsigned int  sourceLen,
+                           int           blockSize100k, 
+                           int           verbosity, 
+                           int           workFactor )
+{
+   bz_stream strm;
+   int ret;
+
+   if (dest == NULL || destLen == NULL || 
+       source == NULL ||
+       blockSize100k < 1 || blockSize100k > 9 ||
+       verbosity < 0 || verbosity > 4 ||
+       workFactor < 0 || workFactor > 250) 
+      return BZ_PARAM_ERROR;
+
+   if (workFactor == 0) workFactor = 30;
+   strm.bzalloc = NULL;
+   strm.bzfree = NULL;
+   strm.opaque = NULL;
+   ret = BZ2_bzCompressInit ( &strm, blockSize100k, 
+                              verbosity, workFactor );
+   if (ret != BZ_OK) return ret;
+
+   strm.next_in = source;
+   strm.next_out = dest;
+   strm.avail_in = sourceLen;
+   strm.avail_out = *destLen;
+
+   ret = BZ2_bzCompress ( &strm, BZ_FINISH );
+   if (ret == BZ_FINISH_OK) goto output_overflow;
+   if (ret != BZ_STREAM_END) goto errhandler;
+
+   /* normal termination */
+   *destLen -= strm.avail_out;   
+   BZ2_bzCompressEnd ( &strm );
+   return BZ_OK;
+
+   output_overflow:
+   BZ2_bzCompressEnd ( &strm );
+   return BZ_OUTBUFF_FULL;
+
+   errhandler:
+   BZ2_bzCompressEnd ( &strm );
+   return ret;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzBuffToBuffDecompress) 
+                           ( char*         dest, 
+                             unsigned int* destLen,
+                             char*         source, 
+                             unsigned int  sourceLen,
+                             int           small,
+                             int           verbosity )
+{
+   bz_stream strm;
+   int ret;
+
+   if (dest == NULL || destLen == NULL || 
+       source == NULL ||
+       (small != 0 && small != 1) ||
+       verbosity < 0 || verbosity > 4) 
+          return BZ_PARAM_ERROR;
+
+   strm.bzalloc = NULL;
+   strm.bzfree = NULL;
+   strm.opaque = NULL;
+   ret = BZ2_bzDecompressInit ( &strm, verbosity, small );
+   if (ret != BZ_OK) return ret;
+
+   strm.next_in = source;
+   strm.next_out = dest;
+   strm.avail_in = sourceLen;
+   strm.avail_out = *destLen;
+
+   ret = BZ2_bzDecompress ( &strm );
+   if (ret == BZ_OK) goto output_overflow_or_eof;
+   if (ret != BZ_STREAM_END) goto errhandler;
+
+   /* normal termination */
+   *destLen -= strm.avail_out;
+   BZ2_bzDecompressEnd ( &strm );
+   return BZ_OK;
+
+   output_overflow_or_eof:
+   if (strm.avail_out > 0) {
+      BZ2_bzDecompressEnd ( &strm );
+      return BZ_UNEXPECTED_EOF;
+   } else {
+      BZ2_bzDecompressEnd ( &strm );
+      return BZ_OUTBUFF_FULL;
+   };      
+
+   errhandler:
+   BZ2_bzDecompressEnd ( &strm );
+   return ret; 
+}
+
+
+/*---------------------------------------------------*/
+/*--
+   Code contributed by Yoshioka Tsuneo
+   (QWF00133@niftyserve.or.jp/tsuneo-y@is.aist-nara.ac.jp),
+   to support better zlib compatibility.
+   This code is not _officially_ part of libbzip2 (yet);
+   I haven't tested it, documented it, or considered the
+   threading-safeness of it.
+   If this code breaks, please contact both Yoshioka and me.
+--*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+/*--
+   return version like "0.9.0c".
+--*/
+const char * BZ_API(BZ2_bzlibVersion)(void)
+{
+   return BZ_VERSION;
+}
+
+
+#ifndef BZ_NO_STDIO
+/*---------------------------------------------------*/
+
+#if defined(_WIN32) || defined(OS2) || defined(MSDOS)
+#   include <fcntl.h>
+#   include <io.h>
+#   define SET_BINARY_MODE(file) setmode(fileno(file),O_BINARY)
+#else
+#   define SET_BINARY_MODE(file)
+#endif
+static
+BZFILE * bzopen_or_bzdopen
+               ( const char *path,   /* no use when bzdopen */
+                 int fd,             /* no use when bzdopen */
+                 const char *mode,
+                 int open_mode)      /* bzopen: 0, bzdopen:1 */
+{
+   int    bzerr;
+   char   unused[BZ_MAX_UNUSED];
+   int    blockSize100k = 9;
+   int    writing       = 0;
+   char   mode2[10]     = "";
+   FILE   *fp           = NULL;
+   BZFILE *bzfp         = NULL;
+   int    verbosity     = 0;
+   int    workFactor    = 30;
+   int    smallMode     = 0;
+   int    nUnused       = 0; 
+
+   if (mode == NULL) return NULL;
+   while (*mode) {
+      switch (*mode) {
+      case 'r':
+         writing = 0; break;
+      case 'w':
+         writing = 1; break;
+      case 's':
+         smallMode = 1; break;
+      default:
+         if (isdigit((int)(*mode))) {
+            blockSize100k = *mode-BZ_HDR_0;
+         }
+      }
+      mode++;
+   }
+   strcat(mode2, writing ? "w" : "r" );
+   strcat(mode2,"b");   /* binary mode */
+
+   if (open_mode==0) {
+      if (path==NULL || strcmp(path,"")==0) {
+        fp = (writing ? stdout : stdin);
+        SET_BINARY_MODE(fp);
+      } else {
+        fp = fopen(path,mode2);
+      }
+   } else {
+#ifdef BZ_STRICT_ANSI
+      fp = NULL;
+#else
+      fp = fdopen(fd,mode2);
+#endif
+   }
+   if (fp == NULL) return NULL;
+
+   if (writing) {
+      /* Guard against total chaos and anarchy -- JRS */
+      if (blockSize100k < 1) blockSize100k = 1;
+      if (blockSize100k > 9) blockSize100k = 9; 
+      bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k,
+                             verbosity,workFactor);
+   } else {
+      bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode,
+                            unused,nUnused);
+   }
+   if (bzfp == NULL) {
+      if (fp != stdin && fp != stdout) fclose(fp);
+      return NULL;
+   }
+   return bzfp;
+}
+
+
+/*---------------------------------------------------*/
+/*--
+   open file for read or write.
+      ex) bzopen("file","w9")
+      case path="" or NULL => use stdin or stdout.
+--*/
+BZFILE * BZ_API(BZ2_bzopen)
+               ( const char *path,
+                 const char *mode )
+{
+   return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0);
+}
+
+
+/*---------------------------------------------------*/
+BZFILE * BZ_API(BZ2_bzdopen)
+               ( int fd,
+                 const char *mode )
+{
+   return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1);
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len )
+{
+   int bzerr, nread;
+   if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0;
+   nread = BZ2_bzRead(&bzerr,b,buf,len);
+   if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) {
+      return nread;
+   } else {
+      return -1;
+   }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len )
+{
+   int bzerr;
+
+   BZ2_bzWrite(&bzerr,b,buf,len);
+   if(bzerr == BZ_OK){
+      return len;
+   }else{
+      return -1;
+   }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzflush) (BZFILE *b)
+{
+   /* do nothing now... */
+   return 0;
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzclose) (BZFILE* b)
+{
+   int bzerr;
+   FILE *fp = ((bzFile *)b)->handle;
+   
+   if (b==NULL) {return;}
+   if(((bzFile*)b)->writing){
+      BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL);
+      if(bzerr != BZ_OK){
+         BZ2_bzWriteClose(NULL,b,1,NULL,NULL);
+      }
+   }else{
+      BZ2_bzReadClose(&bzerr,b);
+   }
+   if(fp!=stdin && fp!=stdout){
+      fclose(fp);
+   }
+}
+
+
+/*---------------------------------------------------*/
+/*--
+   return last error code 
+--*/
+static char *bzerrorstrings[] = {
+       "OK"
+      ,"SEQUENCE_ERROR"
+      ,"PARAM_ERROR"
+      ,"MEM_ERROR"
+      ,"DATA_ERROR"
+      ,"DATA_ERROR_MAGIC"
+      ,"IO_ERROR"
+      ,"UNEXPECTED_EOF"
+      ,"OUTBUFF_FULL"
+      ,"CONFIG_ERROR"
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+};
+
+
+const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum)
+{
+   int err = ((bzFile *)b)->lastErr;
+
+   if(err>0) err = 0;
+   *errnum = err;
+   return bzerrorstrings[err*-1];
+}
+#endif
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                           bzlib.c ---*/
+/*-------------------------------------------------------------*/
+
+
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+
+
+/* A test program written to test robustness to decompression of
+   corrupted data.  Usage is 
+       unzcrash filename
+   and the program will read the specified file, compress it (in memory),
+   and then repeatedly decompress it, each time with a different bit of
+   the compressed data inverted, so as to test all possible one-bit errors.
+   This should not cause any invalid memory accesses.  If it does, 
+   I want to know about it!
+
+   p.s.  As you can see from the above description, the process is
+   incredibly slow.  A file of size eg 5KB will cause it to run for
+   many hours.
+*/
+
+//#include <stdio.h>
+//#include <assert.h>
+//#include "bzlib.h"
+
+#define M_BLOCK 1000000
+
+
+#define M_BLOCK_OUT (M_BLOCK + 1000000)
+ char inbuf[M_BLOCK];
+ char outbuf[M_BLOCK_OUT];
+ char zbuf[M_BLOCK + 600 + (M_BLOCK / 100)];
+
+int nIn;
+unsigned int nOut;
+unsigned int nZ;
+
+#if 0
+static char *bzerrorstrings[] = {
+       "OK"
+      ,"SEQUENCE_ERROR"
+      ,"PARAM_ERROR"
+      ,"MEM_ERROR"
+      ,"DATA_ERROR"
+      ,"DATA_ERROR_MAGIC"
+      ,"IO_ERROR"
+      ,"UNEXPECTED_EOF"
+      ,"OUTBUFF_FULL"
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+};
+#endif
+
+void flip_bit ( int bit )
+{
+   int byteno = bit / 8;
+   int bitno  = bit % 8;
+   UChar mask = 1 << bitno;
+   //fprintf ( stderr, "(byte %d  bit %d  mask %d)",
+   //          byteno, bitno, (int)mask );
+   zbuf[byteno] ^= mask;
+}
+
+void set_inbuf ( void )
+{
+  inbuf[0] = 0;
+  my_strcat(inbuf, "At her sixtieth birthday party, Margaret Thatcher ");
+  my_strcat(inbuf, "blew on the cake to light the candles.\n");
+  my_strcat(inbuf, "This program, bzip2, the associated library libbzip2, and all\n");
+  my_strcat(inbuf, "documentation, are copyright (C) 1996-2004 Julian R Seward.  All\n");
+  my_strcat(inbuf, "rights reserved.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "Redistribution and use in source and binary forms, with or without\n");
+  my_strcat(inbuf, "modification, are permitted provided that the following conditions\n");
+  my_strcat(inbuf, "are met:\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "1. Redistributions of source code must retain the above copyright\n");
+  my_strcat(inbuf, "   notice, this list of conditions and the following disclaimer.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "2. The origin of this software must not be misrepresented; you must\n");
+  my_strcat(inbuf, "   not claim that you wrote the original software.  If you use this\n");
+  my_strcat(inbuf, "   software in a product, an acknowledgment in the product\n");
+  my_strcat(inbuf, "   documentation would be appreciated but is not required.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "3. Altered source versions must be plainly marked as such, and must\n");
+  my_strcat(inbuf, "   not be misrepresented as being the original software.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "4. The name of the author may not be used to endorse or promote\n");
+  my_strcat(inbuf, "   products derived from this software without specific prior written\n");
+  my_strcat(inbuf, "   permission.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS\n");
+  my_strcat(inbuf, "OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n");
+  my_strcat(inbuf, "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n");
+  my_strcat(inbuf, "ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n");
+  my_strcat(inbuf, "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n");
+  my_strcat(inbuf, "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\n");
+  my_strcat(inbuf, "GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n");
+  my_strcat(inbuf, "INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n");
+  my_strcat(inbuf, "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n");
+  my_strcat(inbuf, "NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n");
+  my_strcat(inbuf, "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "ababababababababababababababababababababababababababababababab");
+  my_strcat(inbuf, "		    GNU GENERAL PUBLIC LICENSE\n");
+  my_strcat(inbuf, "		       Version 2, June 1991\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, " Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n");
+  my_strcat(inbuf, "     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n");
+  my_strcat(inbuf, " Everyone is permitted to copy and distribute verbatim copies\n");
+  my_strcat(inbuf, " of this license document, but changing it is not allowed.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "			    Preamble\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  The licenses for most software are designed to take away your\n");
+  my_strcat(inbuf, "freedom to share and change it.  By contrast, the GNU General Public\n");
+  my_strcat(inbuf, "License is intended to guarantee your freedom to share and change free\n");
+  my_strcat(inbuf, "software--to make sure the software is free for all its users.  This\n");
+  my_strcat(inbuf, "General Public License applies to most of the Free Software\n");
+  my_strcat(inbuf, "Foundation's software and to any other program whose authors commit to\n");
+  my_strcat(inbuf, "using it.  (Some other Free Software Foundation software is covered by\n");
+  my_strcat(inbuf, "the GNU Library General Public License instead.)  You can apply it to\n");
+  my_strcat(inbuf, "your programs, too.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  When we speak of free software, we are referring to freedom, not\n");
+  my_strcat(inbuf, "price.  Our General Public Licenses are designed to make sure that you\n");
+  my_strcat(inbuf, "have the freedom to distribute copies of free software (and charge for\n");
+  my_strcat(inbuf, "this service if you wish), that you receive source code or can get it\n");
+  my_strcat(inbuf, "if you want it, that you can change the software or use pieces of it\n");
+  my_strcat(inbuf, "in new free programs; and that you know you can do these things.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  To protect your rights, we need to make restrictions that forbid\n");
+  my_strcat(inbuf, "anyone to deny you these rights or to ask you to surrender the rights.\n");
+  my_strcat(inbuf, "These restrictions translate to certain responsibilities for you if you\n");
+  my_strcat(inbuf, "distribute copies of the software, or if you modify it.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  For example, if you distribute copies of such a program, whether\n");
+  my_strcat(inbuf, "gratis or for a fee, you must give the recipients all the rights that\n");
+  my_strcat(inbuf, "you have.  You must make sure that they, too, receive or can get the\n");
+  my_strcat(inbuf, "source code.  And you must show them these terms so they know their\n");
+  my_strcat(inbuf, "rights.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  We protect your rights with two steps: (1) copyright the software, and\n");
+  my_strcat(inbuf, "(2) offer you this license which gives you legal permission to copy,\n");
+  my_strcat(inbuf, "distribute and/or modify the software.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  Also, for each author's protection and ours, we want to make certain\n");
+  my_strcat(inbuf, "that everyone understands that there is no warranty for this free\n");
+  my_strcat(inbuf, "software.  If the software is modified by someone else and passed on, we\n");
+  my_strcat(inbuf, "want its recipients to know that what they have is not the original, so\n");
+  my_strcat(inbuf, "that any problems introduced by others will not reflect on the original\n");
+  my_strcat(inbuf, "authors' reputations.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  Finally, any free program is threatened constantly by software\n");
+  my_strcat(inbuf, "patents.  We wish to avoid the danger that redistributors of a free\n");
+  my_strcat(inbuf, "program will individually obtain patent licenses, in effect making the\n");
+  my_strcat(inbuf, "program proprietary.  To prevent this, we have made it clear that any\n");
+  my_strcat(inbuf, "patent must be licensed for everyone's free use or not licensed at all.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  The precise terms and conditions for copying, distribution and\n");
+  my_strcat(inbuf, "modification follow.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "		    GNU GENERAL PUBLIC LICENSE\n");
+  my_strcat(inbuf, "   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  0. This License applies to any program or other work which contains\n");
+  my_strcat(inbuf, "a notice placed by the copyright holder saying it may be distributed\n");
+  my_strcat(inbuf, "under the terms of this General Public License.  The Program, below,\n");
+  my_strcat(inbuf, "refers to any such program or work, and a work based on the Program\n");
+  my_strcat(inbuf, "means either the Program or any derivative work under copyright law:\n");
+  my_strcat(inbuf, "that is to say, a work containing the Program or a portion of it,\n");
+  my_strcat(inbuf, "either verbatim or with modifications and/or translated into another\n");
+  my_strcat(inbuf, "language.  (Hereinafter, translation is included without limitation in\n");
+  my_strcat(inbuf, "the term modification.)  Each licensee is addressed as you.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "Activities other than copying, distribution and modification are not\n");
+  my_strcat(inbuf, "covered by this License; they are outside its scope.  The act of\n");
+  my_strcat(inbuf, "running the Program is not restricted, and the output from the Program\n");
+  my_strcat(inbuf, "is covered only if its contents constitute a work based on the\n");
+  my_strcat(inbuf, "Program (independent of having been made by running the Program).\n");
+  my_strcat(inbuf, "Whether that is true depends on what the Program does.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  1. You may copy and distribute verbatim copies of the Program's\n");
+  my_strcat(inbuf, "source code as you receive it, in any medium, provided that you\n");
+  my_strcat(inbuf, "conspicuously and appropriately publish on each copy an appropriate\n");
+  my_strcat(inbuf, "copyright notice and disclaimer of warranty; keep intact all the\n");
+  my_strcat(inbuf, "notices that refer to this License and to the absence of any warranty;\n");
+  my_strcat(inbuf, "and give any other recipients of the Program a copy of this License\n");
+  my_strcat(inbuf, "along with the Program.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "You may charge a fee for the physical act of transferring a copy, and\n");
+  my_strcat(inbuf, "you may at your option offer warranty protection in exchange for a fee.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  2. You may modify your copy or copies of the Program or any portion\n");
+  my_strcat(inbuf, "of it, thus forming a work based on the Program, and copy and\n");
+  my_strcat(inbuf, "distribute such modifications or work under the terms of Section 1\n");
+  my_strcat(inbuf, "above, provided that you also meet all of these conditions:\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    a) You must cause the modified files to carry prominent notices\n");
+  my_strcat(inbuf, "    stating that you changed the files and the date of any change.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    b) You must cause any work that you distribute or publish, that in\n");
+  my_strcat(inbuf, "    whole or in part contains or is derived from the Program or any\n");
+  my_strcat(inbuf, "    part thereof, to be licensed as a whole at no charge to all third\n");
+  my_strcat(inbuf, "    parties under the terms of this License.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    c) If the modified program normally reads commands interactively\n");
+  my_strcat(inbuf, "    when run, you must cause it, when started running for such\n");
+  my_strcat(inbuf, "    interactive use in the most ordinary way, to print or display an\n");
+  my_strcat(inbuf, "    announcement including an appropriate copyright notice and a\n");
+  my_strcat(inbuf, "    notice that there is no warranty (or else, saying that you provide\n");
+  my_strcat(inbuf, "    a warranty) and that users may redistribute the program under\n");
+  my_strcat(inbuf, "    these conditions, and telling the user how to view a copy of this\n");
+  my_strcat(inbuf, "    License.  (Exception: if the Program itself is interactive but\n");
+  my_strcat(inbuf, "    does not normally print such an announcement, your work based on\n");
+  my_strcat(inbuf, "    the Program is not required to print an announcement.)\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "These requirements apply to the modified work as a whole.  If\n");
+  my_strcat(inbuf, "identifiable sections of that work are not derived from the Program,\n");
+  my_strcat(inbuf, "and can be reasonably considered independent and separate works in\n");
+  my_strcat(inbuf, "themselves, then this License, and its terms, do not apply to those\n");
+  my_strcat(inbuf, "sections when you distribute them as separate works.  But when you\n");
+  my_strcat(inbuf, "distribute the same sections as part of a whole which is a work based\n");
+  my_strcat(inbuf, "on the Program, the distribution of the whole must be on the terms of\n");
+  my_strcat(inbuf, "this License, whose permissions for other licensees extend to the\n");
+  my_strcat(inbuf, "entire whole, and thus to each and every part regardless of who wrote it.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "Thus, it is not the intent of this section to claim rights or contest\n");
+  my_strcat(inbuf, "your rights to work written entirely by you; rather, the intent is to\n");
+  my_strcat(inbuf, "exercise the right to control the distribution of derivative or\n");
+  my_strcat(inbuf, "collective works based on the Program.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "In addition, mere aggregation of another work not based on the Program\n");
+  my_strcat(inbuf, "with the Program (or with a work based on the Program) on a volume of\n");
+  my_strcat(inbuf, "a storage or distribution medium does not bring the other work under\n");
+  my_strcat(inbuf, "the scope of this License.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  3. You may copy and distribute the Program (or a work based on it,\n");
+  my_strcat(inbuf, "under Section 2) in object code or executable form under the terms of\n");
+  my_strcat(inbuf, "Sections 1 and 2 above provided that you also do one of the following:\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    a) Accompany it with the complete corresponding machine-readable\n");
+  my_strcat(inbuf, "    source code, which must be distributed under the terms of Sections\n");
+  my_strcat(inbuf, "    1 and 2 above on a medium customarily used for software interchange; or,\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    b) Accompany it with a written offer, valid for at least three\n");
+  my_strcat(inbuf, "    years, to give any third party, for a charge no more than your\n");
+  my_strcat(inbuf, "    cost of physically performing source distribution, a complete\n");
+  my_strcat(inbuf, "    machine-readable copy of the corresponding source code, to be\n");
+  my_strcat(inbuf, "    distributed under the terms of Sections 1 and 2 above on a medium\n");
+  my_strcat(inbuf, "    customarily used for software interchange; or,\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    c) Accompany it with the information you received as to the offer\n");
+  my_strcat(inbuf, "    to distribute corresponding source code.  (This alternative is\n");
+  my_strcat(inbuf, "    allowed only for noncommercial distribution and only if you\n");
+  my_strcat(inbuf, "    received the program in object code or executable form with such\n");
+  my_strcat(inbuf, "    an offer, in accord with Subsection b above.)\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "The source code for a work means the preferred form of the work for\n");
+  my_strcat(inbuf, "making modifications to it.  For an executable work, complete source\n");
+  my_strcat(inbuf, "code means all the source code for all modules it contains, plus any\n");
+  my_strcat(inbuf, "associated interface definition files, plus the scripts used to\n");
+  my_strcat(inbuf, "control compilation and installation of the executable.  However, as a\n");
+  my_strcat(inbuf, "special exception, the source code distributed need not include\n");
+  my_strcat(inbuf, "anything that is normally distributed (in either source or binary\n");
+  my_strcat(inbuf, "form) with the major components (compiler, kernel, and so on) of the\n");
+  my_strcat(inbuf, "operating system on which the executable runs, unless that component\n");
+  my_strcat(inbuf, "itself accompanies the executable.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "If distribution of executable or object code is made by offering\n");
+  my_strcat(inbuf, "access to copy from a designated place, then offering equivalent\n");
+  my_strcat(inbuf, "access to copy the source code from the same place counts as\n");
+  my_strcat(inbuf, "distribution of the source code, even though third parties are not\n");
+  my_strcat(inbuf, "compelled to copy the source along with the object code.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  4. You may not copy, modify, sublicense, or distribute the Program\n");
+  my_strcat(inbuf, "except as expressly provided under this License.  Any attempt\n");
+  my_strcat(inbuf, "otherwise to copy, modify, sublicense or distribute the Program is\n");
+  my_strcat(inbuf, "void, and will automatically terminate your rights under this License.\n");
+  my_strcat(inbuf, "However, parties who have received copies, or rights, from you under\n");
+  my_strcat(inbuf, "this License will not have their licenses terminated so long as such\n");
+  my_strcat(inbuf, "parties remain in full compliance.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  5. You are not required to accept this License, since you have not\n");
+  my_strcat(inbuf, "signed it.  However, nothing else grants you permission to modify or\n");
+  my_strcat(inbuf, "distribute the Program or its derivative works.  These actions are\n");
+  my_strcat(inbuf, "prohibited by law if you do not accept this License.  Therefore, by\n");
+  my_strcat(inbuf, "modifying or distributing the Program (or any work based on the\n");
+  my_strcat(inbuf, "Program), you indicate your acceptance of this License to do so, and\n");
+  my_strcat(inbuf, "all its terms and conditions for copying, distributing or modifying\n");
+  my_strcat(inbuf, "the Program or works based on it.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  6. Each time you redistribute the Program (or any work based on the\n");
+  my_strcat(inbuf, "Program), the recipient automatically receives a license from the\n");
+  my_strcat(inbuf, "original licensor to copy, distribute or modify the Program subject to\n");
+  my_strcat(inbuf, "these terms and conditions.  You may not impose any further\n");
+  my_strcat(inbuf, "restrictions on the recipients' exercise of the rights granted herein.\n");
+  my_strcat(inbuf, "You are not responsible for enforcing compliance by third parties to\n");
+  my_strcat(inbuf, "this License.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  7. If, as a consequence of a court judgment or allegation of patent\n");
+  my_strcat(inbuf, "infringement or for any other reason (not limited to patent issues),\n");
+  my_strcat(inbuf, "conditions are imposed on you (whether by court order, agreement or\n");
+  my_strcat(inbuf, "otherwise) that contradict the conditions of this License, they do not\n");
+  my_strcat(inbuf, "excuse you from the conditions of this License.  If you cannot\n");
+  my_strcat(inbuf, "distribute so as to satisfy simultaneously your obligations under this\n");
+  my_strcat(inbuf, "License and any other pertinent obligations, then as a consequence you\n");
+  my_strcat(inbuf, "may not distribute the Program at all.  For example, if a patent\n");
+  my_strcat(inbuf, "license would not permit royalty-free redistribution of the Program by\n");
+  my_strcat(inbuf, "all those who receive copies directly or indirectly through you, then\n");
+  my_strcat(inbuf, "the only way you could satisfy both it and this License would be to\n");
+  my_strcat(inbuf, "refrain entirely from distribution of the Program.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "If any portion of this section is held invalid or unenforceable under\n");
+  my_strcat(inbuf, "any particular circumstance, the balance of the section is intended to\n");
+  my_strcat(inbuf, "apply and the section as a whole is intended to apply in other\n");
+  my_strcat(inbuf, "circumstances.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "It is not the purpose of this section to induce you to infringe any\n");
+  my_strcat(inbuf, "patents or other property right claims or to contest validity of any\n");
+  my_strcat(inbuf, "such claims; this section has the sole purpose of protecting the\n");
+  my_strcat(inbuf, "integrity of the free software distribution system, which is\n");
+  my_strcat(inbuf, "implemented by public license practices.  Many people have made\n");
+  my_strcat(inbuf, "generous contributions to the wide range of software distributed\n");
+  my_strcat(inbuf, "through that system in reliance on consistent application of that\n");
+  my_strcat(inbuf, "system; it is up to the author/donor to decide if he or she is willing\n");
+  my_strcat(inbuf, "to distribute software through any other system and a licensee cannot\n");
+  my_strcat(inbuf, "impose that choice.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "This section is intended to make thoroughly clear what is believed to\n");
+  my_strcat(inbuf, "be a consequence of the rest of this License.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  8. If the distribution and/or use of the Program is restricted in\n");
+  my_strcat(inbuf, "certain countries either by patents or by copyrighted interfaces, the\n");
+  my_strcat(inbuf, "original copyright holder who places the Program under this License\n");
+  my_strcat(inbuf, "may add an explicit geographical distribution limitation excluding\n");
+  my_strcat(inbuf, "those countries, so that distribution is permitted only in or among\n");
+  my_strcat(inbuf, "countries not thus excluded.  In such case, this License incorporates\n");
+  my_strcat(inbuf, "the limitation as if written in the body of this License.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  9. The Free Software Foundation may publish revised and/or new versions\n");
+  my_strcat(inbuf, "of the General Public License from time to time.  Such new versions will\n");
+  my_strcat(inbuf, "be similar in spirit to the present version, but may differ in detail to\n");
+  my_strcat(inbuf, "address new problems or concerns.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "Each version is given a distinguishing version number.  If the Program\n");
+  my_strcat(inbuf, "specifies a version number of this License which applies to it and any\n");
+  my_strcat(inbuf, "later version, you have the option of following the terms and conditions\n");
+  my_strcat(inbuf, "either of that version or of any later version published by the Free\n");
+  my_strcat(inbuf, "Software Foundation.  If the Program does not specify a version number of\n");
+  my_strcat(inbuf, "this License, you may choose any version ever published by the Free Software\n");
+  my_strcat(inbuf, "Foundation.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  10. If you wish to incorporate parts of the Program into other free\n");
+  my_strcat(inbuf, "programs whose distribution conditions are different, write to the author\n");
+  my_strcat(inbuf, "to ask for permission.  For software which is copyrighted by the Free\n");
+  my_strcat(inbuf, "Software Foundation, write to the Free Software Foundation; we sometimes\n");
+  my_strcat(inbuf, "make exceptions for this.  Our decision will be guided by the two goals\n");
+  my_strcat(inbuf, "of preserving the free status of all derivatives of our free software and\n");
+  my_strcat(inbuf, "of promoting the sharing and reuse of software generally.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "			    NO WARRANTY\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n");
+  my_strcat(inbuf, "FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\n");
+  my_strcat(inbuf, "OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n");
+  my_strcat(inbuf, "PROVIDE THE PROGRAM AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n");
+  my_strcat(inbuf, "OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n");
+  my_strcat(inbuf, "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\n");
+  my_strcat(inbuf, "TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\n");
+  my_strcat(inbuf, "PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n");
+  my_strcat(inbuf, "REPAIR OR CORRECTION.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n");
+  my_strcat(inbuf, "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n");
+  my_strcat(inbuf, "REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n");
+  my_strcat(inbuf, "INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n");
+  my_strcat(inbuf, "OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n");
+  my_strcat(inbuf, "TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n");
+  my_strcat(inbuf, "YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n");
+  my_strcat(inbuf, "PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n");
+  my_strcat(inbuf, "POSSIBILITY OF SUCH DAMAGES.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "		     END OF TERMS AND CONDITIONS\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "	    How to Apply These Terms to Your New Programs\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  If you develop a new program, and you want it to be of the greatest\n");
+  my_strcat(inbuf, "possible use to the public, the best way to achieve this is to make it\n");
+  my_strcat(inbuf, "free software which everyone can redistribute and change under these terms.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  To do so, attach the following notices to the program.  It is safest\n");
+  my_strcat(inbuf, "to attach them to the start of each source file to most effectively\n");
+  my_strcat(inbuf, "convey the exclusion of warranty; and each file should have at least\n");
+  my_strcat(inbuf, "the copyright line and a pointer to where the full notice is found.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    <one line to give the program's name and a brief idea of what it does.>\n");
+  my_strcat(inbuf, "    Copyright (C) <year>  <name of author>\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    This program is free software; you can redistribute it and/or modify\n");
+  my_strcat(inbuf, "    it under the terms of the GNU General Public License as published by\n");
+  my_strcat(inbuf, "    the Free Software Foundation; either version 2 of the License, or\n");
+  my_strcat(inbuf, "    (at your option) any later version.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    This program is distributed in the hope that it will be useful,\n");
+  my_strcat(inbuf, "    but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
+  my_strcat(inbuf, "    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
+  my_strcat(inbuf, "    GNU General Public License for more details.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    You should have received a copy of the GNU General Public License\n");
+  my_strcat(inbuf, "    along with this program; if not, write to the Free Software\n");
+  my_strcat(inbuf, "    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "Also add information on how to contact you by electronic and paper mail.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "If the program is interactive, make it output a short notice like this\n");
+  my_strcat(inbuf, "when it starts in an interactive mode:\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "    Gnomovision version 69, Copyright (C) year  name of author\n");
+  my_strcat(inbuf, "    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n");
+  my_strcat(inbuf, "    This is free software, and you are welcome to redistribute it\n");
+  my_strcat(inbuf, "    under certain conditions; type `show c' for details.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "The hypothetical commands `show w' and `show c' should show the appropriate\n");
+  my_strcat(inbuf, "parts of the General Public License.  Of course, the commands you use may\n");
+  my_strcat(inbuf, "be called something other than `show w' and `show c'; they could even be\n");
+  my_strcat(inbuf, "mouse-clicks or menu items--whatever suits your program.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "You should also get your employer (if you work as a programmer) or your\n");
+  my_strcat(inbuf, "school, if any, to sign a copyright disclaimer for the program, if\n");
+  my_strcat(inbuf, "necessary.  Here is a sample; alter the names:\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n");
+  my_strcat(inbuf, "  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "  <signature of Ty Coon>, 1 April 1989\n");
+  my_strcat(inbuf, "  Ty Coon, President of Vice\n");
+  my_strcat(inbuf, "\n");
+  my_strcat(inbuf, "This General Public License does not permit incorporating your program into\n");
+  my_strcat(inbuf, "proprietary programs.  If your program is a subroutine library, you may\n");
+  my_strcat(inbuf, "consider it more useful to permit linking proprietary applications with the\n");
+  my_strcat(inbuf, "library.  If this is what you want to do, use the GNU Library General\n");
+  my_strcat(inbuf, "Public License instead of this License.\n");
+
+  my_strcat(inbuf, "\n");
+}
+
+#include <stdio.h>
+#include <malloc.h>
+#include <assert.h>
+
+/* For providing services. */
+static HWord g_serviceFn ( HWord arg1, HWord arg2 )
+{
+   switch (arg1) {
+      case 0: /* EXIT */
+         exit(0);
+      case 1: /* PUTC */
+         putchar(arg2);
+         return 0;
+      case 2: /* MALLOC */
+         return (HWord)malloc(arg2);
+      case 3: /* FREE */
+         free((void*)arg2);
+         return 0;
+      default:
+         assert(0);
+   }
+   return 0;
+}
+static char *bzerrorstrings[] = {
+       "OK"
+       ,"SEQUENCE_ERROR"
+       ,"PARAM_ERROR"
+       ,"MEM_ERROR"
+       ,"DATA_ERROR"
+       ,"DATA_ERROR_MAGIC"
+       ,"IO_ERROR"
+       ,"UNEXPECTED_EOF"
+       ,"OUTBUFF_FULL"
+       ,"CONFIG_ERROR"
+       ,"???"   /* for future */
+       ,"???"   /* for future */
+       ,"???"   /* for future */
+       ,"???"   /* for future */
+       ,"???"   /* for future */
+       ,"???"   /* for future */
+};
+
+// If given a cmd line arg, behave as a correctness regtest
+// (run fast and be verbose).  If not, run for a long time
+// which is what is needed for the performance suite.
+int main ( int argc, char** argv )
+{
+   int   r;
+   int   bit;
+   int   i;
+
+   int regtest;
+   assert(argc == 1 || argc == 2);
+   regtest = argc==2;
+
+   /* hardwire one particular behaviour */
+   regtest = 1;
+
+   serviceFn = g_serviceFn;
+
+   set_inbuf();
+   nIn = vex_strlen(inbuf)+1;
+   vex_printf( "%d bytes read\n", nIn );
+
+   nZ = M_BLOCK;
+   r = BZ2_bzBuffToBuffCompress (
+          zbuf, &nZ, inbuf, nIn, 9, 3/*verb*/, 30 );
+
+   if (r != BZ_OK) {
+     vex_printf("initial compress failed!\n");
+     (*serviceFn)(0,0);
+   }
+   vex_printf( "%d after compression\n", nZ );
+
+   for (bit = 0; bit < nZ*8; bit += (bit < 35 ? 3 : (regtest?2377:137))) {
+     if (bit >= 11920) break;
+      if (regtest)
+         vex_printf( "bit %d  ", bit );
+      flip_bit ( bit );
+      nOut = M_BLOCK_OUT;
+      r = BZ2_bzBuffToBuffDecompress (
+             outbuf, &nOut, zbuf, nZ, 1/*small*/, 0 );
+      if (regtest)
+         vex_printf( " %d  %s ", r, bzerrorstrings[-r] );
+
+      if (r != BZ_OK) {
+	 if (regtest)
+            vex_printf( "\n" );
+      } else {
+         if (nOut != nIn) {
+           vex_printf(  "nIn/nOut mismatch %d %d\n", nIn, nOut );
+           (*serviceFn)(0,0);
+         } else {
+           for (i = 0; i < nOut; i++)
+             if (inbuf[i] != outbuf[i]) { 
+                vex_printf(  "mismatch at %d\n", i ); 
+                (*serviceFn)(0,0); 
+           }
+           if (i == nOut) vex_printf( "really ok!\n" );
+         }
+      }
+
+      flip_bit ( bit );
+   }
+
+#if 0
+   assert (nOut == nIn);
+   for (i = 0; i < nOut; i++) {
+     if (inbuf[i] != outbuf[i]) {
+        vex_printf( "difference at %d !\n", i );
+        return 1;
+     }
+   }
+#endif
+
+   vex_printf( "all ok\n" );
+   (*serviceFn)(0,0);
+   /*NOTREACHED*/
+   return 0;
+}
diff --git a/memcheck/tests/varinfo6.stderr.exp-glibc25-amd64 b/memcheck/tests/varinfo6.stderr.exp-glibc25-amd64
new file mode 100644
index 0000000..421f872
--- /dev/null
+++ b/memcheck/tests/varinfo6.stderr.exp-glibc25-amd64
@@ -0,0 +1,27 @@
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo6.c:34)
+   by 0x........: mainSort (varinfo6.c:2999)
+   by 0x........: BZ2_blockSort (varinfo6.c:3143)
+   by 0x........: BZ2_compressBlock (varinfo6.c:4072)
+   by 0x........: handle_compress (varinfo6.c:4790)
+   by 0x........: BZ2_bzCompress (varinfo6.c:4860)
+   by 0x........: BZ2_bzBuffToBuffCompress (varinfo6.c:5667)
+   by 0x........: main (varinfo6.c:6517)
+ Location 0x........ is 2 bytes inside local var "budget"
+ declared at varinfo6.c:3115, in frame #2 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo6.c:34)
+   by 0x........: BZ2_decompress (varinfo6.c:1699)
+   by 0x........: BZ2_bzDecompress (varinfo6.c:5230)
+   by 0x........: BZ2_bzBuffToBuffDecompress (varinfo6.c:5715)
+   by 0x........: main (varinfo6.c:6532)
+ Location 0x........ is 2 bytes inside local var "i"
+ declared at varinfo6.c:1517, in frame #1 of thread 1
+
+ERROR SUMMARY: 10216 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 10,253 allocs, 10,253 frees, 24,368,716 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/varinfo6.stderr.exp-glibc25-x86 b/memcheck/tests/varinfo6.stderr.exp-glibc25-x86
new file mode 100644
index 0000000..c9cb129
--- /dev/null
+++ b/memcheck/tests/varinfo6.stderr.exp-glibc25-x86
@@ -0,0 +1,27 @@
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo6.c:34)
+   by 0x........: mainSort (varinfo6.c:2999)
+   by 0x........: BZ2_blockSort (varinfo6.c:3143)
+   by 0x........: BZ2_compressBlock (varinfo6.c:4072)
+   by 0x........: handle_compress (varinfo6.c:4790)
+   by 0x........: BZ2_bzCompress (varinfo6.c:4860)
+   by 0x........: BZ2_bzBuffToBuffCompress (varinfo6.c:5667)
+   by 0x........: main (varinfo6.c:6517)
+ Location 0x........ is 2 bytes inside local var "budget"
+ declared at varinfo6.c:3115, in frame #2 of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (varinfo6.c:34)
+   by 0x........: BZ2_decompress (varinfo6.c:1699)
+   by 0x........: BZ2_bzDecompress (varinfo6.c:5230)
+   by 0x........: BZ2_bzBuffToBuffDecompress (varinfo6.c:5715)
+   by 0x........: main (varinfo6.c:6532)
+ Location 0x........ is 2 bytes inside local var "i"
+ declared at varinfo6.c:1517, in frame #1 of thread 1
+
+ERROR SUMMARY: 10216 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 10,253 allocs, 10,253 frees, 24,368,200 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/varinfo6.stdout.exp b/memcheck/tests/varinfo6.stdout.exp
new file mode 100644
index 0000000..9a2a2bd
--- /dev/null
+++ b/memcheck/tests/varinfo6.stdout.exp
@@ -0,0 +1,29 @@
+22323 bytes read
+    block 1: crc = 0xA212ABF8, combined CRC = 0xA212ABF8, size = 22373
+    too repetitive; using fallback sorting algorithm
+      22373 in block, 13504 after MTF & 1-2 coding, 79+2 syms in use
+      pass 1: size is 17143, grp uses are 38 62 2 92 6 71 
+      pass 2: size is 6506, grp uses are 28 71 0 86 9 77 
+      pass 3: size is 6479, grp uses are 26 70 0 81 11 83 
+      pass 4: size is 6469, grp uses are 26 69 0 74 17 85 
+      bytes: mapping 19, selectors 66, code lengths 134, codes 6465
+    final combined CRC = 0xA212ABF8
+   6710 after compression
+bit 0   -5  DATA_ERROR_MAGIC 
+bit 3   -5  DATA_ERROR_MAGIC 
+bit 6   -5  DATA_ERROR_MAGIC 
+bit 9   -5  DATA_ERROR_MAGIC 
+bit 12   -5  DATA_ERROR_MAGIC 
+bit 15   -5  DATA_ERROR_MAGIC 
+bit 18   -5  DATA_ERROR_MAGIC 
+bit 21   -5  DATA_ERROR_MAGIC 
+bit 24   0  OK really ok!
+bit 27   0  OK really ok!
+bit 30   -5  DATA_ERROR_MAGIC 
+bit 33   -4  DATA_ERROR 
+bit 36   -4  DATA_ERROR 
+bit 2413   -4  DATA_ERROR 
+bit 4790   -4  DATA_ERROR 
+bit 7167   -4  DATA_ERROR 
+bit 9544   -4  DATA_ERROR 
+all ok
diff --git a/memcheck/tests/varinfo6.vgtest b/memcheck/tests/varinfo6.vgtest
new file mode 100644
index 0000000..b67a27b
--- /dev/null
+++ b/memcheck/tests/varinfo6.vgtest
@@ -0,0 +1,2 @@
+prog: varinfo6
+vgopts: --read-var-info=yes
diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp
index 4eb01ed..d67b726 100644
--- a/none/tests/cmdline2.stdout.exp
+++ b/none/tests/cmdline2.stdout.exp
@@ -63,6 +63,7 @@
     --trace-sched=no|yes      show thread scheduler details? [no]
     --wait-for-gdb=yes|no     pause on startup to wait for gdb attach
     --sym-offsets=yes|no      show syms in form 'name+offset' ? [no]
+    --read-var-info=yes|no    read variable type & location info? [no]
     --command-line-only=no|yes  only use command line options [no]
 
     --vex-iropt-verbosity             0 .. 9 [0]