blob: a038a52dbbcf011b79466ccff4dc04288800eb5c [file] [log] [blame]
/* -*- 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-2013 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 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#if defined(VGO_linux) || defined(VGO_darwin)
#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;
UChar li_default_is_stmt;
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 is_stmt;
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 ( Int is_stmt )
{
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.is_stmt = is_stmt;
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, Int is_stmt)
{
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",
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.is_stmt) {
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 (is_stmt);
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;
}
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
/* 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 )
{
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;
/* 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) );
/* 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);
if (ML_(cur_is_valid)(ui->compdir))
dirname = ML_(addStrFromCursor)(di, ui->compdir);
else
dirname = ML_(addStr)(di, ".", -1);
VG_(addToXA) (dirname_xa, &dirname);
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) {
ML_(symerr)(di, True,
"Only DWARF version 2, 3 and 4 line info "
"is currently supported.");
goto out;
}
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;
}
info.li_default_is_stmt = ML_(cur_step_UChar)(&external);
if (di->ddump_line)
VG_(printf)(" Initial value of 'is_stmt': %d\n",
(Int)info.li_default_is_stmt);
/* Josef Weidendorfer (20021021) writes:
It seems to me that the Intel Fortran compiler generates bad
DWARF2 line info code: It sets "is_stmt" of the state machine in
the the line info reader to be always false. Thus, there is
never a statement boundary generated and therefore never a
instruction range/line number mapping generated for valgrind.
Please have a look at the DWARF2 specification, Ch. 6.2
(x86.ddj.com/ftp/manuals/tools/dwarf.pdf). Perhaps I understand
this wrong, but I don't think so.
I just had a look at the GDB DWARF2 reader... They completely
ignore "is_stmt" when recording line info ;-) That's the reason
"objdump -S" works on files from the the intel fortran compiler.
Therefore: */
info.li_default_is_stmt = True;
/* 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 (info.li_default_is_stmt);
/* 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);
/* 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." : ":" );
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);
/* 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");
}
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);
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", adv );
adv = (op_code % info.li_line_range) + info.li_line_base;
if (0) VG_(printf)("1002: di->o %#lx, smr.a %#lx\n",
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 );
if (state_machine_regs.is_stmt) {
/* 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, info.li_default_is_stmt);
break;
case DW_LNS_copy:
if (0) VG_(printf)("1002: di->o %#lx, smr.a %#lx\n",
di->text_debug_bias, state_machine_regs.address );
if (state_machine_regs.is_stmt) {
/* only add a statement if there was a previous boundary */
if (state_machine_regs.last_address) {
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: {
Word 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 %ld 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: {
Int adv = state_machine_regs.is_stmt;
adv = ! adv;
state_machine_regs.is_stmt = adv;
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", 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 */
Word 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;
do {
name = step_leb128U(&p); /* name */
(void)step_leb128U(&p); /* form */
}
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 )
{
UInt acode, abcode;
ULong atoffs, blklen;
UShort ver;
UChar addr_size;
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 or 4 */
ver = ML_(cur_step_UShort)(&p);
/* 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);
/* 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 abbrevation 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*/ )
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 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;
/* TODO : Following ones just skip data - implement if you need */
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 0x20: /* FORM_ref_sig8 */
p = ML_(cur_plus)(p, 8);
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*/ ) {
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 */
{
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 ) {
ML_(symerr)( di, True,
"Ignoring non-Dwarf2/3/4 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) );
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 %lld, ui.stmt_list %lld %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
);
}
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------*/
/*--- 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 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)
# define FP_REG 5
# define SP_REG 4
# define RA_REG_DEFAULT 8
#elif defined(VGP_amd64_linux)
# 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)
# 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)
# 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_tilegx_linux)
# define FP_REG 52
# define SP_REG 54
# define RA_REG_DEFAULT 55
#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_mips64_linux)
# define N_CFI_REGS 72
#elif defined(VGP_arm_linux) || defined(VGP_tilegx_linux)
# define N_CFI_REGS 320
#elif defined(VGP_arm64_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_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 ]]
| RR_Arch -- dunno
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, RR_Arch } 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)("r%d ", rrule->arg); break;
case RR_ValExpr: VG_(printf)("ve{");
ML_(ppCfiExpr)( exprs, rrule->arg );
VG_(printf)("} ");
break;
case RR_Arch: VG_(printf)("a "); 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 modifed 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;
# endif
}
}
/* A structure which holds information needed by read_encoded_Addr().
*/
typedef
struct {
UChar encoding;
DiCursor ehframe_image;
Addr ehframe_avma;
Addr text_bias;
}
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));
/* 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_mips64) \
|| defined(VGA_tilegx)
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_mips64) \
|| defined(VGA_tilegx)
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) \
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; \
} \
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] );
/* 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);
}
/* 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)
/* --- 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_tilegx)
/* --- entire tail of this fn specialised for tilegx --- */
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_TILEGX_LR);
}
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?) */
# 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)
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)
I_die_here;
# elif defined(VGA_ppc32) || defined(VGA_ppc64be) \
|| defined(VGA_ppc64le)
# elif defined(VGA_tilegx)
if (dwreg == SP_REG)
return ML_(CfiExpr_CfiReg)( dstxa, Creg_TILEGX_SP );
if (dwreg == FP_REG)
return ML_(CfiExpr_CfiReg)( dstxa, Creg_TILEGX_BP );
if (dwreg == srcuc->ra_reg)
return ML_(CfiExpr_CfiReg)( dstxa, Creg_TILEGX_IP );
# 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;
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:
vg_assert(0);
base = /* data base address */ 0;
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_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_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_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;
default:
if (!VG_(clo_xml))
VG_(message)(Vg_DebugMsg,
"Warning: DWARF2 CFI reader: unhandled DW_OP_ "
"opcode 0x%x\n", (Int)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, True/*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;
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(%d)\n", delta);
break;
case DW_CFA_advance_loc2:
delta = (UInt)ML_(cur_step_UShort)(&instr);
VG_(printf)(" sci:DW_CFA_advance_loc2(%d)\n", delta);
break;
case DW_CFA_advance_loc4:
delta = (UInt)ML_(cur_step_UInt)(&instr);
VG_(printf)(" DW_CFA_advance_loc4(%d)\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", (Int)reg, (Int)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",
(Int)reg, (Int)(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;
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 %lld, _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",
ML_(cur_minus)(ciefde_start, frame_image));
ciefde_len = (ULong)ML_(cur_step_UInt)(&data);
if (di->trace_cfi)
VG_(printf)("cie/fde.length = %lld\n", ciefde_len);
/* Apparently, if the .length field is zero, we are at the end
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",
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 = %lld\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;
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 %lld %lld\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;
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;
/* 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;
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)
/*--------------------------------------------------------------------*/
/*--- end ---*/
/*--------------------------------------------------------------------*/