// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" #include using namespace std; DWARFPP_BEGIN_NAMESPACE value::value(const unit *cu, DW_AT name, DW_FORM form, type typ, section_offset offset) : cu(cu), form(form), typ(typ), offset(offset) { if (form == DW_FORM::indirect) resolve_indirect(name); } section_offset value::get_section_offset() const { return cu->get_section_offset() + offset; } taddr value::as_address() const { if (form != DW_FORM::addr) throw value_type_mismatch("cannot read " + to_string(typ) + " as address"); cursor cur(cu->data(), offset); return cur.address(); } const void * value::as_block(size_t *size_out) const { // XXX Blocks can contain all sorts of things, including // references, which couldn't be resolved by callers in the // current minimal API. cursor cur(cu->data(), offset); switch (form) { case DW_FORM::block1: *size_out = cur.fixed(); break; case DW_FORM::block2: *size_out = cur.fixed(); break; case DW_FORM::block4: *size_out = cur.fixed(); break; case DW_FORM::block: case DW_FORM::exprloc: *size_out = cur.uleb128(); break; default: throw value_type_mismatch("cannot read " + to_string(typ) + " as block"); } cur.ensure(*size_out); return cur.pos; } uint64_t value::as_uconstant() const { cursor cur(cu->data(), offset); switch (form) { case DW_FORM::data1: return cur.fixed(); case DW_FORM::data2: return cur.fixed(); case DW_FORM::data4: return cur.fixed(); case DW_FORM::data8: return cur.fixed(); case DW_FORM::udata: return cur.uleb128(); default: throw value_type_mismatch("cannot read " + to_string(typ) + " as uconstant"); } } int64_t value::as_sconstant() const { cursor cur(cu->data(), offset); switch (form) { case DW_FORM::data1: return cur.fixed(); case DW_FORM::data2: return cur.fixed(); case DW_FORM::data4: return cur.fixed(); case DW_FORM::data8: return cur.fixed(); case DW_FORM::sdata: return cur.sleb128(); default: throw value_type_mismatch("cannot read " + to_string(typ) + " as sconstant"); } } expr value::as_exprloc() const { cursor cur(cu->data(), offset); size_t size; // Prior to DWARF 4, exprlocs were encoded as blocks. switch (form) { case DW_FORM::exprloc: case DW_FORM::block: size = cur.uleb128(); break; case DW_FORM::block1: size = cur.fixed(); break; case DW_FORM::block2: size = cur.fixed(); break; case DW_FORM::block4: size = cur.fixed(); break; default: throw value_type_mismatch("cannot read " + to_string(typ) + " as exprloc"); } return expr(cu, cur.get_section_offset(), size); } bool value::as_flag() const { switch (form) { case DW_FORM::flag: { cursor cur(cu->data(), offset); return cur.fixed() != 0; } case DW_FORM::flag_present: return true; default: throw value_type_mismatch("cannot read " + to_string(typ) + " as flag"); } } rangelist value::as_rangelist() const { section_offset off = as_sec_offset(); // The compilation unit may not have a base address. In this // case, the first entry in the range list must be a base // address entry, but we'll just assume 0 for the initial base // address. die cudie = cu->root(); taddr cu_low_pc = cudie.has(DW_AT::low_pc) ? at_low_pc(cudie) : 0; auto sec = cu->get_dwarf().get_section(section_type::ranges); auto cusec = cu->data(); return rangelist(sec, off, cusec->addr_size, cu_low_pc); } die value::as_reference() const { section_offset off; // XXX Would be nice if we could avoid this. The cursor is // all overhead here. cursor cur(cu->data(), offset); switch (form) { case DW_FORM::ref1: off = cur.fixed(); break; case DW_FORM::ref2: off = cur.fixed(); break; case DW_FORM::ref4: off = cur.fixed(); break; case DW_FORM::ref8: off = cur.fixed(); break; case DW_FORM::ref_udata: off = cur.uleb128(); break; case DW_FORM::ref_addr: { off = cur.offset(); // These seem to be extremely rare in practice (I // haven't been able to get gcc to produce a // ref_addr), so it's not worth caching this lookup. const compilation_unit *base_cu = nullptr; for (auto &file_cu : cu->get_dwarf().compilation_units()) { if (file_cu.get_section_offset() > off) break; base_cu = &file_cu; } die d(base_cu); d.read(off - base_cu->get_section_offset()); return d; } case DW_FORM::ref_sig8: { uint64_t sig = cur.fixed(); try { return cu->get_dwarf().get_type_unit(sig).type(); } catch (std::out_of_range &e) { throw format_error("unknown type signature 0x" + to_hex(sig)); } } default: throw value_type_mismatch("cannot read " + to_string(typ) + " as reference"); } die d(cu); d.read(off); return d; } void value::as_string(string &buf) const { size_t size; const char *p = as_cstr(&size); buf.resize(size); memmove(&buf.front(), p, size); } string value::as_string() const { size_t size; const char *s = as_cstr(&size); return string(s, size); } const char * value::as_cstr(size_t *size_out) const { cursor cur(cu->data(), offset); switch (form) { case DW_FORM::string: return cur.cstr(size_out); case DW_FORM::strp: { section_offset off = cur.offset(); cursor scur(cu->get_dwarf().get_section(section_type::str), off); return scur.cstr(size_out); } default: throw value_type_mismatch("cannot read " + to_string(typ) + " as string"); } } section_offset value::as_sec_offset() const { // Prior to DWARF 4, sec_offsets were encoded as data4 or // data8. cursor cur(cu->data(), offset); switch (form) { case DW_FORM::data4: return cur.fixed(); case DW_FORM::data8: return cur.fixed(); case DW_FORM::sec_offset: return cur.offset(); default: throw value_type_mismatch("cannot read " + to_string(typ) + " as sec_offset"); } } void value::resolve_indirect(DW_AT name) { if (form != DW_FORM::indirect) return; cursor c(cu->data(), offset); DW_FORM form; do { form = (DW_FORM)c.uleb128(); } while (form == DW_FORM::indirect); typ = attribute_spec(name, form).type; offset = c.get_section_offset(); } string to_string(const value &v) { switch (v.get_type()) { case value::type::invalid: return ""; case value::type::address: return "0x" + to_hex(v.as_address()); case value::type::block: { size_t size; const char *b = (const char*)v.as_block(&size); string res = ::to_string(size) + " byte block:"; for (size_t pos = 0; pos < size; ++pos) { res += ' '; res += to_hex(b[pos]); } return res; } case value::type::constant: return "0x" + to_hex(v.as_uconstant()); case value::type::uconstant: return ::to_string(v.as_uconstant()); case value::type::sconstant: return ::to_string(v.as_sconstant()); case value::type::exprloc: // XXX return ""; case value::type::flag: return v.as_flag() ? "true" : "false"; case value::type::line: return ""; case value::type::loclist: return ""; case value::type::mac: return ""; case value::type::rangelist: return ""; case value::type::reference: { die d = v.as_reference(); auto tu = dynamic_cast(&d.get_unit()); if (tu) return "<.debug_types+0x" + to_hex(d.get_section_offset()) + ">"; return "<0x" + to_hex(d.get_section_offset()) + ">"; } case value::type::string: return v.as_string(); } return ""; } DWARFPP_END_NAMESPACE