/* -*- mode: C; c-basic-offset: 3; -*- */

/*--------------------------------------------------------------------*/
/*--- Read DWARF1/2/3/4 debug info.                    readdwarf.c ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of Valgrind, a dynamic binary instrumentation
   framework.

   Copyright (C) 2000-2017 Julian Seward
      jseward@acm.org

   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 3 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, see <http://www.gnu.org/licenses/>.

   The GNU General Public License is contained in the file COPYING.
*/

#if defined(VGO_linux) || defined(VGO_darwin) || defined(VGO_solaris) || defined(VGO_freebsd)

#include "pub_core_basics.h"
#include "pub_core_debuginfo.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 "pub_core_tooliface.h"    /* VG_(needs) */
#include "priv_misc.h"             /* dinfo_zalloc/free/strdup */
#include "priv_image.h"
#include "priv_d3basics.h"
#include "priv_tytypes.h"
#include "priv_storage.h"
#include "priv_readdwarf.h"        /* self */


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Read line number and CFI info from DWARF1, DWARF2    ---*/
/*--- and to some extent DWARF3 sections.                  ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* The below "safe_*ix" functions allow to resist to malformed dwarf info:
   if dwarf info contains wrong file or dirname indexes, these are (silently!)
   ignored. */

/* if xa_ix is a valid index in fndn_ix_xa,
    return the element (i.e. the UInt indexing in fndnpool).
   If xa_ix is invalid, return 0 (i.e. the "null" element in fndnpool). */
static UInt safe_fndn_ix (XArray* fndn_ix_xa, Int xa_ix)
{
   if (xa_ix < 0) return 0;
   if (xa_ix >= VG_(sizeXA) (fndn_ix_xa)) return 0;
   return *(UInt*)VG_(indexXA) ( fndn_ix_xa, xa_ix );
}

/* if xa_ix is a valid index in dirname_xa,
    return the element (i.e. the HChar*).
   If xa_ix is invalid, return NULL. */
static const HChar* safe_dirname_ix (XArray* dirname_xa, Int xa_ix)
{
   if (xa_ix < 0) return NULL;
   if (xa_ix >= VG_(sizeXA) (dirname_xa)) return NULL;
   return *(HChar**)VG_(indexXA) ( dirname_xa, xa_ix );
}

/*------------------------------------------------------------*/
/*--- Read DWARF2 format line number info.                 ---*/
/*------------------------------------------------------------*/

/* Structure holding info extracted from the a .debug_line
   section.  */
typedef struct
{
  ULong  li_length;
  UShort li_version;
  ULong  li_header_length;
  UChar  li_min_insn_length;
  UChar  li_max_ops_per_insn;
  Int    li_line_base;
  UChar  li_line_range;
  UChar  li_opcode_base;
}
DebugLineInfo;

/* Structure holding additional infos found from a .debug_info
 * compilation unit block */
typedef struct
{
  /* Feel free to add more members here if you need ! */
  DiCursor compdir;  /* Compilation directory - points to .debug_info */
  DiCursor name;     /* Main file name - points to .debug_info */
  ULong    stmt_list; /* Offset in .debug_line */
  Bool     dw64;      /* 64-bit Dwarf? */
} 
UnitInfo;

/* Line number opcodes.  */
enum dwarf_line_number_ops
  {
    DW_LNS_extended_op = 0,
    DW_LNS_copy = 1,
    DW_LNS_advance_pc = 2,
    DW_LNS_advance_line = 3,
    DW_LNS_set_file = 4,
    DW_LNS_set_column = 5,
    DW_LNS_negate_stmt = 6,
    DW_LNS_set_basic_block = 7,
    DW_LNS_const_add_pc = 8,
    DW_LNS_fixed_advance_pc = 9,
    /* DWARF 3.  */
    DW_LNS_set_prologue_end = 10,
    DW_LNS_set_epilogue_begin = 11,
    DW_LNS_set_isa = 12
  };

/* Line number extended opcodes.  */
enum dwarf_line_number_x_ops
  {
    DW_LNE_end_sequence = 1,
    DW_LNE_set_address = 2,
    DW_LNE_define_file = 3,
    DW_LNE_set_discriminator = 4
  };

typedef struct
{
  /* Information for the last statement boundary.
   * Needed to calculate statement lengths. */
  Addr  last_address;
  UInt  last_file;
  UInt  last_line;

  Addr  address;
  UInt  file;
  UInt  line;
  UInt  column;
  Int   basic_block;
  UChar end_sequence;
} LineSMR;


/* FIXME: duplicated in readdwarf3.c */
/* Read a 'leb128' and advance *data accordingly. */
static ULong step_leb128 ( DiCursor* data, Int sign )
{
   ULong  result = 0;
   Int    shift = 0;
   UChar  byte;

   vg_assert(sign == 0 || sign == 1);

   do {
      byte = ML_(cur_step_UChar)(data);
      result |= ((ULong)(byte & 0x7f)) << shift;
      shift += 7;
   }
   while (byte & 0x80);

   if (sign && (shift < 64) && (byte & 0x40))
      result |= -(1ULL << shift);

   return result;
}

/* FIXME: duplicated in readdwarf3.c */
static ULong step_leb128U( DiCursor* data ) {
   return step_leb128( data, 0 );
}

/* FIXME: duplicated in readdwarf3.c */
static Long step_leb128S( DiCursor* data ) {
   return step_leb128( data, 1 );
}

/* Read what the DWARF3 spec calls an "initial length field".  This
   uses up either 4 or 12 bytes of the input and produces a 32-bit or
   64-bit number respectively.

   Read 32-bit value from p.  If it is 0xFFFFFFFF, instead read a
   64-bit bit value from p+4.  This is used in 64-bit dwarf to encode
   some table lengths.  Advance the cursor (p) accordingly.

   XXX this is a hack: the endianness of the initial length field is
   specified by the DWARF we're reading.  This happens to work only
   because we don't do cross-arch jitting, hence this code runs on a
   platform of the same endianness as the DWARF it is reading.  Same
   applies for initial lengths for CIE/FDEs and probably in zillions
   of other places -- to be precise, exactly the places where
   binutils/dwarf.c calls byte_get().
*/
static
ULong step_initial_length_field ( DiCursor* p_img, /*OUT*/Bool* is64 )
{
   UInt w32 = ML_(cur_step_UInt)(p_img);
   if (w32 == 0xFFFFFFFF) {
      *is64 = True;
      return ML_(cur_step_ULong)(p_img);
   } else {
      *is64 = False;
      return (ULong)w32;
   }
}

static
ULong read_initial_length_field ( DiCursor p_img, /*OUT*/Bool* is64 )
{
   /* Something of a roundabout approach .. the modification to p_img
      is abandoned. */
   return step_initial_length_field( &p_img, is64 );
}


static LineSMR state_machine_regs;

static 
void reset_state_machine ( void )
{
   if (0) VG_(printf)("smr.a := %p (reset)\n", NULL );
   state_machine_regs.last_address = 0;
   state_machine_regs.last_file = 1;
   state_machine_regs.last_line = 1;
   state_machine_regs.address = 0;
   state_machine_regs.file = 1;
   state_machine_regs.line = 1;
   state_machine_regs.column = 0;
   state_machine_regs.basic_block = 0;
   state_machine_regs.end_sequence = 0;
}

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////

/* Handled an extended line op starting at *data, and advance *data
   accordingly. */
static 
void process_extended_line_op( struct _DebugInfo* di,
                               XArray* fndn_ix_xa,
                               DiCursor* data )
{
   UInt len = step_leb128U(data);
   if (len == 0) {
      VG_(message)(Vg_UserMsg,
                   "Warning: DWARF2 reader: "
                   "Badly formed extended line op encountered\n");
      return;
   }

   UChar op_code = ML_(cur_step_UChar)(data);
   if (0) VG_(printf)("dwarf2: ext OPC: %d\n", op_code);

   switch (op_code) {
      case DW_LNE_end_sequence:
         if (0) VG_(printf)("1001: si->o %#lx, smr.a %#lx\n",
                            (UWord)di->text_debug_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; 

         if (state_machine_regs.last_address) {
            ML_(addLineInfo)(
               di,
               safe_fndn_ix(fndn_ix_xa,
                            state_machine_regs.last_file),
               di->text_debug_bias + state_machine_regs.last_address, 
               di->text_debug_bias + state_machine_regs.address, 
               state_machine_regs.last_line, 0
            );
         }
         reset_state_machine();
         if (di->ddump_line)
            VG_(printf)("  Extended opcode %d: End of Sequence\n\n", 
                        (Int)op_code);
         break;

      case DW_LNE_set_address: {
         Addr adr = ML_(cur_step_Addr)(data);
         state_machine_regs.address = adr;
         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: {
         HChar* name = ML_(cur_step_strdup)(data, "di.pelo.1");
         UInt fndn_ix = ML_(addFnDn) (di, name, NULL);
         VG_(addToXA) (fndn_ix_xa, &fndn_ix);
         ML_(dinfo_free)(name);
         (void)step_leb128U(data); // ignored: dir index
         (void)step_leb128U(data); // ignored: mod time
         (void)step_leb128U(data); // ignored: file size
         if (di->ddump_line)
            VG_(printf)("  DWARF2-line: set_address\n");
         break;
      }

      case DW_LNE_set_discriminator:
         (void)step_leb128U(data); // ignored: new 'discriminator' value
         break;

      default:
         if (di->ddump_line)
            VG_(printf)("process_extended_line_op:default\n");
         break;
   }
}

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////

static
HChar * get_line_str (struct _DebugInfo* di, const UnitInfo* ui,
                      DiCursor *data, const UInt form,
                      DiCursor debugstr_img, DiCursor debuglinestr_img)
{
   HChar *str = NULL;
   switch (form) {
   case DW_FORM_string:
      str = ML_(cur_step_strdup)(data, "di.gls.string");
      break;
   case DW_FORM_strp:
      if (!ui->dw64)
         str = ML_(cur_read_strdup)(ML_(cur_plus)(debugstr_img,
                                                  ML_(cur_step_UInt)(data)),
                                    "di.gls.strp.dw32");
      else
         str = ML_(cur_read_strdup)(ML_(cur_plus)(debugstr_img,
                                                  ML_(cur_step_ULong)(data)),
                                    "di.gls.strp.dw64");
      break;
   case DW_FORM_line_strp:
      if (!ui->dw64)
         str = ML_(cur_read_strdup)(ML_(cur_plus)(debuglinestr_img,
                                                  ML_(cur_step_UInt)(data)),
                                    "di.gls.line_strp.dw32");
      else
         str = ML_(cur_read_strdup)(ML_(cur_plus)(debuglinestr_img,
                                                  ML_(cur_step_ULong)(data)),
                                    "di.gls.line_strp.dw64");
      break;
   default:
      ML_(symerr)(di, True,
                  "Unknown path string FORM in .debug_line");
      break;
   }
   return str;
}

static
Int get_line_ndx (struct _DebugInfo* di,
                  DiCursor *data, const UInt form)
{
   Int res = 0;
   switch (form) {
   case DW_FORM_data1:
      res = ML_(cur_step_UChar)(data);
      break;
   case DW_FORM_data2:
      res = ML_(cur_step_UShort)(data);
      break;
   case DW_FORM_udata:
      res = step_leb128U(data);
      break;
   default:
      ML_(symerr)(di, True,
                  "Unknown directory_index value FORM in .debug_line");
      break;
   }
   return res;
}

static
DiCursor skip_line_form (struct _DebugInfo* di, const UnitInfo* ui,
                         DiCursor d, const UInt form)
{
   switch (form) {
   case DW_FORM_block: {
      ULong len = step_leb128U(&d);
      d = ML_(cur_plus)(d, len);
      break;
   }
   case DW_FORM_block1:
      d = ML_(cur_plus)(d, ML_(cur_read_UChar)(d) + 1);
      break;
   case DW_FORM_block2:
      d = ML_(cur_plus)(d, ML_(cur_read_UShort)(d) + 2);
      break;
   case DW_FORM_block4:
      d = ML_(cur_plus)(d, ML_(cur_read_UInt)(d) + 4);
      break;
   case DW_FORM_flag:
   case DW_FORM_data1:
      d = ML_(cur_plus)(d, 1);
      break;
   case DW_FORM_data2:
      d = ML_(cur_plus)(d, 2);
      break;
   case DW_FORM_data4:
      d = ML_(cur_plus)(d, 4);
      break;
   case DW_FORM_data8:
      d = ML_(cur_plus)(d, 8);
      break;
   case DW_FORM_data16:
      d = ML_(cur_plus)(d, 16);
      break;
   case DW_FORM_string:
      d = ML_(cur_plus)(d, ML_(cur_strlen)(d) + 1);
      break;
   case DW_FORM_strp:
   case DW_FORM_line_strp:
   case DW_FORM_sec_offset:
      d = ML_(cur_plus)(d, ui->dw64 ? 8 : 4);
      break;
   case DW_FORM_udata:
      (void)step_leb128U(&d);
      break;
   case DW_FORM_sdata:
      (void)step_leb128S(&d);
      break;
   default:
      ML_(symerr)(di, True, "Unknown FORM in .debug_line");
      break;
   }
   return d;
}

/* read a .debug_line section block for a compilation unit
 *
 * Input:   - theBlock must point to the start of the block
 *            for the given compilation unit
 *          - ui contains additional info like the compilation dir
 *            for this unit
 *
 * Output: - si debug info structures get updated
 */
static 
void read_dwarf2_lineblock ( struct _DebugInfo* di,
                             const UnitInfo* ui, 
                             DiCursor  theBlock, /* IMAGE */
                             Int       noLargerThan,
                             DiCursor  debugstr_img,
                             DiCursor  debuglinestr_img)
{
   Int            i;
   DebugLineInfo  info;
   Bool           is64;
   XArray*        fndn_ix_xa; /* xarray of UInt fndn_ix */
   UInt           fndn_ix;
   XArray*        dirname_xa;   /* xarray of const HChar* dirname */
   const HChar*   dirname;

   DiCursor       external = theBlock;
   DiCursor       data = theBlock;

   UChar          p_ndx = 0, d_ndx = 0; /* DWARF5 path and dir index. */
   UInt           forms[256];           /* DWARF5 forms. */

   /* fndn_ix_xa is an xarray of fndn_ix (indexes in di->fndnpool) which
      are build from file names harvested from the DWARF2
      info.  Entry [0] is the "null" pool index and is never referred to
      by the state machine.

      Similarly, dirname_xa is an xarray of directory names.  Entry [0]
      is also NULL and denotes "we don't know what the path is", since
      that is different from "the path is the empty string".  Unlike
      the fndn_ix_xa table, the state machine does refer to entry [0],
      which basically means "." ("the current directory of the
      compilation", whatever that means, according to the DWARF3
      spec.)
   */

   /* Fails due to gcc padding ...
   vg_assert(sizeof(DWARF2_External_LineInfo)
             == sizeof(DWARF2_Internal_LineInfo));
   */

   dirname_xa = VG_(newXA) (ML_(dinfo_zalloc), "di.rd2l.1", ML_(dinfo_free),
                            sizeof(HChar*) );
   fndn_ix_xa = VG_(newXA) (ML_(dinfo_zalloc), "di.rd2l.2", ML_(dinfo_free),
                            sizeof(UInt) );

   info.li_length = step_initial_length_field( &external, &is64 );
   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)(di, True,
                  "DWARF line info appears to be corrupt "
                  "- the section is too small");
      goto out;
   }

   /* Check its version number.  */
   info.li_version = ML_(cur_step_UShort)(&external);
   if (di->ddump_line)
      VG_(printf)("  DWARF Version:               %d\n", 
                  (Int)info.li_version);

   if (info.li_version != 2 && info.li_version != 3 && info.li_version != 4
       && info.li_version != 5) {
      ML_(symerr)(di, True,
                  "Only DWARF version 2, 3, 4 and 5 line info "
                  "is currently supported.");
      goto out;
   }

   if (info.li_version >= 5) {
      /* UChar addr_size = */ ML_(cur_step_UChar)(&external);
      /* UChar seg_size = */  ML_(cur_step_UChar)(&external);
   }

   info.li_header_length = is64 ? ML_(cur_step_ULong)(&external) 
                                : (ULong)(ML_(cur_step_UInt)(&external));
   if (di->ddump_line)
      VG_(printf)("  Prologue Length:             %llu\n", 
                  info.li_header_length);

   info.li_min_insn_length = ML_(cur_step_UChar)(&external);
   if (di->ddump_line)
      VG_(printf)("  Minimum Instruction Length:  %d\n", 
                  (Int)info.li_min_insn_length);

   /* We only support machines with one opcode per instruction
      for now. If we ever want to support VLIW machines there is
      code to handle multiple opcodes per instruction in the
      patch attached to BZ#233595.
   */
   if (info.li_version >= 4) {
      info.li_max_ops_per_insn = ML_(cur_step_UChar)(&external);
      if (info.li_max_ops_per_insn != 1) {
         ML_(symerr)(di, True,
                     "Invalid Maximum Ops Per Insn in line info.");
         goto out;
      }
      if (di->ddump_line)
         VG_(printf)("  Maximum Ops Per Insn:        %d\n", 
                  (Int)info.li_max_ops_per_insn);
   } else {
      info.li_max_ops_per_insn = 1;
   }

   /* Register is_stmt is not tracked as we are interested only
      in pc -> line info mapping and not other debugger features. */
   /* default_is_stmt = */ ML_(cur_step_UChar)(&external);

   /* JRS: changed (UInt*) to (UChar*) */
   info.li_line_base = ML_(cur_step_UChar)(&external);
   info.li_line_base = (Int)(Char)info.li_line_base;
   if (di->ddump_line)
      VG_(printf)("  Line Base:                   %d\n", 
                  info.li_line_base);

   info.li_line_range = ML_(cur_step_UChar)(&external);
   if (di->ddump_line)
      VG_(printf)("  Line Range:                  %d\n", 
                  (Int)info.li_line_range);

   info.li_opcode_base = ML_(cur_step_UChar)(&external);
   if (di->ddump_line)
      VG_(printf)("  Opcode Base:                 %d\n\n", 
                  info.li_opcode_base);

   if (0) VG_(printf)("dwarf2: line base: %d, range %d, opc base: %d\n",
                      (Int)info.li_line_base, 
                      (Int)info.li_line_range,
                      (Int)info.li_opcode_base);

   DiCursor end_of_sequence
     = ML_(cur_plus)(data, info.li_length + (is64 ? 12 : 4));

   reset_state_machine();

   /* Read the contents of the Opcodes table.  */
   DiCursor standard_opcodes = external;
   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", 
                     i, (Int)ML_(cur_read_UChar)(
                                ML_(cur_plus)(standard_opcodes,
                                              (i-1) * sizeof(UChar)) ));
      }
      VG_(printf)("\n");
   }
   /* skip over "standard_opcode_lengths" */
   data = ML_(cur_plus)(standard_opcodes, info.li_opcode_base - 1);

   if (ML_(cur_is_valid)(ui->compdir))
      dirname = ML_(addStrFromCursor)(di, ui->compdir);
   else
      dirname = ML_(addStr)(di, ".", -1);

   if (info.li_version < 5) {
      /* Read the contents of the Directory table.  */
      if (di->ddump_line)
         VG_(printf)("The Directory Table%s\n",
                     ML_(cur_read_UChar)(data) == 0 ? " is empty." : ":" );

      /* DWARF2 starts numbering filename entries at 1, so we need to
         add a dummy zeroth entry to the table.  */
      fndn_ix = 0; // 0 is the "null" index in a fixed pool.
      VG_(addToXA) (fndn_ix_xa, &fndn_ix);
      VG_(addToXA) (dirname_xa, &dirname);

      while (ML_(cur_read_UChar)(data) != 0) {

         HChar* data_str = ML_(cur_read_strdup)(data, "di.rd2l.1");
         if (di->ddump_line)
            VG_(printf)("  %s\n", data_str);

         /* If data[0] is '/', then 'data' is an absolute path and we
            don't mess with it.  Otherwise, construct the
            path 'ui->compdir' ++ "/" ++ 'data'. */

         if (data_str[0] != '/'
             /* not an absolute path */
             && ML_(cur_is_valid)(ui->compdir)
             /* actually got something sensible for compdir */
             && ML_(cur_strlen)(ui->compdir))
         {
            HChar* compdir_str = ML_(cur_read_strdup)(ui->compdir,
                                                      "di.rd2l.1b");
            SizeT  len = VG_(strlen)(compdir_str) + 1 + VG_(strlen)(data_str);
            HChar *buf = ML_(dinfo_zalloc)("di.rd2l.1c", len + 1);

            VG_(strcpy)(buf, compdir_str);
            VG_(strcat)(buf, "/");
            VG_(strcat)(buf, data_str);

            dirname = ML_(addStr)(di, buf, len);
            VG_(addToXA) (dirname_xa, &dirname);
            if (0) VG_(printf)("rel path  %s\n", buf);
            ML_(dinfo_free)(compdir_str);
            ML_(dinfo_free)(buf);
         } else {
            /* just use 'data'. */
            dirname = ML_(addStr)(di,data_str,-1);
            VG_(addToXA) (dirname_xa, &dirname);
            if (0) VG_(printf)("abs path  %s\n", data_str);
         }

         data = ML_(cur_plus)(data, VG_(strlen)(data_str) + 1);
         ML_(dinfo_free)(data_str);
      }

      if (di->ddump_line)
         VG_(printf)("\n");

      if (ML_(cur_read_UChar)(data) != 0) {
         ML_(symerr)(di, True,
                     "can't find NUL at end of DWARF2 directory table");
         goto out;
      }
      data = ML_(cur_plus)(data, 1);
   } else {
      UInt directories_count;
      UChar directory_entry_format_count = ML_(cur_step_UChar)(&data);
      UInt n;
      for (n = 0; n < directory_entry_format_count; n++) {
         UInt lnct = step_leb128U(&data);
         UInt form = step_leb128U(&data);
         if (lnct == DW_LNCT_path)
            p_ndx = n;
         forms[n] = form;
      }
      directories_count = step_leb128U(&data);
      /* Read the contents of the Directory table.  */
      if (di->ddump_line)
         VG_(printf)(" The Directory Table%s\n",
                     directories_count == 0 ? " is empty." : ":" );

      for (n = 0; n < directories_count; n++) {
         UInt f;
         for (f = 0; f < directory_entry_format_count; f++) {
            UInt form = forms[f];
            if (f == p_ndx) {
               HChar *data_str = get_line_str (di, ui, &data, form,
                                               debugstr_img,
                                               debuglinestr_img);
               if (di->ddump_line)
                  VG_(printf)("  %s\n", data_str);

               /* If data[0] is '/', then 'data' is an absolute path and we
                  don't mess with it.  Otherwise, construct the
                  path 'ui->compdir' ++ "/" ++ 'data'. */

               if (data_str[0] != '/'
                   /* not an absolute path */
                   && ML_(cur_is_valid)(ui->compdir)
                   /* actually got something sensible for compdir */
                   && ML_(cur_strlen)(ui->compdir))
               {
                  HChar* compdir_str = ML_(cur_read_strdup)(ui->compdir,
                                                            "di.rd2l.1b");
                  SizeT  len = VG_(strlen)(compdir_str) + 1
                     + VG_(strlen)(data_str);
                  HChar *buf = ML_(dinfo_zalloc)("di.rd2l.1c", len + 1);

                  VG_(strcpy)(buf, compdir_str);
                  VG_(strcat)(buf, "/");
                  VG_(strcat)(buf, data_str);

                  dirname = ML_(addStr)(di, buf, len);
                  VG_(addToXA) (dirname_xa, &dirname);
                  if (0) VG_(printf)("rel path  %s\n", buf);
                  ML_(dinfo_free)(compdir_str);
                  ML_(dinfo_free)(buf);
               } else {
                  /* just use 'data'. */
                  dirname = ML_(addStr)(di,data_str,-1);
                  VG_(addToXA) (dirname_xa, &dirname);
                  if (0) VG_(printf)("abs path  %s\n", data_str);
               }

               ML_(dinfo_free)(data_str);

            } else {
               data = skip_line_form (di, ui, data, form);
            }
         }
      }
   }

   /* Read the contents of the File Name table.  This produces a bunch
      of fndn_ix in fndn_ix_xa. */
   if (di->ddump_line) {
      VG_(printf)(" The File Name Table:\n");
      VG_(printf)("  Entry	Dir	Time	Size	Name\n");
   }

   if (info.li_version < 5) {
      i = 1;
      while (ML_(cur_read_UChar)(data) != 0) {
         HChar* name    = ML_(cur_step_strdup)(&data, "di.rd2l.2");
         Int    diridx  = step_leb128U(&data);
         Int    uu_time = step_leb128U(&data); /* unused */
         Int    uu_size = step_leb128U(&data); /* unused */

         dirname = safe_dirname_ix( dirname_xa, diridx );
         fndn_ix = ML_(addFnDn) (di, name, dirname);
         VG_(addToXA) (fndn_ix_xa, &fndn_ix);
         if (0) VG_(printf)("file %s diridx %d\n", name, diridx );
         if (di->ddump_line)
            VG_(printf)("  %d\t%d\t%d\t%d\t%s\n",
                        i, diridx, uu_time, uu_size, name);
         i++;
         ML_(dinfo_free)(name);
      }

      if (di->ddump_line)
         VG_(printf)("\n");

      if (ML_(cur_read_UChar)(data) != 0) {
         ML_(symerr)(di, True,
                     "can't find NUL at end of DWARF2 file name table");
         goto out;
      }
      data = ML_(cur_plus)(data, 1);
   } else {
      UInt file_names_count;
      UChar file_names_entry_format_count = ML_(cur_step_UChar)(&data);
      UInt n;
      for (n = 0; n < file_names_entry_format_count; n++) {
         UInt lnct = step_leb128U(&data);
         UInt form = step_leb128U(&data);
         if (lnct == DW_LNCT_path)
            p_ndx = n;
         if (lnct == DW_LNCT_directory_index)
            d_ndx = n;
         forms[n] = form;
      }
      file_names_count = step_leb128U(&data);
      for (n = 0; n < file_names_count; n++) {
         UInt f;
         HChar* name = NULL;
         Int diridx  = 0;
         for (f = 0; f < file_names_entry_format_count; f++) {
            UInt form = forms[f];
            if (f == p_ndx)
               name = get_line_str (di, ui, &data, form,
                                    debugstr_img, debuglinestr_img);
            else if (f == d_ndx)
               diridx = get_line_ndx (di, &data, form);
            else
               data = skip_line_form (di, ui, data, form);
         }

         dirname = safe_dirname_ix( dirname_xa, diridx );
         fndn_ix = ML_(addFnDn) (di, name, dirname);
         VG_(addToXA) (fndn_ix_xa, &fndn_ix);
         if (0) VG_(printf)("file %s diridx %d\n", name, diridx );
         if (di->ddump_line)
            VG_(printf)("  %u\t%d\t%d\t%d\t%s\n",
                        n, diridx, 0, 0, name);
         ML_(dinfo_free)(name);
      }

      if (di->ddump_line)
         VG_(printf)("\n");
   }

   if (di->ddump_line)
      VG_(printf)(" Line Number Statements:\n");

   /* Now display the statements.  */

   while (ML_(cur_cmpLT)(data, end_of_sequence)) {
      UChar op_code = ML_(cur_step_UChar)(&data);

      if (0) VG_(printf)("dwarf2: OPC: %d\n", op_code);

      if (op_code >= info.li_opcode_base) {
         op_code -= info.li_opcode_base;
         Word adv = (op_code / info.li_line_range)
                       * info.li_min_insn_length;
         Int advAddr = adv;
         state_machine_regs.address += adv;

         if (0) VG_(printf)("smr.a += %#lx\n", (UWord)adv );
         adv = (op_code % info.li_line_range) + info.li_line_base;
         if (0) VG_(printf)("1002: di->o %#lx, smr.a %#lx\n",
                            (UWord)di->text_debug_bias,
                            state_machine_regs.address );
         state_machine_regs.line += adv;

         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,
                        (Int)adv, (Int)state_machine_regs.line );

         /* only add a statement if there was a previous boundary */
         if (state_machine_regs.last_address) {
            ML_(addLineInfo)(
               di,
               safe_fndn_ix(fndn_ix_xa,
                            state_machine_regs.last_file),
               di->text_debug_bias + state_machine_regs.last_address, 
               di->text_debug_bias + state_machine_regs.address, 
               state_machine_regs.last_line, 
               0
            );
         }
         state_machine_regs.last_address = state_machine_regs.address;
         state_machine_regs.last_file = state_machine_regs.file;
         state_machine_regs.last_line = state_machine_regs.line;
      }

      else { /* ! (op_code >= info.li_opcode_base) */

      switch (op_code) {
         case DW_LNS_extended_op:
            process_extended_line_op(di, fndn_ix_xa, &data);
            break;

         case DW_LNS_copy:
            if (0) VG_(printf)("1002: di->o %#lx, smr.a %#lx\n",
                               (UWord)di->text_debug_bias,
                               state_machine_regs.address );
            /* only add a statement if there was a previous boundary */
            if (state_machine_regs.last_address) {
               ML_(addLineInfo)(
                  di,
                  safe_fndn_ix(fndn_ix_xa,
                               state_machine_regs.last_file), 
                  di->text_debug_bias + state_machine_regs.last_address, 
                  di->text_debug_bias + state_machine_regs.address,
                  state_machine_regs.last_line, 
                  0
               );
            }
            state_machine_regs.last_address = state_machine_regs.address;
            state_machine_regs.last_file = state_machine_regs.file;
            state_machine_regs.last_line = state_machine_regs.line;
            state_machine_regs.basic_block = 0; /* JRS added */
            if (di->ddump_line)
               VG_(printf)("  Copy\n");
            break;

         case DW_LNS_advance_pc: {
            UWord adv = info.li_min_insn_length * step_leb128U(&data);
            state_machine_regs.address += adv;
            if (0) VG_(printf)("smr.a += %#lx\n", adv );
            if (di->ddump_line)
               VG_(printf)("  Advance PC by %lu to 0x%lx\n", 
                           adv, state_machine_regs.address);
            break;
         }
         case DW_LNS_advance_line: {
            Word adv = step_leb128S(&data);
            state_machine_regs.line += adv;
            if (di->ddump_line)
               VG_(printf)("  Advance Line by %ld to %d\n", 
                           adv, (Int)state_machine_regs.line);
            break;
         }
         case DW_LNS_set_file: {
            Word adv = step_leb128U(&data);
            state_machine_regs.file = adv;
            if (di->ddump_line)
               VG_(printf)("  Set File Name to entry %ld in the "
                           "File Name Table\n", adv);
            break;
         }
         case DW_LNS_set_column: {
            Word adv = step_leb128U(&data);
            state_machine_regs.column = adv;
            if (di->ddump_line)
               VG_(printf)("  Set column to %ld\n", adv);
            break;
         }
         case DW_LNS_negate_stmt: {
            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 (di->ddump_line)
               VG_(printf)("  DWARF2-line: set_basic_block\n");
            break;
         }
         case DW_LNS_const_add_pc: {
            Word adv = (((255 - info.li_opcode_base) / info.li_line_range)
                          * info.li_min_insn_length);
            state_machine_regs.address += adv;
            if (0) VG_(printf)("smr.a += %#lx\n", (UWord)adv );
            if (di->ddump_line)
               VG_(printf)("  Advance PC by constant %ld to 0x%lx\n", 
                           adv, (Addr)state_machine_regs.address);
            break;
         }
         case DW_LNS_fixed_advance_pc: {
            /* XXX: Need something to get 2 bytes */
            UWord adv = ML_(cur_step_UShort)(&data);
            state_machine_regs.address += adv;
            if (0) VG_(printf)("smr.a += %#lx\n", adv );
            if (di->ddump_line)
               VG_(printf)("  DWARF2-line: fixed_advance_pc\n");
            break;
         }
         case DW_LNS_set_prologue_end:
            if (di->ddump_line)
               VG_(printf)("  DWARF2-line: set_prologue_end\n");
            break;

         case DW_LNS_set_epilogue_begin:
            if (di->ddump_line)
               VG_(printf)("  DWARF2-line: set_epilogue_begin\n");
            break;

         case DW_LNS_set_isa:
            (void)step_leb128U(&data);
            if (di->ddump_line)
               VG_(printf)("  DWARF2-line: set_isa\n");
            break;

         default: {
            Int j;
            for (j = (Int)ML_(cur_read_UChar)(
                             ML_(cur_plus)(standard_opcodes,
                                           (op_code-1) * sizeof(UChar)));
                 j > 0 ; --j) {
               step_leb128U(&data);
            }
            if (di->ddump_line)
               VG_(printf)("  Unknown opcode %d\n", (Int)op_code);
            break;
         }
      } /* switch (op_code) */

      } /* if (op_code >= info.li_opcode_base) */

   } /* while (data < end_of_sequence) */

   if (di->ddump_line)
      VG_(printf)("\n");

  out:
   VG_(deleteXA)(dirname_xa);
   VG_(deleteXA)(fndn_ix_xa);
}

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////

/* Return abbrev for given code 
 * Returned cursor points to the tag
 * */
static DiCursor lookup_abbrev( DiCursor p, ULong acode )
{
   while (1) {
      ULong code = step_leb128U(&p);
      if (code == acode)
         return p;
      (void)step_leb128U(&p);  /* skip tag */
      p = ML_(cur_plus)(p,1);  /* skip has_children flag */
      ULong name;
      ULong form;
      do {
         name = step_leb128U(&p); /* name */
         form = step_leb128U(&p);  /* form */
         if (form == 0x21) /* DW_FORM_implicit_const */
            step_leb128S(&p);
      }
      while (name != 0); /* until name == form == 0 */
   }
}

/* Read general information for a particular compile unit block in
 * the .debug_info section. In particular read the name, compdir and
 * stmt_list needed to parse the line number information.
 * 
 * Input: - unitblock is the start of a compilation
 *          unit block in .debuginfo section
 *        - debugabbrev is start of .debug_abbrev section
 *        - debugstr is start of .debug_str section
 *        - debugstr_alt_img is start of .debug_str section in alt debug file
 *        
 * Output: Fill members of ui pertaining to the compilation unit:
 *         - ui->name is the name of the compilation unit
 *         - ui->compdir is the compilation unit directory
 *         - ui->stmt_list is the offset in .debug_line section
 *                for the dbginfos of this compilation unit
 *                
 * Note : the output strings are not allocated and point
 * directly to the memory-mapped section.
 */
static 
void read_unitinfo_dwarf2( /*OUT*/UnitInfo* ui,
                                  DiCursor  unitblock_img,
                                  DiCursor  debugabbrev_img,
                                  DiCursor  debugstr_img,
                                  DiCursor  debugstr_alt_img,
                                  DiCursor  debuglinestr_img)
{
   UInt   acode, abcode;
   ULong  atoffs, blklen;
   UShort ver;

   UChar    addr_size = 0;
   UChar    unit_type = 0;
   DiCursor p = unitblock_img;
   DiCursor end_img;
   DiCursor abbrev_img;

   VG_(memset)( ui, 0, sizeof( UnitInfo ) );
   ui->stmt_list = -1LL;
   
   /* Read the compilation unit header in .debug_info section - See p 70 */

   /* This block length */
   blklen = step_initial_length_field( &p, &ui->dw64 );

   /* version should be 2, 3, 4 or 5 */
   ver = ML_(cur_step_UShort)(&p);

   if (ver >= 5)
      /* unit_type for DWARF5 */
      unit_type = ML_(cur_step_UChar)(&p);
   else
      /* get offset in abbrev */
      atoffs = ui->dw64 ? ML_(cur_step_ULong)(&p)
                        : (ULong)(ML_(cur_step_UInt)(&p));

   /* Address size */
   addr_size = ML_(cur_step_UChar)(&p);

   if (ver >= 5) {
      /* get offset in abbrev */
      atoffs = ui->dw64 ? ML_(cur_step_ULong)(&p)
                        : (ULong)(ML_(cur_step_UInt)(&p));

      /* read any extra fields */
      switch(unit_type) {
         case DW_UT_compile:
         case DW_UT_partial:
            break;
         case DW_UT_skeleton:
         case DW_UT_split_compile:
            /* dwo_id = */ ML_(cur_step_ULong)(&p);
            break;
         case DW_UT_type:
         case DW_UT_split_type:
            /* type_signature = */ ML_(cur_step_ULong)(&p);
            /* type_offset = */ ui->dw64 ? ML_(cur_step_ULong)(&p)
                                         : (ULong)(ML_(cur_step_UInt)(&p));
            break;
         default:
            VG_(printf)( "### unhandled dwarf2 unit_type code 0x%x\n",
                         unit_type );
            break;
      }
   }

   /* End of this block */
   end_img = ML_(cur_plus)(unitblock_img, blklen + (ui->dw64 ? 12 : 4)); 

   /* Abbreviation data for this block */
   abbrev_img = ML_(cur_plus)(debugabbrev_img, atoffs);
   
   /* Read the compilation unit entry - this is always the first DIE.
    * See DWARF4 para 7.5. */
   if (ML_(cur_cmpLT)(p, end_img)) {
      UInt tag;

      acode = step_leb128U( &p ); /* abbreviation code */
      
      /* Read abbreviation header */
      abcode = step_leb128U( &abbrev_img ); /* abbreviation code */
      if ( acode != abcode ) {
         /* This isn't illegal, but somewhat unlikely. Normally the
          * first abbrev describes the first DIE, the compile_unit.
          * But maybe this abbreviation data is shared with another
          * or it is a NULL entry used for padding. See para 7.5.3. */
         abbrev_img = lookup_abbrev( ML_(cur_plus)(debugabbrev_img, atoffs),
                                     acode );
      }

      tag = step_leb128U( &abbrev_img );

      if ( tag != 0x0011 /*TAG_compile_unit*/
           && tag != 0x004a /*TAG_skeleton_unit*/ )
         return; /* Not a compile unit (might be partial) or broken DWARF. */

      /* DW_CHILDREN_yes or DW_CHILDREN_no */
      abbrev_img = ML_(cur_plus)(abbrev_img, 1);

      /* And loop on entries */
      for ( ; ; ) {
         /* Read entry definition */
         ULong    cval = -1LL;  /* Constant value read */
         DiCursor sval = DiCursor_INVALID; /* String value read */
         UInt     name = step_leb128U( &abbrev_img );
         UInt     form = step_leb128U( &abbrev_img );
         if (name == 0)
            break;
       
         /* Read data */
         /* Attributes encoding explained p 71 */
         if ( form == 0x16 /* FORM_indirect */ )
            form = step_leb128U( &p );
         /* Decode form. For most kinds, Just skip the amount of data since
            we don't use it for now */
         /* JRS 9 Feb 06: This now handles 64-bit DWARF too.  In
            64-bit DWARF, lineptr (and loclistptr,macptr,rangelistptr
            classes) use FORM_data8, not FORM_data4.  Also,
            FORM_ref_addr and FORM_strp are 64-bit values, not 32-bit
            values. */
         /* TJH 27 Apr 10: in DWARF 4 lineptr (and loclistptr,macptr,
            rangelistptr classes) use FORM_sec_offset which is 64 bits
            in 64 bit DWARF and 32 bits in 32 bit DWARF. */
         /* JRS 20 Apr 11: LLVM-2.9 encodes DW_AT_stmt_list using
            FORM_addr rather than the FORM_data4 that GCC uses.  Hence
            handle FORM_addr too. */
         switch( form ) {
            /* Those cases extract the data properly */
            case 0x05: /* FORM_data2 */
               cval = ML_(cur_step_UShort)(&p);
               break;
            case 0x06: /* FORM_data4 */
               cval = ML_(cur_step_UInt)(&p);
               break;
            case 0x0e: /* FORM_strp */      /* pointer in .debug_str */
               /* 2006-01-01: only generate a value if a debug_str
                  section was found) */
               if (ML_(cur_is_valid)(debugstr_img) && !ui->dw64)
                  sval = ML_(cur_plus)(debugstr_img, ML_(cur_read_UInt)(p));
               if (ML_(cur_is_valid)(debugstr_img) && ui->dw64)
                  sval = ML_(cur_plus)(debugstr_img, ML_(cur_read_ULong)(p));
               p = ML_(cur_plus)(p, ui->dw64 ? 8 : 4);
               break;
            case 0x1f: /* FORM_line_strp */ /* pointer in .debug_line_str */
               /* 2006-01-01: only generate a value if a debug_str
                  section was found) */
               if (ML_(cur_is_valid)(debuglinestr_img) && !ui->dw64)
                  sval = ML_(cur_plus)(debuglinestr_img,
                                       ML_(cur_read_UInt)(p));
               if (ML_(cur_is_valid)(debuglinestr_img) && ui->dw64)
                  sval = ML_(cur_plus)(debuglinestr_img,
                                       ML_(cur_read_ULong)(p));
               p = ML_(cur_plus)(p, ui->dw64 ? 8 : 4);
               break;
            case 0x08: /* FORM_string */
               sval = p;
               p = ML_(cur_plus)(p, ML_(cur_strlen)(p) + 1);
               break;
            case 0x0b: /* FORM_data1 */
               cval = ML_(cur_step_UChar)(&p);
               break;
            case 0x17: /* FORM_sec_offset */
               if (ui->dw64) {
                 cval = ML_(cur_step_ULong)(&p);
               } else {
                 cval = ML_(cur_step_UInt)(&p);
               };
               break;
            case 0x07: /* FORM_data8 */
               if (ui->dw64) cval = ML_(cur_read_ULong)(p);
               p = ML_(cur_plus)(p, 8);
               /* perhaps should assign unconditionally to cval? */
               break;
            case 0x21: /* FORM_implicit_const */
               cval = step_leb128S (&abbrev_img);
               break;
            /* TODO : Following ones just skip data - implement if you need */
            case 0x1e: /* FORM_data16 */
               p = ML_(cur_plus)(p, 16);
               break;
            case 0x01: /* FORM_addr */
               p = ML_(cur_plus)(p, addr_size);
               break;
            case 0x03: /* FORM_block2 */
               p = ML_(cur_plus)(p, ML_(cur_read_UShort)(p) + 2);
               break;
            case 0x04: /* FORM_block4 */
               p = ML_(cur_plus)(p, ML_(cur_read_UInt)(p) + 4);
               break;
            case 0x09:   /* FORM_block */     /* fallthrough */
            case 0x18: { /* FORM_exprloc */
               ULong block_len = step_leb128U(&p);
               p = ML_(cur_plus)(p, block_len);
               break;
            }
            case 0x0a: /* FORM_block1 */
               p = ML_(cur_plus)(p, ML_(cur_read_UChar)(p) + 1);
               break;
            case 0x0c: /* FORM_flag */
               p = ML_(cur_plus)(p, 1);
               break;
            case 0x0d: /* FORM_sdata */
               (void)step_leb128S(&p);
               break;
            case 0x0f: /* FORM_udata */
               (void)step_leb128U(&p);
               break;
            case 0x10: /* FORM_ref_addr */
               p = ML_(cur_plus)(p, (ver == 2) ? addr_size 
                                               : (ui->dw64 ? 8 : 4));
               break;
            case 0x11: /* FORM_ref1 */
               p = ML_(cur_plus)(p, 1);
               break;
            case 0x12: /* FORM_ref2 */
               p = ML_(cur_plus)(p, 2);
               break;
            case 0x13: /* FORM_ref4 */
               p = ML_(cur_plus)(p, 4);
               break;
            case 0x14: /* FORM_ref8 */
               p = ML_(cur_plus)(p, 8);
               break;
            case 0x15: /* FORM_ref_udata */
               (void)step_leb128U(&p);
               break;
            case 0x19: /* FORM_flag_present */
               break;
            case 0x1a: /* FORM_strx */
               (void)step_leb128U(&p);
               break;
            case 0x1b: /* FORM_addrx */
               (void)step_leb128U(&p);
               break;
            case 0x20: /* FORM_ref_sig8 */
               p = ML_(cur_plus)(p, 8);
               break;
            case 0x22: /* FORM_loclistx */
               (void)step_leb128U(&p);
               break;
            case 0x23: /* FORM_rnglistx */
               (void)step_leb128U(&p);
               break;
            case 0x25: /* FORM_strx1 */
               p = ML_(cur_plus)(p, 1);
               break;
            case 0x26: /* FORM_strx2 */
               p = ML_(cur_plus)(p, 2);
               break;
            case 0x27: /* FORM_strx3 */
               p = ML_(cur_plus)(p, 3);
               break;
            case 0x28: /* FORM_strx4 */
               p = ML_(cur_plus)(p, 4);
               break;
            case 0x29: /* FORM_addrx1 */
               p = ML_(cur_plus)(p, 1);
               break;
            case 0x2a: /* FORM_addrx2 */
               p = ML_(cur_plus)(p, 2);
               break;
            case 0x2b: /* FORM_addrx3 */
               p = ML_(cur_plus)(p, 3);
               break;
            case 0x2c: /* FORM_addrx4 */
               p = ML_(cur_plus)(p, 4);
               break;
            case 0x1f20: /* FORM_GNU_ref_alt */
               p = ML_(cur_plus)(p, ui->dw64 ? 8 : 4);
               break;
            case 0x1f21: /* FORM_GNU_strp_alt */
               if (ML_(cur_is_valid)(debugstr_alt_img) && !ui->dw64)
                  sval = ML_(cur_plus)(debugstr_alt_img,
                                       ML_(cur_read_UInt)(p));
               if (ML_(cur_is_valid)(debugstr_alt_img) && ui->dw64)
                  sval = ML_(cur_plus)(debugstr_alt_img,
                                       ML_(cur_read_ULong)(p));
               p = ML_(cur_plus)(p, ui->dw64 ? 8 : 4);
               break;

            default:
               VG_(printf)( "### unhandled dwarf2 abbrev form code 0x%x\n",
                            form );
               break;
         }
         
         /* Now store the members we need in the UnitInfo structure */
         if ( tag == 0x0011 /*TAG_compile_unit*/
              || tag == 0x004a /*TAG_skeleton_unit*/ ) {
                 if ( name == 0x03 ) ui->name = sval;      /* DW_AT_name */
            else if ( name == 0x1b ) ui->compdir = sval;   /* DW_AT_compdir */
            else if ( name == 0x10 ) ui->stmt_list = cval; /* DW_AT_stmt_list */
         }
      }
   } /* Just read the first DIE, if that wasn't the compile_unit then
      * this might have been a partial unit or broken DWARF info.
      * That's enough info for us, and we are not gdb ! */
}


////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////

/* Collect the debug info from DWARF3 debugging sections
 * of a given module.
 * 
 * Inputs: given .debug_xxx sections
 * Output: update di to contain all the DWARF3 debug infos
 */
void ML_(read_debuginfo_dwarf3)
        ( struct _DebugInfo* di,
          DiSlice escn_debug_info,      /* .debug_info */
          DiSlice escn_debug_types,     /* .debug_types */
          DiSlice escn_debug_abbv,      /* .debug_abbrev */
          DiSlice escn_debug_line,      /* .debug_line */
          DiSlice escn_debug_str,       /* .debug_str */
          DiSlice escn_debug_str_alt,   /* .debug_str */
          DiSlice escn_debug_line_str)  /* .debug_line_str */
{
   UnitInfo ui;
   UShort   ver;
   ULong    blklen;
   Bool     blklen_is_64;

   /* Make sure we at least have a header for the first block */
   if (escn_debug_info.szB < 4) {
      ML_(symerr)( di, True, 
                   "Last block truncated in .debug_info; ignoring" );
      return;
   }

   DiCursor block_img = DiCursor_INVALID;
   DiCursor end1_img  = ML_(cur_plus)( ML_(cur_from_sli)(escn_debug_info), 
                                       escn_debug_info.szB );
   Int blklen_len = 0;

   /* Iterate on all the blocks we find in .debug_info */
   for ( block_img = ML_(cur_from_sli)(escn_debug_info);
         ML_(cur_cmpLT)(block_img, ML_(cur_plus)(end1_img, -(DiOffT)4));
         block_img = ML_(cur_plus)(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 (ML_(cur_cmpGT)( ML_(cur_plus)(block_img, blklen + blklen_len),
                          end1_img )) {
         ML_(symerr)( di, True,
                      "Last block truncated in .debug_info; ignoring" );
         return;
      }

      /* version should be 2 */
      ver = ML_(cur_read_UShort)( ML_(cur_plus)(block_img, blklen_len) );
      if ( ver != 2 && ver != 3 && ver != 4 && ver != 5) {
         ML_(symerr)( di, True,
                      "Ignoring non-Dwarf2/3/4/5 block in .debug_info" );
         continue;
      }
      
      /* Fill ui with offset in .debug_line and compdir */
      if (0)
         VG_(printf)(
            "Reading UnitInfo at 0x%llx.....\n",
            (ULong)ML_(cur_minus)( block_img,
                                   ML_(cur_from_sli)(escn_debug_info)) );
      read_unitinfo_dwarf2( &ui, block_img, 
                                 ML_(cur_from_sli)(escn_debug_abbv),
                                 ML_(cur_from_sli)(escn_debug_str),
                                 ML_(cur_from_sli)(escn_debug_str_alt),
                                 ML_(cur_from_sli)(escn_debug_line_str));
      if (0) {
         HChar* str_name    = ML_(cur_read_strdup)(ui.name,    "di.rdd3.1");
         HChar* str_compdir = ML_(cur_read_strdup)(ui.compdir, "di.rdd3.2");
         VG_(printf)( "   => LINES=0x%llx    NAME=%s     DIR=%s\n", 
                      ui.stmt_list, str_name, str_compdir );
         ML_(dinfo_free)(str_name);
         ML_(dinfo_free)(str_compdir);
      }

      /* Ignore blocks with no .debug_line associated block */
      if ( ui.stmt_list == -1LL )
         continue;
      
      if (0) {
         HChar* str_name = ML_(cur_read_strdup)(ui.name, "di.rdd3.3");
         VG_(printf)("debug_line_sz %llu, ui.stmt_list %llu  %s\n",
                     escn_debug_line.szB, ui.stmt_list, str_name );
         ML_(dinfo_free)(str_name);
      }

      /* Read the .debug_line block for this compile unit */
      read_dwarf2_lineblock(
         di, &ui,
         ML_(cur_plus)(ML_(cur_from_sli)(escn_debug_line), ui.stmt_list),
         escn_debug_line.szB  - ui.stmt_list,
         ML_(cur_from_sli)(escn_debug_str),
         ML_(cur_from_sli)(escn_debug_line_str)
      );
   }
}


////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////

/*------------------------------------------------------------*/
/*--- Read DWARF1 format line number info.                 ---*/
/*------------------------------------------------------------*/

/* DWARF1 appears to be redundant, but nevertheless the Lahey Fortran
   compiler generates it.
*/

/* The following three enums (dwarf_tag, dwarf_form, dwarf_attribute)
   are taken from the file include/elf/dwarf.h in the GNU gdb-6.0
   sources, which are Copyright 1992, 1993, 1995, 1999 Free Software
   Foundation, Inc and naturally licensed under the GNU General Public
   License version 2 or later. 
*/

/* Tag names and codes.  */

enum dwarf_tag {
    TAG_padding			= 0x0000,
    TAG_array_type		= 0x0001,
    TAG_class_type		= 0x0002,
    TAG_entry_point		= 0x0003,
    TAG_enumeration_type	= 0x0004,
    TAG_formal_parameter	= 0x0005,
    TAG_global_subroutine	= 0x0006,
    TAG_global_variable		= 0x0007,
    				/* 0x0008 -- reserved */
				/* 0x0009 -- reserved */
    TAG_label			= 0x000a,
    TAG_lexical_block		= 0x000b,
    TAG_local_variable		= 0x000c,
    TAG_member			= 0x000d,
				/* 0x000e -- reserved */
    TAG_pointer_type		= 0x000f,
    TAG_reference_type		= 0x0010,
    TAG_compile_unit		= 0x0011,
    TAG_string_type		= 0x0012,
    TAG_structure_type		= 0x0013,
    TAG_subroutine		= 0x0014,
    TAG_subroutine_type		= 0x0015,
    TAG_typedef			= 0x0016,
    TAG_union_type		= 0x0017,
    TAG_unspecified_parameters	= 0x0018,
    TAG_variant			= 0x0019,
    TAG_common_block		= 0x001a,
    TAG_common_inclusion	= 0x001b,
    TAG_inheritance		= 0x001c,
    TAG_inlined_subroutine	= 0x001d,
    TAG_module			= 0x001e,
    TAG_ptr_to_member_type	= 0x001f,
    TAG_set_type		= 0x0020,
    TAG_subrange_type		= 0x0021,
    TAG_with_stmt		= 0x0022,

    /* GNU extensions */

    TAG_format_label		= 0x8000,  /* for FORTRAN 77 and Fortran 90 */
    TAG_namelist		= 0x8001,  /* For Fortran 90 */
    TAG_function_template	= 0x8002,  /* for C++ */
    TAG_class_template		= 0x8003   /* for C++ */
};

/* Form names and codes.  */

enum dwarf_form {
    FORM_ADDR	= 0x1,
    FORM_REF	= 0x2,
    FORM_BLOCK2	= 0x3,
    FORM_BLOCK4	= 0x4,
    FORM_DATA2	= 0x5,
    FORM_DATA4	= 0x6,
    FORM_DATA8	= 0x7,
    FORM_STRING	= 0x8
};

/* Attribute names and codes.  */

enum dwarf_attribute {
    AT_sibling			= (0x0010|FORM_REF),
    AT_location			= (0x0020|FORM_BLOCK2),
    AT_name			= (0x0030|FORM_STRING),
    AT_fund_type		= (0x0050|FORM_DATA2),
    AT_mod_fund_type		= (0x0060|FORM_BLOCK2),
    AT_user_def_type		= (0x0070|FORM_REF),
    AT_mod_u_d_type		= (0x0080|FORM_BLOCK2),
    AT_ordering			= (0x0090|FORM_DATA2),
    AT_subscr_data		= (0x00a0|FORM_BLOCK2),
    AT_byte_size		= (0x00b0|FORM_DATA4),
    AT_bit_offset		= (0x00c0|FORM_DATA2),
    AT_bit_size			= (0x00d0|FORM_DATA4),
				/* (0x00e0|FORM_xxxx) -- reserved */
    AT_element_list		= (0x00f0|FORM_BLOCK4),
    AT_stmt_list		= (0x0100|FORM_DATA4),
    AT_low_pc			= (0x0110|FORM_ADDR),
    AT_high_pc			= (0x0120|FORM_ADDR),
    AT_language			= (0x0130|FORM_DATA4),
    AT_member			= (0x0140|FORM_REF),
    AT_discr			= (0x0150|FORM_REF),
    AT_discr_value		= (0x0160|FORM_BLOCK2),
				/* (0x0170|FORM_xxxx) -- reserved */
				/* (0x0180|FORM_xxxx) -- reserved */
    AT_string_length		= (0x0190|FORM_BLOCK2),
    AT_common_reference		= (0x01a0|FORM_REF),
    AT_comp_dir			= (0x01b0|FORM_STRING),
        AT_const_value_string	= (0x01c0|FORM_STRING),
        AT_const_value_data2	= (0x01c0|FORM_DATA2),
        AT_const_value_data4	= (0x01c0|FORM_DATA4),
        AT_const_value_data8	= (0x01c0|FORM_DATA8),
        AT_const_value_block2	= (0x01c0|FORM_BLOCK2),
        AT_const_value_block4	= (0x01c0|FORM_BLOCK4),
    AT_containing_type		= (0x01d0|FORM_REF),
        AT_default_value_addr	= (0x01e0|FORM_ADDR),
        AT_default_value_data2	= (0x01e0|FORM_DATA2),
        AT_default_value_data4	= (0x01e0|FORM_DATA4),
        AT_default_value_data8	= (0x01e0|FORM_DATA8),
        AT_default_value_string	= (0x01e0|FORM_STRING),
    AT_friends			= (0x01f0|FORM_BLOCK2),
    AT_inline			= (0x0200|FORM_STRING),
    AT_is_optional		= (0x0210|FORM_STRING),
        AT_lower_bound_ref	= (0x0220|FORM_REF),
        AT_lower_bound_data2	= (0x0220|FORM_DATA2),
        AT_lower_bound_data4	= (0x0220|FORM_DATA4),
        AT_lower_bound_data8	= (0x0220|FORM_DATA8),
    AT_private			= (0x0240|FORM_STRING),
    AT_producer			= (0x0250|FORM_STRING),
    AT_program			= (0x0230|FORM_STRING),
    AT_protected		= (0x0260|FORM_STRING),
    AT_prototyped		= (0x0270|FORM_STRING),
    AT_public			= (0x0280|FORM_STRING),
    AT_pure_virtual		= (0x0290|FORM_STRING),
    AT_return_addr		= (0x02a0|FORM_BLOCK2),
    AT_abstract_origin		= (0x02b0|FORM_REF),
    AT_start_scope		= (0x02c0|FORM_DATA4),
    AT_stride_size		= (0x02e0|FORM_DATA4),
        AT_upper_bound_ref	= (0x02f0|FORM_REF),
        AT_upper_bound_data2	= (0x02f0|FORM_DATA2),
        AT_upper_bound_data4	= (0x02f0|FORM_DATA4),
        AT_upper_bound_data8	= (0x02f0|FORM_DATA8),
    AT_virtual			= (0x0300|FORM_STRING),

    /* GNU extensions.  */

    AT_sf_names			= (0x8000|FORM_DATA4),
    AT_src_info			= (0x8010|FORM_DATA4),
    AT_mac_info			= (0x8020|FORM_DATA4),
    AT_src_coords		= (0x8030|FORM_DATA4),
    AT_body_begin		= (0x8040|FORM_ADDR),
    AT_body_end			= (0x8050|FORM_ADDR)
};

/* end of enums taken from gdb-6.0 sources */
#if 0
void ML_(read_debuginfo_dwarf1) ( 
        struct _DebugInfo* di, 
        UChar* dwarf1d, Int dwarf1d_sz, 
        UChar* dwarf1l, Int dwarf1l_sz )
{
   UInt   stmt_list;
   Bool   stmt_list_found;
   Int    die_offset, die_szb, at_offset;
   UShort die_kind, at_kind;
   UChar* at_base;
   HChar* src_filename;

   if (0) 
      VG_(printf)("read_debuginfo_dwarf1 ( %p, %d, %p, %d )\n",
	          dwarf1d, dwarf1d_sz, dwarf1l, dwarf1l_sz );

   /* This loop scans the DIEs. */
   die_offset = 0;
   while (True) {
      if (die_offset >= dwarf1d_sz) break;

      die_szb  = ML_(read_Int)(dwarf1d + die_offset);
      die_kind = ML_(read_UShort)(dwarf1d + die_offset + 4);

      /* We're only interested in compile_unit DIEs; ignore others. */
      if (die_kind != TAG_compile_unit) {
         die_offset += die_szb;
         continue; 
      }

      if (0) 
         VG_(printf)("compile-unit DIE: offset %d, tag 0x%x, size %d\n", 
                     die_offset, (Int)die_kind, die_szb );

      /* We've got a compile_unit DIE starting at (dwarf1d +
         die_offset+6).  Try and find the AT_name and AT_stmt_list
         attributes.  Then, finally, we can read the line number info
         for this source file. */

      /* The next 3 are set as we find the relevant attrs. */
      src_filename    = NULL;
      stmt_list_found = False;
      stmt_list       = 0;

      /* This loop scans the Attrs inside compile_unit DIEs. */
      at_base = dwarf1d + die_offset + 6;
      at_offset = 0;
      while (True) {
         if (at_offset >= die_szb-6) break;

         at_kind = ML_(read_UShort)(at_base + at_offset);
         if (0) VG_(printf)("atoffset %d, attag 0x%x\n", 
                            at_offset, (Int)at_kind );
         at_offset += 2; /* step over the attribute itself */
	 /* We have to examine the attribute to figure out its
            length. */
         switch (at_kind) {
            case AT_stmt_list:
            case AT_language:
            case AT_sibling:
               if (at_kind == AT_stmt_list) {
                  stmt_list_found = True;
                  stmt_list = ML_(read_Int)(at_base+at_offset);
               }
               at_offset += 4; break;
            case AT_high_pc:
            case AT_low_pc: 
               at_offset += sizeof(void*); break;
            case AT_name: 
            case AT_producer:
            case AT_comp_dir:
               /* Zero terminated string, step over it. */
               if (at_kind == AT_name)
                 src_filename = (HChar *)(at_base + at_offset);
               while (at_offset < die_szb-6 && at_base[at_offset] != 0)
                  at_offset++;
               at_offset++;
               break;
            default: 
               VG_(printf)("Unhandled DWARF-1 attribute 0x%x\n", 
                           (Int)at_kind );
               VG_(core_panic)("Unhandled DWARF-1 attribute");
         } /* switch (at_kind) */
      } /* looping over attributes */

      /* So, did we find the required stuff for a line number table in
         this DIE?  If yes, read it. */
      if (stmt_list_found /* there is a line number table */
          && src_filename != NULL /* we know the source filename */
         ) {
         /* Table starts:
               Length: 
                  4 bytes, includes the entire table
               Base address: 
                  unclear (4? 8?), assuming native pointer size here.
            Then a sequence of triples
               (source line number -- 32 bits
                source line column -- 16 bits
                address delta -- 32 bits)
	 */
         Addr   base;
	 Int    len;
         HChar* curr_filenm;
         UChar* ptr;
         UInt   prev_line, prev_delta;

         curr_filenm = ML_(addStr) ( di, src_filename, -1 );
         prev_line = prev_delta = 0;

         ptr = dwarf1l + stmt_list;
         len  = ML_(read_Int)(ptr);  ptr += sizeof(Int);
         base = ML_(read_Addr)(ptr); ptr += sizeof(void*);
         len -= (sizeof(Int) + sizeof(void*));
         while (len > 0) {
            UInt   line;
            UShort col;
            UInt   delta;
            line = ML_(read_UInt)(ptr);    ptr += sizeof(UInt);
            col  = ML_(read_UShort)(ptr);  ptr += sizeof(UShort);
            delta = ML_(read_UInt)(ptr);   ptr += sizeof(UInt);
	    if (0) VG_(printf)("line %d, col %d, delta %d\n", 
                               line, (Int)col, delta );
            len -= (sizeof(UInt) + sizeof(UShort) + sizeof(UInt));

	    if (delta > 0 && prev_line > 0) {
	       if (0) VG_(printf) ("     %d  %d-%d\n",
                                   prev_line, prev_delta, delta-1);
	       ML_(addLineInfo) ( di, curr_filenm, NULL,
		 	          base + prev_delta, base + delta,
			          prev_line, 0 );
	    }
	    prev_line = line;
	    prev_delta = delta;
	 }        
      }  

      /* Move on the next DIE. */
      die_offset += die_szb;

   } /* Looping over DIEs */

}
#endif

/*------------------------------------------------------------*/
/*--- Read call-frame info from an .eh_frame section       ---*/
/*------------------------------------------------------------*/

/* Sources of info:

   The DWARF3 spec, available from http://www.dwarfstd.org/Download.php 

   This describes how to read CFA data from .debug_frame sections.
   So as to maximise everybody's annoyance and confusion, .eh_frame
   sections are almost the same as .debug_frame sections, but differ
   in a few subtle and ill documented but important aspects.

   Generic ELF Specification, sections 7.5 (DWARF Extensions) and 7.6
   (Exception Frames), available from

   http://www.linux-foundation.org/spec/book/ELF-generic/ELF-generic.html

   This really does describe .eh_frame, at least the aspects that
   differ from standard DWARF3.  It's better than guessing, and
   (marginally) more fun than reading the gdb source code.
*/

/* Useful info ..

   In general:
   gdb-6.3/gdb/dwarf2-frame.c

   gdb-6.3/gdb/i386-tdep.c:

   DWARF2/GCC uses the stack address *before* the function call as a
   frame's CFA.  [jrs: I presume this means %esp before the call as
   the CFA]. 

   JRS: on amd64, the dwarf register numbering is, as per
   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

      8  ...  15
      R8 ... R15

      16 is the return address (RIP)
      "The table defines Return Address to have a register number,
      even though the address is stored in 0(%rsp) and not in a 
      physical register."

      17   ...   24
      XMM0 ... XMM7

      25   ...    32
      XMM8 ... XMM15

      33   ...   40
      ST0  ...  ST7

      41   ...   48
      MM0  ...  MM7

      49                  RFLAGS
      50,51,52,53,54,55   ES,CS,SS,DS,FS,GS
      58                  FS.BASE  (what's that?)
      59                  GS.BASE  (what's that?)
      62                  TR (task register)
      63                  LDTR (LDT register)
      64                  MXCSR
      65                  FCW (x87 control word)
      66                  FSW (x86 status word)

   On x86 I cannot find any documentation.  It _appears_ to be the
   actual instruction encoding, viz:

      0    1    2    3    4    5    6    7
      EAX  ECX  EDX  EBX  ESP  EBP  ESI  EDI

      8 is the return address (EIP) */


/* Comments re DW_CFA_set_loc, 16 Nov 06.

   JRS:
   Someone recently sent me a libcrypto.so.0.9.8 as distributed with
   Ubuntu of some flavour, compiled with gcc 4.1.2 on amd64.  It
   causes V's CF reader to complain a lot:

   >> --19976-- DWARF2 CFI reader: unhandled CFI instruction 0:24
   >> --19976-- DWARF2 CFI reader: unhandled CFI instruction 0:24
   >> --19976-- DWARF2 CFI reader: unhandled CFI instruction 0:24
   >> --19976-- DWARF2 CFI reader: unhandled CFI instruction 0:24
   >> --19976-- DWARF2 CFI reader: unhandled CFI instruction 0:48
   >> --19976-- DWARF2 CFI reader: unhandled CFI instruction 0:24

   After chasing this around a bit it seems that the CF bytecode
   parser lost sync at a DW_CFA_set_loc, which has a single argument
   denoting an address.

   As it stands that address is extracted by read_Addr().  On amd64
   that just fetches 8 bytes regardless of anything else.

   read_encoded_Addr() is more sophisticated.  This appears to take
   into account some kind of encoding flag.  When I replace the uses
   of read_Addr by read_encoded_Addr for DW_CFA_set_loc, the
   complaints go away, there is no loss of sync, and the parsed CF
   instructions are the same as shown by readelf --debug-dump=frames.

   So it seems a plausible fix.  The problem is I looked in the DWARF3
   spec and completely failed to figure out whether or not the arg to
   DW_CFA_set_loc is supposed to be encoded in a way suitable for
   read_encoded_Addr, nor for that matter any description of what it
   is that read_encoded_Addr is really decoding.

   TomH:
   The problem is that the encoding is not standard - the eh_frame
   section uses the same encoding as the dwarf_frame section except
   for a few small changes, and this is one of them. So this is not
   something the DWARF standard covers.

   There is an augmentation string to indicate what is going on though
   so that programs can recognise it.

   What we are doing seems to match what gdb 6.5 and libdwarf 20060614
   do though. I'm not sure about readelf though.

   (later): Well dwarfdump barfs on it:

      dwarfdump ERROR:  dwarf_get_fde_info_for_reg:  
                        DW_DLE_DF_FRAME_DECODING_ERROR(193) (193)

   I've looked at binutils as well now, and the code in readelf agrees
   with your patch - ie it treats set_loc as having an encoded address
   if there is a zR augmentation indicating an encoding.

   Quite why gdb and libdwarf don't understand this is an interesting
   question...

   Final outcome: all uses of read_Addr were replaced by
   read_encoded_Addr.  A new type AddressDecodingInfo was added to
   make it relatively clean to plumb through the extra info needed by
   read_encoded_Addr.
*/

/* More badness re address encoding, 12 Jan 07.

   Most gcc provided CIEs have a "zR" augmentation, which means they
   supply their own address encoding, and that works fine.  However,
   some icc9 supplied CIEs have no augmentation, which means they use
   the default_Addr_encoding().  That says to use a machine-word sized
   value, literally unmodified.

   Since .so's are, in general, relocated when loaded, having absolute
   addresses in the CFI data makes no sense when read_encoded_Addr is
   used to find the initial location for a FDE.  The resulting saga:

   TomH:
   > I'm chasing a stack backtrace failure for an amd64 .so which was 
   > created I believe by icc 9.1.  After a while I wound up looking at
   > this: (readdwarf.c)
   >
   >   5083        tom static UChar default_Addr_encoding ( void )
   >   3584        tom {
   >   3584        tom    switch (sizeof(Addr)) {
   >   3584        tom       case 4: return DW_EH_PE_udata4;
   >   3584        tom       case 8: return DW_EH_PE_udata8;
   >   3584        tom       default: vg_assert(0);
   >   3584        tom    }
   >   3584        tom }
   >
   > If a CIE does not have an "augmentation string" (typically "zR") then 
   > addresses are decoded as described by default_Addr_encoding.  If there
   > is an 'R' in the augmentation string then the encoding to use 
   > is specified by the CIE itself, which works fine with GCC compiled code
   > since that always appears to specify zR.

   Correct.

   > Problem is this .so has no augmentation string and so uses the
   > default encoding, viz DW_EH_PE_udata8.  That appears to mean
   > "read a 64 bit number" and use that as-is (for the starting value
   > of the program counter when running the CFA program).

   Strictly speaking the default is DW_EH_PE_absptr, but that amounts
   to either udata4 or udata8 depending on the platform's pointer size
   which is a shortcut I used.

   > For this .so that gives nonsense (very small) PCs which are later
   > rejected by the sanity check which ensures PC ranges fall inside
   > the mapped text segment.  It seems like the .so expects to have the
   > start VMA of the text segment added on.  This would correspond to
   >
   >   static UChar default_Addr_encoding ( void )
   >   {
   >      switch (sizeof(Addr)) {
   >         case 4: return DW_EH_PE_textrel + DW_EH_PE_udata4;
   >         case 8: return DW_EH_PE_textrel + DW_EH_PE_udata8;
   >         default: vg_assert(0);
   >      }
   >   }

   The problem you're seeing is that you have absolute pointers inside
   a shared library, which obviously makes little sense on the face of
   things as how would the linker know where the library will be
   loaded?

   The answer of course is that it doesn't, so if it points absolute
   pointers in the frame unwind data is has to include relocations for
   them, and I'm betting that if you look at the relocations in the
   library you will there are some for that data.

   That is fine of course when ld.so maps the library - it will
   relocate the eh_frame data as it maps it (or prelinking will
   already have done so) and when the g++ exception code kicks in and
   unwinds the stack it will see relocated data.

   We of course are mapping the section from the ELF file ourselves
   and are not applying the relocations, hence the problem you are
   seeing.

   Strictly speaking we should apply the relocations but the cheap
   solution is essentially to do what you've done - strictly speaking
   you should adjust by the difference between the address the library
   was linked for and the address it has been loaded at, but a shared
   library will normally be linked for address zero I believe. It's
   possible that prelinking might change that though?

   JRS:
   That all syncs with what I am seeing.

   So what I am inclined to do is:

   - Leave default_Addr_encoding as it is

   - Change read_encoded_Addr's handling of "case DW_EH_PE_absptr" so
     it sets base to, as you say, the difference between the address
     the library was linked for and the address it has been loaded at
     (== the SegInfo's text_bias)

   Does that sound sane?  I think it should even handle the prelinked
   case.

   (JRS, later)

   Hmm.  Plausible as it sounds, it doesn't work.  It now produces
   bogus backtraces for locations inside the (statically linked)
   memcheck executable.

   Besides, there are a couple of other places where read_encoded_Addr
   is used -- one of which is used to establish the length of the
   address range covered by the current FDE:

         fde_arange = read_encoded_Addr(&nbytes, &adi, data);

   and it doesn't seem to make any sense for read_encoded_Addr to add
   on the text segment bias in that context.  The DWARF3 spec says
   that both the initial_location and address_range (length) fields
   are encoded the same way ("target address"), so it is unclear at
   what stage in the process it would be appropriate to relocate the
   former but not the latter.

   One unprincipled kludge that does work is the following: just
   before handing one of the address range fragments off to
   ML_(addDiCfSI) for permanent storage, check its start address.  If
   that is very low (less than 2 M), and is far below the mapped text
   segment, and adding the text bias would move the fragment entirely
   inside the mapped text segment, then do so.  A kind of kludged
   last-minute relocation, if you like.

   12 Jan 07: committing said kludge (see kludge_then_addDiCfSI).  If
   the situation clarifies, it can easily enough be backed out and
   replaced by a better fix.
*/

/* --------------- Decls --------------- */

#if defined(VGP_x86_linux) || defined(VGP_x86_solaris) || defined(VGP_x86_freebsd)
#  define FP_REG         5
#  define SP_REG         4
#  define RA_REG_DEFAULT 8
#elif defined(VGP_amd64_linux) || defined(VGP_amd64_solaris) || defined(VGP_amd64_freebsd)
#  define FP_REG         6
#  define SP_REG         7
#  define RA_REG_DEFAULT 16
#elif defined(VGP_ppc32_linux)
#  define FP_REG         1
#  define SP_REG         1
#  define RA_REG_DEFAULT 65
#elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux)
#  define FP_REG         1
#  define SP_REG         1
#  define RA_REG_DEFAULT 65
#elif defined(VGP_arm_linux)
#  define FP_REG         12
#  define SP_REG         13
#  define RA_REG_DEFAULT 14
#elif defined(VGP_arm64_linux) || defined(VGP_arm64_freebsd)
#  define FP_REG         29
#  define SP_REG         31
#  define RA_REG_DEFAULT 30
#elif defined(VGP_x86_darwin)
#  define FP_REG         5
#  define SP_REG         4
#  define RA_REG_DEFAULT 8
#elif defined(VGP_amd64_darwin)
#  define FP_REG         6
#  define SP_REG         7
#  define RA_REG_DEFAULT 16
#elif defined(VGP_s390x_linux)
#  define FP_REG         11    // sometimes s390 has a frame pointer in r11
#  define SP_REG         15    // stack is always r15
#  define RA_REG_DEFAULT 14    // the return address is in r14
#elif defined(VGP_mips32_linux) || defined(VGP_nanomips_linux)
#  define FP_REG         30
#  define SP_REG         29
#  define RA_REG_DEFAULT 31
#elif defined(VGP_mips64_linux)
#  define FP_REG         30
#  define SP_REG         29
#  define RA_REG_DEFAULT 31
#elif defined(VGP_riscv64_linux)
#  define FP_REG         8
#  define SP_REG         2
#  define RA_REG_DEFAULT 1
#else
#  error "Unknown platform"
#endif

/* The number of regs we are prepared to unwind.  The number for
   arm-linux (320) seems ludicrously high, but the ARM IHI 0040A page
   7 (DWARF for the ARM Architecture) specifies that values up to 320
   might exist, for Neon/VFP-v3. */
#if defined(VGP_ppc32_linux) || defined(VGP_ppc64be_linux) \
     || defined(VGP_ppc64le_linux) || defined(VGP_mips32_linux) \
     || defined(VGP_nanomips_linux) || defined(VGP_mips64_linux)
# define N_CFI_REGS 72
#elif defined(VGP_arm_linux)
# define N_CFI_REGS 320
#elif defined(VGP_arm64_linux) || defined(VGP_arm64_freebsd)
# define N_CFI_REGS 128
#elif defined(VGP_s390x_linux)
# define N_CFI_REGS 66
#elif defined(VGP_riscv64_linux)
# define N_CFI_REGS 128
#else
# define N_CFI_REGS 20
#endif

/* Instructions for the automaton */
enum dwarf_cfa_primary_ops
  {
    DW_CFA_use_secondary = 0,
    DW_CFA_advance_loc   = 1,
    DW_CFA_offset        = 2,
    DW_CFA_restore       = 3
  };

enum dwarf_cfa_secondary_ops
  {
    DW_CFA_nop                = 0x00,
    DW_CFA_set_loc            = 0x01,
    DW_CFA_advance_loc1       = 0x02,
    DW_CFA_advance_loc2       = 0x03,
    DW_CFA_advance_loc4       = 0x04,
    DW_CFA_offset_extended    = 0x05,
    DW_CFA_restore_extended   = 0x06,
    DW_CFA_undefined          = 0x07,
    DW_CFA_same_value         = 0x08,
    DW_CFA_register           = 0x09,
    DW_CFA_remember_state     = 0x0a,
    DW_CFA_restore_state      = 0x0b,
    DW_CFA_def_cfa            = 0x0c,
    DW_CFA_def_cfa_register   = 0x0d,
    DW_CFA_def_cfa_offset     = 0x0e,
    DW_CFA_def_cfa_expression = 0x0f, /* DWARF3 only */
    DW_CFA_expression         = 0x10, /* DWARF3 only */
    DW_CFA_offset_extended_sf = 0x11, /* DWARF3 only */
    DW_CFA_def_cfa_sf         = 0x12, /* DWARF3 only */
    DW_CFA_def_cfa_offset_sf  = 0x13, /* DWARF3 only */
    DW_CFA_val_offset         = 0x14, /* DWARF3 only */
    DW_CFA_val_offset_sf      = 0x15, /* DWARF3 only */
    DW_CFA_val_expression     = 0x16, /* DWARF3 only */
    DW_CFA_lo_user            = 0x1c,
    DW_CFA_GNU_window_save    = 0x2d, /* GNU extension */
    DW_CFA_GNU_args_size      = 0x2e, /* GNU extension */
    DW_CFA_GNU_negative_offset_extended = 0x2f, /* GNU extension */
    DW_CFA_ORCL_arg_loc       = 0x30, /* Oracle extension */
    DW_CFA_hi_user            = 0x3f
  };

#define DW_EH_PE_absptr		0x00
#define DW_EH_PE_omit		0xff

#define DW_EH_PE_uleb128	0x01
#define DW_EH_PE_udata2		0x02
#define DW_EH_PE_udata4		0x03
#define DW_EH_PE_udata8		0x04
#define DW_EH_PE_sleb128	0x09
#define DW_EH_PE_sdata2		0x0A
#define DW_EH_PE_sdata4		0x0B
#define DW_EH_PE_sdata8		0x0C
#define DW_EH_PE_signed		0x08

#define DW_EH_PE_pcrel		0x10
#define DW_EH_PE_textrel	0x20
#define DW_EH_PE_datarel	0x30
#define DW_EH_PE_funcrel	0x40
#define DW_EH_PE_aligned	0x50

#define DW_EH_PE_indirect	0x80


/* RegRule and UnwindContext are used temporarily to do the unwinding.
   The result is then summarised into a sequence of CfiSIs, if
   possible.  UnwindContext effectively holds the state of the
   abstract machine whilst it is running.

   The CFA can either be a signed offset from a register,
   or an expression:

   CFA = cfa_reg + cfa_off   when UnwindContext.cfa_is_regoff==True
       | [[ cfa_expr_id ]]

   When .cfa_is_regoff == True,  cfa_expr_id must be zero
   When .cfa_is_regoff == False, cfa_reg must be zero
                                 and cfa_off must be zero

   RegRule describes, for each register, how to get its
   value in the previous frame, where 'cfa' denotes the cfa
   for the frame as a whole:

   RegRule = RR_Undef          -- undefined
           | RR_Same           -- same as in previous frame
           | RR_CFAOff    arg  -- is at * ( cfa + arg )
           | RR_CFAValOff arg  -- is ( cfa + arg )
           | RR_Reg       arg  -- is in register 'arg' 
           | RR_Expr      arg  -- is at * [[ arg ]]
           | RR_ValExpr   arg  -- is [[ arg ]]

   Note that RR_Expr is redundant since the same can be represented
   using RR_ValExpr with an explicit dereference (CfiExpr_Deref) at
   the outermost level.

   All expressions are stored in exprs in the containing
   UnwindContext.  Since the UnwindContext gets reinitialised for each
   new FDE, summarise_context needs to copy out any expressions it
   wants to keep into the cfsi_exprs field of the containing SegInfo.
*/
typedef
   struct {
      enum { RR_Undef, RR_Same, RR_CFAOff, RR_CFAValOff, 
             RR_Reg, /*RR_Expr,*/ RR_ValExpr } tag;
      /* meaning:  int offset for CFAoff/CFAValOff
                   reg # for Reg
                   expr index for Expr/ValExpr */
      Int arg;
   }
   RegRule;

static void ppRegRule ( const XArray* exprs, const RegRule* rrule )
{
   vg_assert(exprs);
   switch (rrule->tag) {
      case RR_Undef:     VG_(printf)("u  "); break;
      case RR_Same:      VG_(printf)("s  "); break;
      case RR_CFAOff:    VG_(printf)("c%d ", rrule->arg); break;
      case RR_CFAValOff: VG_(printf)("v%d ", rrule->arg); break;
      case RR_Reg:       VG_(printf)("dwReg%d ", rrule->arg); break;
      case RR_ValExpr:   VG_(printf)("ve{"); 
                         ML_(ppCfiExpr)( exprs, rrule->arg ); 
                         VG_(printf)("} "); 
                         break;
      default:           VG_(core_panic)("ppRegRule");
   }
}


/* Size of the stack of register unwind rules.  This is only
   exceedingly rarely used, so a stack of size 1 should actually work
   with almost all compiler-generated CFA. */
#define N_RR_STACK 4

typedef
   struct {
      /* Read-only fields (set by the CIE) */
      Int     code_a_f;
      Int     data_a_f;
      Addr    initloc;
      Int     ra_reg;
      /* The rest of these fields can be modified by
         run_CF_instruction. */
      /* The LOC entry */
      Addr    loc;
      /* We need a stack of these in order to handle
         DW_CFA_{remember,restore}_state. */
      struct UnwindContextState {
          /* The CFA entry.  This can be either reg+/-offset or an expr. */
          Bool    cfa_is_regoff; /* True=>is reg+offset; False=>is expr */
          Int     cfa_reg;
          Int     cfa_off;  /* in bytes */
          Int     cfa_expr_ix; /* index into cfa_exprs */
          /* Register unwind rules.  */
          RegRule reg[N_CFI_REGS];
      }
      state[N_RR_STACK];
      Int     state_sp; /* 0 <= state_sp < N_RR_STACK; points at the
                           currently-in-use rule set. */
      /* array of CfiExpr, shared by reg[] and cfa_expr_ix */
      XArray* exprs;
   }
   UnwindContext;

static void ppUnwindContext ( const UnwindContext* ctx )
{
   Int j, i;
   VG_(printf)("0x%llx: ", (ULong)ctx->loc);
   for (j = 0; j <= ctx->state_sp; j++) {
      const struct UnwindContextState* ctxs = &ctx->state[j];
      VG_(printf)("%s[%d]={ ", j > 0 ? " " : "", j);
      if (ctxs->cfa_is_regoff) {
         VG_(printf)("%d(r%d) ", ctxs->cfa_off, ctxs->cfa_reg);
      } else {
         vg_assert(ctx->exprs);
         VG_(printf)("{");
         ML_(ppCfiExpr)( ctx->exprs, ctxs->cfa_expr_ix );
         VG_(printf)("} ");
      }
      VG_(printf)("{ ");
      for (i = 0; i < N_CFI_REGS; i++)
         ppRegRule(ctx->exprs, &ctxs->reg[i]);
      VG_(printf)("}");
   }
   VG_(printf)("\n");
}

static void initUnwindContext ( /*OUT*/UnwindContext* ctx )
{
   Int j, i;
   VG_(memset)(ctx, 0, sizeof(*ctx));
   /* ctx->code_a_f   = 0;
   ctx->data_a_f      = 0;
   ctx->initloc       = 0; */
   ctx->ra_reg        = RA_REG_DEFAULT;
   /* ctx->loc        = 0;
   ctx->exprs         = NULL;
   ctx->state_sp        = 0; */
   for (j = 0; j < N_RR_STACK; j++) {
      ctx->state[j].cfa_is_regoff = True;
      /* ctx->state[j].cfa_reg    = 0;
      ctx->state[j].cfa_off       = 0;
      ctx->state[j].cfa_expr_ix   = 0; */
      for (i = 0; i < N_CFI_REGS; i++) {
         if (RR_Undef != 0)
           ctx->state[j].reg[i].tag = RR_Undef;
         /* ctx->state[j].reg[i].arg = 0; */
      }
#     if defined(VGA_arm)
      /* All callee-saved registers (or at least the ones we are
         summarising for) should start out as RR_Same, on ARM. */
      ctx->state[j].reg[11].tag = RR_Same;
      /* ctx->state[j].reg[13].tag = RR_Same; */
      ctx->state[j].reg[14].tag = RR_Same;
      ctx->state[j].reg[12].tag = RR_Same;
      ctx->state[j].reg[7].tag  = RR_Same;
      /* this can't be right though: R12 (IP) isn't callee saved. */
#     elif defined(VGA_arm64)
      /* Callee-saved registers (that we are interested in) should
         start out as RR_Same. */
      ctx->state[j].reg[29/*FP*/].tag = RR_Same;
      ctx->state[j].reg[30/*LR*/].tag = RR_Same;
#     elif defined(VGA_riscv64)
      /* Registers fp and ra start out implicitly as RR_Same. */
      ctx->state[j].reg[FP_REG].tag = RR_Same;
      ctx->state[j].reg[RA_REG_DEFAULT].tag = RR_Same;
#     endif
   }
}


/* A structure which holds information needed by read_encoded_Addr(). 
*/
typedef
   struct {
      UChar    encoding;
      DiCursor ehframe_image;
      Addr     ehframe_avma;
      Addr     text_bias;
      Addr     got_avma;
   }
   AddressDecodingInfo;


/* ------------ Deal with summary-info records ------------ */

/* --------------- Summarisation --------------- */

/* Forward */
static 
Int copy_convert_CfiExpr_tree ( XArray* dst, const UnwindContext* srcuc, 
                                Int nd );

/* Summarise ctx into si, if possible.  Returns True if successful.
   This is taken to be just after ctx's loc advances; hence the
   summary is up to but not including the current loc.  This works
   on both x86 and amd64.
*/
static Bool summarise_context(/*OUT*/Addr* base,
                              /*OUT*/UInt* len,
                              /*OUT*/DiCfSI_m* si_m,
                               Addr loc_start,
	                       const UnwindContext* ctx,
                               DebugInfo* debuginfo )
{
   Int why = 0;
   const struct UnwindContextState* ctxs;

   *base = 0;
   *len = 0;
   VG_(bzero_inline)(si_m, sizeof(*si_m));

#  if defined(VGP_s390x_linux)
   #define is_s390x_linux True
#  else
   #define is_s390x_linux False
#  endif

   /* Guard against obviously stupid settings of the reg-rule stack
      pointer. */
   if (ctx->state_sp < 0)           { why = 8; goto failed; }
   if (ctx->state_sp >= N_RR_STACK) { why = 9; goto failed; }
   ctxs = &ctx->state[ctx->state_sp];

   /* First, summarise the method for generating the CFA */
   if (!ctxs->cfa_is_regoff) {
      /* it was set by DW_CFA_def_cfa_expression; try to convert */
      XArray *src, *dst;
      Int    conv;
      src = ctx->exprs;
      dst = debuginfo->cfsi_exprs;
      if (src && (VG_(sizeXA)(src) > 0) && (!dst)) {
         dst = VG_(newXA)( ML_(dinfo_zalloc), "di.ccCt.1", ML_(dinfo_free),
                           sizeof(CfiExpr) );
         debuginfo->cfsi_exprs = dst;
      }
      conv = copy_convert_CfiExpr_tree
                    ( dst, ctx, ctxs->cfa_expr_ix );
      vg_assert(conv >= -1);
      if (conv == -1) { why = 6; goto failed; }
      si_m->cfa_how = CFIC_EXPR;
      si_m->cfa_off = conv;
      if (0 && debuginfo->ddump_frames)
         ML_(ppCfiExpr)(dst, conv);
   }
   else
   if (ctxs->cfa_is_regoff && ctxs->cfa_reg == SP_REG) {
      si_m->cfa_off = ctxs->cfa_off;
#     if defined(VGA_x86) || defined(VGA_amd64) || defined(VGA_s390x) \
         || defined(VGA_mips32) || defined(VGA_nanomips) \
         || defined(VGA_mips64) || defined(VGA_riscv64)
      si_m->cfa_how = CFIC_IA_SPREL;
#     elif defined(VGA_arm)
      si_m->cfa_how = CFIC_ARM_R13REL;
#     elif defined(VGA_arm64)
      si_m->cfa_how = CFIC_ARM64_SPREL;
#     else
      si_m->cfa_how = 0; /* invalid */
#     endif
   }
   else
   if (ctxs->cfa_is_regoff && ctxs->cfa_reg == FP_REG) {
      si_m->cfa_off = ctxs->cfa_off;
#     if defined(VGA_x86) || defined(VGA_amd64) || defined(VGA_s390x) \
         || defined(VGA_mips32) || defined(VGA_nanomips) \
         || defined(VGA_mips64) || defined(VGA_riscv64)
      si_m->cfa_how = CFIC_IA_BPREL;
#     elif defined(VGA_arm)
      si_m->cfa_how = CFIC_ARM_R12REL;
#     elif defined(VGA_arm64)
      si_m->cfa_how = CFIC_ARM64_X29REL;
#     else
      si_m->cfa_how = 0; /* invalid */
#     endif
   }
#  if defined(VGA_arm)
   else
   if (ctxs->cfa_is_regoff && ctxs->cfa_reg == 11/*??_REG*/) {
      si_m->cfa_how = CFIC_ARM_R11REL;
      si_m->cfa_off = ctxs->cfa_off;
   }
   else
   if (ctxs->cfa_is_regoff && ctxs->cfa_reg == 7/*??_REG*/) {
      si_m->cfa_how = CFIC_ARM_R7REL;
      si_m->cfa_off = ctxs->cfa_off;
   }
#  elif defined(VGA_arm64)
   // do we need any arm64 specifics here?
#  endif
   else {
      why = 1;
      goto failed;
   }

#  define SUMMARISE_HOW(_how, _off, _ctxreg)                  \
   _how = CFIR_UNKNOWN; /* install safe initial values */     \
   _off = 0;                                                  \
   switch (_ctxreg.tag) {                                     \
      case RR_Undef:                                          \
         _how = CFIR_UNKNOWN;   _off = 0; break;              \
      case RR_Same:                                           \
         _how = CFIR_SAME;      _off = 0; break;              \
      case RR_CFAOff:                                         \
         _how = CFIR_MEMCFAREL; _off = _ctxreg.arg; break;    \
      case RR_CFAValOff:                                      \
         _how = CFIR_CFAREL;    _off = _ctxreg.arg; break;    \
      case RR_ValExpr: {                                      \
         XArray *src, *dst;                                   \
         Int    conv;                                         \
         src = ctx->exprs;                                    \
         dst = debuginfo->cfsi_exprs;                         \
         if (src && (VG_(sizeXA)(src) > 0) && (!dst)) {       \
            dst = VG_(newXA)( ML_(dinfo_zalloc),              \
                              "di.ccCt.2",                    \
                              ML_(dinfo_free),                \
                              sizeof(CfiExpr) );              \
            debuginfo->cfsi_exprs = dst;                      \
         }                                                    \
         conv = copy_convert_CfiExpr_tree                     \
                       ( dst, ctx, _ctxreg.arg );             \
         vg_assert(conv >= -1);                               \
         if (conv == -1) { why = 7; goto failed; }            \
         _how = CFIR_EXPR;                                    \
         _off = conv;                                         \
         if (0 && debuginfo->ddump_frames)                    \
            ML_(ppCfiExpr)(dst, conv);                        \
         break;                                               \
      }                                                       \
      case RR_Reg:                                            \
         if (is_s390x_linux) {                                \
            if (_ctxreg.arg == 16/*dwarf reg 16 is %f0*/) {   \
               _how = CFIR_S390X_F0;                          \
               _off = 0;                                      \
               break;                                         \
            }                                                 \
            else if (_ctxreg.arg == 17/*dwarf reg 17 is %f2*/) { \
               _how = CFIR_S390X_F2;                          \
               _off = 0;                                      \
               break;                                         \
            }                                                 \
            else if (_ctxreg.arg == 18/*dwarf reg 18 is %f4*/) { \
               _how = CFIR_S390X_F4;                          \
               _off = 0;                                      \
               break;                                         \
            }                                                 \
            else if (_ctxreg.arg == 19/*dwarf reg 19 is %f6*/) { \
               _how = CFIR_S390X_F6;                          \
               _off = 0;                                      \
               break;                                         \
            }                                                 \
            else if (_ctxreg.arg == 20/*dwarf reg 20 is %f1*/) { \
               _how = CFIR_S390X_F1;                          \
               _off = 0;                                      \
               break;                                         \
            }                                                 \
            else if (_ctxreg.arg == 21/*dwarf reg 21 is %f3*/) { \
               _how = CFIR_S390X_F3;                          \
               _off = 0;                                      \
               break;                                         \
            }                                                 \
            else if (_ctxreg.arg == 22/*dwarf reg 22 is %f5*/) { \
               _how = CFIR_S390X_F5;                          \
               _off = 0;                                      \
               break;                                         \
            }                                                 \
            else if (_ctxreg.arg == 23/*dwarf reg 23 is %f7*/) { \
               _how = CFIR_S390X_F7;                          \
               _off = 0;                                      \
               break;                                         \
            }                                                 \
         }                                                    \
         /* Currently we only support RR_Reg for s390. */     \
         why = 2; goto failed;                                \
      default:                                                \
         why = 2; goto failed; /* otherwise give up */        \
   }


#  if defined(VGA_x86) || defined(VGA_amd64)

   /* --- entire tail of this fn specialised for x86/amd64 --- */

   SUMMARISE_HOW(si_m->ra_how, si_m->ra_off,
                               ctxs->reg[ctx->ra_reg] );
   SUMMARISE_HOW(si_m->bp_how, si_m->bp_off,
                               ctxs->reg[FP_REG] );

   /* on x86/amd64, it seems the old %{e,r}sp value before the call is
      always the same as the CFA.  Therefore ... */
   si_m->sp_how = CFIR_CFAREL;
   si_m->sp_off = 0;

   /* also, gcc says "Undef" for %{e,r}bp when it is unchanged.  So
      .. */
   if (ctxs->reg[FP_REG].tag == RR_Undef)
      si_m->bp_how = CFIR_SAME;

   /* knock out some obviously stupid cases */
   if (si_m->ra_how == CFIR_SAME) 
      { why = 3; goto failed; }

   /* bogus looking range?  Note, we require that the difference is
      representable in 32 bits. */
   if (loc_start >= ctx->loc) 
      { why = 4; goto failed; }
   if (ctx->loc - loc_start > 10000000 /* let's say */)
      { why = 5; goto failed; }

   *base = loc_start + ctx->initloc;
   *len  = (UInt)(ctx->loc - loc_start);

   return True;

#  elif defined(VGA_arm)

   /* ---- entire tail of this fn specialised for arm ---- */

   SUMMARISE_HOW(si_m->r14_how, si_m->r14_off,
                                ctxs->reg[14] );

   //SUMMARISE_HOW(si_m->r13_how, si_m->r13_off,
   //                             ctxs->reg[13] );

   SUMMARISE_HOW(si_m->r12_how, si_m->r12_off,
                                ctxs->reg[FP_REG] );

   SUMMARISE_HOW(si_m->r11_how, si_m->r11_off,
                                ctxs->reg[11/*FP_REG*/] );

   SUMMARISE_HOW(si_m->r7_how, si_m->r7_off,
                               ctxs->reg[7] );

   if (ctxs->reg[14/*LR*/].tag == RR_Same
       && ctx->ra_reg == 14/*as we expect it always to be*/) {
      /* Generate a trivial CfiExpr, which merely says "r14".  First
         ensure this DebugInfo has a cfsi_expr array in which to park
         it. */
      if (!debuginfo->cfsi_exprs)
         debuginfo->cfsi_exprs = VG_(newXA)( ML_(dinfo_zalloc),
                                             "di.ccCt.2a",
                                             ML_(dinfo_free),
                                             sizeof(CfiExpr) );
      si_m->ra_off = ML_(CfiExpr_CfiReg)( debuginfo->cfsi_exprs,
                                         Creg_ARM_R14);
      si_m->ra_how = CFIR_EXPR;
   } else {
      /* Just summarise it in the normal way */
      SUMMARISE_HOW(si_m->ra_how, si_m->ra_off,
                                  ctxs->reg[ctx->ra_reg] );
   }

   /* on arm, it seems the old r13 (SP) value before the call is
      always the same as the CFA.  Therefore ... */
   si_m->r13_how = CFIR_CFAREL;
   si_m->r13_off = 0;

   /* bogus looking range?  Note, we require that the difference is
      representable in 32 bits. */
   if (loc_start >= ctx->loc) 
      { why = 4; goto failed; }
   if (ctx->loc - loc_start > 10000000 /* let's say */)
      { why = 5; goto failed; }

   *base = loc_start + ctx->initloc;
   *len  = (UInt)(ctx->loc - loc_start);

   return True;

#  elif defined(VGA_arm64)

   /* --- entire tail of this fn specialised for arm64 --- */

   SUMMARISE_HOW(si_m->x30_how, si_m->x30_off, ctxs->reg[30/*LR*/]);
   SUMMARISE_HOW(si_m->x29_how, si_m->x29_off, ctxs->reg[29/*FP*/]);

   if (ctxs->reg[30/*LR*/].tag == RR_Same
       && ctx->ra_reg == 30/*as we expect it always to be*/) {
      /* Generate a trivial CfiExpr, which merely says "x30".  First
         ensure this DebugInfo has a cfsi_expr array in which to park
         it. */
      if (!debuginfo->cfsi_exprs)
         debuginfo->cfsi_exprs = VG_(newXA)( ML_(dinfo_zalloc),
                                             "di.ccCt.2a-arm64",
                                             ML_(dinfo_free),
                                             sizeof(CfiExpr) );
      si_m->ra_off = ML_(CfiExpr_CfiReg)( debuginfo->cfsi_exprs,
                                          Creg_ARM64_X30);
      si_m->ra_how = CFIR_EXPR;
   } else {
      /* Just summarise it in the normal way */
      SUMMARISE_HOW(si_m->ra_how, si_m->ra_off, ctxs->reg[ctx->ra_reg]);
   }

   /* on arm64, it seems the old SP value before the call is always
      the same as the CFA.  Therefore ... */
   si_m->sp_how = CFIR_CFAREL;
   si_m->sp_off = 0;

   /* bogus looking range?  Note, we require that the difference is
      representable in 32 bits. */
   if (loc_start >= ctx->loc) 
      { why = 4; goto failed; }
   if (ctx->loc - loc_start > 10000000 /* let's say */)
      { why = 5; goto failed; }

   *base = loc_start + ctx->initloc;
   *len  = (UInt)(ctx->loc - loc_start);

   return True;

#  elif defined(VGA_s390x)

   /* --- entire tail of this fn specialised for s390 --- */

   SUMMARISE_HOW(si_m->ra_how, si_m->ra_off,
                               ctxs->reg[ctx->ra_reg] );
   SUMMARISE_HOW(si_m->fp_how, si_m->fp_off,
                               ctxs->reg[FP_REG] );
   SUMMARISE_HOW(si_m->sp_how, si_m->sp_off,
                               ctxs->reg[SP_REG] );
   SUMMARISE_HOW(si_m->f0_how, si_m->f0_off,
                               ctxs->reg[16/*%f0*/]);
   SUMMARISE_HOW(si_m->f2_how, si_m->f2_off,
                               ctxs->reg[17/*%f2*/]);
   SUMMARISE_HOW(si_m->f4_how, si_m->f4_off,
                               ctxs->reg[18/*%f4*/]);
   SUMMARISE_HOW(si_m->f6_how, si_m->f6_off,
                               ctxs->reg[19/*%f6*/]);
   SUMMARISE_HOW(si_m->f1_how, si_m->f1_off,
                               ctxs->reg[20/*%f1*/]);
   SUMMARISE_HOW(si_m->f3_how, si_m->f3_off,
                               ctxs->reg[21/*%f3*/]);
   SUMMARISE_HOW(si_m->f5_how, si_m->f5_off,
                               ctxs->reg[22/*%f5*/]);
   SUMMARISE_HOW(si_m->f7_how, si_m->f7_off,
                               ctxs->reg[23/*%f7*/]);

   /* change some defaults to consumable values */
   if (si_m->sp_how == CFIR_UNKNOWN)
      si_m->sp_how = CFIR_SAME;

   if (si_m->fp_how == CFIR_UNKNOWN)
      si_m->fp_how = CFIR_SAME;

   if (si_m->cfa_how == CFIR_UNKNOWN) {
      si_m->cfa_how = CFIC_IA_SPREL;
      si_m->cfa_off = 160;
   }

   if (si_m->ra_how == CFIR_UNKNOWN) {
      if (!debuginfo->cfsi_exprs)
         debuginfo->cfsi_exprs = VG_(newXA)( ML_(dinfo_zalloc),
                                             "di.ccCt.2a",
                                             ML_(dinfo_free),
                                             sizeof(CfiExpr) );
      si_m->ra_how = CFIR_EXPR;
      si_m->ra_off = ML_(CfiExpr_CfiReg)( debuginfo->cfsi_exprs,
                                          Creg_S390_LR);
   }

   if (si_m->f0_how == CFIR_UNKNOWN)
      si_m->f0_how = CFIR_SAME;

   if (si_m->f1_how == CFIR_UNKNOWN)
      si_m->f1_how = CFIR_SAME;

   if (si_m->f2_how == CFIR_UNKNOWN)
      si_m->f2_how = CFIR_SAME;

   if (si_m->f3_how == CFIR_UNKNOWN)
      si_m->f3_how = CFIR_SAME;

   if (si_m->f4_how == CFIR_UNKNOWN)
      si_m->f4_how = CFIR_SAME;

   if (si_m->f5_how == CFIR_UNKNOWN)
      si_m->f5_how = CFIR_SAME;

   if (si_m->f6_how == CFIR_UNKNOWN)
      si_m->f6_how = CFIR_SAME;

   if (si_m->f7_how == CFIR_UNKNOWN)
      si_m->f7_how = CFIR_SAME;

   /* knock out some obviously stupid cases */
   if (si_m->ra_how == CFIR_SAME)
      { why = 3; goto failed; }

   /* bogus looking range?  Note, we require that the difference is
      representable in 32 bits. */
   if (loc_start >= ctx->loc)
      { why = 4; goto failed; }
   if (ctx->loc - loc_start > 10000000 /* let's say */)
      { why = 5; goto failed; }

   *base = loc_start + ctx->initloc;
   *len  = (UInt)(ctx->loc - loc_start);

   return True;

#  elif defined(VGA_mips32) || defined(VGA_mips64) || defined(VGA_nanomips)

   /* --- entire tail of this fn specialised for mips --- */

   SUMMARISE_HOW(si_m->ra_how, si_m->ra_off,
                               ctxs->reg[ctx->ra_reg] );
   SUMMARISE_HOW(si_m->fp_how, si_m->fp_off,
                               ctxs->reg[FP_REG] );
   SUMMARISE_HOW(si_m->sp_how, si_m->sp_off,
                               ctxs->reg[SP_REG] );
   si_m->sp_how = CFIR_CFAREL;
   si_m->sp_off = 0;

   if (si_m->fp_how == CFIR_UNKNOWN)
       si_m->fp_how = CFIR_SAME;
   if (si_m->cfa_how == CFIR_UNKNOWN) {
      si_m->cfa_how = CFIC_IA_SPREL;
      si_m->cfa_off = 160;
   }
   if (si_m->ra_how == CFIR_UNKNOWN) {
      if (!debuginfo->cfsi_exprs)
         debuginfo->cfsi_exprs = VG_(newXA)( ML_(dinfo_zalloc),
                                             "di.ccCt.2a",
                                             ML_(dinfo_free),
                                             sizeof(CfiExpr) );
      si_m->ra_how = CFIR_EXPR;
      si_m->ra_off = ML_(CfiExpr_CfiReg)( debuginfo->cfsi_exprs,
                                          Creg_MIPS_RA);
   }

   if (si_m->ra_how == CFIR_SAME)
      { why = 3; goto failed; }

   if (loc_start >= ctx->loc) 
      { why = 4; goto failed; }
   if (ctx->loc - loc_start > 10000000 /* let's say */)
      { why = 5; goto failed; }

   *base = loc_start + ctx->initloc;
   *len  = (UInt)(ctx->loc - loc_start);

   return True;
#  elif defined(VGA_ppc32) || defined(VGA_ppc64be) || defined(VGA_ppc64le)
   /* These don't use CFI based unwinding (is that really true?) */

#  elif defined(VGA_riscv64)

   /* --- entire tail of this fn specialised for riscv64 --- */

   SUMMARISE_HOW(si_m->ra_how, si_m->ra_off, ctxs->reg[ctx->ra_reg]);
   SUMMARISE_HOW(si_m->fp_how, si_m->fp_off, ctxs->reg[FP_REG]);

   /* on riscv64, it seems the old sp value before the call is always
      the same as the CFA.  Therefore ... */
   si_m->sp_how = CFIR_CFAREL;
   si_m->sp_off = 0;

   /* bogus looking range?  Note, we require that the difference is
      representable in 32 bits. */
   if (loc_start >= ctx->loc)
      { why = 4; goto failed; }
   if (ctx->loc - loc_start > 10000000 /* let's say */)
      { why = 5; goto failed; }

   *base = loc_start + ctx->initloc;
   *len  = (UInt)(ctx->loc - loc_start);

   return True;

#  else
#    error "Unknown arch"
#  endif

   /* --- non-specialised code after this point --- */

#  undef SUMMARISE_HOW

  failed:
   if (VG_(clo_verbosity) > 2 || debuginfo->trace_cfi) {
      VG_(message)(Vg_DebugMsg,
                  "summarise_context(loc_start = %#lx)"
                  ": cannot summarise(why=%d):   \n", loc_start, why);
      ppUnwindContext(ctx);
   }
   return False;
}

/* Copy the tree rooted at srcuc->exprs node srcix to dstxa, on the
   way converting any DwReg regs (regs numbered using the Dwarf scheme
   defined by each architecture's ABI) into CfiRegs, which are
   platform independent.  If the conversion isn't possible because
   there is no equivalent register, return -1.  This has the
   undesirable side effect of de-dagifying the input; oh well. */
static Int copy_convert_CfiExpr_tree ( XArray*        dstxa,
                                       const UnwindContext* srcuc, 
                                       Int            srcix )
{
   CfiExpr* src;
   Int      cpL, cpR, cpA;
   XArray*  srcxa = srcuc->exprs;
   vg_assert(srcxa);
   vg_assert(dstxa);
   vg_assert(srcix >= 0 && srcix < VG_(sizeXA)(srcxa));

   src = VG_(indexXA)( srcxa, srcix );
   switch (src->tag) {
      case Cex_Undef:
         return ML_(CfiExpr_Undef)( dstxa );
      case Cex_Deref:
         cpA = copy_convert_CfiExpr_tree( dstxa, srcuc, src->Cex.Deref.ixAddr );
         if (cpA == -1)
            return -1; /* propagate failure */
         return ML_(CfiExpr_Deref)( dstxa, cpA );
      case Cex_Const:
         return ML_(CfiExpr_Const)( dstxa, src->Cex.Const.con );
      case Cex_Binop:
         cpL = copy_convert_CfiExpr_tree( dstxa, srcuc, src->Cex.Binop.ixL );
         cpR = copy_convert_CfiExpr_tree( dstxa, srcuc, src->Cex.Binop.ixR );
         vg_assert(cpL >= -1 && cpR >= -1);
         if (cpL == -1 || cpR == -1)
            return -1; /* propagate failure */
         return ML_(CfiExpr_Binop)( dstxa, src->Cex.Binop.op, cpL, cpR );
      case Cex_CfiReg:
         /* should not see these in input (are created only by this
            conversion step!) */
         VG_(core_panic)("copy_convert_CfiExpr_tree: CfiReg in input");
      case Cex_DwReg: {
         /* This is the only place where the conversion can fail. */
         Int dwreg __attribute__((unused));
         dwreg = src->Cex.DwReg.reg;
#        if defined(VGA_x86) || defined(VGA_amd64)
         if (dwreg == SP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_IA_SP );
         if (dwreg == FP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_IA_BP );
         if (dwreg == srcuc->ra_reg)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_IA_IP ); /* correct? */
#        elif defined(VGA_arm)
         if (dwreg == SP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_ARM_R13 );
         if (dwreg == FP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_ARM_R12 );
         if (dwreg == srcuc->ra_reg)
           return ML_(CfiExpr_CfiReg)( dstxa, Creg_ARM_R15 ); /* correct? */
#        elif defined(VGA_s390x)
         if (dwreg == SP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_S390_SP );
         if (dwreg == FP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_S390_FP );
         if (dwreg == srcuc->ra_reg)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_S390_IA );
#        elif defined(VGA_mips32) || defined(VGA_mips64) \
           || defined(VGA_nanomips)
         if (dwreg == SP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_IA_SP );
         if (dwreg == FP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_IA_BP );
         if (dwreg == srcuc->ra_reg)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_IA_IP );
#        elif defined(VGA_arm64)
         if (dwreg == SP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_ARM64_SP );
         if (dwreg == FP_REG)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_ARM64_X29 );
         if (dwreg == srcuc->ra_reg)
            return ML_(CfiExpr_CfiReg)( dstxa, Creg_ARM64_X30 );
#        elif defined(VGA_ppc32) || defined(VGA_ppc64be) \
            || defined(VGA_ppc64le) || defined(VGA_riscv64)
#        else
#           error "Unknown arch"
#        endif
         /* else we must fail - can't represent the reg */
         return -1;
      }
      default:
         VG_(core_panic)("copy_convert_CfiExpr_tree: default");
   }
}


static void ppUnwindContext_summary ( const UnwindContext* ctx )
{
   const struct UnwindContextState* ctxs = &ctx->state[ctx->state_sp];

   VG_(printf)("0x%llx-1: ", (ULong)ctx->loc);

   if (ctxs->cfa_reg == SP_REG) {
      VG_(printf)("SP/CFA=%d+SP   ", ctxs->cfa_off);
   } else
   if (ctxs->cfa_reg == FP_REG) {
      VG_(printf)("SP/CFA=%d+FP   ", ctxs->cfa_off);
   } else {
      VG_(printf)("SP/CFA=unknown  ");
   }

   VG_(printf)("RA=");
   ppRegRule( ctx->exprs, &ctxs->reg[ctx->ra_reg] );

   VG_(printf)("FP=");
   ppRegRule( ctx->exprs, &ctxs->reg[FP_REG] );
   VG_(printf)("\n");
}


/* ------------ Pick apart DWARF2 byte streams ------------ */

static ULong step_le_u_encoded_literal ( DiCursor* data, UInt size )
{
   switch (size) {
      case 8:  return (ULong)ML_(cur_step_ULong)( data );
      case 4:  return (ULong)ML_(cur_step_UInt)( data );
      case 2:  return (ULong)ML_(cur_step_UShort)( data );
      case 1:  return (ULong)ML_(cur_step_UChar)( data );
      default: vg_assert(0); /*NOTREACHED*/ return 0;
   }
}

static Long step_le_s_encoded_literal ( DiCursor* data, UInt size )
{
   ULong u64 = step_le_u_encoded_literal( data, size );
   Long s64;
   switch (size) {
      case 8:  s64 = u64; break;
      case 4:  s64 = u64 << 32; s64 >>= 32; break;
      case 2:  s64 = u64 << 48; s64 >>= 48; break;
      case 1:  s64 = u64 << 56; s64 >>= 56; break;
      default: vg_assert(0); /*NOTREACHED*/ return 0;
   }
   return s64;
}

static UChar default_Addr_encoding ( void )
{
   switch (sizeof(Addr)) {
      case 4: return DW_EH_PE_udata4;
      case 8: return DW_EH_PE_udata8;
      default: vg_assert(0);
   }
}

static UInt size_of_encoded_Addr ( UChar encoding )
{
   if (encoding == DW_EH_PE_omit)
      return 0;

   switch (encoding & 0x07) {
      case DW_EH_PE_absptr: return sizeof(Addr);
      case DW_EH_PE_udata2: return sizeof(UShort);
      case DW_EH_PE_udata4: return sizeof(UInt);
      case DW_EH_PE_udata8: return sizeof(ULong);
      default: vg_assert(0);
   }
}

static Addr step_encoded_Addr ( const AddressDecodingInfo* adi,
                                /*MOD*/DiCursor* data )
{
   /* Regarding the handling of DW_EH_PE_absptr.  DWARF3 says this
      denotes an absolute address, hence you would think 'base' is
      zero.  However, that is nonsensical (unless relocations are to
      be applied to the unwind data before reading it, which sounds
      unlikely).  My interpretation is that DW_EH_PE_absptr indicates
      an address relative to where the object was loaded (technically,
      relative to its stated load VMA, hence the use of text_bias
      rather than text_avma).  Hmm, should we use text_bias or
      text_avma here?  Not sure.

      This view appears to be supported by DWARF3 spec sec 7.3
      "Executable Objects and Shared Objects":

         This requirement makes the debugging information for shared
         objects position independent.  Virtual addresses in a shared
         object may be calculated by adding the offset to the base
         address at which the object was attached.  This offset is
         available in the run-time linker's data structures.
   */
   Addr     base;
   Word     offset;
   UChar    encoding      = adi->encoding;
   DiCursor ehframe_image = adi->ehframe_image;
   Addr     ehframe_avma  = adi->ehframe_avma;
   Addr     got_avma      = adi->got_avma;

   vg_assert((encoding & DW_EH_PE_indirect) == 0);

   switch (encoding & 0x70) {
      case DW_EH_PE_absptr:
         base = adi->text_bias;
         break;
      case DW_EH_PE_pcrel:
         base = ehframe_avma + ML_(cur_minus)(*data, ehframe_image);
         break;
      case DW_EH_PE_datarel:
         base = got_avma;
         break;
      case DW_EH_PE_textrel:
         vg_assert(0);
         base = /* text base address */ 0;
         break;
      case DW_EH_PE_funcrel:
         base = 0;
         break;
      case DW_EH_PE_aligned:
         base = 0;
         offset = ML_(cur_minus)(*data, ehframe_image);
         if ((offset % sizeof(Addr)) != 0) {
            Word nbytes = sizeof(Addr) - (offset % sizeof(Addr));
            *data = ML_(cur_plus)(*data, nbytes);
         }
         break;
      default:
         vg_assert(0);
   }

   if ((encoding & 0x07) == 0x00)
      encoding |= default_Addr_encoding();

   switch (encoding & 0x0f) {
      case DW_EH_PE_udata2:
         return base + ML_(cur_step_UShort)(data);
      case DW_EH_PE_udata4:
         return base + ML_(cur_step_UInt)(data);
      case DW_EH_PE_udata8:
         return base + ML_(cur_step_ULong)(data);
      case DW_EH_PE_sdata2:
         return base + ML_(cur_step_Short)(data);
      case DW_EH_PE_sdata4:
         return base + ML_(cur_step_Int)(data);
      case DW_EH_PE_sdata8:
         return base + ML_(cur_step_Long)(data);
      default:
         vg_assert2(0, "read encoded address %d\n", encoding & 0x0f);
   }
}


/* ------------ Run/show DWARF3 expressions ---------- */

/* 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. */
/* IMPORTANT: when adding expression forms here, also remember to
   add suitable evaluation code in evalCfiExpr in debuginfo.c. */
static Int dwarfexpr_to_dag ( const UnwindContext* ctx, 
                              DiCursor expr, Int exprlen, 
                              Bool push_cfa_at_start,
                              Bool ddump_frames )
{
#  define N_EXPR_STACK 20

#  define PUSH(_arg)                               \
      do {                                         \
         vg_assert(sp >= -1 && sp < N_EXPR_STACK); \
         if (sp == N_EXPR_STACK-1)                 \
            return -1;                             \
         sp++;                                     \
         stack[sp] = (_arg);                       \
      } while (0)

#  define POP(_lval)                               \
      do {                                         \
         vg_assert(sp >= -1 && sp < N_EXPR_STACK); \
         if (sp == -1)                             \
            return -1;                             \
         _lval = stack[sp];                        \
         sp--;                                     \
      } while (0)

   Int      ix, ix2, reg;
   UChar    opcode;
   Word     sw;
   UWord    uw;
   CfiUnop  uop;
   CfiBinop bop;
   const HChar* opname;

   Int sp; /* # of top element: valid is -1 .. N_EXPR_STACK-1 */
   Int stack[N_EXPR_STACK];  /* indices into ctx->exprs */
   const struct UnwindContextState* ctxs = &ctx->state[ctx->state_sp];

   XArray*  dst   = ctx->exprs;
   DiCursor limit = ML_(cur_plus)(expr, exprlen);

   vg_assert(dst);
   vg_assert(exprlen >= 0);

   sp = -1; /* empty */

   /* Synthesise the CFA as a CfiExpr */
   if (push_cfa_at_start) {
      if (ctxs->cfa_is_regoff) {
         /* cfa is reg +/- offset */
         ix = ML_(CfiExpr_Binop)( dst,
                 Cbinop_Add,
                 ML_(CfiExpr_DwReg)( dst, ctxs->cfa_reg ),
                 ML_(CfiExpr_Const)( dst, (UWord)(Word)ctxs->cfa_off )
              );
         PUSH(ix);
      } else {
         /* CFA is already an expr; use its root node */
         PUSH(ctxs->cfa_expr_ix);
      }
   }

   while (True) {

      vg_assert(sp >= -1 && sp < N_EXPR_STACK);

      if (ML_(cur_cmpGT)(expr, limit)) /* "expr > limit" */
         return -1;  /* overrun - something's wrong */

      if (ML_(cur_cmpEQ)(expr, limit)) { /* "expr == limit" */
        /* end of expr - return expr on the top of stack. */
        if (sp == -1)
           return -1; /* stack empty.  Bad. */
        else
           break;
      }

      uop = 0; bop = 0; opname = NULL; /* excessively conservative */

      opcode = ML_(cur_step_UChar)(&expr);
      switch (opcode) {

         case DW_OP_lit0 ... DW_OP_lit31:
            /* push: literal 0 .. 31 */
            sw = (Word)opcode - (Word)DW_OP_lit0;
            vg_assert(sw >= 0 && sw <= 31);
            PUSH( ML_(CfiExpr_Const)( dst, (UWord)sw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_lit%ld", sw);
            break;

         case DW_OP_breg0 ... DW_OP_breg31:
            /* push: reg + sleb128 */
            reg = (Int)opcode - (Int)DW_OP_breg0;
            vg_assert(reg >= 0 && reg <= 31);
            sw = step_leb128S( &expr );
            ix = ML_(CfiExpr_Binop)( dst,
                    Cbinop_Add,
                    ML_(CfiExpr_DwReg)( dst, reg ),
                    ML_(CfiExpr_Const)( dst, (UWord)sw )
                 );
            PUSH(ix);
            if (ddump_frames)
               VG_(printf)("DW_OP_breg%d: %ld", reg, sw);
            break;

         case DW_OP_bregx:
            /* push: reg + sleb128 */
            reg = (Int)step_leb128U( &expr );
            sw = step_leb128S( &expr );
            ix = ML_(CfiExpr_Binop)( dst,
                    Cbinop_Add,
                    ML_(CfiExpr_DwReg)( dst, reg ),
                    ML_(CfiExpr_Const)( dst, (UWord)sw )
                 );
            PUSH(ix);
            if (ddump_frames)
               VG_(printf)("DW_OP_bregx: %d %ld", reg, sw);
            break;

         case DW_OP_reg0 ... DW_OP_reg31:
            /* push: reg */
            reg = (Int)opcode - (Int)DW_OP_reg0;
            vg_assert(reg >= 0 && reg <= 31);
            ix = ML_(CfiExpr_DwReg)( dst, reg );
            PUSH(ix);
            if (ddump_frames)
               VG_(printf)("DW_OP_reg%d", reg);
            break;

         case DW_OP_plus_uconst:
            uw = step_leb128U( &expr );
            PUSH( ML_(CfiExpr_Const)( dst, uw ) );
            POP( ix );
            POP( ix2 );
            PUSH( ML_(CfiExpr_Binop)( dst, Cbinop_Add, ix2, ix ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_plus_uconst: %lu", uw);
            break;

         case DW_OP_consts:
            sw = step_leb128S( &expr );
            PUSH( ML_(CfiExpr_Const)( dst, (UWord)sw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_consts: %ld", sw);
            break;

         case DW_OP_const8s:
            /* push: 64-bit signed immediate */
            sw = step_le_s_encoded_literal( &expr, 8 );
            PUSH( ML_(CfiExpr_Const)( dst, (UWord)sw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_const8s: %ld", sw);
            break;

         case DW_OP_const4s:
            /* push: 32-bit signed immediate */
            sw = step_le_s_encoded_literal( &expr, 4 );
            PUSH( ML_(CfiExpr_Const)( dst, (UWord)sw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_const4s: %ld", sw);
            break;

         case DW_OP_const2s:
            /* push: 16-bit signed immediate */
            sw = step_le_s_encoded_literal( &expr, 2 );
            PUSH( ML_(CfiExpr_Const)( dst, (UWord)sw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_const2s: %ld", sw);
            break;

         case DW_OP_const1s:
            /* push: 8-bit signed immediate */
            sw = step_le_s_encoded_literal( &expr, 1 );
            PUSH( ML_(CfiExpr_Const)( dst, (UWord)sw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_const1s: %ld", sw);
            break;

         case DW_OP_const1u:
            /* push: 8-bit unsigned immediate */
            uw = step_le_u_encoded_literal( &expr, 1 );
            PUSH( ML_(CfiExpr_Const)( dst, uw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_const1: %lu", uw);
            break;

         case DW_OP_const2u:
            /* push: 16-bit unsigned immediate */
            uw = step_le_u_encoded_literal( &expr, 2 );
            PUSH( ML_(CfiExpr_Const)( dst, uw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_const2: %lu", uw);
            break;

         case DW_OP_const4u:
            /* push: 32-bit unsigned immediate */
            uw = step_le_u_encoded_literal( &expr, 4 );
            PUSH( ML_(CfiExpr_Const)( dst, uw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_const4: %lu", uw);
            break;

         case DW_OP_const8u:
            /* push: 64-bit unsigned immediate */
            uw = step_le_u_encoded_literal( &expr, 8 );
            PUSH( ML_(CfiExpr_Const)( dst, uw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_const8: %lu", uw);
            break;

         case DW_OP_constu:
            uw = step_leb128S ( &expr );
            PUSH( ML_(CfiExpr_Const)( dst, uw ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_constu: %lu", uw);
            break;

         case DW_OP_abs:
            uop = Cunop_Abs; opname = "abs"; goto unop;
         case DW_OP_neg:
            uop = Cunop_Neg; opname = "neg"; goto unop;
         case DW_OP_not:
            uop = Cunop_Not; opname = "not"; goto unop;
         unop:
            POP( ix );
            PUSH( ML_(CfiExpr_Unop)( dst, uop, ix ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_%s", opname);
            break;

         case DW_OP_minus:
            bop = Cbinop_Sub; opname = "minus"; goto binop;
         case DW_OP_plus:
            bop = Cbinop_Add; opname = "plus"; goto binop;
         case DW_OP_and:
            bop = Cbinop_And; opname = "and"; goto binop;
         case DW_OP_mul:
            bop = Cbinop_Mul; opname = "mul"; goto binop;
         case DW_OP_shl:
            bop = Cbinop_Shl; opname = "shl"; goto binop;
         case DW_OP_shr:
            bop = Cbinop_Shr; opname = "shr"; goto binop;
         case DW_OP_eq:
            bop = Cbinop_Eq; opname = "eq"; goto binop;
         case DW_OP_ge:
            bop = Cbinop_Ge; opname = "ge"; goto binop;
         case DW_OP_gt:
            bop = Cbinop_Gt; opname = "gt"; goto binop;
         case DW_OP_le:
            bop = Cbinop_Le; opname = "le"; goto binop;
         case DW_OP_lt:
            bop = Cbinop_Lt; opname = "lt"; goto binop;
         case DW_OP_ne:
            bop = Cbinop_Ne; opname = "ne"; goto binop;
         binop:
            POP( ix );
            POP( ix2 );
            PUSH( ML_(CfiExpr_Binop)( dst, bop, ix2, ix ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_%s", opname);
            break;

         case DW_OP_deref:
            POP( ix );
            PUSH( ML_(CfiExpr_Deref)( dst, ix ) );
            if (ddump_frames)
               VG_(printf)("DW_OP_deref");
            break;

         case DW_OP_drop:
            POP( ix );
            if (ddump_frames)
               VG_(printf)("DW_OP_drop");
            break;

         default:
            if (!VG_(clo_xml))
               VG_(message)(Vg_DebugMsg, 
                            "Warning: DWARF2 CFI reader: unhandled DW_OP_ "
                            "opcode 0x%x\n", (UInt)opcode);
            return -1;
      }

      if (ML_(cur_cmpLT)(expr, limit) && ddump_frames)
         VG_(printf)("; ");

   }

   vg_assert(sp >= -1 && sp < N_EXPR_STACK);
   if (sp == -1)
      return -1;

   if (0 && ddump_frames)
      ML_(ppCfiExpr)( dst, stack[sp] );
   return stack[sp];

#  undef POP
#  undef PUSH
#  undef N_EXPR_STACK
}


/* ------------ Run/show CFI instructions ------------ */

/* Run a CFI instruction, and also return its length.
   Returns 0 if the instruction could not be executed. 
*/
static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx, 
                                DiCursor instrIN,
                                const UnwindContext* restore_ctx,
                                const AddressDecodingInfo* adi,
                                const DebugInfo* di )
{
   Int      off, reg, reg2, len, j;
   UInt     delta;
   Addr     printing_bias = ((Addr)ctx->initloc) - ((Addr)di->text_bias);
   struct UnwindContextState* ctxs;

   DiCursor instr   = instrIN;
   UChar    instr_0 = ML_(cur_step_UChar)(&instr);
   UChar    hi2     = (instr_0 >> 6) & 3;
   UChar    lo6     = instr_0 & 0x3F;

   if (ctx->state_sp < 0 || ctx->state_sp >= N_RR_STACK)
      return 0; /* bogus reg-rule stack pointer */

   ctxs = &ctx->state[ctx->state_sp];
   if (hi2 == DW_CFA_advance_loc) {
      delta = (UInt)lo6;
      delta *= ctx->code_a_f;
      ctx->loc += delta;
      if (di->ddump_frames)
         VG_(printf)("  DW_CFA_advance_loc: %d to %08lx\n", 
                     (Int)delta, (Addr)ctx->loc + printing_bias);
      return ML_(cur_minus)(instr, instrIN);
   }

   if (hi2 == DW_CFA_offset) {
      /* Set rule for reg 'lo6' to CFAOff(off * data_af) */
      off = step_leb128( &instr, 0 );
      reg = (Int)lo6;
      if (reg < 0 || reg >= N_CFI_REGS) 
         return 0; /* fail */
      ctxs->reg[reg].tag = RR_CFAOff;
      ctxs->reg[reg].arg = off * ctx->data_a_f;
      if (di->ddump_frames)
         VG_(printf)("  DW_CFA_offset: r%d at cfa%s%d\n",
                     (Int)reg,
                     ctxs->reg[reg].arg < 0 ? "" : "+", 
                     (Int)ctxs->reg[reg].arg );
      return ML_(cur_minus)(instr, instrIN);
   }

   if (hi2 == DW_CFA_restore) {
      reg = (Int)lo6;
      if (reg < 0 || reg >= N_CFI_REGS) 
         return 0; /* fail */
      if (restore_ctx == NULL)
         return 0; /* fail */
      ctxs->reg[reg] = restore_ctx->state[restore_ctx->state_sp].reg[reg];
      if (di->ddump_frames)
         VG_(printf)("  DW_CFA_restore: r%d\n", (Int)reg);
      return ML_(cur_minus)(instr, instrIN);
   }

   vg_assert(hi2 == DW_CFA_use_secondary);

   switch (lo6) {
      case DW_CFA_nop: 
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_nop\n");
         break;
      case DW_CFA_set_loc:
         /* WAS: 
            ctx->loc = read_Addr(&instr[i]) - ctx->initloc; i+= sizeof(Addr);
            Was this ever right? */
         /* 2007 Feb 23: No.  binutils/dwarf.c treats it as an encoded
            address and that appears to be in accordance with the
            DWARF3 spec. */
         ctx->loc = step_encoded_Addr(adi, &instr);
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_set_loc\n");
         break;
      case DW_CFA_advance_loc1:
         delta = (UInt)ML_(cur_step_UChar)(&instr);
         delta *= ctx->code_a_f;
         ctx->loc += delta;
         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)ML_(cur_step_UShort)(&instr);
         delta *= ctx->code_a_f;
         ctx->loc += delta;
         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)ML_(cur_step_UInt)(&instr);
         delta *= ctx->code_a_f;
         ctx->loc += delta;
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_advance_loc4: %d to %08lx\n", 
                        (Int)delta, (Addr)ctx->loc + printing_bias);
         break;

      case DW_CFA_def_cfa:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 0 );
         if (reg < 0 || reg >= N_CFI_REGS) 
            return 0; /* fail */
         ctxs->cfa_is_regoff = True;
         ctxs->cfa_expr_ix   = 0;
         ctxs->cfa_reg       = reg;
         ctxs->cfa_off       = off;
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_def_cfa: r%d ofs %d\n", (Int)reg, (Int)off);
         break;

      case DW_CFA_def_cfa_sf:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 1 );
         if (reg < 0 || reg >= N_CFI_REGS)
            return 0; /* fail */
         ctxs->cfa_is_regoff = True;
         ctxs->cfa_expr_ix   = 0;
         ctxs->cfa_reg       = reg;
         ctxs->cfa_off       = off * ctx->data_a_f;
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_def_cfa_sf\n");
         break;

      case DW_CFA_register:
         reg  = step_leb128( &instr, 0 );
         reg2 = step_leb128( &instr, 0 );
         if (reg < 0 || reg >= N_CFI_REGS) 
            return 0; /* fail */
         if (reg2 < 0 || reg2 >= N_CFI_REGS) 
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_Reg;
         ctxs->reg[reg].arg = reg2;
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_register: r%d in r%d\n", 
                        (Int)reg, (Int)reg2);
         break;

      case DW_CFA_offset_extended:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 0 );
         if (reg < 0 || reg >= N_CFI_REGS)
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_CFAOff;
         ctxs->reg[reg].arg = off * ctx->data_a_f;
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_offset_extended\n");
         break;

      case DW_CFA_offset_extended_sf:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 1 );
         if (reg < 0 || reg >= N_CFI_REGS) 
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_CFAOff;
         ctxs->reg[reg].arg = off * ctx->data_a_f;
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_offset_extended_sf: r%d at cfa%s%d\n", 
                        reg,
                        ctxs->reg[reg].arg < 0 ? "" : "+", 
                        (Int)ctxs->reg[reg].arg);
         break;

      case DW_CFA_GNU_negative_offset_extended:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 0 );
         if (reg < 0 || reg >= N_CFI_REGS)
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_CFAOff;
         ctxs->reg[reg].arg = (-off) * ctx->data_a_f;
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_GNU_negative_offset_extended\n");
         break;

      case DW_CFA_restore_extended:
         reg = step_leb128( &instr, 0 );
         if (reg < 0 || reg >= N_CFI_REGS)
            return 0; /* fail */
	 if (restore_ctx == NULL)
	    return 0; /* fail */
	 ctxs->reg[reg] = restore_ctx->state[restore_ctx->state_sp].reg[reg];
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_restore_extended\n");
         break;

      case DW_CFA_val_offset:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 0 );
         if (reg < 0 || reg >= N_CFI_REGS)
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_CFAValOff;
         ctxs->reg[reg].arg = off * ctx->data_a_f;
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_val_offset\n");
         break;

      case DW_CFA_val_offset_sf:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 1 );
         if (reg < 0 || reg >= N_CFI_REGS)
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_CFAValOff;
         ctxs->reg[reg].arg = off * ctx->data_a_f;
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_val_offset_sf\n");
         break;

      case DW_CFA_def_cfa_register:
         reg = step_leb128( &instr, 0);
         if (reg < 0 || reg >= N_CFI_REGS) 
            return 0; /* fail */
         ctxs->cfa_is_regoff = True;
         ctxs->cfa_expr_ix   = 0;
         ctxs->cfa_reg       = reg;
         /* ->cfa_off unchanged */
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_def_cfa_register: r%d\n", (Int)reg );
         break;

      case DW_CFA_def_cfa_offset:
         off = step_leb128( &instr, 0);
         ctxs->cfa_is_regoff = True;
         ctxs->cfa_expr_ix   = 0;
         /* ->reg is unchanged */
         ctxs->cfa_off       = off;
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_def_cfa_offset: %d\n", (Int)off);
         break;

      case DW_CFA_def_cfa_offset_sf:
         off = step_leb128( &instr, 1);
         ctxs->cfa_is_regoff = True;
         ctxs->cfa_expr_ix   = 0;
         /* ->reg is unchanged */
         ctxs->cfa_off       = off * ctx->data_a_f;
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_def_cfa_offset_sf: %d\n", ctxs->cfa_off);
         break;

      case DW_CFA_undefined:
         reg = step_leb128( &instr, 0);
         if (reg < 0 || reg >= N_CFI_REGS) 
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_Undef;
         ctxs->reg[reg].arg = 0;
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_undefined\n");
         break;

      case DW_CFA_same_value:
         reg = step_leb128( &instr, 0);
         if (reg < 0 || reg >= N_CFI_REGS) 
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_Same;
         ctxs->reg[reg].arg = 0;
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_same_value\n");
         break;

      case DW_CFA_GNU_args_size:
         /* No idea what is supposed to happen.  gdb-6.3 simply
            ignores these. */
         /*off = */ (void)step_leb128( &instr, 0 );
         if (di->ddump_frames)
            VG_(printf)("  rci:DW_CFA_GNU_args_size (ignored)\n");
         break;

      case DW_CFA_expression: {
         /* Identical to DW_CFA_val_expression except that the value
            computed is an address and so needs one final
            dereference. */
         DiCursor expr;
         reg = step_leb128( &instr, 0 );
         len = step_leb128( &instr, 0 );
         expr = instr;
         instr = ML_(cur_plus)(instr, len);
         if (reg < 0 || reg >= N_CFI_REGS)
            return 0; /* fail */
         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*/, 
                                di->ddump_frames);
         if (di->ddump_frames)
            VG_(printf)(")\n");
         vg_assert(j >= -1);
         if (j >= 0) {
            vg_assert(ctx->exprs);
            vg_assert( j < VG_(sizeXA)(ctx->exprs) );
         }
         if (j == -1)
            return 0; /* fail */
         /* Add an extra dereference */
         j = ML_(CfiExpr_Deref)( ctx->exprs, j );
         ctxs->reg[reg].tag = RR_ValExpr;
         ctxs->reg[reg].arg = j;
         break;
      }

      case DW_CFA_val_expression: {
         DiCursor expr;
         reg = step_leb128( &instr, 0 );
         len = step_leb128( &instr, 0 );
         expr = instr;
         instr = ML_(cur_plus)(instr, len);
         if (reg < 0 || reg >= N_CFI_REGS)
            return 0; /* fail */
         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*/, 
                                di->ddump_frames);
         if (di->ddump_frames)
            VG_(printf)(")\n");
         vg_assert(j >= -1);
         if (j >= 0) {
            vg_assert(ctx->exprs);
            vg_assert( j < VG_(sizeXA)(ctx->exprs) );
         }
         if (j == -1)
            return 0; /* fail */
         ctxs->reg[reg].tag = RR_ValExpr;
         ctxs->reg[reg].arg = j;
         break;
      }

      case DW_CFA_def_cfa_expression: {
         DiCursor expr;
         len = step_leb128( &instr, 0 );
         expr = instr;
         instr = ML_(cur_plus)(instr, len);
         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, False/*!push CFA at start*/, 
                                di->ddump_frames);
         if (di->ddump_frames)
            VG_(printf)(")\n");
         ctxs->cfa_is_regoff = False;
         ctxs->cfa_reg       = 0;
         ctxs->cfa_off       = 0;
         ctxs->cfa_expr_ix   = j;
         break;
      }

      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 (di->ddump_frames)
            VG_(printf)("  DW_CFA_GNU_window_save\n");
         break;

      case DW_CFA_remember_state:
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_remember_state\n");
         /* we just checked this at entry, so: */
         vg_assert(ctx->state_sp >= 0 && ctx->state_sp < N_RR_STACK);
         ctx->state_sp++;
         if (ctx->state_sp == N_RR_STACK) {
            /* stack overflow.  We're hosed. */
            VG_(message)(Vg_DebugMsg, "DWARF2 CFI reader: N_RR_STACK is "
                                      "too low; increase and recompile.");
            return 0; /* indicate failure */
         } else {
            VG_(memcpy)(/*dst*/&ctx->state[ctx->state_sp],
                        /*src*/&ctx->state[ctx->state_sp - 1],
                        sizeof(ctx->state[ctx->state_sp]) );
         }
         break;

      case DW_CFA_restore_state:
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_restore_state\n");
         /* we just checked this at entry, so: */
         vg_assert(ctx->state_sp >= 0 && ctx->state_sp < N_RR_STACK);
         if (ctx->state_sp == 0) {
            /* stack undefflow.  Give up. */
            return 0; /* indicate failure */
         } else {
            /* simply fall back to previous entry */
            ctx->state_sp--;
         }
         break;

      case DW_CFA_ORCL_arg_loc:
         if (di->ddump_frames)
            VG_(printf)("  DW_CFA_ORCL_arg_loc\n");
         break;

      default: 
         VG_(message)(Vg_DebugMsg, "DWARF2 CFI reader: unhandled CFI "
                                   "instruction 0:%d\n", (Int)lo6); 
         if (di->ddump_frames)
            VG_(printf)("  rci:run_CF_instruction:default\n");
         return 0; /* failure */
         /*NOTREACHED*/
   }

   return ML_(cur_minus)(instr, instrIN);
}


/* Show a CFI instruction, and also return its length.  Show it as
   close as possible (preferably identical) to how GNU binutils
   readelf --debug-dump=frames would. */

static Int show_CF_instruction ( DiCursor instrIN,
                                 const AddressDecodingInfo* adi,
                                 Int code_a_f, Int data_a_f )
{
   Int      off, coff, reg, reg2, len;
   UInt     delta;
   Addr     loc;
   DiCursor instr   = instrIN;
   UChar    instr_0 = ML_(cur_step_UChar)(&instr);
   UChar    hi2     = (instr_0 >> 6) & 3;
   UChar    lo6     = instr_0 & 0x3F;

   if (0) {
      DiCursor tmpi = instrIN;
      UInt i_0 = ML_(cur_step_UChar)(&tmpi);
      UInt i_1 = ML_(cur_step_UChar)(&tmpi);
      UInt i_2 = ML_(cur_step_UChar)(&tmpi);
      UInt i_3 = ML_(cur_step_UChar)(&tmpi);
      UInt i_4 = ML_(cur_step_UChar)(&tmpi);
      UInt i_5 = ML_(cur_step_UChar)(&tmpi);
      UInt i_6 = ML_(cur_step_UChar)(&tmpi);
      UInt i_7 = ML_(cur_step_UChar)(&tmpi);
      VG_(printf)("raw:%x/%x:%x:%x:%x:%x:%x:%x:%x:%x\n",
                  hi2, lo6, i_0, i_1, i_2, i_3, i_4, i_5, i_6, i_7);
   }
   
   if (hi2 == DW_CFA_advance_loc) {
      VG_(printf)("  sci:DW_CFA_advance_loc(%d)\n", (Int)lo6);
      return ML_(cur_minus)(instr, instrIN);
   }

   if (hi2 == DW_CFA_offset) {
      off = step_leb128( &instr, 0 );
      coff = off * data_a_f;
      VG_(printf)("  DW_CFA_offset: r%d at cfa%s%d\n",
                  (Int)lo6, coff < 0 ? "" : "+", (Int)coff );
      return ML_(cur_minus)(instr, instrIN);
   }

   if (hi2 == DW_CFA_restore) {
      VG_(printf)("  sci:DW_CFA_restore(r%d)\n", (Int)lo6);
      return ML_(cur_minus)(instr, instrIN);
   }

   vg_assert(hi2 == DW_CFA_use_secondary);

   switch (lo6) {

      case DW_CFA_nop: 
         VG_(printf)("  DW_CFA_nop\n"); 
         break;

      case DW_CFA_set_loc:
         /* WAS: loc = read_Addr(&instr[i]); i+= sizeof(Addr); 
            (now known to be incorrect -- the address is encoded) */
         loc = step_encoded_Addr(adi, &instr);
         VG_(printf)("  sci:DW_CFA_set_loc(%#lx)\n", loc);
         break;

      case DW_CFA_advance_loc1:
         delta = (UInt)ML_(cur_step_UChar)(&instr);
         VG_(printf)("  sci:DW_CFA_advance_loc1(%u)\n", delta); 
         break;

      case DW_CFA_advance_loc2:
         delta = (UInt)ML_(cur_step_UShort)(&instr);
         VG_(printf)("  sci:DW_CFA_advance_loc2(%u)\n", delta); 
         break;

      case DW_CFA_advance_loc4:
         delta = (UInt)ML_(cur_step_UInt)(&instr);
         VG_(printf)("  DW_CFA_advance_loc4(%u)\n", delta); 
         break;

      case DW_CFA_def_cfa:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 0 );
         VG_(printf)("  DW_CFA_def_cfa: r%d ofs %d\n", reg, off); 
         break;

      case DW_CFA_def_cfa_sf:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 1 );
         VG_(printf)("  DW_CFA_def_cfa_sf: r%d ofs %d\n", 
                     reg, off * data_a_f);
         break;

      case DW_CFA_register:
         reg  = step_leb128( &instr, 0);
         reg2 = step_leb128( &instr, 0);
         VG_(printf)("  sci:DW_CFA_register(r%d, r%d)\n", reg, reg2); 
         break;

      case DW_CFA_def_cfa_register:
         reg = step_leb128( &instr, 0);
         VG_(printf)("  sci:DW_CFA_def_cfa_register(r%d)\n", reg); 
         break;

      case DW_CFA_def_cfa_offset: 
         off = step_leb128( &instr, 0);
         VG_(printf)("  sci:DW_CFA_def_cfa_offset(%d)\n", off); 
         break;

      case DW_CFA_def_cfa_offset_sf:
         off = step_leb128( &instr, 1);
         VG_(printf)("  sci:DW_CFA_def_cfa_offset_sf(%d)\n", off);
         break;

      case DW_CFA_restore_extended:
         reg = step_leb128( &instr, 0);
         VG_(printf)("  sci:DW_CFA_restore_extended(r%d)\n", reg);
         break;

      case DW_CFA_undefined:
         reg = step_leb128( &instr, 0);
         VG_(printf)("  sci:DW_CFA_undefined(r%d)\n", reg);
         break;

      case DW_CFA_same_value:
         reg = step_leb128( &instr, 0);
         VG_(printf)("  sci:DW_CFA_same_value(r%d)\n", reg);
         break;

      case DW_CFA_remember_state:
         VG_(printf)("  sci:DW_CFA_remember_state\n");
         break;

      case DW_CFA_restore_state:
         VG_(printf)("  sci:DW_CFA_restore_state\n");
         break;

      case DW_CFA_GNU_args_size:
         off = step_leb128( &instr, 0 );
         VG_(printf)("  sci:DW_CFA_GNU_args_size(%d)\n", off ); 
         break;

      case DW_CFA_def_cfa_expression:
         len = step_leb128( &instr, 0 );
         instr = ML_(cur_plus)(instr, len);
         VG_(printf)("  sci:DW_CFA_def_cfa_expression(length %d)\n", len);
         break;

      case DW_CFA_expression:
         reg = step_leb128( &instr, 0 );
         len = step_leb128( &instr, 0 );
         instr = ML_(cur_plus)(instr, len);
         VG_(printf)("  sci:DW_CFA_expression(r%d, length %d)\n", reg, len);
         break;

      case DW_CFA_val_expression:
         reg = step_leb128( &instr, 0 );
         len = step_leb128( &instr, 0 );
         instr = ML_(cur_plus)(instr, len);
         VG_(printf)("  sci:DW_CFA_val_expression(r%d, length %d)\n", reg, len);
         break;

      case DW_CFA_offset_extended:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 0 );
         VG_(printf)("  sci:DW_CFA_offset_extended(r%d, "
                     "off %d x data_af)\n", reg, off);
         break;

      case DW_CFA_offset_extended_sf:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 1 );
	 coff = (Int)(off * data_a_f);
         VG_(printf)("  DW_CFA_offset_extended_sf: r%d at cfa%s%d\n", 
                        reg, coff < 0 ? "" : "+", coff);
         break;

      case DW_CFA_GNU_negative_offset_extended:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 0 );
         VG_(printf)("  sci:DW_CFA_GNU_negative_offset_extended"
                     "(r%d, off %d x data_af)\n", reg, -off);
         break;

      case DW_CFA_val_offset:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 0 );
         VG_(printf)("  sci:DW_CFA_val_offset(r%d, off %d x data_af)\n", 
                     reg, off);
         break;

       case DW_CFA_val_offset_sf:
         reg = step_leb128( &instr, 0 );
         off = step_leb128( &instr, 1 );
         VG_(printf)("  sci:DW_CFA_val_offset_sf(r%d, off %d x data_af)\n", 
                     reg, off);
         break;

      case DW_CFA_GNU_window_save:
         VG_(printf)("  sci:DW_CFA_GNU_window_save\n");
         break;

      case DW_CFA_ORCL_arg_loc:
         reg = step_leb128( &instr, 0 );
         len = step_leb128( &instr, 0 );
         VG_(printf)("  sci:DW_CFA_ORCL_arg_loc(%d, length %d)\n", reg, len);
         break;

      default: 
         VG_(printf)("  sci:0:%d\n", (Int)lo6); 
         break;
   }

   return ML_(cur_minus)(instr, instrIN);
}


/* Show the instructions in instrs[0 .. ilen-1]. */
static void show_CF_instructions ( DiCursor instrs, Int ilen,
                                   const AddressDecodingInfo* adi,
                                   Int code_a_f, Int data_a_f )
{
   Int i = 0;
   while (True) {
      if (i >= ilen) break;
      i += show_CF_instruction( ML_(cur_plus)(instrs, i),
                                adi, code_a_f, data_a_f );
   }
}


/* Run the CF instructions in instrs[0 .. ilen-1], until the end is
   reached, or until there is a failure.  Return True iff success. 
*/
static 
Bool run_CF_instructions ( DebugInfo* di,
                           Bool record,
                           UnwindContext* ctx, DiCursor instrs, Int ilen,
                           UWord fde_arange,
                           const UnwindContext* restore_ctx,
                           const AddressDecodingInfo* adi )
{
   Addr base;
   UInt len;
   DiCfSI_m cfsi_m;
   Bool summ_ok;
   Int j, i = 0;
   Addr loc_prev;
   if (0) ppUnwindContext(ctx);
   if (0) ppUnwindContext_summary(ctx);
   while (True) {
      loc_prev = ctx->loc;
      if (i >= ilen) break;
      if (0) (void)show_CF_instruction( ML_(cur_plus)(instrs,i), adi, 
                                        ctx->code_a_f, ctx->data_a_f );
      j = run_CF_instruction( ctx, ML_(cur_plus)(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 ( &base, &len, &cfsi_m,
                                       loc_prev, ctx, di );
         if (summ_ok) {
            ML_(addDiCfSI)(di, base, len, &cfsi_m);
            if (di->trace_cfi)
               ML_(ppDiCfSI)(di->cfsi_exprs, base, len, &cfsi_m);
         }
      }
   }
   if (ctx->loc < fde_arange) {
      loc_prev = ctx->loc;
      ctx->loc = fde_arange;
      if (record) {
         summ_ok = summarise_context ( &base, &len, &cfsi_m,
                                       loc_prev, ctx, di );
         if (summ_ok) {
            ML_(addDiCfSI)(di, base, len, &cfsi_m);
            if (di->trace_cfi)
               ML_(ppDiCfSI)(di->cfsi_exprs, base, len, &cfsi_m);
         }
      }
   }
   return True;
}


/* ------------ Main entry point for CFI reading ------------ */

typedef
   struct {
      /* This gives the CIE an identity to which FDEs will refer. */
      ULong    offset;
      /* Code, data factors. */
      Int      code_a_f;
      Int      data_a_f;
      /* Return-address pseudo-register. */
      Int      ra_reg;
      UChar    address_encoding;
      /* Where are the instrs? */
      DiCursor instrs;
      Int      ilen;
      /* God knows .. don't ask */
      Bool     saw_z_augmentation;
   }
   CIE;

static void init_CIE ( CIE* cie )
{
   cie->offset             = 0;
   cie->code_a_f           = 0;
   cie->data_a_f           = 0;
   cie->ra_reg             = 0;
   cie->address_encoding   = 0;
   cie->instrs             = DiCursor_INVALID;
   cie->ilen               = 0;
   cie->saw_z_augmentation = False;
}

static CIE *the_CIEs = NULL;
static SizeT N_CIEs = 0;

/* Read, summarise and store CFA unwind info from .eh_frame and
   .debug_frame sections.  is_ehframe tells us which kind we are
   dealing with -- they are slightly different. */
void ML_(read_callframe_info_dwarf3)
        ( /*OUT*/struct _DebugInfo* di,
          DiSlice escn_frame, Addr frame_avma, Bool is_ehframe )
{
   const HChar* how = NULL;
   Int      n_CIEs = 0;
   DiCursor frame_image = ML_(cur_from_sli)(escn_frame); /* fixed */
   DiOffT   frame_size  = escn_frame.szB;
   DiCursor data        = frame_image;
   UWord    cfsi_used_orig;

   /* If we're dealing with a .debug_frame, assume zero frame_avma. */
   if (!is_ehframe)
      vg_assert(frame_avma == 0);

#  if defined(VGP_ppc32_linux) || defined(VGP_ppc64be_linux) \
      || defined(VGP_ppc64le_linux)
   /* These targets don't use CFI-based stack unwinding.  */
   return;
#  endif

   /* If we read more than one .debug_frame or .eh_frame for this
      DebugInfo*, the second and subsequent reads should only add FDEs
      for address ranges not already covered by the FDEs already
      present.  To be able to quickly check which address ranges are
      already present, any existing records (DiCFSIs) must be sorted,
      so we can binary-search them in the code below.  We also record
      di->cfsi_used so that we know where the boundary is between
      existing and new records. */
   if (di->cfsi_used > 0) {
      ML_(canonicaliseCFI) ( di );
   }
   cfsi_used_orig = di->cfsi_used;

   if (di->trace_cfi) {
      VG_(printf)("\n-----------------------------------------------\n");
      VG_(printf)("CFI info: szB %llu, _avma %#lx\n",
                  escn_frame.szB, frame_avma );
      VG_(printf)("CFI info: name %s\n", di->fsm.filename );
   }

   /* Loop over CIEs/FDEs */

   /* Conceptually, the frame info is a sequence of FDEs, one for each
      function.  Inside an FDE is a miniature program for a special
      state machine, which, when run, produces the stack-unwinding
      info for that function.

      Because the FDEs typically have much in common, and because the
      DWARF designers appear to have been fanatical about space
      saving, the common parts are factored out into so-called CIEs.
      That means that what we traverse is a sequence of structs, each
      of which is either a FDE (usually) or a CIE (occasionally).
      Each FDE has a field indicating which CIE is the one pertaining
      to it.

      The following loop traverses the sequence.  FDEs are dealt with
      immediately; once we harvest the useful info in an FDE, it is
      then forgotten about.  By contrast, CIEs are validated and
      dumped into an array, because later FDEs may refer to any
      previously-seen CIE.
   */
   while (True) {
      DiCursor ciefde_start;
      ULong    ciefde_len;
      ULong    cie_pointer;
      Bool     dw64;

      /* Are we done? */
      if (ML_(cur_cmpEQ)(data, ML_(cur_plus)(frame_image, frame_size)))
         return;

      /* Overshot the end?  Means something is wrong */
      if (ML_(cur_cmpGT)(data, ML_(cur_plus)(frame_image, frame_size))) {
         how = "overran the end of .eh_frame";
         goto bad;
      }

      /* Ok, we must be looking at the start of a new CIE or FDE.
         Figure out which it is. */

      ciefde_start = data;
      if (di->trace_cfi) 
         VG_(printf)("\ncie/fde.start   = (frame_image + 0x%llx)\n", 
                     (ULong)ML_(cur_minus)(ciefde_start, frame_image));

      ciefde_len = (ULong)ML_(cur_step_UInt)(&data);
      if (di->trace_cfi) 
         VG_(printf)("cie/fde.length  = %llu\n", ciefde_len);

      /* Apparently, if the .length field is zero, we are at the end
         of the sequence.  This is stated in the Generic Elf
         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 (di->ddump_frames)
            VG_(printf)("%08llx ZERO terminator\n\n",
                        (ULong)ML_(cur_minus)(ciefde_start, frame_image));
         return;
      }

      /* If the .length field is 0xFFFFFFFF then we're dealing with
         64-bit DWARF, and the real length is stored as a 64-bit
         number immediately following it. */
      dw64 = False;
      if (ciefde_len == 0xFFFFFFFFUL) {
         dw64 = True;
         ciefde_len = ML_(cur_step_ULong)(&data);
      }

      /* Now get the CIE ID, whose size depends on the DWARF 32 vs
	 64-ness. */
      if (dw64) {
         /* see XXX below */
         cie_pointer = ML_(cur_step_ULong)(&data); 
      } else {
         /* see XXX below */
         cie_pointer = (ULong)ML_(cur_step_UInt)(&data); 
      }

      if (di->trace_cfi) 
         VG_(printf)("cie.pointer     = %llu\n", cie_pointer);

      /* If cie_pointer is zero for .eh_frame or all ones for .debug_frame,
         we've got a CIE; else it's an FDE. */
      if (cie_pointer == (is_ehframe ? 0ULL
                          : dw64 ? 0xFFFFFFFFFFFFFFFFULL : 0xFFFFFFFFULL)) {

         Int      this_CIE;
         UChar    cie_version;
         DiCursor cie_augmentation;

         /* --------- CIE --------- */
	 if (di->trace_cfi) 
            VG_(printf)("------ new CIE #%d ------\n", n_CIEs);

	 /* Allocate a new CIE record. */
         vg_assert(n_CIEs >= 0);
         if (n_CIEs == N_CIEs) {
            N_CIEs += 1000;
            the_CIEs = ML_(dinfo_realloc)("di.rcid3.2", the_CIEs,
                                          N_CIEs * sizeof the_CIEs[0]);
         }

         this_CIE = n_CIEs;
         n_CIEs++;
         init_CIE( &the_CIEs[this_CIE] );

	 /* Record its offset.  This is how we will find it again
            later when looking at an FDE. */
         the_CIEs[this_CIE].offset
            = (ULong)ML_(cur_minus)(ciefde_start, frame_image);

         if (di->ddump_frames)
            VG_(printf)("%08lx %08lx %08lx CIE\n",
                        (Addr)ML_(cur_minus)(ciefde_start, frame_image),
                        (Addr)ciefde_len,
                        (Addr)(UWord)cie_pointer );

         cie_version = ML_(cur_step_UChar)(&data);
         if (di->trace_cfi)
            VG_(printf)("cie.version     = %d\n", (Int)cie_version);
         if (di->ddump_frames)
            VG_(printf)("  Version:               %d\n", (Int)cie_version);
         if (cie_version != 1 && cie_version != 3 && cie_version != 4) {
            how = "unexpected CIE version (not 1 nor 3 nor 4)";
            goto bad;
         }

         cie_augmentation = data;
         data = ML_(cur_plus)(data, 1 + ML_(cur_strlen)(cie_augmentation));

         if (di->trace_cfi || di->ddump_frames) {
            HChar* str = ML_(cur_read_strdup)(cie_augmentation, "di.rcid3.1");
            if (di->trace_cfi) 
               VG_(printf)("cie.augment     = \"%s\"\n", str);
            if (di->ddump_frames)
               VG_(printf)("  Augmentation:          \"%s\"\n", str);
            ML_(dinfo_free)(str);
         }

         if (ML_(cur_read_UChar)(cie_augmentation) == 'e'
             && ML_(cur_read_UChar)
                   (ML_(cur_plus)(cie_augmentation, 1)) == 'h') {
            data = ML_(cur_plus)(data, sizeof(Addr));
            cie_augmentation = ML_(cur_plus)(cie_augmentation, 2);
         }

         if (cie_version >= 4) {
            if (ML_(cur_step_UChar)(&data) != sizeof(Addr)) {
               how = "unexpected address size";
               goto bad;
            }
            if (ML_(cur_step_UChar)(&data) != 0) {
               how = "unexpected non-zero segment size";
               goto bad;
            }
         }

         the_CIEs[this_CIE].code_a_f = step_leb128( &data, 0);
         if (di->trace_cfi) 
            VG_(printf)("cie.code_af     = %d\n", 
                        the_CIEs[this_CIE].code_a_f);
         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 = step_leb128( &data, 1);
         if (di->trace_cfi) 
            VG_(printf)("cie.data_af     = %d\n",
                        the_CIEs[this_CIE].data_a_f);
         if (di->ddump_frames)
            VG_(printf)("  Data alignment factor: %d\n",
                        (Int)the_CIEs[this_CIE].data_a_f);

         if (cie_version == 1) {
            the_CIEs[this_CIE].ra_reg = (Int)ML_(cur_step_UChar)(&data); 
         } else {
            the_CIEs[this_CIE].ra_reg = step_leb128( &data, 0);
         }
         if (di->trace_cfi) 
            VG_(printf)("cie.ra_reg      = %d\n", 
                        the_CIEs[this_CIE].ra_reg);
         if (di->ddump_frames)
            VG_(printf)("  Return address column: %d\n",
                        (Int)the_CIEs[this_CIE].ra_reg);

         if (the_CIEs[this_CIE].ra_reg < 0 
             || the_CIEs[this_CIE].ra_reg >= N_CFI_REGS) {
            how = "cie.ra_reg has implausible value";
            goto bad;
         }

         the_CIEs[this_CIE].saw_z_augmentation 
            = ML_(cur_read_UChar)(cie_augmentation) == 'z';
         if (the_CIEs[this_CIE].saw_z_augmentation) {
            UInt length = step_leb128( &data, 0);
            the_CIEs[this_CIE].instrs = ML_(cur_plus)(data, length);
            cie_augmentation = ML_(cur_plus)(cie_augmentation, 1);
            if (di->ddump_frames) {
               UInt i;
               VG_(printf)("  Augmentation data:    ");
               for (i = 0; i < length; i++)
                  VG_(printf)(" %02x", (UInt)ML_(cur_read_UChar)
                                                (ML_(cur_plus)(data, i)));
               VG_(printf)("\n");
            }
         } else {
            the_CIEs[this_CIE].instrs = DiCursor_INVALID;
         }

         the_CIEs[this_CIE].address_encoding = default_Addr_encoding();

         while (ML_(cur_read_UChar)(cie_augmentation)) {
            switch (ML_(cur_read_UChar)(cie_augmentation)) {
               case 'L':
                  data = ML_(cur_plus)(data, 1);
                  cie_augmentation = ML_(cur_plus)(cie_augmentation, 1);
                  break;
               case 'R':
                  the_CIEs[this_CIE].address_encoding 
                     = ML_(cur_step_UChar)(&data);
                  cie_augmentation = ML_(cur_plus)(cie_augmentation, 1);
                  break;
               case 'P':
                  data = ML_(cur_plus)(data, size_of_encoded_Addr(
                                                ML_(cur_read_UChar)(data) ));
                  data = ML_(cur_plus)(data, 1);
                  cie_augmentation = ML_(cur_plus)(cie_augmentation, 1);
                  break;
               case 'S':
                  cie_augmentation = ML_(cur_plus)(cie_augmentation, 1);
                  break;
               default:
                  if (!ML_(cur_is_valid)(the_CIEs[this_CIE].instrs)) {
                     how = "unhandled cie.augmentation";
                     goto bad;
                  }
                  data = the_CIEs[this_CIE].instrs;
                  goto done_augmentation;
            }
         }

        done_augmentation:

         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   = ML_(cur_minus)(ciefde_start, data) 
                                     + (Long)ciefde_len + (Long)sizeof(UInt);
         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 > frame_size) {
            how = "implausible # cie initial insns";
            goto bad;
         }

         data = ML_(cur_plus)(data, the_CIEs[this_CIE].ilen);

         /* Show the CIE's instructions (the preamble for each FDE
            that uses this CIE). */ 
         if (di->ddump_frames)
            VG_(printf)("\n");

         if (di->trace_cfi || di->ddump_frames) {
            AddressDecodingInfo adi;
            adi.encoding      = the_CIEs[this_CIE].address_encoding;
            adi.ehframe_image = frame_image;
            adi.ehframe_avma  = frame_avma;
            adi.text_bias     = di->text_debug_bias;
            adi.got_avma      = di->got_avma;
            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 (di->ddump_frames)
            VG_(printf)("\n");

      } else {

         AddressDecodingInfo adi;
         UnwindContext ctx, restore_ctx;
         Int      cie;
         ULong    look_for;
         Bool     ok;
         Addr     fde_initloc;
         UWord    fde_arange;
         DiCursor fde_instrs;
         Int      fde_ilen;

         /* --------- FDE --------- */

         /* Find the relevant CIE.  The CIE we want is located
            cie_pointer bytes back from here. */

         /* re sizeof(UInt) / sizeof(ULong), matches XXX above. */
         if (is_ehframe)
            look_for = ML_(cur_minus)(data, frame_image)
                       - (dw64 ? sizeof(ULong) : sizeof(UInt))
                       - cie_pointer;
         else
            look_for = cie_pointer;

         for (cie = 0; cie < n_CIEs; cie++) {
            if (0) VG_(printf)("look for %llu   %llu\n",
                               look_for, the_CIEs[cie].offset );
            if (the_CIEs[cie].offset == look_for)
               break;
	 }
         vg_assert(cie >= 0 && cie <= n_CIEs);
         if (cie == n_CIEs) {
            how = "FDE refers to not-findable CIE";
            goto bad;
	 }

         adi.encoding      = the_CIEs[cie].address_encoding;
         adi.ehframe_image = frame_image;
         adi.ehframe_avma  = frame_avma;
         adi.text_bias     = di->text_debug_bias;
         adi.got_avma      = di->got_avma;
         fde_initloc = step_encoded_Addr(&adi, &data);
         if (di->trace_cfi) 
            VG_(printf)("fde.initloc     = %#lx\n", fde_initloc);

         adi.encoding      = the_CIEs[cie].address_encoding & 0xf;
         adi.ehframe_image = frame_image;
         adi.ehframe_avma  = frame_avma;
         adi.text_bias     = di->text_debug_bias;
         adi.got_avma      = di->got_avma;

         /* WAS (incorrectly):
            fde_arange = read_encoded_Addr(&nbytes, &adi, data);
            data += nbytes;
            The following corresponds to what binutils/dwarf.c does:
         */
         { UInt ptr_size = size_of_encoded_Addr( adi.encoding );
           switch (ptr_size) {
              case 8: case 4: case 2: case 1: 
                 fde_arange 
                    = (UWord)step_le_u_encoded_literal(&data, ptr_size);
                 break;
              default: 
                 how = "unknown arange field encoding in FDE";
                 goto bad;
           }
         }

         if (di->trace_cfi) 
            VG_(printf)("fde.arangec     = %#lx\n", fde_arange);

         if (di->ddump_frames)
            VG_(printf)("%08lx %08lx %08lx FDE cie=%08lx pc=%08lx..%08lx\n",
                        (Addr)ML_(cur_minus)(ciefde_start, frame_image),
                        (Addr)ciefde_len,
                        (Addr)(UWord)cie_pointer,
                        (Addr)look_for, 
                        ((Addr)fde_initloc) - di->text_debug_bias, 
                        ((Addr)fde_initloc) - di->text_debug_bias + fde_arange);

         if (the_CIEs[cie].saw_z_augmentation) {
            UInt length = step_leb128( &data, 0);
            if (di->ddump_frames && (length > 0)) {
               UInt i;
               VG_(printf)("  Augmentation data:    ");
               for (i = 0; i < length; i++)
                  VG_(printf)(" %02x", (UInt)ML_(cur_read_UChar)
                                                (ML_(cur_plus)(data, i)));
               VG_(printf)("\n\n");
            }
            data = ML_(cur_plus)(data, length);
         }

         fde_instrs = data;
         fde_ilen   = ML_(cur_minus)(ciefde_start, data)
                      + (Long)ciefde_len + (Long)sizeof(UInt);
         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 > frame_size) {
            how = "implausible # fde insns";
            goto bad;
         }

	 data = ML_(cur_plus)(data, fde_ilen);

         /* If this object's DebugInfo* had some DiCFSIs from a
            previous .eh_frame or .debug_frame read, we must check
            that we're not adding a duplicate. */
         if (cfsi_used_orig > 0) {
            Addr a_mid_lo, a_mid_hi;
            Word mid, size, 
                 lo = 0, 
                 hi = cfsi_used_orig-1;
            while (True) {
               /* current unsearched space is from lo to hi, inclusive. */
               if (lo > hi) break; /* not found */
               mid      = (lo + hi) / 2;
               a_mid_lo = di->cfsi_rd[mid].base;
               size     = di->cfsi_rd[mid].len;
               a_mid_hi = a_mid_lo + size - 1;
               vg_assert(a_mid_hi >= a_mid_lo);
               if (fde_initloc + fde_arange <= a_mid_lo) {
                  hi = mid-1; continue;
               }
               if (fde_initloc > a_mid_hi) { lo = mid+1; continue; }
               break;
            }

            /* The range this .debug_frame FDE covers has been already
               covered in .eh_frame section.  Don't add it from .debug_frame
               section again.  */            
            if (lo <= hi)
               continue;
         }

         adi.encoding      = the_CIEs[cie].address_encoding;
         adi.ehframe_image = frame_image;
         adi.ehframe_avma  = frame_avma;
         adi.text_bias     = di->text_debug_bias;
         adi.got_avma      = di->got_avma;

         if (di->trace_cfi)
            show_CF_instructions( fde_instrs, fde_ilen, &adi,
                                  the_CIEs[cie].code_a_f,
                                  the_CIEs[cie].data_a_f );

	 initUnwindContext(&ctx);
         ctx.code_a_f = the_CIEs[cie].code_a_f;
         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)( ML_(dinfo_zalloc), "di.rcid.1",
                                    ML_(dinfo_free), 
                                    sizeof(CfiExpr) );

	 /* Run the CIE's instructions.  Ugly hack: if
            --debug-dump=frames is in effect, suppress output for
            these instructions since they will already have been shown
            at the time the CIE was first encountered.  Note, not
            thread safe - if this reader is ever made threaded, should
            fix properly. */
	 { Bool hack = di->ddump_frames; 
           di->ddump_frames = False;
           initUnwindContext(&restore_ctx);
           ok = run_CF_instructions(
                   di, False, &ctx, the_CIEs[cie].instrs, 
                   the_CIEs[cie].ilen, 0, NULL, &adi
                );
           di->ddump_frames = hack;
         }
         /* And now run the instructions for the FDE, starting from
            the state created by running the CIE preamble
            instructions. */
         if (ok) {
            restore_ctx = ctx;
	    ok = run_CF_instructions(
                    di, True, &ctx, fde_instrs, fde_ilen, fde_arange, 
                    &restore_ctx, &adi
                 );
            if (di->ddump_frames)
               VG_(printf)("\n");
	 }

         VG_(deleteXA)( ctx.exprs );
      }
   }

   return;

   bad:
    if (!VG_(clo_xml) && VG_(clo_verbosity) > 1)
       VG_(message)(Vg_UserMsg,
                    "Warning: %s in DWARF2 CFI reading\n", how);
    return;
}

#endif // defined(VGO_linux) || defined(VGO_darwin) || defined(VGO_solaris) || defined(VGO_freebsd)

/*--------------------------------------------------------------------*/
/*--- end                                                          ---*/
/*--------------------------------------------------------------------*/
