389 lines
13 KiB
C++
389 lines
13 KiB
C++
/* dwarfpp: C++ binding for a useful subset of libdwarf, plus extra goodies.
|
|
*
|
|
* expr.hpp: simple C++ abstraction of DWARF expressions and location lists.
|
|
*
|
|
* Copyright (c) 2010--17, Stephen Kell. For licensing information, see the
|
|
* LICENSE file in the root of the libdwarfpp tree.
|
|
*/
|
|
|
|
#ifndef DWARFPP_EXPR_HPP_
|
|
#define DWARFPP_EXPR_HPP_
|
|
|
|
#include <vector>
|
|
#include <stack>
|
|
#include <boost/icl/interval_map.hpp>
|
|
#include <strings.h> // for bzero
|
|
#include "spec.hpp"
|
|
#include "libdwarf.hpp"
|
|
#include "libdwarf-handles.hpp"
|
|
#include "abstract.hpp"
|
|
#include "opt.hpp"
|
|
|
|
namespace dwarf
|
|
{
|
|
using std::vector;
|
|
using std::pair;
|
|
using std::set;
|
|
using std::vector;
|
|
using std::string;
|
|
|
|
namespace expr
|
|
{
|
|
class evaluator;
|
|
class loclist;
|
|
|
|
/* We don't support all expressions. */
|
|
class Not_supported
|
|
{
|
|
const string& m_msg;
|
|
public:
|
|
Not_supported(const string& msg) : m_msg(msg) {}
|
|
};
|
|
class regs
|
|
{
|
|
public:
|
|
virtual lib::Dwarf_Signed get(int regnum) = 0;
|
|
virtual void set(int regnum, lib::Dwarf_Signed val)
|
|
{ throw Not_supported("writing registers"); }
|
|
};
|
|
}
|
|
|
|
namespace core
|
|
{ struct Locdesc; struct LocdescList; struct RangesList; struct FrameSection; struct Cie; struct FdeRange; }
|
|
namespace encap
|
|
{
|
|
using namespace dwarf::lib;
|
|
using dwarf::spec::opt;
|
|
|
|
class rangelist : public vector<lib::Dwarf_Ranges>
|
|
{
|
|
public:
|
|
template <class In> rangelist(In first, In last)
|
|
: vector<lib::Dwarf_Ranges>(first, last) {}
|
|
rangelist() : vector<lib::Dwarf_Ranges>() {}
|
|
|
|
rangelist(const core::RangesList& rl);
|
|
|
|
opt<std::pair<Dwarf_Off, long> >
|
|
find_addr(Dwarf_Off file_relative_addr);
|
|
};
|
|
std::ostream& operator<<(std::ostream& s, const rangelist& rl);
|
|
|
|
typedef ::dwarf::lib::Dwarf_Loc expr_instr;
|
|
inline bool operator==(const expr_instr& i1, const expr_instr& i2)
|
|
{
|
|
// FIXME: ignore don't-care fields? needs spec knowledge :-(
|
|
return i1.lr_atom == i2.lr_atom
|
|
&& i1.lr_number == i2.lr_number
|
|
&& i1.lr_number2 == i2.lr_number2
|
|
&& i1.lr_offset == i2.lr_offset;
|
|
}
|
|
|
|
struct loc_expr : public vector<expr_instr>
|
|
{
|
|
/* We used to have NO_LOCATION here. But we don't need it! Recap:
|
|
* In DWARF, hipc == 0 && lopc == 0 means an "end of list entry".
|
|
* BUT libdwarf abstracts this so that we don't see end-of-list
|
|
* entries (I *think*).
|
|
* THEN it uses hipc==0 and lopc==0 to mean "all vaddrs"
|
|
* (see libdwarf2.1.pdf sec 2.3.2).
|
|
* So we have to interpret it that way. If we want to encode
|
|
* "no location", e.g. in with_dynamic_location_die::get_dynamic_location(),
|
|
* we use an empty loclist. */
|
|
const dwarf::spec::abstract_def& spec;
|
|
Dwarf_Addr hipc;
|
|
Dwarf_Addr lopc;
|
|
loc_expr(spec::abstract_def& spec = spec::dwarf_current)
|
|
: spec(spec), hipc(0), lopc(0)/*, m_expr(*this)*/ {}
|
|
loc_expr(const lib::Dwarf_Locdesc& desc, const spec::abstract_def& spec = spec::dwarf_current) :
|
|
vector<expr_instr>(desc.ld_s, desc.ld_s + desc.ld_cents),
|
|
spec(spec), hipc(desc.ld_hipc), lopc(desc.ld_lopc)/*,
|
|
m_expr(*this)*/ {}
|
|
loc_expr(Dwarf_Debug dbg, lib::Dwarf_Ptr instrs, lib::Dwarf_Unsigned len, const spec::abstract_def& spec = spec::dwarf_current);
|
|
loc_expr(const vector<expr_instr>& expr,
|
|
const spec::abstract_def& spec = spec::dwarf_current)
|
|
: vector<expr_instr>(expr),
|
|
spec(spec), hipc(0), lopc(0)/*, m_expr(*this)*/ {}
|
|
loc_expr(const loc_expr& arg) // copy constructor
|
|
: vector<expr_instr>(arg.begin(), arg.end()),
|
|
spec(arg.spec), hipc(arg.hipc), lopc(arg.lopc)/*,
|
|
m_expr(*this)*/ {}
|
|
|
|
loc_expr piece_for_offset(Dwarf_Off offset) const;
|
|
vector<std::pair<loc_expr, Dwarf_Unsigned> > pieces() const;
|
|
|
|
// this is languishing here because it's a HACK.. should take the value as argument
|
|
// too, to calculate variable-length encodings correctly
|
|
size_t form_encoded_size(Dwarf_Half form)
|
|
{
|
|
switch(form)
|
|
{
|
|
case DW_FORM_addr: return sizeof (Dwarf_Addr);
|
|
case DW_FORM_block2: return 2;
|
|
case DW_FORM_block4: return 4;
|
|
case DW_FORM_data2: return 2;
|
|
case DW_FORM_data4: return 4;
|
|
case DW_FORM_data8: return 8;
|
|
case DW_FORM_string: return sizeof (Dwarf_Unsigned);
|
|
case DW_FORM_block: return sizeof (Dwarf_Unsigned);
|
|
case DW_FORM_block1: return 1;
|
|
case DW_FORM_data1: return 1;
|
|
case DW_FORM_flag: return 1;
|
|
case DW_FORM_sdata: return sizeof (Dwarf_Unsigned);
|
|
case DW_FORM_strp: return sizeof (Dwarf_Addr);
|
|
case DW_FORM_udata: return sizeof (Dwarf_Unsigned);
|
|
case DW_FORM_ref_addr: return sizeof (Dwarf_Addr);
|
|
case DW_FORM_ref1: return 1;
|
|
case DW_FORM_ref2: return 2;
|
|
case DW_FORM_ref4: return 4;
|
|
case DW_FORM_ref8: return 8;
|
|
case DW_FORM_ref_udata: return sizeof (Dwarf_Unsigned);
|
|
case DW_FORM_indirect: return sizeof (Dwarf_Addr);
|
|
default: assert(false); return 0;
|
|
}
|
|
}
|
|
|
|
template <class In> loc_expr(In first, In last,
|
|
const spec::abstract_def& spec = spec::dwarf_current)
|
|
: vector<expr_instr>(first, last),
|
|
spec(spec), /*m_expr(first, last), */hipc(0), lopc(0) {}
|
|
|
|
/* This template parses a location expression out of an array of unsigneds. */
|
|
template<size_t s>
|
|
loc_expr(Dwarf_Unsigned const (&arr)[s], Dwarf_Addr lopc, Dwarf_Addr hipc,
|
|
const spec::abstract_def& spec = spec::dwarf_current)
|
|
: spec(spec), hipc(hipc), lopc(lopc)
|
|
{
|
|
initialize_from_opcode_array(&arr[0], &arr[s], lopc, hipc, spec);
|
|
}
|
|
|
|
template <class In>
|
|
loc_expr(In begin, In end, Dwarf_Addr lopc, Dwarf_Addr hipc,
|
|
const spec::abstract_def& spec = spec::dwarf_current)
|
|
: spec(spec), hipc(hipc), lopc(lopc)
|
|
{
|
|
initialize_from_opcode_array(begin, end, lopc, hipc, spec);
|
|
}
|
|
private:
|
|
template <class In>
|
|
void initialize_from_opcode_array(In begin, In end,
|
|
Dwarf_Addr lopc, Dwarf_Addr hipc,
|
|
const spec::abstract_def& spec)
|
|
{
|
|
//size_t s = end - begin;
|
|
auto iter = begin; // &arr[0];
|
|
Dwarf_Unsigned next_offset = 0U;
|
|
while (iter < /* arr + s */ end)
|
|
{
|
|
Dwarf_Loc loc;
|
|
bzero(&loc, sizeof loc);
|
|
loc.lr_offset = next_offset;
|
|
loc.lr_atom = *iter++; // read opcode
|
|
next_offset += 1; // opcodes are one byte
|
|
switch (spec.op_operand_count(loc.lr_atom))
|
|
{
|
|
case 2:
|
|
loc.lr_number = *iter++;
|
|
loc.lr_number2 = *iter++;
|
|
// how many bytes of DWARF binary encoding?
|
|
next_offset += form_encoded_size(
|
|
spec.op_operand_form_list(loc.lr_atom)[0]
|
|
);
|
|
next_offset += form_encoded_size(
|
|
spec.op_operand_form_list(loc.lr_atom)[1]
|
|
);
|
|
break;
|
|
case 1:
|
|
loc.lr_number = *iter++;
|
|
// how many bytes of DWARF binary encoding?
|
|
next_offset += form_encoded_size(
|
|
spec.op_operand_form_list(loc.lr_atom)[0]
|
|
);
|
|
break;
|
|
case 0:
|
|
break;
|
|
default: assert(false);
|
|
}
|
|
/*m_expr.*/push_back(loc);
|
|
}
|
|
}
|
|
public:
|
|
bool operator==(const loc_expr& e) const
|
|
{
|
|
//expr_instr e1; expr_instr e2;
|
|
return hipc == e.hipc &&
|
|
lopc == e.lopc &&
|
|
//e1 == e2;
|
|
static_cast<const vector<expr_instr>&>(*this)
|
|
== static_cast<const vector<expr_instr>&>(e);
|
|
}
|
|
bool operator!=(const loc_expr& e) const { return !(*this == e); }
|
|
loc_expr& operator=(const loc_expr& e)
|
|
{
|
|
assert(&(this->spec) == &(e.spec)); // references aren't assignable
|
|
*static_cast<vector<expr_instr> *>(this) = *static_cast<const vector<expr_instr> *>(&e);
|
|
this->hipc = e.hipc;
|
|
this->lopc = e.lopc;
|
|
return *this;
|
|
}
|
|
friend std::ostream& operator<<(std::ostream& s, const loc_expr& e);
|
|
};
|
|
|
|
struct loclist : public vector<loc_expr>
|
|
{
|
|
friend class ::dwarf::expr::evaluator;
|
|
friend class attribute_value;
|
|
static loclist NO_LOCATION;
|
|
|
|
loclist() {} // empty loclist
|
|
loclist(const dwarf::expr::loclist& dll);
|
|
/* We can construct a loc_expr from a Loc_Desc.
|
|
* So we can construct a loclist from a LocdescList. */
|
|
loclist(const core::LocdescList& ll);
|
|
// would ideally repeat all vector constructors
|
|
template <class In> loclist(In first, In last) : vector<loc_expr>(first, last) {}
|
|
loclist(const core::Locdesc& l);
|
|
loclist(const vector<loc_expr>& v) : vector<loc_expr>(v) {}
|
|
loclist(const loc_expr& loc) : vector<loc_expr>(1, loc) {}
|
|
loc_expr loc_for_vaddr(Dwarf_Addr vaddr) const;
|
|
// boost::icl::interval_map<Dwarf_Addr, vector<expr_instr> > as_interval_map() const;
|
|
set< boost::icl::discrete_interval<Dwarf_Addr> > intervals() const
|
|
{
|
|
set< boost::icl::discrete_interval<Dwarf_Addr> > working;
|
|
for (auto i_expr = begin(); i_expr != end(); ++i_expr)
|
|
{
|
|
working.insert(boost::icl::discrete_interval<Dwarf_Addr>::right_open(
|
|
i_expr->lopc,
|
|
i_expr->hipc
|
|
));
|
|
}
|
|
return working;
|
|
}
|
|
};
|
|
std::ostream& operator<<(std::ostream& s, const loclist& ll);
|
|
|
|
/* Instruction sequences in a CIE/FDE. */
|
|
struct frame_instrlist;
|
|
/* We need this extension so that we can define operator<<, since to construct a
|
|
* loc_expr will require us to pass the Dwarf_Debug. */
|
|
struct frame_instr : public Dwarf_Frame_Op3
|
|
{
|
|
Dwarf_Debug dbg;
|
|
frame_instr(Dwarf_Debug dbg, const Dwarf_Frame_Op3 arg)
|
|
: Dwarf_Frame_Op3(arg), dbg(dbg) {}
|
|
};
|
|
std::ostream& operator<<(std::ostream& s, const frame_instr& arg);
|
|
|
|
struct frame_instrlist : public vector<frame_instr>
|
|
{
|
|
using vector::vector;
|
|
|
|
frame_instrlist(const core::Cie& cie, int addrlen, const pair<unsigned char*, unsigned char*>& seq, bool use_host_byte_order = true);
|
|
};
|
|
std::ostream& operator<<(std::ostream& s, const frame_instrlist& arg);
|
|
|
|
/* Utility function for loclists. */
|
|
loclist absolute_loclist_to_additive_loclist(const loclist& l);
|
|
|
|
loclist rewrite_loclist_in_terms_of_cfa(
|
|
const loclist& l,
|
|
const core::FrameSection& fs,
|
|
dwarf::spec::opt<const loclist&> opt_fbreg // fbreg is special -- loc exprs can refer to it
|
|
);
|
|
Dwarf_Unsigned read_uleb128(unsigned char const **cur, unsigned char const *limit);
|
|
Dwarf_Signed read_sleb128(unsigned char const **cur, unsigned char const *limit);
|
|
uint64_t read_8byte_le(unsigned char const **cur, unsigned char const *limit);
|
|
uint32_t read_4byte_le(unsigned char const **cur, unsigned char const *limit);
|
|
uint16_t read_2byte_le(unsigned char const **cur, unsigned char const *limit);
|
|
uint64_t read_8byte_be(unsigned char const **cur, unsigned char const *limit);
|
|
uint32_t read_4byte_be(unsigned char const **cur, unsigned char const *limit);
|
|
uint16_t read_2byte_be(unsigned char const **cur, unsigned char const *limit);
|
|
uint16_t read_2byte_be(unsigned char const **cur, unsigned char const *limit);
|
|
} // end namespace encap
|
|
|
|
namespace expr
|
|
{
|
|
using namespace dwarf::lib;
|
|
using dwarf::spec::opt;
|
|
using std::stack;
|
|
using std::ostream;
|
|
class evaluator {
|
|
std::stack<Dwarf_Unsigned> m_stack;
|
|
vector<Dwarf_Loc> expr;
|
|
const ::dwarf::spec::abstract_def& spec;
|
|
regs *p_regs; // optional set of register values, for DW_OP_breg*
|
|
bool tos_is_value; // whether we saw a DW_OP_stack_value hence have calculated a value not an addr
|
|
opt<Dwarf_Signed> frame_base;
|
|
vector<Dwarf_Loc>::iterator i;
|
|
void eval();
|
|
public:
|
|
evaluator(const vector<unsigned char> expr,
|
|
const ::dwarf::spec::abstract_def& spec) : spec(spec), p_regs(0), tos_is_value(false)
|
|
{
|
|
//i = expr.begin();
|
|
assert(false);
|
|
}
|
|
evaluator(const encap::loclist& loclist,
|
|
Dwarf_Addr vaddr,
|
|
const ::dwarf::spec::abstract_def& spec = spec::DEFAULT_DWARF_SPEC,
|
|
regs *p_regs = 0,
|
|
opt<Dwarf_Signed> frame_base = opt<Dwarf_Signed>(),
|
|
const stack<Dwarf_Unsigned>& initial_stack = stack<Dwarf_Unsigned>());
|
|
|
|
evaluator(const vector<Dwarf_Loc>& loc_desc,
|
|
const ::dwarf::spec::abstract_def& spec,
|
|
const stack<Dwarf_Unsigned>& initial_stack = stack<Dwarf_Unsigned>())
|
|
: m_stack(initial_stack), spec(spec), p_regs(0), tos_is_value(false)
|
|
{
|
|
expr = loc_desc;
|
|
i = expr.begin();
|
|
eval();
|
|
}
|
|
evaluator(const vector<Dwarf_Loc>& loc_desc,
|
|
const ::dwarf::spec::abstract_def& spec,
|
|
regs& regs,
|
|
Dwarf_Signed frame_base,
|
|
const stack<Dwarf_Unsigned>& initial_stack = stack<Dwarf_Unsigned>())
|
|
: m_stack(initial_stack), spec(spec), p_regs(®s), tos_is_value(false)
|
|
{
|
|
expr = loc_desc;
|
|
i = expr.begin();
|
|
this->frame_base = frame_base;
|
|
eval();
|
|
}
|
|
|
|
evaluator(const vector<Dwarf_Loc>& loc_desc,
|
|
const ::dwarf::spec::abstract_def& spec,
|
|
Dwarf_Signed frame_base,
|
|
const stack<Dwarf_Unsigned>& initial_stack = stack<Dwarf_Unsigned>())
|
|
: m_stack(initial_stack), spec(spec), p_regs(0), tos_is_value(false)
|
|
{
|
|
//if (av.get_form() != dwarf::encap::attribute_value::LOCLIST) throw "not a DWARF expression";
|
|
//if (av.get_loclist().size() != 1) throw "only support singleton loclists for now";
|
|
//expr = *(av.get_loclist().begin());
|
|
expr = loc_desc;
|
|
i = expr.begin();
|
|
this->frame_base = frame_base;
|
|
eval();
|
|
}
|
|
|
|
Dwarf_Unsigned tos() const { return m_stack.top(); }
|
|
Dwarf_Unsigned tos(bool may_be_value) const { // FIXME: more complete+orthogonal interface
|
|
if (may_be_value) return m_stack.top();
|
|
if (!tos_is_value && !may_be_value) return m_stack.top();
|
|
throw No_entry();
|
|
}
|
|
bool finished() const { return i == expr.end(); }
|
|
Dwarf_Loc current() const { return *i; }
|
|
};
|
|
Dwarf_Unsigned eval(const encap::loclist& loclist,
|
|
Dwarf_Addr vaddr,
|
|
Dwarf_Signed frame_base,
|
|
opt<regs&> rs,
|
|
const ::dwarf::spec::abstract_def& spec,
|
|
const stack<Dwarf_Unsigned>& initial_stack);
|
|
} // end namespace expr
|
|
}
|
|
|
|
#endif
|