diff --git a/src/platform/error.hpp b/src/platform/error.hpp index 37cc224..6bec118 100644 --- a/src/platform/error.hpp +++ b/src/platform/error.hpp @@ -122,8 +122,18 @@ namespace detail { ##__VA_ARGS__) \ ) + // Workaround a compiler warning + template + std::string as_string(T&& value) { + return std::string(std::forward(value)); + } + + inline std::string as_string() { + return ""; + } + // Check condition in both debug and release. std::runtime_error on failure. - #define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, {}, std::string(__VA_ARGS__))) + #define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, {}, ::cpptrace::detail::as_string(__VA_ARGS__))) #ifndef NDEBUG // Check condition in both debug. std::runtime_error on failure. diff --git a/src/platform/utils.hpp b/src/platform/utils.hpp index 69145bb..f12eb05 100644 --- a/src/platform/utils.hpp +++ b/src/platform/utils.hpp @@ -349,13 +349,17 @@ namespace detail { typename std::enable_if< std::is_same()(std::declval())), void>::value, int + >::type = 0, + typename std::enable_if< + std::is_standard_layout::value && std::is_trivial::value, + int >::type = 0 > class raii_wrapper { T obj; optional deleter; public: - raii_wrapper(T&& obj, D deleter) : obj(std::move(obj)), deleter(deleter) {} + raii_wrapper(T obj, D deleter) : obj(obj), deleter(deleter) {} raii_wrapper(raii_wrapper&& other) : obj(std::move(other.obj)), deleter(other.deleter) { other.deleter = nullopt; } @@ -373,6 +377,12 @@ namespace detail { operator const T&() const { return obj; } + T& get() { + return obj; + } + const T& get() const { + return obj; + } }; template< @@ -381,10 +391,14 @@ namespace detail { typename std::enable_if< std::is_same()(std::declval())), void>::value, int + >::type = 0, + typename std::enable_if< + std::is_standard_layout::value && std::is_trivial::value, + int >::type = 0 > - raii_wrapper raii_wrap(T&& obj, D deleter) { - return raii_wrapper(std::move(obj), deleter); + raii_wrapper::type, D> raii_wrap(T obj, D deleter) { + return raii_wrapper::type, D>(obj, deleter); } inline void file_deleter(FILE* ptr) { diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index 3656b51..4b74f81 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -21,9 +21,10 @@ #include #include -// some stuff is based on https://github.com/davea42/libdwarf-addr2line/blob/master/addr2line.c, mainly line handling -// then much expanded for symbols and efficiency -// dwarf5_ranges and dwarf4_ranges utility functions are taken from there directly, also pc_in_die +// It's been tricky to piece together how to handle all this dwarf stuff. Some resources I've used are +// https://www.prevanders.net/libdwarf.pdf +// https://github.com/davea42/libdwarf-addr2line +// https://github.com/ruby/ruby/blob/master/addr2line.c // TODO // Inlined calls @@ -88,7 +89,7 @@ namespace libdwarf { ~die_object() { if(die) { - dwarf_dealloc(dbg, die, DW_DLA_DIE); + dwarf_dealloc_die(die); } } @@ -111,10 +112,10 @@ namespace libdwarf { } die_object clone() const { - Dwarf_Off off = get_global_offset(); + Dwarf_Off global_offset = get_global_offset(); Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); Dwarf_Die die_copy = 0; - VERIFY(wrap(dwarf_offdie_b, dbg, off, is_info, &die_copy) == DW_DLV_OK); + VERIFY(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &die_copy) == DW_DLV_OK); return {dbg, die_copy}; } @@ -154,32 +155,31 @@ namespace libdwarf { char empty[] = ""; char* name = empty; int ret = wrap(dwarf_diename, die, &name); + auto wrapper = raii_wrap(name, [this] (char* str) { dwarf_dealloc(dbg, str, DW_DLA_STRING); }); std::string str; if(ret != DW_DLV_NO_ENTRY) { str = name; - dwarf_dealloc(dbg, name, DW_DLA_STRING); } return name; } - optional get_string_attribute(Dwarf_Half dw_attrnum) const { + optional get_string_attribute(Dwarf_Half attr_num) const { Dwarf_Attribute attr; - if(wrap(dwarf_attr, die, dw_attrnum, &attr) == DW_DLV_OK) { + if(wrap(dwarf_attr, die, attr_num, &attr) == DW_DLV_OK) { + auto attwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); char* raw_str; - std::string str; VERIFY(wrap(dwarf_formstring, attr, &raw_str) == DW_DLV_OK); - str = raw_str; - dwarf_dealloc(dbg, raw_str, DW_DLA_STRING); - dwarf_dealloc_attribute(attr); + auto strwrapper = raii_wrap(raw_str, [this] (char* str) { dwarf_dealloc(dbg, str, DW_DLA_STRING); }); + std::string str = raw_str; return str; } else { return nullopt; } } - bool has_attr(Dwarf_Half dw_attrnum) const { + bool has_attr(Dwarf_Half attr_num) const { Dwarf_Bool present = false; - VERIFY(wrap(dwarf_hasattr, die, dw_attrnum, &present) == DW_DLV_OK); + VERIFY(wrap(dwarf_hasattr, die, attr_num, &present) == DW_DLV_OK); return present; } @@ -204,9 +204,10 @@ namespace libdwarf { return off; } - die_object resolve_reference_attribute(Dwarf_Half dw_attrnum) const { + die_object resolve_reference_attribute(Dwarf_Half attr_num) const { Dwarf_Attribute attr; - VERIFY(dwarf_attr(die, dw_attrnum, &attr, nullptr) == DW_DLV_OK); + VERIFY(dwarf_attr(die, attr_num, &attr, nullptr) == DW_DLV_OK); + auto wrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); Dwarf_Half form = 0; VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); switch(form) { @@ -219,44 +220,40 @@ namespace libdwarf { Dwarf_Off off = 0; Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); VERIFY(wrap(dwarf_formref, attr, &off, &is_info) == DW_DLV_OK); - Dwarf_Off goff = 0; - VERIFY(wrap(dwarf_convert_to_global_offset, attr, off, &goff) == DW_DLV_OK); - Dwarf_Die targ_die_a = 0; - VERIFY(wrap(dwarf_offdie_b, dbg, goff, is_info, &targ_die_a) == DW_DLV_OK); - dwarf_dealloc_attribute(attr); - return die_object(dbg, targ_die_a); + Dwarf_Off global_offset = 0; + VERIFY(wrap(dwarf_convert_to_global_offset, attr, off, &global_offset) == DW_DLV_OK); + Dwarf_Die target = 0; + VERIFY(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &target) == DW_DLV_OK); + return die_object(dbg, target); } case DW_FORM_ref_addr: { Dwarf_Off off; VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK); - int is_info_a = dwarf_get_die_infotypes_flag(die); - Dwarf_Die targ_die_a = 0; - VERIFY(wrap(dwarf_offdie_b, dbg, off, is_info_a, &targ_die_a) == DW_DLV_OK); - dwarf_dealloc_attribute(attr); - return die_object(dbg, targ_die_a); + int is_info = dwarf_get_die_infotypes_flag(die); + Dwarf_Die target = 0; + VERIFY(wrap(dwarf_offdie_b, dbg, off, is_info, &target) == DW_DLV_OK); + return die_object(dbg, target); } case DW_FORM_ref_sig8: { Dwarf_Sig8 signature; VERIFY(wrap(dwarf_formsig8, attr, &signature) == DW_DLV_OK); - Dwarf_Die targdie = 0; + Dwarf_Die target = 0; Dwarf_Bool targ_is_info = false; - VERIFY(wrap(dwarf_find_die_given_sig8, dbg, &signature, &targdie, &targ_is_info) == DW_DLV_OK); - dwarf_dealloc_attribute(attr); - return die_object(dbg, targdie); + VERIFY(wrap(dwarf_find_die_given_sig8, dbg, &signature, &target, &targ_is_info) == DW_DLV_OK); + return die_object(dbg, target); } default: - fprintf(stderr, "unknown form for attribute %d %d\n", dw_attrnum, form); - exit(1); + PANIC(stringf("unknown form for attribute %d %d\n", attr_num, form)); } } Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) const { Dwarf_Unsigned off = 0; - Dwarf_Half attrform = 0; - VERIFY(wrap(dwarf_whatform, attr, &attrform) == DW_DLV_OK); - if (attrform == DW_FORM_rnglistx) { + Dwarf_Half form = 0; + VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); + if (form == DW_FORM_rnglistx) { VERIFY(wrap(dwarf_formudata, attr, &off) == DW_DLV_OK); } else { VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK); @@ -264,233 +261,174 @@ namespace libdwarf { return off; } - // ranges code based on libdwarf-addr2line - int dwarf5_ranges( - Dwarf_Die cu_die, - Dwarf_Addr *lowest, - Dwarf_Addr *highest, - std::vector>* ranges_vec // TODO: Super hacky - ) const { - Dwarf_Unsigned offset = 0; + template + void dwarf5_ranges(F callback) const { Dwarf_Attribute attr = 0; - Dwarf_Half attrform = 0; - Dwarf_Unsigned i = 0; - int res = 0; - - res = wrap(dwarf_attr, cu_die, DW_AT_ranges, &attr); - if(res != DW_DLV_OK) { - return res; + VERIFY(wrap(dwarf_attr, die, DW_AT_ranges, &attr) == DW_DLV_OK); + auto attrwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); + Dwarf_Unsigned offset = get_ranges_offset(attr); + Dwarf_Half form = 0; + VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); + // get .debug_rnglists info + Dwarf_Rnglists_Head head = 0; + Dwarf_Unsigned rnglists_entries = 0; + Dwarf_Unsigned dw_global_offset_of_rle_set = 0; + int res = wrap( + dwarf_rnglists_get_rle_head, + attr, + form, + offset, + &head, + &rnglists_entries, + &dw_global_offset_of_rle_set + ); + auto headwrapper = raii_wrap(head, [] (Dwarf_Rnglists_Head head) { dwarf_dealloc_rnglists_head(head); }); + if(res == DW_DLV_NO_ENTRY) { + return; } - offset = get_ranges_offset(attr); - if(true) { - Dwarf_Unsigned rlesetoffset = 0; - Dwarf_Unsigned rnglists_count = 0; - Dwarf_Rnglists_Head head = 0; - - VERIFY(wrap(dwarf_whatform, attr, &attrform) == DW_DLV_OK); - // offset is in .debug_rnglists + VERIFY(res == DW_DLV_OK); + for(size_t i = 0 ; i < rnglists_entries; i++) { + unsigned entrylen = 0; + unsigned rle_value_out = 0; + Dwarf_Unsigned raw1 = 0; + Dwarf_Unsigned raw2 = 0; + Dwarf_Bool unavailable = 0; + Dwarf_Unsigned cooked1 = 0; + Dwarf_Unsigned cooked2 = 0; res = wrap( - dwarf_rnglists_get_rle_head, - attr, - attrform, - offset, - &head, - &rnglists_count, - &rlesetoffset + dwarf_get_rnglists_entry_fields_a, + head, + i, + &entrylen, + &rle_value_out, + &raw1, + &raw2, + &unavailable, + &cooked1, + &cooked2 ); - VERIFY(res == DW_DLV_OK); - if(res != DW_DLV_OK) { - /* ASSERT: is DW_DLV_NO_ENTRY */ - dwarf_dealloc_attribute(attr); - return res; + if(res == DW_DLV_NO_ENTRY) { + continue; } - for( ; i < rnglists_count; ++i) { - unsigned entrylen = 0; - unsigned rle_val = 0; - Dwarf_Unsigned raw1 = 0; - Dwarf_Unsigned raw2 = 0; - Dwarf_Bool unavail = 0; - Dwarf_Unsigned cooked1 = 0; - Dwarf_Unsigned cooked2 = 0; - - res = wrap( - dwarf_get_rnglists_entry_fields_a, - head, - i, - &entrylen, - &rle_val, - &raw1, - &raw2, - &unavail, - &cooked1, - &cooked2 - ); - if(res != DW_DLV_OK) { - ASSERT(res == DW_DLV_NO_ENTRY); - continue; - } - if(unavail) { - continue; - } - switch(rle_val) { + VERIFY(res == DW_DLV_OK); + if(unavailable) { + continue; + } + switch(rle_value_out) { + // Following the same scheme from libdwarf-addr2line case DW_RLE_end_of_list: case DW_RLE_base_address: case DW_RLE_base_addressx: - /* These are accounted for already */ + // Already handled break; case DW_RLE_offset_pair: case DW_RLE_startx_endx: case DW_RLE_start_end: case DW_RLE_startx_length: case DW_RLE_start_length: - if(ranges_vec) { - ranges_vec->push_back({cooked1, cooked2}); - } - if(cooked1 < *lowest) { - *lowest = cooked1; - } - if(cooked2 > *highest) { - *highest = cooked2; + if(!callback(cooked1, cooked2)) { + return; } break; default: PANIC("Something is wrong"); break; - } } - dwarf_dealloc_rnglists_head(head); } - dwarf_dealloc_attribute(attr); - return DW_DLV_OK; } - // ranges code based on libdwarf-addr2line - int dwarf4_ranges( - Dwarf_Die cu_die, - Dwarf_Addr cu_lowpc, - Dwarf_Addr *lowest, - Dwarf_Addr *highest, - std::vector>* ranges_vec // TODO: Super hacky - ) const { - Dwarf_Unsigned offset; + template + void dwarf4_ranges(Dwarf_Addr lowpc, F callback) const { Dwarf_Attribute attr = 0; - int res = 0; - - res = wrap(dwarf_attr, cu_die, DW_AT_ranges, &attr); - if(res != DW_DLV_OK) { - return res; + if(wrap(dwarf_attr, die, DW_AT_ranges, &attr) != DW_DLV_OK) { + return; } - if(wrap(dwarf_global_formref, attr, &offset) == DW_DLV_OK) { - Dwarf_Signed count = 0; - Dwarf_Ranges *ranges = 0; - Dwarf_Addr baseaddr = 0; - if(cu_lowpc != 0xffffffffffffffff) { - baseaddr = cu_lowpc; - } - VERIFY( - wrap( - dwarf_get_ranges_b, - dbg, - offset, - cu_die, - nullptr, - &ranges, - &count, - nullptr - ) == DW_DLV_OK - ); - for(int i = 0; i < count; i++) { - Dwarf_Ranges *cur = ranges + i; - - if(cur->dwr_type == DW_RANGES_ENTRY) { - Dwarf_Addr rng_lowpc, rng_highpc; - rng_lowpc = baseaddr + cur->dwr_addr1; - rng_highpc = baseaddr + cur->dwr_addr2; - if(ranges_vec) { - ranges_vec->push_back({rng_lowpc, rng_highpc}); - } - if(rng_lowpc < *lowest) { - *lowest = rng_lowpc; - } - if(rng_highpc > *highest) { - *highest = rng_highpc; - } - } else if(cur->dwr_type == - DW_RANGES_ADDRESS_SELECTION) { - baseaddr = cur->dwr_addr2; - } else { // DW_RANGES_END - baseaddr = cu_lowpc; + auto attrwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); + Dwarf_Unsigned offset; + if(wrap(dwarf_global_formref, attr, &offset) != DW_DLV_OK) { + return; + } + Dwarf_Addr baseaddr = 0; + if(lowpc != std::numeric_limits::max()) { + baseaddr = lowpc; + } + Dwarf_Ranges* ranges = 0; + Dwarf_Signed count = 0; + VERIFY( + wrap( + dwarf_get_ranges_b, + dbg, + offset, + die, + nullptr, + &ranges, + &count, + nullptr + ) == DW_DLV_OK + ); + auto rangeswrapper = raii_wrap( + ranges, + [this, count] (Dwarf_Ranges* ranges) { dwarf_dealloc_ranges(dbg, ranges, count); } + ); + for(int i = 0; i < count; i++) { + if(ranges[i].dwr_type == DW_RANGES_ENTRY) { + if(!callback(baseaddr + ranges[i].dwr_addr1, baseaddr + ranges[i].dwr_addr2)) { + return; } + } else if(ranges[i].dwr_type == DW_RANGES_ADDRESS_SELECTION) { + baseaddr = ranges[i].dwr_addr2; + } else { + ASSERT(ranges[i].dwr_type == DW_RANGES_END); + baseaddr = lowpc; } - dwarf_dealloc_ranges(dbg, ranges, count); } - dwarf_dealloc_attribute(attr); - return DW_DLV_OK; } - // pc_in_die code based on libdwarf-addr2line - // TODO: Super hacky. And ugly code duplication. - std::vector> get_pc_range(int version) const { - int ret; - Dwarf_Addr cu_lowpc = 0xffffffffffffffff; - Dwarf_Addr cu_highpc = 0; - enum Dwarf_Form_Class highpc_cls; - Dwarf_Addr lowest = 0xffffffffffffffff; - Dwarf_Addr highest = 0; - - ret = wrap(dwarf_lowpc, die, &cu_lowpc); - if(ret == DW_DLV_OK) { - ret = wrap(dwarf_highpc_b, die, &cu_highpc, nullptr, &highpc_cls); - if(ret == DW_DLV_OK) { - if(highpc_cls == DW_FORM_CLASS_CONSTANT) { - cu_highpc += cu_lowpc; - } - return {{cu_lowpc, cu_highpc}}; + template + void dwarf_ranges(int version, optional pc, F callback) const { + Dwarf_Addr lowpc = std::numeric_limits::max(); + if(wrap(dwarf_lowpc, die, &lowpc) == DW_DLV_OK) { + if(pc.has_value() && pc.unwrap() == lowpc) { + callback(lowpc, lowpc + 1); + return; + } + Dwarf_Addr highpc = 0; + enum Dwarf_Form_Class return_class; + if(wrap(dwarf_highpc_b, die, &highpc, nullptr, &return_class) == DW_DLV_OK) { + if(return_class == DW_FORM_CLASS_CONSTANT) { + highpc += lowpc; + } + if(!callback(lowpc, highpc)) { + return; + } } - } - std::vector> ranges; if(version >= 5) { - ret = dwarf5_ranges(die, &lowest, &highest, &ranges); + dwarf5_ranges(callback); } else { - ret = dwarf4_ranges(die, cu_lowpc, &lowest, &highest, &ranges); + dwarf4_ranges(lowpc, callback); } - return ranges; - //return {lowest, highest}; + } + + std::vector> get_rangelist_entries(int version) const { + std::vector> vec; + dwarf_ranges(version, nullopt, [&vec] (Dwarf_Addr low, Dwarf_Addr high) { + vec.push_back({low, high}); + return true; + }); + return vec; } Dwarf_Bool pc_in_die(int version, Dwarf_Addr pc) const { - int ret; - Dwarf_Addr cu_lowpc = 0xffffffffffffffff; - Dwarf_Addr cu_highpc = 0; - enum Dwarf_Form_Class highpc_cls; - Dwarf_Addr lowest = 0xffffffffffffffff; - Dwarf_Addr highest = 0; - - ret = wrap(dwarf_lowpc, die, &cu_lowpc); - if(ret == DW_DLV_OK) { - if(pc == cu_lowpc) { - return true; + bool found = false; + dwarf_ranges(version, pc, [&found, pc] (Dwarf_Addr low, Dwarf_Addr high) { + if(pc >= low && pc < high) { + found = true; + return false; } - ret = wrap(dwarf_highpc_b, die, &cu_highpc, nullptr, &highpc_cls); - if(ret == DW_DLV_OK) { - if(highpc_cls == DW_FORM_CLASS_CONSTANT) { - cu_highpc += cu_lowpc; - } - if(pc >= cu_lowpc && pc < cu_highpc) { - return true; - } - } - } - if(version >= 5) { - ret = dwarf5_ranges(die, &lowest, &highest, nullptr); - } else { - ret = dwarf4_ranges(die, cu_lowpc, &lowest, &highest, nullptr); - } - if(pc >= lowest && pc < highest) { return true; - } - return false; + }); + return found; } void print() const { @@ -510,8 +448,7 @@ namespace libdwarf { struct line_context { Dwarf_Unsigned version; - Dwarf_Small table_count; - Dwarf_Line_Context ctx; + Dwarf_Line_Context line_context; }; struct subprogram_entry { @@ -522,7 +459,7 @@ namespace libdwarf { struct dwarf_resolver { std::string obj_path; - Dwarf_Debug dbg; + Dwarf_Debug dbg = nullptr; bool ok = false; std::unordered_map line_contexts; std::unordered_map> subprograms_cache; @@ -614,7 +551,7 @@ namespace libdwarf { // TODO: Maybe redundant since dwarf_finish(dbg); will clean up the line stuff anyway but may as well just // for thoroughness for(auto& entry : line_contexts) { - dwarf_srclines_dealloc_b(entry.second.ctx); + dwarf_srclines_dealloc_b(entry.second.line_context); } // subprograms_cache needs to be destroyed before dbg otherwise there will be another use after free subprograms_cache.clear(); @@ -673,7 +610,6 @@ namespace libdwarf { void retrieve_symbol_for_subprogram( const die_object& die, - Dwarf_Addr pc, Dwarf_Half dwversion, stacktrace_frame& frame ) { @@ -691,34 +627,12 @@ namespace libdwarf { } else { if(die.has_attr(DW_AT_specification)) { die_object spec = die.resolve_reference_attribute(DW_AT_specification); - // TODO: Passing pc here is misleading - return retrieve_symbol_for_subprogram(spec, pc, dwversion, frame); + return retrieve_symbol_for_subprogram(spec, dwversion, frame); } else if(die.has_attr(DW_AT_abstract_origin)) { die_object spec = die.resolve_reference_attribute(DW_AT_abstract_origin); - // TODO: Passing pc here is misleading - return retrieve_symbol_for_subprogram(spec, pc, dwversion, frame); + return retrieve_symbol_for_subprogram(spec, dwversion, frame); } } - // TODO: Disabled for now - // TODO: Handle namespaces - /*std::string name = die.get_name(); - std::vector params; - auto child = die.get_child(); - if(child) { - walk_die_list_recursive( - dbg, - child, - [pc, dwversion, &frame, ¶ms] (Dwarf_Debug dbg, const die_object& die) { - if(die.get_tag() == DW_TAG_formal_parameter) { - // TODO: Ignore DW_AT_artificial - params.push_back(resolve_type(dbg, get_type_die(dbg, die))); - } - } - ); - } else { - fprintf(stderr, "no child %s\n", name.c_str()); - } - frame.symbol = name + "(" + join(params, ", ") + ")";*/ } // returns true if this call found the symbol @@ -742,7 +656,6 @@ namespace libdwarf { die.get_name().c_str() ); } - if(!(die.get_tag() == DW_TAG_namespace || die.pc_in_die(dwversion, pc))) { if(dump_dwarf) { fprintf(stderr, "pc not in die\n"); @@ -758,7 +671,7 @@ namespace libdwarf { ); } if(die.get_tag() == DW_TAG_subprogram) { - retrieve_symbol_for_subprogram(die, pc, dwversion, frame); + retrieve_symbol_for_subprogram(die, dwversion, frame); found = true; return false; } @@ -789,11 +702,10 @@ namespace libdwarf { walk_die_list( die, [this, dwversion, &vec] (const die_object& die) { - //die.print(); switch(die.get_tag()) { case DW_TAG_subprogram: { - auto ranges_vec = die.get_pc_range(dwversion); + auto ranges_vec = die.get_rangelist_entries(dwversion); // TODO: Feels super inefficient and some day should maybe use an interval tree. for(auto range : ranges_vec) { vec.push_back({ die.clone(), range.first, range.second }); @@ -828,8 +740,6 @@ namespace libdwarf { Dwarf_Half dwversion, stacktrace_frame& frame ) { - //retrieve_symbol_walk(std::move(cu_die), pc, dwversion, frame); - //return; auto off = cu_die.get_global_offset(); auto it = subprograms_cache.find(off); if(it == subprograms_cache.end()) { @@ -838,9 +748,6 @@ namespace libdwarf { std::sort(vec.begin(), vec.end(), [] (const subprogram_entry& a, const subprogram_entry& b) { return a.low < b.low; }); - //for(const auto& entry : vec) { - // fprintf(stderr, "vec -> %llx %llx %s\n", entry.low, entry.high, entry.die.get_name().c_str()); - //} subprograms_cache.emplace(off, std::move(vec)); it = subprograms_cache.find(off); } @@ -850,11 +757,9 @@ namespace libdwarf { vec.end(), pc, [] (const subprogram_entry& entry, Dwarf_Addr pc) { - //fprintf(stderr, "%llx %llx\n", entry.low, pc); return entry.low < pc; } ); - //fprintf(stderr, "retrieve_symbol %llx\n", pc); // vec_it is first >= pc // we want first <= pc if(vec_it != vec.begin()) { @@ -864,105 +769,99 @@ namespace libdwarf { if(vec_it != vec.end()) { //vec_it->die.print(); if(vec_it->die.pc_in_die(dwversion, pc)) { - retrieve_symbol_for_subprogram(vec_it->die, pc, dwversion, frame); + retrieve_symbol_for_subprogram(vec_it->die, dwversion, frame); } } else { ASSERT(vec.size() == 0, "Vec should be empty?"); } } - void handle_line(Dwarf_Line line, stacktrace_frame& frame) { - char what[] = "??"; - char * linesrc = what; - Dwarf_Unsigned lineno = 0; - - if(line) { - VERIFY(wrap(dwarf_linesrc, line, &linesrc) == DW_DLV_OK); - VERIFY(wrap(dwarf_lineno, line, &lineno) == DW_DLV_OK); - } - if(dump_dwarf) { - printf("%s:%u\n", linesrc, to(lineno)); - } - frame.line = static_cast(lineno); - frame.filename = linesrc; - if(line) { - dwarf_dealloc(dbg, linesrc, DW_DLA_STRING); - } - } - - // retrieve_line_info code based on libdwarf-addr2line CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING void retrieve_line_info( const die_object& die, Dwarf_Addr pc, - Dwarf_Half dwversion, stacktrace_frame& frame ) { - Dwarf_Unsigned version; - Dwarf_Small table_count; - Dwarf_Line_Context ctxt; - (void)dwversion; auto off = die.get_global_offset(); auto it = line_contexts.find(off); + Dwarf_Line_Context line_context; if(it != line_contexts.end()) { auto& entry = it->second; - version = entry.version; - table_count = entry.table_count; - ctxt = entry.ctx; + line_context = entry.line_context; } else { + Dwarf_Unsigned version; + Dwarf_Small table_count; int ret = wrap( dwarf_srclines_b, die.get(), &version, &table_count, - &ctxt + &line_context ); + VERIFY(table_count >= 0 && table_count <= 2, "Unknown dwarf line table count"); if(ret == DW_DLV_NO_ENTRY) { // TODO: Failing silently for now return; } - line_contexts.insert({off, {version, table_count, ctxt}}); + VERIFY(ret == DW_DLV_OK); + line_contexts.insert({off, {version, line_context}}); } - if(table_count == 1) { - Dwarf_Line* linebuf = 0; - Dwarf_Signed linecount = 0; - Dwarf_Addr prev_lineaddr = 0; - VERIFY( - wrap( - dwarf_srclines_from_linecontext, - ctxt, - &linebuf, - &linecount - ) == DW_DLV_OK - ); - Dwarf_Line prev_line = 0; - for(int i = 0; i < linecount; i++) { - Dwarf_Line line = linebuf[i]; - Dwarf_Addr lineaddr = 0; - VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK); - if(pc == lineaddr) { - // Find the last line entry containing current pc - Dwarf_Line last_pc_line = line; - for(int j = i + 1; j < linecount; j++) { - Dwarf_Line j_line = linebuf[j]; - VERIFY(wrap(dwarf_lineaddr, j_line, &lineaddr) == DW_DLV_OK); - if(pc == lineaddr) { - last_pc_line = j_line; - } + Dwarf_Line* line_buffer = 0; + Dwarf_Signed line_count = 0; + Dwarf_Line* linebuf_actuals = nullptr; + Dwarf_Signed linecount_actuals = 0; + VERIFY( + wrap( + dwarf_srclines_two_level_from_linecontext, + line_context, + &line_buffer, + &line_count, + &linebuf_actuals, + &linecount_actuals + ) == DW_DLV_OK + ); + Dwarf_Addr last_lineaddr = 0; + Dwarf_Line last_line = 0; + for(int i = 0; i < line_count; i++) { + Dwarf_Line line = line_buffer[i]; + Dwarf_Addr lineaddr = 0; + VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK); + Dwarf_Line found_line = nullptr; + if(pc == lineaddr) { + // Multiple PCs may correspond to a line, find the last one + found_line = line; + for(int j = i + 1; j < line_count; j++) { + Dwarf_Line line = line_buffer[j]; + Dwarf_Addr lineaddr = 0; + VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK); + if(pc == lineaddr) { + found_line = line; } - handle_line(last_pc_line, frame); - break; - } else if(prev_line && pc > prev_lineaddr && pc < lineaddr) { - handle_line(prev_line, frame); - break; } - Dwarf_Bool is_lne; - VERIFY(wrap(dwarf_lineendsequence, line, &is_lne) == DW_DLV_OK); - if(is_lne) { - prev_line = 0; + } else if(last_line && pc > last_lineaddr && pc < lineaddr) { + // Guess that the last line had it + found_line = last_line; + } + if(found_line) { + Dwarf_Unsigned line_number = 0; + VERIFY(wrap(dwarf_lineno, found_line, &line_number) == DW_DLV_OK); + frame.line = static_cast(line_number); + char* filename = nullptr; + VERIFY(wrap(dwarf_linesrc, found_line, &filename) == DW_DLV_OK); + auto wrapper = raii_wrap( + filename, + [this] (char* str) { if(str) dwarf_dealloc(dbg, str, DW_DLA_STRING); } + ); + frame.filename = filename; + } else { + Dwarf_Bool is_line_end; + VERIFY(wrap(dwarf_lineendsequence, line, &is_line_end) == DW_DLV_OK); + if(is_line_end) { + last_lineaddr = 0; + last_line = 0; } else { - prev_lineaddr = lineaddr; - prev_line = line; + last_lineaddr = lineaddr; + last_line = line; } } } @@ -1002,7 +901,7 @@ namespace libdwarf { to_ull(pc) ); } - retrieve_line_info(cu_die, pc, dwversion, frame); // no offset for line info + retrieve_line_info(cu_die, pc, frame); // no offset for line info retrieve_symbol(cu_die, pc, dwversion, frame); return false; } @@ -1057,12 +956,20 @@ namespace libdwarf { fprintf(stderr, "%llx\n", to_ull(pc)); } // Check for .debug_aranges for fast lookup - Dwarf_Arange *aranges; + Dwarf_Arange* aranges; Dwarf_Signed arange_count; if(wrap(dwarf_get_aranges, dbg, &aranges, &arange_count) == DW_DLV_OK) { + auto aranges_wrapper = raii_wrap( + aranges, + [this] (Dwarf_Arange* aranges) { dwarf_dealloc(dbg, aranges, DW_DLA_LIST); } + ); // Try to find pc in aranges Dwarf_Arange arange; if(wrap(dwarf_get_arange, aranges, arange_count, pc, &arange) == DW_DLV_OK) { + auto arange_wrapper = raii_wrap( + arange, + [this] (Dwarf_Arange arange) { dwarf_dealloc(dbg, arange, DW_DLA_ARANGE); } + ); // Address in table, load CU die Dwarf_Off cu_die_offset; VERIFY(wrap(dwarf_get_cu_die_offset, arange, &cu_die_offset) == DW_DLV_OK); @@ -1077,11 +984,9 @@ namespace libdwarf { fprintf(stderr, "Found CU in aranges\n"); cu_die.print(); } - retrieve_line_info(cu_die, pc, dwversion, frame); // no offset for line info + retrieve_line_info(cu_die, pc, frame); // no offset for line info retrieve_symbol(cu_die, pc, dwversion, frame); - dwarf_dealloc(dbg, arange, DW_DLA_ARANGE); } - dwarf_dealloc(dbg, aranges, DW_DLA_LIST); } else { walk_dbg(pc, frame); } @@ -1142,248 +1047,6 @@ namespace libdwarf { } return trace; } - - // Currently commented out. Need to decide if this is worth it. The mangled name is easier and maybe more - // trustworthy. This is a lot of effort for something that would only be relevant for extern "C" methods. - /*die_object get_type_die(Dwarf_Debug dbg, const die_object& die) { - Dwarf_Off type_offset; - Dwarf_Bool is_info; - int ret = dwarf_dietype_offset(die.get(), &type_offset, &is_info, nullptr); - if(ret == DW_DLV_OK) { - Dwarf_Die type_die; - ret = dwarf_offdie_b( - dbg, - type_offset, - is_info, - &type_die, - nullptr - ); - if(ret == DW_DLV_OK) { - return die_object(dbg, type_die); - } else { - fprintf(stderr, "Error\n"); - exit(1); - } - } else { - fprintf(stderr, "no type offset??\n"); - } - return die_object(dbg, nullptr); - } - - bool has_type(Dwarf_Debug dbg, const die_object& die) { - Dwarf_Attribute attr; - int ret = dwarf_attr(die.get(), DW_AT_type, &attr, nullptr); - if(ret == DW_DLV_NO_ENTRY) { - return false; - } else if(ret == DW_DLV_OK) { - dwarf_dealloc_attribute(attr); - return true; - } else { - fprintf(stderr, "Error\n"); - exit(1); - } - } - - struct type_result { - std::string base; - std::string extent; - - std::string get_type() { - return base + extent; - } - }; - - // TODO: ::*, namespace lookup, arrays - // DW_TAG_namespace - const char* tag_to_keyword(Dwarf_Half tag) { - switch(tag) { - case DW_TAG_atomic_type: - return "_Atomic"; - case DW_TAG_const_type: - return "const"; - case DW_TAG_volatile_type: - return "volatile"; - case DW_TAG_restrict_type: - return "restrict"; - default: - { - const char* tag_name = nullptr; - dwarf_get_TAG_name(tag, &tag_name); - fprintf(stderr, "tag_to_keyword unknown tag %s\n", tag_name); - exit(1); - } - } - } - const char* tag_to_ptr_ref(Dwarf_Half tag) { - switch(tag) { - case DW_TAG_pointer_type: - return "*"; - case DW_TAG_ptr_to_member_type: - return "::*"; // TODO - case DW_TAG_reference_type: - return "&"; - case DW_TAG_rvalue_reference_type: - return "&&"; - default: - { - const char* tag_name = nullptr; - dwarf_get_TAG_name(tag, &tag_name); - fprintf(stderr, "tag_to_ptr_ref unknown tag %s\n", tag_name); - exit(1); - } - } - } - - std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = ""); - - std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) { - VERIFY(die.get_tag() == DW_TAG_array_type); - std::string extents = ""; - walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) { - if(subrange.get_tag() == DW_TAG_subrange_type) { - Dwarf_Attribute attr = 0; - int res = 0; - res = dwarf_attr(subrange.get(), DW_AT_upper_bound, &attr, nullptr); - if(res != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - return; - } - Dwarf_Half form; - res = dwarf_whatform(attr, &form, nullptr); - if(res != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - return; - } - //fprintf(stderr, "form: %d\n", form); - Dwarf_Unsigned val; - res = dwarf_formudata(attr, &val, nullptr); - if(res != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - return; - } - extents += "[" + std::to_string(val + 1) + "]"; - dwarf_dealloc_attribute(attr); - } else { - fprintf(stderr, "unknown tag %s\n", subrange.get_tag_name()); - } - }); - return extents; - } - - std::string get_parameters(Dwarf_Debug dbg, const die_object& die) { - VERIFY(die.get_tag() == DW_TAG_subroutine_type); - std::vector params; - walk_die_list(dbg, die.get_child(), [¶ms](Dwarf_Debug dbg, const die_object& die) { - if(die.get_tag() == DW_TAG_formal_parameter) { - // TODO: Ignore DW_AT_artificial - params.push_back(resolve_type(dbg, get_type_die(dbg, die))); - } - }); - return "(" + join(params, ", ") + ")"; - } - - std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build) { - switch(auto tag = die.get_tag()) { - case DW_TAG_base_type: - case DW_TAG_class_type: - case DW_TAG_structure_type: - case DW_TAG_union_type: - case DW_TAG_enumeration_type: - return die.get_name() + build; - case DW_TAG_typedef: - return resolve_type(dbg, get_type_die(dbg, die)); - //case DW_TAG_subroutine_type: - // { - // // If there's no DW_AT_type then it's a void - // std::vector params; - // // TODO: Code duplication with retrieve_symbol_for_subprogram? - // walk_die_list(dbg, die.get_child(), [¶ms] (Dwarf_Debug dbg, const die_object& die) { - // if(die.get_tag() == DW_TAG_formal_parameter) { - // // TODO: Ignore DW_AT_artificial - // params.push_back(resolve_type(dbg, get_type_die(dbg, die))); - // } - // }); - // if(!has_type(dbg, die)) { - // return "void" + (build.empty() ? "" : "(" + build + ")") + "(" + join(params, ", ") + ")"; - // } else { - // // resolving return type, building on build - // return resolve_type( - // dbg, get_type_die(dbg, die), - // (build.empty() ? "" : "(" + build + ")") - // + "(" - // + join(params, ", ") - // + ")" - // ); - // } - // } - //case DW_TAG_array_type: - // return resolve_type(dbg, get_type_die(dbg, die), (build.empty() ? "" : "(" + build + ")") + "[" + "x" + "]"); - case DW_TAG_pointer_type: - case DW_TAG_reference_type: - case DW_TAG_rvalue_reference_type: - case DW_TAG_ptr_to_member_type: - { - const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child - const auto child_tag = child.get_tag(); - switch(child_tag) { - case DW_TAG_subroutine_type: - if(!has_type(dbg, child)) { - return "void(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child); - } else { - return resolve_type( - dbg, - get_type_die(dbg, child), - "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child) - ); - } - case DW_TAG_array_type: - return resolve_type( - dbg, - get_type_die(dbg, child), - "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_array_extents(dbg, child) - ); - default: - if(build.empty()) { - return resolve_type(dbg, get_type_die(dbg, die), tag_to_ptr_ref(tag)); - } else { - return resolve_type( - dbg, - get_type_die(dbg, die), - std::string(tag_to_ptr_ref(tag)) + " " + build - ); - } - } - } - case DW_TAG_const_type: - case DW_TAG_atomic_type: - case DW_TAG_volatile_type: - case DW_TAG_restrict_type: - { - const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child - const auto child_tag = child.get_tag(); - switch(child_tag) { - case DW_TAG_base_type: - case DW_TAG_class_type: - case DW_TAG_typedef: - return std::string(tag_to_keyword(tag)) - + " " - + resolve_type(dbg, get_type_die(dbg, die), build); - default: - return resolve_type( - dbg, - get_type_die(dbg, die), - std::string(tag_to_keyword(tag)) + " " + build - ); - } - } - default: - { - fprintf(stderr, "unknown tag %s\n", die.get_tag_name()); - exit(1); - } - } - return {"", ""}; - }*/ } } }