blob: 335074fb28315953719de34c93d1e141dfe3f94e [file] [log] [blame]
/* -*- C++ -*- interfaces for libdw.
Copyright (C) 2009-2011 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
it under the terms of either
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at
your option) any later version
or
* 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
or both in parallel, as here.
elfutils 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 copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <cassert>
#include "dwarf"
#include "dwarf_edit"
#include "dwarf_output"
#include "data-values.hh"
extern "C"
{
#include "libdwP.h"
}
using namespace elfutils;
using namespace std;
#include "dwarf-knowledge.cc"
// dwarf::attr_value disambiguation and dispatch.
/* For ambiguous the forms, we need to look up the expected
value spaces for this attribute to disambiguate.
*/
dwarf::value_space
dwarf::attr_value::what_space () const
{
const uint_fast16_t version = _m_attr.cu->version;
unsigned int expected = expected_value_space (dwarf_whatattr (thisattr ()),
_m_tag);
unsigned int possible = 0;
switch (dwarf_whatform (thisattr ()))
{
case DW_FORM_flag:
case DW_FORM_flag_present:
return VS_flag;
case DW_FORM_addr:
return VS_address;
case DW_FORM_block:
case DW_FORM_block1:
case DW_FORM_block2:
case DW_FORM_block4:
/* Location expression in DWARF 3, or target constant. */
possible = VS(constant);
if (version >= 4)
break;
possible |= VS(location);
if ((expected & possible) != possible)
/* When both are expected, a block is a location expression. */
break;
/* Fall through. */
case DW_FORM_exprloc:
return VS_location;
case DW_FORM_data1:
case DW_FORM_data2:
case DW_FORM_udata:
case DW_FORM_sdata:
/* Target constant, known DWARF constant. */
possible = (VS(dwarf_constant) | VS(constant)
| VS(source_file) | VS(source_line) | VS(source_column));
break;
case DW_FORM_data4:
case DW_FORM_data8:
// If a constant is not expected, these can be *ptr instead in DWARF 3.
possible = (VS(dwarf_constant) | VS(constant)
| VS(source_file) | VS(source_line) | VS(source_column));
if (version >= 4 || (expected & possible))
break;
case DW_FORM_sec_offset:
possible = VS(location) | VS(lineptr) | VS(macptr) | VS(rangelistptr);
break;
case DW_FORM_string:
case DW_FORM_strp:
/* Identifier, file name, or string. */
possible = VS(identifier) | VS(source_file) | VS(string);
break;
case DW_FORM_ref_addr:
case DW_FORM_ref1:
case DW_FORM_ref2:
case DW_FORM_ref4:
case DW_FORM_ref8:
case DW_FORM_ref_udata:
case DW_FORM_ref_sig8:
return VS_reference;
default:
throw std::runtime_error ("XXX bad form");
}
if (unlikely ((expected & possible) == 0))
{
// Otherwise we don't know enough to treat it robustly.
throw std::runtime_error ("XXX ambiguous form in unexpected attribute");
}
const int first = ffs (expected & possible) - 1;
if (likely ((expected & possible) == (1U << first)))
return static_cast<value_space> (first);
throw std::runtime_error ("XXX ambiguous form");
}
static string
hex_string (Dwarf_Word value, const char *before = "", const char *after = "")
{
std::ostringstream os;
os << std::hex << std::showbase << before << value << after;
return os.str ();
}
static string
dec_string (Dwarf_Word value, const char *before = "", const char *after = "")
{
std::ostringstream os;
os << before << value << after;
return os.str ();
}
static string
addr_string (Dwarf_Addr value)
{
// XXX some hook for symbol resolver??
return hex_string (value);
}
static inline string
plain_string (const char *filename)
{
return string ("\"") + filename + "\"";
}
static inline string
plain_string (const string &filename)
{
return "\"" + filename + "\"";
}
template<class value_type>
static inline string
value_string (const value_type &value)
{
switch (value.what_space ())
{
case dwarf::VS_flag:
return value.flag () ? "1" : "0";
case dwarf::VS_rangelistptr:
return value.ranges ().to_string ();
case dwarf::VS_lineptr:
return value.line_info ().to_string ();
case dwarf::VS_macptr: // XXX punt for now, treat as constant
case dwarf::VS_constant:
if (value.constant_is_integer ())
return hex_string (value.constant ());
return dec_string (value.constant_block ().size (),
"{block of ", " bytes}");
case dwarf::VS_dwarf_constant:
return value.dwarf_constant ().to_string ();
case dwarf::VS_source_line:
return dec_string (value.source_line ());
case dwarf::VS_source_column:
return dec_string (value.source_column ());
case dwarf::VS_identifier:
return plain_string (value.identifier ());
case dwarf::VS_string:
return plain_string (value.string ());
case dwarf::VS_address:
return addr_string (value.address ());
case dwarf::VS_reference:
return hex_string (value.reference ()->offset (), "[", "]");
case dwarf::VS_source_file:
return value.source_file ().to_string ();
case dwarf::VS_location:
return value.location ().to_string ();
case dwarf::VS_discr_list:
break; // XXX DW_AT_discr_list unimplemented
}
throw std::runtime_error ("XXX unsupported value space");
}
template<>
string
to_string<dwarf::attribute> (const dwarf::attribute &attr)
{
return attribute_string (attr);
}
template<>
string
to_string<dwarf::attr_value> (const dwarf::attr_value &value)
{
return value_string (value);
}
template<>
string
to_string<dwarf_edit::attr_value> (const dwarf_edit::attr_value &value)
{
return value_string (value);
}
template<>
string
to_string<dwarf_output::attr_value> (const dwarf_output::attr_value &value)
{
return value_string (value);
}
// A few cases are trivial.
#define SIMPLE(type, name, form) \
type \
dwarf::attr_value::name () const \
{ \
type result; \
xif (thisattr (), dwarf_form##form (thisattr (), &result) < 0); \
return result; \
}
SIMPLE (bool, flag, flag)
// XXX check value_space is really constantish?? vs *ptr
SIMPLE (Dwarf_Word, constant, udata)
SIMPLE (Dwarf_Sword, signed_constant, sdata)
SIMPLE (Dwarf_Addr, address, addr)
const char *
dwarf::attr_value::string () const
{
const char *result = dwarf_formstring (thisattr ());
xif (thisattr(), result == NULL);
return result;
}
bool
dwarf::attr_value::constant_is_integer () const
{
switch (dwarf_whatform (thisattr ()))
{
case DW_FORM_block:
case DW_FORM_block1:
case DW_FORM_block2:
case DW_FORM_block4:
return false;
case DW_FORM_data1:
case DW_FORM_data2:
case DW_FORM_data4:
case DW_FORM_data8:
case DW_FORM_udata:
case DW_FORM_sdata:
return true;
default:
throw std::runtime_error ("XXX wrong form");
}
}
const_vector<uint8_t>
dwarf::attr_value::constant_block () const
{
Dwarf_Block block;
switch (dwarf_whatform (thisattr ()))
{
case DW_FORM_block:
case DW_FORM_block1:
case DW_FORM_block2:
case DW_FORM_block4:
case DW_FORM_exprloc:
xif (thisattr(), dwarf_formblock (thisattr (), &block) < 0);
break;
case DW_FORM_data1:
block.length = 1;
block.data = thisattr ()->valp;
break;
case DW_FORM_data2:
block.length = 2;
block.data = thisattr ()->valp;
break;
case DW_FORM_data4:
block.length = 4;
block.data = thisattr ()->valp;
break;
case DW_FORM_data8:
block.length = 8;
block.data = thisattr ()->valp;
break;
case DW_FORM_udata:
case DW_FORM_sdata:
// XXX ?
if ((*(const uint8_t *) thisattr ()->valp & 0x80) == 0)
{
block.length = 1;
block.data = thisattr ()->valp;
break;
}
default:
throw std::runtime_error ("XXX wrong form");
}
return const_vector<uint8_t> (block);
}
namespace elfutils
{
template<>
std::string to_string (const dwarf::debug_info_entry &die)
{
return die_string (die);
}
};
// dwarf::range_list
unsigned char *
dwarf::range_list::const_iterator::formptr (int secndx, Dwarf_Attribute *attr)
{
unsigned char *readptr = __libdw_formptr (attr, secndx,
DWARF_E_NO_DEBUG_RANGES,
NULL, NULL);
xif (attr, readptr == NULL);
return readptr;
}
dwarf::range_list::const_iterator
dwarf::range_list::begin () const
{
const_iterator it (IDX_debug_ranges, _m_attr.thisattr (), 0);
return ++it;
}
dwarf::range_list::const_iterator::const_iterator (int secndx,
Dwarf_Attribute *attr,
unsigned char *readptr)
: _m_base (-1), _m_begin (0), _m_end (0), _m_cu (attr->cu)
, _m_readptr (readptr)
{
if (_m_readptr == NULL)
{
_m_readptr = formptr (secndx, attr);
xif (attr, _m_readptr == NULL);
}
}
static bool
range_list_advance (int secndx,
Dwarf_CU *cu,
Dwarf_Addr &base,
Dwarf_Addr &begin,
Dwarf_Addr &end,
unsigned char *&readp,
unsigned char **valp)
{
const Elf_Data *d = cu->dbg->sectiondata[secndx];
if (unlikely (d == NULL))
throw std::runtime_error ("XXX no ranges");
if (unlikely (readp >= (unsigned char *)d->d_buf + d->d_size))
throw std::runtime_error ("XXX bad readptr in ranges iterator");
unsigned char *const readendp
= reinterpret_cast<unsigned char *> (d->d_buf) + d->d_size;
while (true)
{
if (readendp - readp < cu->address_size * 2)
throw std::runtime_error ("XXX bad ranges");
if (cu->address_size == 8)
{
begin = read_8ubyte_unaligned_inc (cu->dbg, readp);
end = read_8ubyte_unaligned_inc (cu->dbg, readp);
if (begin == (uint64_t) -1l) /* Base address entry. */
{
base = end;
continue;
}
}
else
{
begin = read_4ubyte_unaligned_inc (cu->dbg, readp);
end = read_4ubyte_unaligned_inc (cu->dbg, readp);
if (begin == (uint32_t) -1) /* Base address entry. */
{
base = end;
continue;
}
}
break;
}
if (begin == 0 && end == 0) /* End of list entry. */
readp = (unsigned char *)-1;
else
{
if (valp)
*valp = readp;
if (base == (Dwarf_Addr) -1)
{
CUDIE (cudie, cu);
/* Find the base address of the compilation unit. It will
normally be specified by DW_AT_low_pc. In DWARF-3 draft 4,
the base address could be overridden by DW_AT_entry_pc. It's
been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
for compilation units with discontinuous ranges. */
Dwarf_Attribute attr_mem;
if (unlikely (dwarf_lowpc (&cudie, &base) != 0)
&& dwarf_formaddr (dwarf_attr (&cudie,
DW_AT_entry_pc,
&attr_mem),
&base) != 0)
{
return true; // XXX
}
}
}
return false;
}
dwarf::range_list::const_iterator &
dwarf::range_list::const_iterator::operator++ ()
{
xif (_m_cu, range_list_advance (IDX_debug_ranges, _m_cu, _m_base,
_m_begin, _m_end, _m_readptr, NULL));
return *this;
}
template<typename container>
string
__libdw_ranges_to_string (const container &c)
{
std::ostringstream os;
os << "<" << std::hex << std::showbase;
bool first = true;
for (typename container::const_iterator i = c.begin (); i != c.end (); ++i)
{
const typename container::value_type range = *i;
if (!first)
os << ",";
os << range.first << "-" << range.second;
first = false;
}
os << ">";
return os.str ();
}
string
dwarf::range_list::to_string () const
{
return __libdw_ranges_to_string (*this);
}
string
dwarf::ranges::to_string () const
{
return __libdw_ranges_to_string (*this);
}
string
dwarf::arange_list::to_string () const
{
return __libdw_ranges_to_string (*this);
}
dwarf::aranges_map
dwarf::aranges () const
{
Dwarf_Aranges *these;
xif (dwarf_getaranges (_m_dw, &these, NULL) < 0);
if (these == NULL)
return aranges_map ();
aranges_map result;
for (const Dwarf_Aranges_s::Dwarf_Arange_s *r = &these->info[0];
r < &these->info[these->naranges];
++r)
result[compile_unit (debug_info_entry (_m_dw, r->offset))].insert
(arange_list::value_type (r->addr, r->addr + r->length));
return result;
}
// dwarf::location_attr
const dwarf::location_attr
dwarf::attr_value::location () const
{
if (what_space () != VS_location)
throw std::runtime_error ("XXX not a location");
return location_attr (*this);
}
bool
dwarf::location_attr::is_list () const
{
if (_m_attr.thisattr ()->cu->version >= 4)
return dwarf_whatform (_m_attr.thisattr ()) == DW_FORM_sec_offset;
switch (dwarf_whatform (_m_attr.thisattr ()))
{
case DW_FORM_block:
case DW_FORM_block1:
case DW_FORM_block2:
case DW_FORM_block4:
return false;
}
return true;
}
inline void
dwarf::location_attr::const_iterator::advance ()
{
xif (_m_cu, range_list_advance (IDX_debug_loc, _m_cu,
_m_base, _m_begin, _m_end, _m_readptr,
&_m_block.data));
// Special values are (unsigned char *){-1, 0, 1}.
if (((uintptr_t)_m_readptr + 1) > 2)
_m_readptr += 2 + (_m_block.length
= read_2ubyte_unaligned_inc (_m_cu->dbg, _m_block.data));
else
// End iterator.
_m_block = Dwarf_Block ();
}
dwarf::location_attr::const_iterator
dwarf::location_attr::begin () const
{
const_iterator i (_m_attr.thisattr ());
if (is_list ())
{
i._m_readptr = const_iterator::formptr (IDX_debug_loc,
_m_attr.thisattr ());
xif (_m_attr.thisattr (), i._m_readptr == NULL);
i.advance ();
}
else
{
xif (_m_attr.thisattr (),
dwarf_formblock (_m_attr.thisattr (), &i._m_block) < 0);
i._m_base = 0;
i._m_end = -1;
i._m_readptr = NULL;
}
return i;
}
dwarf::location_attr::const_iterator &
dwarf::location_attr::const_iterator::operator++ ()
{
if (unlikely (_m_readptr == (unsigned char *)-1))
throw std::runtime_error ("incrementing end iterator");
if (_m_readptr == NULL)
{
// Singleton, now at end.
_m_readptr = (unsigned char *)-1;
_m_block.data = NULL;
_m_block.length = 0;
}
else
// Advance to next list entry.
advance ();
return *this;
}
template<typename locattr>
static string
locattr_string (const locattr *loc)
{
return (loc->is_list () ? dec_string (loc->size (), "{loclist ", " entries}")
: "{locexpr}");
}
string
dwarf::location_attr::to_string () const
{
return locattr_string (this);
}
string
dwarf_data::location_attr::to_string () const
{
return locattr_string (this);
}
// dwarf::line_info_table
template<typename line_info_table>
static inline std::string
line_info_string (const line_info_table *table)
{
return ("[" + table->lines ().to_string () + "]");
}
std::string
dwarf::line_info_table::to_string () const
{
return line_info_string (this);
}
namespace elfutils
{
template<>
std::string
dwarf_edit::line_info_table::to_string () const
{
return line_info_string (this);
}
};
// dwarf::line_table
std::string
dwarf::line_table::to_string () const
{
return dec_string (_m_lines->nlines, "{", " line entries}");
}
namespace elfutils
{
template<>
std::string
dwarf_edit::line_table::to_string () const
{
return dec_string (size (), "{", " line entries}");
}
};
::Dwarf_Off
dwarf::debug_info_entry::cost () const
{
Dwarf_Die next;
int result = dwarf_siblingof (thisdie (), &next);
xif (result < 0);
if (result == 0)
return (const char *) next.addr - (const char *) _m_die.addr;
if (next.addr != NULL)
return (const char *) next.addr - (const char *) _m_die.addr + 1;
return _m_die.cu->end - dwarf_dieoffset (thisdie ());
}