mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-04 02:18:37 +00:00
that hold various kinds of addresses during debuginfo reading, so as to make it easier to understand. See comment at top of debuginfo.c. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6506
2811 lines
88 KiB
C
2811 lines
88 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Read DWARF1/2/3 debug info. readdwarf.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2000-2007 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.
|
|
*/
|
|
/*
|
|
Stabs reader greatly improved by Nick Nethercote, Apr 02.
|
|
This module was also extensively hacked on by Jeremy Fitzhardinge
|
|
and Tom Hughes.
|
|
*/
|
|
|
|
#include "pub_core_basics.h"
|
|
#include "pub_core_libcbase.h"
|
|
#include "pub_core_libcassert.h"
|
|
#include "pub_core_libcprint.h"
|
|
#include "pub_core_mallocfree.h"
|
|
#include "pub_core_options.h"
|
|
#include "priv_storage.h"
|
|
#include "priv_readdwarf.h" /* self */
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Read line number and CFI info from DWARF1, DWARF2 ---*/
|
|
/*--- and to some extent DWARF3 sections. ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Expanding arrays of words, for holding file name and ---*/
|
|
/*--- directory name arrays. ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
typedef
|
|
struct {
|
|
Word* tab;
|
|
UInt tab_size;
|
|
UInt tab_used;
|
|
}
|
|
WordArray;
|
|
|
|
static void init_WordArray ( WordArray* wa )
|
|
{
|
|
wa->tab = NULL;
|
|
wa->tab_size = 0;
|
|
wa->tab_used = 0;
|
|
}
|
|
|
|
static void free_WordArray ( WordArray* wa )
|
|
{
|
|
if (wa->tab) {
|
|
vg_assert(wa->tab_size > 0);
|
|
VG_(arena_free)(VG_AR_SYMTAB, wa->tab);
|
|
}
|
|
init_WordArray(wa);
|
|
}
|
|
|
|
static void addto_WordArray ( WordArray* wa, Word w )
|
|
{
|
|
UInt new_size, i;
|
|
Word* new_tab;
|
|
|
|
if (0) VG_(printf)("<<ADD %p (new sz = %d) >>\n",
|
|
(HChar*)w, wa->tab_used+1);
|
|
|
|
if (wa->tab_used < wa->tab_size) {
|
|
/* fine */
|
|
} else {
|
|
/* expand array */
|
|
if (0) VG_(printf)("EXPAND ARRAY from %d\n", wa->tab_size);
|
|
vg_assert(wa->tab_used == wa->tab_size);
|
|
vg_assert( (wa->tab_size == 0 && wa->tab == NULL)
|
|
|| (wa->tab_size != 0 && wa->tab != NULL) );
|
|
new_size = wa->tab_size == 0 ? 8 : 2 * wa->tab_size;
|
|
new_tab = VG_(arena_malloc)(VG_AR_SYMTAB,
|
|
new_size * sizeof(Word));
|
|
vg_assert(new_tab != NULL);
|
|
for (i = 0; i < wa->tab_used; i++)
|
|
new_tab[i] = wa->tab[i];
|
|
wa->tab_size = new_size;
|
|
if (wa->tab)
|
|
VG_(arena_free)(VG_AR_SYMTAB, wa->tab);
|
|
wa->tab = new_tab;
|
|
}
|
|
|
|
vg_assert(wa->tab_used < wa->tab_size);
|
|
vg_assert(wa->tab_size > 0);
|
|
wa->tab[wa->tab_used] = w;
|
|
wa->tab_used++;
|
|
}
|
|
|
|
static Word index_WordArray ( WordArray* wa, Int i )
|
|
{
|
|
vg_assert(i >= 0 && i < wa->tab_used);
|
|
return wa->tab[i];
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- 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_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 ! */
|
|
Char* compdir; /* Compilation directory - points to .debug_info */
|
|
Char* 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
|
|
};
|
|
|
|
typedef struct State_Machine_Registers
|
|
{
|
|
/* 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;
|
|
Int end_sequence;
|
|
} SMR;
|
|
|
|
|
|
static
|
|
UInt read_leb128 ( UChar* data, Int* length_return, Int sign )
|
|
{
|
|
UInt result = 0;
|
|
UInt num_read = 0;
|
|
Int shift = 0;
|
|
UChar byte;
|
|
|
|
do
|
|
{
|
|
byte = * data ++;
|
|
num_read ++;
|
|
|
|
result |= (byte & 0x7f) << shift;
|
|
|
|
shift += 7;
|
|
|
|
}
|
|
while (byte & 0x80);
|
|
|
|
if (length_return != NULL)
|
|
* length_return = num_read;
|
|
|
|
if (sign && (shift < 32) && (byte & 0x40))
|
|
result |= -1 << shift;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Small helper functions easier to use
|
|
* value is returned and the given pointer is
|
|
* moved past end of leb128 data */
|
|
static UInt read_leb128U( UChar **data )
|
|
{
|
|
Int len;
|
|
UInt val = read_leb128( *data, &len, 0 );
|
|
*data += len;
|
|
return val;
|
|
}
|
|
|
|
/* Same for signed data */
|
|
static Int read_leb128S( UChar **data )
|
|
{
|
|
Int len;
|
|
UInt val = read_leb128( *data, &len, 1 );
|
|
*data += len;
|
|
return val;
|
|
}
|
|
|
|
/* 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. */
|
|
static ULong read_initial_length_field ( UChar* p, /*OUT*/Bool* is64 )
|
|
{
|
|
UInt w32 = *((UInt*)p);
|
|
if (w32 == 0xFFFFFFFF) {
|
|
*is64 = True;
|
|
return *((ULong*)(p+4));
|
|
} else {
|
|
*is64 = False;
|
|
return (ULong)w32;
|
|
}
|
|
}
|
|
|
|
|
|
static SMR state_machine_regs;
|
|
|
|
static
|
|
void reset_state_machine ( Int is_stmt )
|
|
{
|
|
if (0) VG_(printf)("smr.a := %p (reset)\n", 0 );
|
|
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;
|
|
}
|
|
|
|
/* Look up a directory name, or return NULL if unknown. */
|
|
static
|
|
Char* lookupDir ( Int filename_index,
|
|
WordArray* fnidx2dir,
|
|
WordArray* dirnames )
|
|
{
|
|
Word diridx = index_WordArray( fnidx2dir, filename_index );
|
|
Word dirname = index_WordArray( dirnames, (Int)diridx );
|
|
return (Char*)dirname;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
/* Handled an extend line op. Returns true if this is the end
|
|
of sequence. */
|
|
static
|
|
Int process_extended_line_op( struct _SegInfo* si, OffT debug_offset,
|
|
WordArray* filenames,
|
|
WordArray* dirnames,
|
|
WordArray* fnidx2dir,
|
|
UChar* data, Int is_stmt)
|
|
{
|
|
UChar op_code;
|
|
Int bytes_read;
|
|
UInt len;
|
|
UChar* name;
|
|
Addr adr;
|
|
|
|
len = read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
|
|
if (len == 0) {
|
|
VG_(message)(Vg_UserMsg,
|
|
"badly formed extended line op encountered!\n");
|
|
return bytes_read;
|
|
}
|
|
|
|
len += bytes_read;
|
|
op_code = * 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 %p, smr.a %p\n",
|
|
debug_offset, 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) (
|
|
si,
|
|
(Char*)index_WordArray(filenames,
|
|
state_machine_regs.last_file),
|
|
lookupDir( state_machine_regs.last_file,
|
|
fnidx2dir, dirnames ),
|
|
debug_offset + state_machine_regs.last_address,
|
|
debug_offset + state_machine_regs.address,
|
|
state_machine_regs.last_line, 0
|
|
);
|
|
}
|
|
reset_state_machine (is_stmt);
|
|
break;
|
|
|
|
case DW_LNE_set_address:
|
|
adr = *((Addr *)data);
|
|
if (0) VG_(printf)("smr.a := %p\n", adr );
|
|
state_machine_regs.address = adr;
|
|
break;
|
|
|
|
case DW_LNE_define_file:
|
|
name = data;
|
|
addto_WordArray( filenames, (Word)ML_(addStr)(si,name,-1) );
|
|
data += VG_(strlen) ((char *) data) + 1;
|
|
read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
read_leb128 (data, & bytes_read, 0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
/* 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 _SegInfo* si, OffT debug_offset,
|
|
UnitInfo* ui,
|
|
UChar* theBlock,
|
|
Int noLargerThan )
|
|
{
|
|
DebugLineInfo info;
|
|
UChar* standard_opcodes;
|
|
UChar* end_of_sequence;
|
|
Bool is64;
|
|
WordArray filenames;
|
|
WordArray dirnames;
|
|
WordArray fnidx2dir;
|
|
|
|
UChar* external = theBlock;
|
|
UChar* data = theBlock;
|
|
|
|
/* filenames is an array of file names harvested from the DWARF2
|
|
info. Entry [0] is NULL and is never referred to by the state
|
|
machine.
|
|
|
|
Similarly, dirnames is an array 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 file name 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.)
|
|
|
|
fnidx2dir is an array of indexes into the dirnames table.
|
|
(confused yet?) filenames[] and fnidx2dir[] are indexed
|
|
together. That is, for some index i in the filename table, then
|
|
|
|
the filename is filenames[i]
|
|
the directory is dirnames[ fnidx2dir[i] ] */
|
|
|
|
/* Fails due to gcc padding ...
|
|
vg_assert(sizeof(DWARF2_External_LineInfo)
|
|
== sizeof(DWARF2_Internal_LineInfo));
|
|
*/
|
|
|
|
init_WordArray(&filenames);
|
|
init_WordArray(&dirnames);
|
|
init_WordArray(&fnidx2dir);
|
|
|
|
/* DWARF2 starts numbering filename entries at 1, so we need to
|
|
add a dummy zeroth entry to the table. The zeroth dirnames
|
|
entry denotes 'current directory of compilation' so we might
|
|
as well make the fnidx2dir zeroth entry denote that.
|
|
*/
|
|
addto_WordArray( &filenames, (Word)NULL );
|
|
|
|
if (ui->compdir)
|
|
addto_WordArray( &dirnames, (Word)ML_(addStr)(si, ui->compdir, -1) );
|
|
else
|
|
addto_WordArray( &dirnames, (Word)ML_(addStr)(si, ".", -1) );
|
|
|
|
addto_WordArray( &fnidx2dir, (Word)0 ); /* compilation dir */
|
|
|
|
info.li_length = read_initial_length_field( external, &is64 );
|
|
external += is64 ? 12 : 4;
|
|
/* Check the length of the block. */
|
|
if (info.li_length > noLargerThan) {
|
|
ML_(symerr)("DWARF line info appears to be corrupt "
|
|
"- the section is too small");
|
|
goto out;
|
|
}
|
|
|
|
/* Check its version number. */
|
|
info.li_version = * ((UShort *)external);
|
|
external += 2;
|
|
if (info.li_version != 2) {
|
|
ML_(symerr)("Only DWARF version 2 line info "
|
|
"is currently supported.");
|
|
goto out;
|
|
}
|
|
|
|
info.li_header_length = ui->dw64 ? *((ULong*)external)
|
|
: (ULong)(*((UInt*)external));
|
|
external += ui->dw64 ? 8 : 4;
|
|
|
|
info.li_min_insn_length = * ((UChar *)external);
|
|
external += 1;
|
|
|
|
info.li_default_is_stmt = True;
|
|
/* WAS: = * ((UChar *)(external->li_default_is_stmt)); */
|
|
external += 1;
|
|
/* 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.
|
|
*/
|
|
|
|
/* JRS: changed (UInt*) to (UChar*) */
|
|
info.li_line_base = * ((UChar *)external);
|
|
info.li_line_base = (Int)(signed char)info.li_line_base;
|
|
external += 1;
|
|
|
|
info.li_line_range = * ((UChar *)external);
|
|
external += 1;
|
|
|
|
info.li_opcode_base = * ((UChar *)external);
|
|
external += 1;
|
|
|
|
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);
|
|
|
|
end_of_sequence = data + info.li_length
|
|
+ (is64 ? 12 : 4);
|
|
|
|
reset_state_machine (info.li_default_is_stmt);
|
|
|
|
/* Read the contents of the Opcodes table. */
|
|
standard_opcodes = external;
|
|
|
|
/* Read the contents of the Directory table. */
|
|
data = standard_opcodes + info.li_opcode_base - 1;
|
|
|
|
while (* data != 0) {
|
|
|
|
# define NBUF 4096
|
|
static Char buf[NBUF];
|
|
|
|
/* If data[0] is '/', then 'data' is an absolute path and we
|
|
don't mess with it. Otherwise, if we can, construct the
|
|
'path ui->compdir' ++ "/" ++ 'data'. */
|
|
|
|
if (*data != '/'
|
|
/* not an absolute path */
|
|
&& ui->compdir != NULL
|
|
/* actually got something sensible for compdir */
|
|
&& VG_(strlen)(ui->compdir) + VG_(strlen)(data) + 5/*paranoia*/ < NBUF
|
|
/* it's short enough to concatenate */)
|
|
{
|
|
buf[0] = 0;
|
|
VG_(strcat)(buf, ui->compdir);
|
|
VG_(strcat)(buf, "/");
|
|
VG_(strcat)(buf, data);
|
|
vg_assert(VG_(strlen)(buf) < NBUF);
|
|
addto_WordArray( &dirnames, (Word)ML_(addStr)(si,buf,-1) );
|
|
if (0) VG_(printf)("rel path %s\n", buf);
|
|
} else {
|
|
/* just use 'data'. */
|
|
addto_WordArray( &dirnames, (Word)ML_(addStr)(si,data,-1) );
|
|
if (0) VG_(printf)("abs path %s\n", data);
|
|
}
|
|
|
|
data += VG_(strlen)(data) + 1;
|
|
|
|
# undef NBUF
|
|
}
|
|
if (*data != 0) {
|
|
ML_(symerr)("can't find NUL at end of DWARF2 directory table");
|
|
goto out;
|
|
}
|
|
data ++;
|
|
|
|
/* Read the contents of the File Name table. This produces a bunch
|
|
of file names, and for each, an index to the corresponding
|
|
direcory name entry. */
|
|
while (* data != 0) {
|
|
UChar* name;
|
|
Int bytes_read, diridx;
|
|
name = data;
|
|
data += VG_(strlen) ((Char *) data) + 1;
|
|
|
|
diridx = read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
|
|
addto_WordArray( &filenames, (Word)ML_(addStr)(si,name,-1) );
|
|
addto_WordArray( &fnidx2dir, (Word)diridx );
|
|
if (0) VG_(printf)("file %s diridx %d\n", name, diridx );
|
|
}
|
|
if (*data != 0) {
|
|
ML_(symerr)("can't find NUL at end of DWARF2 file name table");
|
|
goto out;
|
|
}
|
|
data ++;
|
|
|
|
/* Now display the statements. */
|
|
|
|
while (data < end_of_sequence) {
|
|
|
|
UChar op_code;
|
|
Int adv;
|
|
Int bytes_read;
|
|
|
|
op_code = * data ++;
|
|
|
|
if (0) VG_(printf)("dwarf2: OPC: %d\n", op_code);
|
|
|
|
if (op_code >= info.li_opcode_base) {
|
|
|
|
Int advAddr;
|
|
op_code -= info.li_opcode_base;
|
|
adv = (op_code / info.li_line_range)
|
|
* info.li_min_insn_length;
|
|
advAddr = adv;
|
|
state_machine_regs.address += adv;
|
|
if (0) VG_(printf)("smr.a += %p\n", adv );
|
|
adv = (op_code % info.li_line_range) + info.li_line_base;
|
|
if (0) VG_(printf)("1002: si->o %p, smr.a %p\n",
|
|
debug_offset, state_machine_regs.address );
|
|
state_machine_regs.line += adv;
|
|
|
|
if (state_machine_regs.is_stmt) {
|
|
/* only add a statement if there was a previous boundary */
|
|
if (state_machine_regs.last_address)
|
|
ML_(addLineInfo)(
|
|
si,
|
|
(Char*)index_WordArray( &filenames,
|
|
state_machine_regs.last_file ),
|
|
lookupDir( state_machine_regs.last_file,
|
|
&fnidx2dir, &dirnames ),
|
|
debug_offset + state_machine_regs.last_address,
|
|
debug_offset + 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:
|
|
data += process_extended_line_op (
|
|
si, debug_offset, &filenames, &dirnames, &fnidx2dir,
|
|
data, info.li_default_is_stmt);
|
|
break;
|
|
|
|
case DW_LNS_copy:
|
|
if (0) VG_(printf)("1002: si->o %p, smr.a %p\n",
|
|
debug_offset, state_machine_regs.address );
|
|
if (state_machine_regs.is_stmt) {
|
|
/* only add a statement if there was a previous boundary */
|
|
if (state_machine_regs.last_address)
|
|
ML_(addLineInfo)(
|
|
si,
|
|
(Char*)index_WordArray( &filenames,
|
|
state_machine_regs.last_file ),
|
|
lookupDir( state_machine_regs.last_file,
|
|
&fnidx2dir, &dirnames ),
|
|
debug_offset + state_machine_regs.last_address,
|
|
debug_offset + 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 */
|
|
break;
|
|
|
|
case DW_LNS_advance_pc:
|
|
adv = info.li_min_insn_length
|
|
* read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
state_machine_regs.address += adv;
|
|
if (0) VG_(printf)("smr.a += %p\n", adv );
|
|
break;
|
|
|
|
case DW_LNS_advance_line:
|
|
adv = read_leb128 (data, & bytes_read, 1);
|
|
data += bytes_read;
|
|
state_machine_regs.line += adv;
|
|
break;
|
|
|
|
case DW_LNS_set_file:
|
|
adv = read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
state_machine_regs.file = adv;
|
|
break;
|
|
|
|
case DW_LNS_set_column:
|
|
adv = read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
state_machine_regs.column = adv;
|
|
break;
|
|
|
|
case DW_LNS_negate_stmt:
|
|
adv = state_machine_regs.is_stmt;
|
|
adv = ! adv;
|
|
state_machine_regs.is_stmt = adv;
|
|
break;
|
|
|
|
case DW_LNS_set_basic_block:
|
|
state_machine_regs.basic_block = 1;
|
|
break;
|
|
|
|
case DW_LNS_const_add_pc:
|
|
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 += %p\n", adv );
|
|
break;
|
|
|
|
case DW_LNS_fixed_advance_pc:
|
|
/* XXX: Need something to get 2 bytes */
|
|
adv = *((UShort *)data);
|
|
data += 2;
|
|
state_machine_regs.address += adv;
|
|
if (0) VG_(printf)("smr.a += %p\n", adv );
|
|
break;
|
|
|
|
case DW_LNS_set_prologue_end:
|
|
break;
|
|
|
|
case DW_LNS_set_epilogue_begin:
|
|
break;
|
|
|
|
case DW_LNS_set_isa:
|
|
adv = read_leb128 (data, & bytes_read, 0);
|
|
data += bytes_read;
|
|
break;
|
|
|
|
default: {
|
|
Int j;
|
|
for (j = standard_opcodes[op_code - 1]; j > 0 ; --j) {
|
|
read_leb128 (data, &bytes_read, 0);
|
|
data += bytes_read;
|
|
}
|
|
}
|
|
break;
|
|
} /* switch (op_code) */
|
|
|
|
} /* while (data < end_of_sequence) */
|
|
|
|
out:
|
|
free_WordArray(&filenames);
|
|
free_WordArray(&dirnames);
|
|
free_WordArray(&fnidx2dir);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
/* Return abbrev for given code
|
|
* Returned pointer points to the tag
|
|
* */
|
|
static UChar* lookup_abbrev( UChar* p, UInt acode )
|
|
{
|
|
UInt code;
|
|
UInt name;
|
|
for( ; ; ) {
|
|
code = read_leb128U( &p );
|
|
if ( code == acode )
|
|
return p;
|
|
read_leb128U( &p ); /* skip tag */
|
|
p++; /* skip has_children flag */
|
|
do {
|
|
name = read_leb128U( &p ); /* name */
|
|
read_leb128U( &p ); /* form */
|
|
}
|
|
while( name != 0 ); /* until name == form == 0 */
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Read general information for a particular compile unit block in
|
|
* the .debug_info section.
|
|
*
|
|
* 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
|
|
*
|
|
* 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,
|
|
UChar* unitblock,
|
|
UChar* debugabbrev,
|
|
UChar* debugstr )
|
|
{
|
|
UInt acode, abcode;
|
|
ULong atoffs, blklen;
|
|
Int level;
|
|
UShort ver;
|
|
|
|
UChar addr_size;
|
|
UChar* p = unitblock;
|
|
UChar* end;
|
|
UChar* abbrev;
|
|
|
|
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 = read_initial_length_field( p, &ui->dw64 );
|
|
p += ui->dw64 ? 12 : 4;
|
|
|
|
/* version should be 2 */
|
|
ver = *((UShort*)p);
|
|
p += 2;
|
|
|
|
/* get offset in abbrev */
|
|
atoffs = ui->dw64 ? *((ULong*)p) : (ULong)(*((UInt*)p));
|
|
p += ui->dw64 ? 8 : 4;
|
|
|
|
/* Address size */
|
|
addr_size = *p;
|
|
p += 1;
|
|
|
|
end = unitblock + blklen + (ui->dw64 ? 12 : 4); /* End of this block */
|
|
level = 0; /* Level in the abbrev tree */
|
|
abbrev = debugabbrev + atoffs; /* Abbreviation data for this block */
|
|
|
|
/* Read the compilation unit entries */
|
|
while ( p < end ) {
|
|
Bool has_child;
|
|
UInt tag;
|
|
|
|
acode = read_leb128U( &p ); /* abbreviation code */
|
|
if ( acode == 0 ) {
|
|
/* NULL entry used for padding - or last child for a sequence
|
|
- see para 7.5.3 */
|
|
level--;
|
|
continue;
|
|
}
|
|
|
|
/* Read abbreviation header */
|
|
abcode = read_leb128U( &abbrev ); /* abbreviation code */
|
|
if ( acode != abcode ) {
|
|
/* We are in in children list, and must rewind to a
|
|
* previously declared abbrev code. This code works but is
|
|
* not triggered since we shortcut the parsing once we have
|
|
* read the compile_unit block. This should only occur when
|
|
* level > 0 */
|
|
abbrev = lookup_abbrev( debugabbrev + atoffs, acode );
|
|
}
|
|
|
|
tag = read_leb128U( &abbrev );
|
|
has_child = *(abbrev++) == 1; /* DW_CHILDREN_yes */
|
|
|
|
if ( has_child )
|
|
level++;
|
|
|
|
/* And loop on entries */
|
|
for ( ; ; ) {
|
|
/* Read entry definition */
|
|
UInt name, form;
|
|
ULong cval = -1LL; /* Constant value read */
|
|
Char *sval = NULL; /* String value read */
|
|
name = read_leb128U( &abbrev );
|
|
form = read_leb128U( &abbrev );
|
|
if ( name == 0 )
|
|
break;
|
|
|
|
/* Read data */
|
|
/* Attributes encoding explained p 71 */
|
|
if ( form == 0x16 /* FORM_indirect */ )
|
|
form = read_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. */
|
|
switch( form ) {
|
|
/* Those cases extract the data properly */
|
|
case 0x05: /* FORM_data2 */ cval = *((UShort*)p); p +=2; break;
|
|
case 0x06: /* FORM_data4 */ cval = *((UInt*)p);p +=4; break;
|
|
case 0x0e: /* FORM_strp */ /* pointer in .debug_str */
|
|
/* 2006-01-01: only generate a value if
|
|
debugstr is non-NULL (which means that a
|
|
debug_str section was found) */
|
|
if (debugstr && !ui->dw64)
|
|
sval = debugstr + *((UInt*)p);
|
|
if (debugstr && ui->dw64)
|
|
sval = debugstr + *((ULong*)p);
|
|
p += ui->dw64 ? 8 : 4;
|
|
break;
|
|
case 0x08: /* FORM_string */ sval = (Char*)p;
|
|
p += VG_(strlen)((Char*)p) + 1; break;
|
|
case 0x0b: /* FORM_data1 */ cval = *p; p++; break;
|
|
|
|
/* TODO : Following ones just skip data - implement if you need */
|
|
case 0x01: /* FORM_addr */ p += addr_size; break;
|
|
case 0x03: /* FORM_block2 */ p += *((UShort*)p) + 2; break;
|
|
case 0x04: /* FORM_block4 */ p += *((UInt*)p) + 4; break;
|
|
case 0x07: /* FORM_data8 */ if (ui->dw64) cval = *((ULong*)p);
|
|
p += 8; break;
|
|
/* perhaps should assign unconditionally to cval? */
|
|
case 0x09: /* FORM_block */ p += read_leb128U( &p ); break;
|
|
case 0x0a: /* FORM_block1 */ p += *p + 1; break;
|
|
case 0x0c: /* FORM_flag */ p++; break;
|
|
case 0x0d: /* FORM_sdata */ read_leb128S( &p ); break;
|
|
case 0x0f: /* FORM_udata */ read_leb128U( &p ); break;
|
|
case 0x10: /* FORM_ref_addr */ p += ui->dw64 ? 8 : 4; break;
|
|
case 0x11: /* FORM_ref1 */ p++; break;
|
|
case 0x12: /* FORM_ref2 */ p += 2; break;
|
|
case 0x13: /* FORM_ref4 */ p += 4; break;
|
|
case 0x14: /* FORM_ref8 */ p += 8; break;
|
|
case 0x15: /* FORM_ref_udata */ read_leb128U( &p ); 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 */
|
|
}
|
|
}
|
|
/* Shortcut the parsing once we have read the compile_unit block
|
|
* That's enough info for us, and we are not gdb ! */
|
|
if ( tag == 0x0011 /*TAG_compile_unit*/ )
|
|
break;
|
|
} /* Loop on each sub block */
|
|
|
|
/* This test would be valid if we were not shortcutting the parsing
|
|
if (level != 0)
|
|
VG_(printf)( "#### Exiting debuginfo block at level %d !!!\n", level );
|
|
*/
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/* Collect the debug info from dwarf2 debugging sections
|
|
* of a given module.
|
|
*
|
|
* Inputs: given .debug_xxx sections
|
|
* Output: update si to contain all the dwarf2 debug infos
|
|
*/
|
|
void ML_(read_debuginfo_dwarf2)
|
|
( struct _SegInfo* si, OffT debug_offset,
|
|
UChar* debuginfo, Int debug_info_sz, /* .debug_info */
|
|
UChar* debugabbrev, /* .debug_abbrev */
|
|
UChar* debugline, Int debug_line_sz, /* .debug_line */
|
|
UChar* debugstr ) /* .debug_str */
|
|
{
|
|
UnitInfo ui;
|
|
UShort ver;
|
|
UChar* block;
|
|
UChar* end = debuginfo + debug_info_sz;
|
|
ULong blklen;
|
|
Bool blklen_is_64;
|
|
Int blklen_len = 0;
|
|
|
|
/* Make sure we at least have a header for the first block */
|
|
if (debug_info_sz < 4) {
|
|
ML_(symerr)( "Last block truncated in .debug_info; ignoring" );
|
|
return;
|
|
}
|
|
|
|
/* Iterate on all the blocks we find in .debug_info */
|
|
for ( block = debuginfo; block < end - 4; block += blklen + blklen_len ) {
|
|
|
|
/* Read the compilation unit header in .debug_info section - See
|
|
p 70 */
|
|
/* This block length */
|
|
blklen = read_initial_length_field( block, &blklen_is_64 );
|
|
blklen_len = blklen_is_64 ? 12 : 4;
|
|
if ( block + blklen + blklen_len > end ) {
|
|
ML_(symerr)( "Last block truncated in .debug_info; ignoring" );
|
|
return;
|
|
}
|
|
|
|
/* version should be 2 */
|
|
ver = *((UShort*)( block + blklen_len ));
|
|
if ( ver != 2 ) {
|
|
ML_(symerr)( "Ignoring non-dwarf2 block in .debug_info" );
|
|
continue;
|
|
}
|
|
|
|
/* Fill ui with offset in .debug_line and compdir */
|
|
if (0)
|
|
VG_(printf)( "Reading UnitInfo at 0x%x.....\n", block - debuginfo );
|
|
read_unitinfo_dwarf2( &ui, block, debugabbrev, debugstr );
|
|
if (0)
|
|
VG_(printf)( " => LINES=0x%llx NAME=%s DIR=%s\n",
|
|
ui.stmt_list, ui.name, ui.compdir );
|
|
|
|
/* Ignore blocks with no .debug_line associated block */
|
|
if ( ui.stmt_list == -1LL )
|
|
continue;
|
|
|
|
if (0)
|
|
VG_(printf)("debug_line_sz %d, ui.stmt_list %lld %s\n",
|
|
debug_line_sz, ui.stmt_list, ui.name );
|
|
/* Read the .debug_line block for this compile unit */
|
|
read_dwarf2_lineblock( si, debug_offset, &ui, debugline + ui.stmt_list,
|
|
debug_line_sz - 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 */
|
|
|
|
void ML_(read_debuginfo_dwarf1) (
|
|
struct _SegInfo* si,
|
|
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;
|
|
UChar* 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 = *(Int*)(dwarf1d + die_offset);
|
|
die_kind = *(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 = *(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 = *(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 = 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;
|
|
Char* curr_filenm;
|
|
UChar* ptr;
|
|
UInt prev_line, prev_delta;
|
|
|
|
curr_filenm = ML_(addStr) ( si, src_filename, -1 );
|
|
prev_line = prev_delta = 0;
|
|
|
|
ptr = dwarf1l + stmt_list;
|
|
len = *(Int*)ptr; ptr += sizeof(Int);
|
|
base = (Addr)(*(void**)ptr); ptr += sizeof(void*);
|
|
len -= (sizeof(Int) + sizeof(void*));
|
|
while (len > 0) {
|
|
UInt line;
|
|
UShort col;
|
|
UInt delta;
|
|
line = *(UInt*)ptr; ptr += sizeof(UInt);
|
|
col = *(UShort*)ptr; ptr += sizeof(UShort);
|
|
delta = *(UShort*)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) ( si, 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 */
|
|
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Read call-frame info from an .eh_frame section ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* 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/tdep-amd64.c and also amd64-abi-0.95.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)
|
|
|
|
This is pretty strange given this not the encoding scheme for
|
|
registers used in amd64 code.
|
|
|
|
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) */
|
|
|
|
/* Note that we don't support DWARF3 expressions (DW_CFA_expression,
|
|
DW_CFA_def_cfa_expression, DW_CFA_val_expression). The code just
|
|
reads over them and ignores them.
|
|
|
|
Note also, does not support the 64-bit DWARF format (only known
|
|
compiler that generates it so far is IBM's xlc/xlC/xlf suite).
|
|
Only handles 32-bit DWARF.
|
|
*/
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
/* --------------- 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 8 // CAB: What's a good default ?
|
|
#elif defined(VGP_ppc64_linux)
|
|
# define FP_REG 1
|
|
# define SP_REG 1
|
|
# define RA_REG_DEFAULT 8 // CAB: What's a good default ?
|
|
#else
|
|
# error Unknown platform
|
|
#endif
|
|
|
|
/* the number of regs we are prepared to unwind */
|
|
#define N_CFI_REGS 20
|
|
|
|
/* 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.
|
|
*/
|
|
typedef
|
|
struct {
|
|
enum { RR_Undef, RR_Same, RR_CFAoff, RR_Reg, RR_Arch, RR_Expr,
|
|
RR_CFAValoff, RR_ValExpr } tag;
|
|
|
|
/* Note, .coff and .reg are never both in use. Therefore could
|
|
merge them into one. */
|
|
|
|
/* CFA offset if tag==RR_CFAoff */
|
|
Int coff;
|
|
|
|
/* reg, if tag==RR_Reg */
|
|
Int reg;
|
|
}
|
|
RegRule;
|
|
|
|
static void ppRegRule ( RegRule* reg )
|
|
{
|
|
switch (reg->tag) {
|
|
case RR_Undef: VG_(printf)("u "); break;
|
|
case RR_Same: VG_(printf)("s "); break;
|
|
case RR_CFAoff: VG_(printf)("c%d ", reg->coff); break;
|
|
case RR_CFAValoff: VG_(printf)("v%d ", reg->coff); break;
|
|
case RR_Reg: VG_(printf)("r%d ", reg->reg); break;
|
|
case RR_Arch: VG_(printf)("a "); break;
|
|
case RR_Expr: VG_(printf)("e "); break;
|
|
case RR_ValExpr: VG_(printf)("ve "); break;
|
|
default: VG_(core_panic)("ppRegRule");
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
/* The CFA entry. If -1, means we don't know (Dwarf3 Expression). */
|
|
Int cfa_reg;
|
|
Int cfa_offset; /* in bytes */
|
|
/* register unwind rules */
|
|
RegRule reg[N_CFI_REGS];
|
|
}
|
|
UnwindContext;
|
|
|
|
static void ppUnwindContext ( UnwindContext* ctx )
|
|
{
|
|
Int i;
|
|
VG_(printf)("0x%llx: ", (ULong)ctx->loc);
|
|
VG_(printf)("%d(r%d) ", ctx->cfa_offset, ctx->cfa_reg);
|
|
for (i = 0; i < N_CFI_REGS; i++)
|
|
ppRegRule(&ctx->reg[i]);
|
|
VG_(printf)("\n");
|
|
}
|
|
|
|
static void initUnwindContext ( /*OUT*/UnwindContext* ctx )
|
|
{
|
|
Int i;
|
|
ctx->code_a_f = 0;
|
|
ctx->data_a_f = 0;
|
|
ctx->initloc = 0;
|
|
ctx->ra_reg = RA_REG_DEFAULT;
|
|
ctx->loc = 0;
|
|
ctx->cfa_reg = 0;
|
|
ctx->cfa_offset = 0;
|
|
for (i = 0; i < N_CFI_REGS; i++) {
|
|
ctx->reg[i].tag = RR_Undef;
|
|
ctx->reg[i].coff = 0;
|
|
ctx->reg[i].reg = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* A structure which holds information needed by read_encoded_Addr().
|
|
*/
|
|
typedef
|
|
struct {
|
|
UChar encoding;
|
|
UChar* ehframe_image;
|
|
Addr ehframe_avma;
|
|
}
|
|
AddressDecodingInfo;
|
|
|
|
|
|
/* ------------ Deal with summary-info records ------------ */
|
|
|
|
static void initCfiSI ( DiCfSI* si )
|
|
{
|
|
si->base = 0;
|
|
si->len = 0;
|
|
si->cfa_sprel = False;
|
|
si->ra_how = 0;
|
|
si->sp_how = 0;
|
|
si->fp_how = 0;
|
|
si->cfa_off = 0;
|
|
si->ra_off = 0;
|
|
si->sp_off = 0;
|
|
si->fp_off = 0;
|
|
}
|
|
|
|
|
|
/* --------------- Summarisation --------------- */
|
|
|
|
/* 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*/DiCfSI* si,
|
|
Addr loc_start,
|
|
UnwindContext* ctx )
|
|
{
|
|
Int why = 0;
|
|
initCfiSI(si);
|
|
|
|
/* How to generate the CFA */
|
|
if (ctx->cfa_reg == -1) {
|
|
/* it was set by DW_CFA_def_cfa_expression; we don't know what
|
|
it really is */
|
|
why = 6;
|
|
goto failed;
|
|
} else
|
|
if (ctx->cfa_reg == SP_REG) {
|
|
si->cfa_sprel = True;
|
|
si->cfa_off = ctx->cfa_offset;
|
|
} else
|
|
if (ctx->cfa_reg == FP_REG) {
|
|
si->cfa_sprel = False;
|
|
si->cfa_off = ctx->cfa_offset;
|
|
} 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.coff; break; \
|
|
case RR_CFAValoff: _how = CFIR_CFAREL; _off = _ctxreg.coff; break; \
|
|
default: { why = 2; goto failed; } /* otherwise give up */ \
|
|
}
|
|
|
|
SUMMARISE_HOW(si->ra_how, si->ra_off, ctx->reg[ctx->ra_reg] );
|
|
SUMMARISE_HOW(si->fp_how, si->fp_off, ctx->reg[FP_REG] );
|
|
|
|
# undef SUMMARISE_HOW
|
|
|
|
/* on x86/amd64, it seems the old %{e,r}sp value before the call is
|
|
always the same as the CFA. Therefore ... */
|
|
si->sp_how = CFIR_CFAREL;
|
|
si->sp_off = 0;
|
|
|
|
/* also, gcc says "Undef" for %{e,r}bp when it is unchanged. So
|
|
.. */
|
|
if (ctx->reg[FP_REG].tag == RR_Undef)
|
|
si->fp_how = CFIR_SAME;
|
|
|
|
/* knock out some obviously stupid cases */
|
|
if (si->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; }
|
|
|
|
si->base = loc_start + ctx->initloc;
|
|
si->len = (UInt)(ctx->loc - loc_start);
|
|
|
|
return True;
|
|
|
|
failed:
|
|
if (VG_(clo_verbosity) > 2 || VG_(clo_trace_cfi)) {
|
|
VG_(message)(Vg_DebugMsg,
|
|
"summarise_context(loc_start = %p)"
|
|
": cannot summarise(why=%d): ", loc_start, why);
|
|
ppUnwindContext(ctx);
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static void ppUnwindContext_summary ( UnwindContext* ctx )
|
|
{
|
|
VG_(printf)("0x%llx-1: ", (ULong)ctx->loc);
|
|
|
|
if (ctx->cfa_reg == SP_REG) {
|
|
VG_(printf)("SP/CFA=%d+SP ", ctx->cfa_offset);
|
|
} else
|
|
if (ctx->cfa_reg == FP_REG) {
|
|
VG_(printf)("SP/CFA=%d+FP ", ctx->cfa_offset);
|
|
} else {
|
|
VG_(printf)("SP/CFA=unknown ", ctx->cfa_offset);
|
|
}
|
|
|
|
VG_(printf)("RA=");
|
|
ppRegRule( &ctx->reg[ctx->ra_reg] );
|
|
|
|
VG_(printf)("FP=");
|
|
ppRegRule( &ctx->reg[FP_REG] );
|
|
VG_(printf)("\n");
|
|
}
|
|
|
|
|
|
/* ------------ Pick apart DWARF2 byte streams ------------ */
|
|
|
|
static inline Bool host_is_little_endian ( void )
|
|
{
|
|
UInt x = 0x76543210;
|
|
UChar* p = (UChar*)(&x);
|
|
return toBool(*p == 0x10);
|
|
}
|
|
|
|
static Short read_Short ( UChar* data )
|
|
{
|
|
Short r = 0;
|
|
vg_assert(host_is_little_endian());
|
|
r = data[0]
|
|
| ( ((UInt)data[1]) << 8 );
|
|
return r;
|
|
}
|
|
|
|
static Int read_Int ( UChar* data )
|
|
{
|
|
Int r = 0;
|
|
vg_assert(host_is_little_endian());
|
|
r = data[0]
|
|
| ( ((UInt)data[1]) << 8 )
|
|
| ( ((UInt)data[2]) << 16 )
|
|
| ( ((UInt)data[3]) << 24 );
|
|
return r;
|
|
}
|
|
|
|
static Long read_Long ( UChar* data )
|
|
{
|
|
Long r = 0;
|
|
vg_assert(host_is_little_endian());
|
|
r = data[0]
|
|
| ( ((ULong)data[1]) << 8 )
|
|
| ( ((ULong)data[2]) << 16 )
|
|
| ( ((ULong)data[3]) << 24 )
|
|
| ( ((ULong)data[4]) << 32 )
|
|
| ( ((ULong)data[5]) << 40 )
|
|
| ( ((ULong)data[6]) << 48 )
|
|
| ( ((ULong)data[7]) << 56 );
|
|
return r;
|
|
}
|
|
|
|
static UShort read_UShort ( UChar* data )
|
|
{
|
|
UInt r = 0;
|
|
vg_assert(host_is_little_endian());
|
|
r = data[0]
|
|
| ( ((UInt)data[1]) << 8 );
|
|
return r;
|
|
}
|
|
|
|
static UInt read_UInt ( UChar* data )
|
|
{
|
|
UInt r = 0;
|
|
vg_assert(host_is_little_endian());
|
|
r = data[0]
|
|
| ( ((UInt)data[1]) << 8 )
|
|
| ( ((UInt)data[2]) << 16 )
|
|
| ( ((UInt)data[3]) << 24 );
|
|
return r;
|
|
}
|
|
|
|
static ULong read_ULong ( UChar* data )
|
|
{
|
|
ULong r = 0;
|
|
vg_assert(host_is_little_endian());
|
|
r = data[0]
|
|
| ( ((ULong)data[1]) << 8 )
|
|
| ( ((ULong)data[2]) << 16 )
|
|
| ( ((ULong)data[3]) << 24 )
|
|
| ( ((ULong)data[4]) << 32 )
|
|
| ( ((ULong)data[5]) << 40 )
|
|
| ( ((ULong)data[6]) << 48 )
|
|
| ( ((ULong)data[7]) << 56 );
|
|
return r;
|
|
}
|
|
|
|
static UChar read_UChar ( UChar* data )
|
|
{
|
|
return data[0];
|
|
}
|
|
|
|
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 read_encoded_Addr ( /*OUT*/Int* nbytes,
|
|
AddressDecodingInfo* adi,
|
|
UChar* data )
|
|
{
|
|
Addr base;
|
|
Word offset;
|
|
UChar encoding = adi->encoding;
|
|
UChar* ehframe_image = adi->ehframe_image;
|
|
Addr ehframe_avma = adi->ehframe_avma;
|
|
|
|
vg_assert((encoding & DW_EH_PE_indirect) == 0);
|
|
|
|
*nbytes = 0;
|
|
|
|
switch (encoding & 0x70) {
|
|
case DW_EH_PE_absptr:
|
|
base = 0;
|
|
break;
|
|
case DW_EH_PE_pcrel:
|
|
base = ehframe_avma + ( 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 = data - ehframe_image;
|
|
if ((offset % sizeof(Addr)) != 0) {
|
|
*nbytes = sizeof(Addr) - (offset % sizeof(Addr));
|
|
data += *nbytes;
|
|
}
|
|
break;
|
|
default:
|
|
vg_assert(0);
|
|
}
|
|
|
|
if ((encoding & 0x07) == 0x00)
|
|
encoding |= default_Addr_encoding();
|
|
|
|
switch (encoding & 0x0f) {
|
|
case DW_EH_PE_udata2:
|
|
*nbytes += sizeof(UShort);
|
|
return base + read_UShort(data);
|
|
case DW_EH_PE_udata4:
|
|
*nbytes += sizeof(UInt);
|
|
return base + read_UInt(data);
|
|
case DW_EH_PE_udata8:
|
|
*nbytes += sizeof(ULong);
|
|
return base + read_ULong(data);
|
|
case DW_EH_PE_sdata2:
|
|
*nbytes += sizeof(Short);
|
|
return base + read_Short(data);
|
|
case DW_EH_PE_sdata4:
|
|
*nbytes += sizeof(Int);
|
|
return base + read_Int(data);
|
|
case DW_EH_PE_sdata8:
|
|
*nbytes += sizeof(Long);
|
|
return base + read_Long(data);
|
|
default:
|
|
vg_assert2(0, "read encoded address %d\n", encoding & 0x0f);
|
|
}
|
|
}
|
|
|
|
|
|
/* ------------ 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,
|
|
UChar* instr,
|
|
UnwindContext* restore_ctx,
|
|
AddressDecodingInfo* adi )
|
|
{
|
|
Int off, reg, reg2, nleb, len;
|
|
UInt delta;
|
|
Int i = 0;
|
|
UChar hi2 = (instr[i] >> 6) & 3;
|
|
UChar lo6 = instr[i] & 0x3F;
|
|
i++;
|
|
|
|
if (hi2 == DW_CFA_advance_loc) {
|
|
delta = (UInt)lo6;
|
|
ctx->loc += delta;
|
|
return i;
|
|
}
|
|
|
|
if (hi2 == DW_CFA_offset) {
|
|
/* Set rule for reg 'lo6' to CFAoffset(off * data_af) */
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
reg = (Int)lo6;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_CFAoff;
|
|
ctx->reg[reg].coff = off * ctx->data_a_f;
|
|
return i;
|
|
}
|
|
|
|
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 */
|
|
ctx->reg[reg] = restore_ctx->reg[reg];
|
|
return i;
|
|
}
|
|
|
|
vg_assert(hi2 == DW_CFA_use_secondary);
|
|
|
|
switch (lo6) {
|
|
case DW_CFA_nop:
|
|
break;
|
|
case DW_CFA_set_loc:
|
|
/* WAS:
|
|
ctx->loc = read_Addr(&instr[i]) - ctx->initloc; i+= sizeof(Addr);
|
|
Was this ever right? */
|
|
ctx->loc = read_encoded_Addr(&len, adi, &instr[i]);
|
|
i += len;
|
|
break;
|
|
case DW_CFA_advance_loc1:
|
|
delta = (UInt)read_UChar(&instr[i]); i+= sizeof(UChar);
|
|
ctx->loc += delta;
|
|
break;
|
|
case DW_CFA_advance_loc2:
|
|
delta = (UInt)read_UShort(&instr[i]); i+= sizeof(UShort);
|
|
ctx->loc += delta;
|
|
break;
|
|
case DW_CFA_advance_loc4:
|
|
delta = (UInt)read_UInt(&instr[i]); i+= sizeof(UInt);
|
|
ctx->loc += delta;
|
|
break;
|
|
|
|
case DW_CFA_def_cfa:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->cfa_reg = reg;
|
|
ctx->cfa_offset = off;
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_sf:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 1 );
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->cfa_reg = reg;
|
|
ctx->cfa_offset = off;
|
|
break;
|
|
|
|
case DW_CFA_register:
|
|
reg = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
reg2 = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
if (reg2 < 0 || reg2 >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_Reg;
|
|
ctx->reg[reg].reg = reg2;
|
|
break;
|
|
|
|
case DW_CFA_offset_extended:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_CFAoff;
|
|
ctx->reg[reg].coff = off * ctx->data_a_f;
|
|
break;
|
|
|
|
case DW_CFA_offset_extended_sf:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 1 );
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_CFAoff;
|
|
ctx->reg[reg].coff = off * ctx->data_a_f;
|
|
break;
|
|
|
|
case DW_CFA_GNU_negative_offset_extended:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_CFAoff;
|
|
ctx->reg[reg].coff = -off * ctx->data_a_f;
|
|
break;
|
|
|
|
case DW_CFA_restore_extended:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
if (restore_ctx == NULL)
|
|
return 0; /* fail */
|
|
ctx->reg[reg] = restore_ctx->reg[reg];
|
|
break;
|
|
|
|
case DW_CFA_val_offset:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_CFAValoff;
|
|
ctx->reg[reg].coff = off * ctx->data_a_f;
|
|
break;
|
|
|
|
case DW_CFA_val_offset_sf:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 1 );
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_CFAValoff;
|
|
ctx->reg[reg].coff = off * ctx->data_a_f;
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_register:
|
|
reg = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->cfa_reg = reg;
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_offset:
|
|
off = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
ctx->cfa_offset = off;
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_offset_sf:
|
|
off = read_leb128( &instr[i], &nleb, 1);
|
|
i += nleb;
|
|
ctx->cfa_offset = off * ctx->data_a_f;
|
|
break;
|
|
|
|
case DW_CFA_GNU_args_size:
|
|
/* No idea what is supposed to happen. gdb-6.3 simply
|
|
ignores these. */
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
break;
|
|
|
|
case DW_CFA_expression:
|
|
/* Too difficult to really handle; just skip over it and say
|
|
that we don't know what do to with the register. */
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("DWARF2 CFI reader: "
|
|
"ignoring DW_CFA_expression\n");
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
len = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
i += len;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_Expr;
|
|
break;
|
|
|
|
case DW_CFA_val_expression:
|
|
/* Too difficult to really handle; just skip over it and say
|
|
that we don't know what do to with the register. */
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("DWARF2 CFI reader: "
|
|
"ignoring DW_CFA_val_expression\n");
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
len = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
i += len;
|
|
if (reg < 0 || reg >= N_CFI_REGS)
|
|
return 0; /* fail */
|
|
ctx->reg[reg].tag = RR_ValExpr;
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_expression:
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("DWARF2 CFI reader: "
|
|
"ignoring DW_CFA_def_cfa_expression\n");
|
|
len = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
i += len;
|
|
ctx->cfa_reg = -1; /* indicating we don't know */
|
|
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. */
|
|
break;
|
|
|
|
default:
|
|
VG_(message)(Vg_DebugMsg, "DWARF2 CFI reader: unhandled CFI "
|
|
"instruction 0:%d", (Int)lo6);
|
|
i = 0;
|
|
break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
/* Show a CFI instruction, and also return its length. */
|
|
|
|
static Int show_CF_instruction ( UChar* instr,
|
|
AddressDecodingInfo* adi )
|
|
{
|
|
UInt delta;
|
|
Int off, reg, reg2, nleb, len;
|
|
Addr loc;
|
|
Int i = 0;
|
|
UChar hi2 = (instr[i] >> 6) & 3;
|
|
UChar lo6 = instr[i] & 0x3F;
|
|
i++;
|
|
|
|
if (0) VG_(printf)("raw:%x/%x:%x:%x:%x:%x:%x:%x:%x:%x\n",
|
|
hi2, lo6,
|
|
instr[i+0], instr[i+1], instr[i+2], instr[i+3],
|
|
instr[i+4], instr[i+5], instr[i+6], instr[i+7] );
|
|
|
|
if (hi2 == DW_CFA_advance_loc) {
|
|
VG_(printf)("DW_CFA_advance_loc(%d)\n", (Int)lo6);
|
|
return i;
|
|
}
|
|
|
|
if (hi2 == DW_CFA_offset) {
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_offset(r%d + %d x data_af)\n", (Int)lo6, off);
|
|
return i;
|
|
}
|
|
|
|
if (hi2 == DW_CFA_restore) {
|
|
VG_(printf)("DW_CFA_restore(r%d)\n", (Int)lo6);
|
|
return i;
|
|
}
|
|
|
|
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); */
|
|
loc = read_encoded_Addr(&len, adi, &instr[i]);
|
|
i += len;
|
|
VG_(printf)("DW_CFA_set_loc(%p)\n", loc);
|
|
break;
|
|
|
|
case DW_CFA_advance_loc1:
|
|
delta = (UInt)read_UChar(&instr[i]); i+= sizeof(UChar);
|
|
VG_(printf)("DW_CFA_advance_loc1(%d)\n", delta);
|
|
break;
|
|
|
|
case DW_CFA_advance_loc2:
|
|
delta = (UInt)read_UShort(&instr[i]); i+= sizeof(UShort);
|
|
VG_(printf)("DW_CFA_advance_loc2(%d)\n", delta);
|
|
break;
|
|
|
|
case DW_CFA_advance_loc4:
|
|
delta = (UInt)read_UInt(&instr[i]); i+= sizeof(UInt);
|
|
VG_(printf)("DW_CFA_advance_loc4(%d)\n", delta);
|
|
break;
|
|
|
|
case DW_CFA_def_cfa:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_def_cfa(r%d, off %d)\n", reg, off);
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_sf:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 1 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_def_cfa_sf(r%d, off %d)\n", reg, off);
|
|
break;
|
|
|
|
case DW_CFA_register:
|
|
reg = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
reg2 = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_register(r%d, r%d)\n", reg, reg2);
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_register:
|
|
reg = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_def_cfa_register(r%d)\n", reg);
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_offset:
|
|
off = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_def_cfa_offset(%d)\n", off);
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_offset_sf:
|
|
off = read_leb128( &instr[i], &nleb, 1);
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_def_cfa_offset_sf(%d)\n", off);
|
|
break;
|
|
|
|
case DW_CFA_restore_extended:
|
|
reg = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_restore_extended(r%d)\n", reg);
|
|
break;
|
|
|
|
case DW_CFA_undefined:
|
|
reg = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_undefined(r%d)\n", reg);
|
|
break;
|
|
|
|
case DW_CFA_same_value:
|
|
reg = read_leb128( &instr[i], &nleb, 0);
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_same_value(r%d)\n", reg);
|
|
break;
|
|
|
|
case DW_CFA_remember_state:
|
|
VG_(printf)("DW_CFA_remember_state\n");
|
|
break;
|
|
|
|
case DW_CFA_restore_state:
|
|
VG_(printf)("DW_CFA_restore_state\n");
|
|
break;
|
|
|
|
case DW_CFA_GNU_args_size:
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_GNU_args_size(%d)\n", off );
|
|
break;
|
|
|
|
case DW_CFA_def_cfa_expression:
|
|
len = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
i += len;
|
|
VG_(printf)("DW_CFA_def_cfa_expression(length %d)\n", len);
|
|
break;
|
|
|
|
case DW_CFA_expression:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
len = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
i += len;
|
|
VG_(printf)("DW_CFA_expression(r%d, length %d)\n", reg, len);
|
|
break;
|
|
|
|
case DW_CFA_val_expression:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
len = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
i += len;
|
|
VG_(printf)("DW_CFA_val_expression(r%d, length %d)\n", reg, len);
|
|
break;
|
|
|
|
case DW_CFA_offset_extended:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_offset_extended(r%d, off %d x data_af)\n", reg, off);
|
|
break;
|
|
|
|
case DW_CFA_offset_extended_sf:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 1 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_offset_extended_sf(r%d, off %d x data_af)\n", reg, off);
|
|
break;
|
|
|
|
case DW_CFA_GNU_negative_offset_extended:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_GNU_negative_offset_extended(r%d, off %d x data_af)\n", reg, -off);
|
|
break;
|
|
|
|
case DW_CFA_val_offset:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_val_offset(r%d, off %d x data_af)\n", reg, off);
|
|
break;
|
|
|
|
case DW_CFA_val_offset_sf:
|
|
reg = read_leb128( &instr[i], &nleb, 0 );
|
|
i += nleb;
|
|
off = read_leb128( &instr[i], &nleb, 1 );
|
|
i += nleb;
|
|
VG_(printf)("DW_CFA_val_offset_sf(r%d, off %d x data_af)\n", reg, off);
|
|
break;
|
|
|
|
case DW_CFA_GNU_window_save:
|
|
VG_(printf)("DW_CFA_GNU_window_save\n");
|
|
break;
|
|
|
|
default:
|
|
VG_(printf)("0:%d\n", (Int)lo6);
|
|
break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
static void show_CF_instructions ( UChar* instrs, Int ilen,
|
|
AddressDecodingInfo* adi )
|
|
{
|
|
Int i = 0;
|
|
while (True) {
|
|
if (i >= ilen) break;
|
|
i += show_CF_instruction( &instrs[i], adi );
|
|
}
|
|
}
|
|
|
|
/* 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 ( struct _SegInfo* si,
|
|
UnwindContext* ctx, UChar* instrs, Int ilen,
|
|
UWord fde_arange,
|
|
UnwindContext* restore_ctx,
|
|
AddressDecodingInfo* adi )
|
|
{
|
|
DiCfSI cfsi;
|
|
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( &instrs[i], adi );
|
|
j = run_CF_instruction( ctx, &instrs[i], restore_ctx, adi );
|
|
if (j == 0)
|
|
return False; /* execution failed */
|
|
i += j;
|
|
if (0) ppUnwindContext(ctx);
|
|
if (loc_prev != ctx->loc && si) {
|
|
summ_ok = summarise_context ( &cfsi, loc_prev, ctx );
|
|
if (summ_ok) {
|
|
ML_(addDiCfSI)(si, &cfsi);
|
|
if (VG_(clo_trace_cfi))
|
|
ML_(ppDiCfSI)(&cfsi);
|
|
}
|
|
}
|
|
}
|
|
if (ctx->loc < fde_arange) {
|
|
loc_prev = ctx->loc;
|
|
ctx->loc = fde_arange;
|
|
if (si) {
|
|
summ_ok = summarise_context ( &cfsi, loc_prev, ctx );
|
|
if (summ_ok) {
|
|
ML_(addDiCfSI)(si, &cfsi);
|
|
if (VG_(clo_trace_cfi))
|
|
ML_(ppDiCfSI)(&cfsi);
|
|
}
|
|
}
|
|
}
|
|
return True;
|
|
}
|
|
|
|
|
|
/* ------------ Main entry point for CFI reading ------------ */
|
|
|
|
typedef
|
|
struct {
|
|
/* This gives the CIE an identity to which FDEs will refer. */
|
|
UInt 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? Note, this are simply pointers back to
|
|
the transiently-mapped-in section. */
|
|
UChar* 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 = NULL;
|
|
cie->ilen = 0;
|
|
cie->saw_z_augmentation = False;
|
|
}
|
|
|
|
#define N_CIEs 2000
|
|
static CIE the_CIEs[N_CIEs];
|
|
|
|
|
|
void ML_(read_callframe_info_dwarf2)
|
|
( /*OUT*/struct _SegInfo* si,
|
|
UChar* ehframe_image, Int ehframe_sz, Addr ehframe_avma )
|
|
{
|
|
Int nbytes;
|
|
HChar* how = NULL;
|
|
Int n_CIEs = 0;
|
|
UChar* data = ehframe_image;
|
|
|
|
# if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
|
|
/* These targets don't use CFI-based stack unwinding. */
|
|
return;
|
|
# endif
|
|
|
|
if (VG_(clo_trace_cfi)) {
|
|
VG_(printf)("\n-----------------------------------------------\n");
|
|
VG_(printf)("CFI info: szB %d, _avma %p, _image %p\n",
|
|
ehframe_sz, (void*)ehframe_avma, (void*)ehframe_image );
|
|
VG_(printf)("CFI info: name %s\n",
|
|
si->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) {
|
|
UChar* ciefde_start;
|
|
UInt ciefde_len;
|
|
UInt cie_pointer;
|
|
|
|
/* Are we done? */
|
|
if (data == ehframe_image + ehframe_sz)
|
|
return;
|
|
|
|
/* Overshot the end? Means something is wrong */
|
|
if (data > ehframe_image + ehframe_sz) {
|
|
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 (VG_(clo_trace_cfi))
|
|
VG_(printf)("\ncie/fde.start = %p (ehframe_image + 0x%x)\n",
|
|
ciefde_start, ciefde_start - ehframe_image);
|
|
|
|
ciefde_len = read_UInt(data); data += sizeof(UInt);
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("cie/fde.length = %d\n", ciefde_len);
|
|
|
|
/* Apparently, if the .length field is zero, we are at the end
|
|
of the sequence. ?? Neither the DWARF2 spec not the AMD64
|
|
ABI spec say this, though. */
|
|
if (ciefde_len == 0) {
|
|
if (data == ehframe_image + ehframe_sz)
|
|
return;
|
|
how = "zero-sized CIE/FDE but not at section end";
|
|
goto bad;
|
|
}
|
|
|
|
cie_pointer = read_UInt(data);
|
|
data += sizeof(UInt); /* XXX see XXX below */
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("cie.pointer = %d\n", cie_pointer);
|
|
|
|
/* If cie_pointer is zero, we've got a CIE; else it's an FDE. */
|
|
if (cie_pointer == 0) {
|
|
|
|
Int this_CIE;
|
|
UChar cie_version;
|
|
UChar* cie_augmentation;
|
|
|
|
/* --------- CIE --------- */
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("------ new CIE (#%d of 0 .. %d) ------\n",
|
|
n_CIEs, N_CIEs - 1);
|
|
|
|
/* Allocate a new CIE record. */
|
|
vg_assert(n_CIEs >= 0 && n_CIEs <= N_CIEs);
|
|
if (n_CIEs == N_CIEs) {
|
|
how = "N_CIEs is too low. Increase and recompile.";
|
|
goto bad;
|
|
}
|
|
|
|
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 = ciefde_start - ehframe_image;
|
|
|
|
cie_version = read_UChar(data); data += sizeof(UChar);
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("cie.version = %d\n", (Int)cie_version);
|
|
if (cie_version != 1) {
|
|
how = "unexpected CIE version (not 1)";
|
|
goto bad;
|
|
}
|
|
|
|
cie_augmentation = data;
|
|
data += 1 + VG_(strlen)(cie_augmentation);
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("cie.augment = \"%s\"\n", cie_augmentation);
|
|
|
|
if (cie_augmentation[0] == 'e' && cie_augmentation[1] == 'h') {
|
|
data += sizeof(Addr);
|
|
cie_augmentation += 2;
|
|
}
|
|
|
|
the_CIEs[this_CIE].code_a_f = read_leb128( data, &nbytes, 0);
|
|
data += nbytes;
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("cie.code_af = %d\n",
|
|
the_CIEs[this_CIE].code_a_f);
|
|
|
|
the_CIEs[this_CIE].data_a_f = read_leb128( data, &nbytes, 1);
|
|
data += nbytes;
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("cie.data_af = %d\n",
|
|
the_CIEs[this_CIE].data_a_f);
|
|
|
|
the_CIEs[this_CIE].ra_reg = (Int)read_UChar(data);
|
|
data += sizeof(UChar);
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("cie.ra_reg = %d\n",
|
|
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
|
|
= *cie_augmentation == 'z';
|
|
if (the_CIEs[this_CIE].saw_z_augmentation) {
|
|
UInt length = read_leb128( data, &nbytes, 0);
|
|
data += nbytes;
|
|
the_CIEs[this_CIE].instrs = data + length;
|
|
cie_augmentation++;
|
|
} else {
|
|
the_CIEs[this_CIE].instrs = NULL;
|
|
}
|
|
|
|
the_CIEs[this_CIE].address_encoding = default_Addr_encoding();
|
|
|
|
while (*cie_augmentation) {
|
|
switch (*cie_augmentation) {
|
|
case 'L':
|
|
data++;
|
|
cie_augmentation++;
|
|
break;
|
|
case 'R':
|
|
the_CIEs[this_CIE].address_encoding
|
|
= read_UChar(data); data += sizeof(UChar);
|
|
cie_augmentation++;
|
|
break;
|
|
case 'P':
|
|
data += size_of_encoded_Addr( read_UChar(data) );
|
|
data++;
|
|
cie_augmentation++;
|
|
break;
|
|
case 'S':
|
|
cie_augmentation++;
|
|
break;
|
|
default:
|
|
if (the_CIEs[this_CIE].instrs == NULL) {
|
|
how = "unhandled cie.augmentation";
|
|
goto bad;
|
|
}
|
|
data = the_CIEs[this_CIE].instrs;
|
|
goto done_augmentation;
|
|
}
|
|
}
|
|
|
|
done_augmentation:
|
|
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("cie.encoding = 0x%x\n",
|
|
the_CIEs[this_CIE].address_encoding);
|
|
|
|
the_CIEs[this_CIE].instrs = data;
|
|
the_CIEs[this_CIE].ilen
|
|
= ciefde_start + ciefde_len + sizeof(UInt) - data;
|
|
if (VG_(clo_trace_cfi)) {
|
|
VG_(printf)("cie.instrs = %p\n", the_CIEs[this_CIE].instrs);
|
|
VG_(printf)("cie.ilen = %d\n", the_CIEs[this_CIE].ilen);
|
|
}
|
|
|
|
if (the_CIEs[this_CIE].ilen < 0
|
|
|| the_CIEs[this_CIE].ilen > ehframe_sz) {
|
|
how = "implausible # cie initial insns";
|
|
goto bad;
|
|
}
|
|
|
|
data += the_CIEs[this_CIE].ilen;
|
|
|
|
if (VG_(clo_trace_cfi)) {
|
|
AddressDecodingInfo adi;
|
|
adi.encoding = the_CIEs[this_CIE].address_encoding;
|
|
adi.ehframe_image = ehframe_image;
|
|
adi.ehframe_avma = ehframe_avma;
|
|
show_CF_instructions(the_CIEs[this_CIE].instrs,
|
|
the_CIEs[this_CIE].ilen, &adi );
|
|
}
|
|
|
|
} else {
|
|
|
|
AddressDecodingInfo adi;
|
|
UnwindContext ctx, restore_ctx;
|
|
Int cie;
|
|
UInt look_for;
|
|
Bool ok;
|
|
Addr fde_initloc;
|
|
UWord fde_arange;
|
|
UChar* 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), matches XXX above. For 64-bit dwarf this
|
|
will have to be a ULong instead. */
|
|
look_for = (data - sizeof(UInt) - ehframe_image) - cie_pointer;
|
|
|
|
for (cie = 0; cie < n_CIEs; cie++) {
|
|
if (0) VG_(printf)("look for %d %d\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 = ehframe_image;
|
|
adi.ehframe_avma = ehframe_avma;
|
|
fde_initloc = read_encoded_Addr(&nbytes, &adi, data);
|
|
data += nbytes;
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("fde.initloc = %p\n", (void*)fde_initloc);
|
|
|
|
adi.encoding = the_CIEs[cie].address_encoding & 0xf;
|
|
adi.ehframe_image = ehframe_image;
|
|
adi.ehframe_avma = ehframe_avma;
|
|
fde_arange = read_encoded_Addr(&nbytes, &adi, data);
|
|
data += nbytes;
|
|
if (VG_(clo_trace_cfi))
|
|
VG_(printf)("fde.arangec = %p\n", (void*)fde_arange);
|
|
|
|
if (the_CIEs[cie].saw_z_augmentation) {
|
|
data += read_leb128( data, &nbytes, 0);
|
|
data += nbytes;
|
|
}
|
|
|
|
fde_instrs = data;
|
|
fde_ilen = ciefde_start + ciefde_len + sizeof(UInt) - data;
|
|
if (VG_(clo_trace_cfi)) {
|
|
VG_(printf)("fde.instrs = %p\n", fde_instrs);
|
|
VG_(printf)("fde.ilen = %d\n", (Int)fde_ilen);
|
|
}
|
|
|
|
if (fde_ilen < 0 || fde_ilen > ehframe_sz) {
|
|
how = "implausible # fde insns";
|
|
goto bad;
|
|
}
|
|
|
|
data += fde_ilen;
|
|
|
|
adi.encoding = the_CIEs[cie].address_encoding;
|
|
adi.ehframe_image = ehframe_image;
|
|
adi.ehframe_avma = ehframe_avma;
|
|
|
|
if (VG_(clo_trace_cfi))
|
|
show_CF_instructions(fde_instrs, fde_ilen, &adi);
|
|
|
|
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;
|
|
|
|
initUnwindContext(&restore_ctx);
|
|
|
|
ok = run_CF_instructions(
|
|
NULL, &ctx, the_CIEs[cie].instrs,
|
|
the_CIEs[cie].ilen, 0, NULL, &adi
|
|
);
|
|
if (ok) {
|
|
restore_ctx = ctx;
|
|
ok = run_CF_instructions(
|
|
si, &ctx, fde_instrs, fde_ilen, fde_arange,
|
|
&restore_ctx, &adi
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
bad:
|
|
if (!VG_(clo_xml) && VG_(clo_verbosity) > 1)
|
|
VG_(message)(Vg_UserMsg, "Warning: %s in DWARF2 CFI reading", how);
|
|
return;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end ---*/
|
|
/*--------------------------------------------------------------------*/
|