From 63ed689b839b659af7806ae325a1782629cf9c56 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Tue, 6 Oct 2009 18:23:44 +0200 Subject: [PATCH] dwarflint: Add test for ranges running off the scope --- src/dwarflint-hl.cc | 228 ++++++++++++++++++++++++++++++++++++++++++++ src/dwarflint.c | 6 +- src/dwarflint.h | 8 +- 3 files changed, 239 insertions(+), 3 deletions(-) diff --git a/src/dwarflint-hl.cc b/src/dwarflint-hl.cc index 2179e2e1b..f033f6b22 100644 --- a/src/dwarflint-hl.cc +++ b/src/dwarflint-hl.cc @@ -37,6 +37,7 @@ #include #include "dwarflint.h" +#include "dwarflint-coverage.hh" #include "dwarflint-expected.hh" #include "dwarfstrings.h" #include "c++/dwarf" @@ -98,6 +99,13 @@ hl_ctx_delete (hl_ctx *hlctx) delete hlctx; } +std::string +range_fmt (uint64_t start, uint64_t end) +{ + char buf[128]; + return std::string (range_fmt (buf, sizeof buf, start, end)); +} + static const expected_at_map expected_at; //static const expected_children_map expected_children; @@ -303,3 +311,223 @@ check_expected_trees (hl_ctx *hlctx) return false; } } + +bool +check_range_out_of_scope (hl_ctx *hlctx) +{ + try + { + typedef std::vector > + ranges_t; + + struct + { + void operator () (dwarf::compile_unit const &cu, + dwarf::debug_info_entry const &die, + ranges_t const &ranges, + where const &wh_parent) + { + where wh = WHERE (sec_info, NULL); + where_reset_1 (&wh, cu.offset ()); + where_reset_2 (&wh, die.offset ()); + + ::Dwarf_Addr low_pc = 0; + ::Dwarf_Addr high_pc = -1; + ranges_t my_ranges; + for (dwarf::debug_info_entry::attributes_type::const_iterator + at = die.attributes ().begin (); + at != die.attributes ().end (); ++at) + { + dwarf::attr_value const &value = (*at).second; + dwarf::value_space vs = value.what_space (); + if ((*at).first == DW_AT_low_pc) + low_pc = value.address (); + else if ((*at).first == DW_AT_high_pc) + high_pc = value.address (); + else if (vs == dwarf::VS_rangelistptr) + for (dwarf::range_list::const_iterator + it = value.ranges ().begin (); + it != value.ranges ().end (); ++it) + my_ranges.push_back (*it); + } + if (low_pc != 0 || high_pc != (::Dwarf_Addr)-1) + { + // Simultaneous appearance of both low_pc/high_pc pair + // and rangelist pointer is forbidden by 3.1.1 #1. + // Presence of low_pc on itself is OK on compile_unit + // and partial_unit DIEs, otherwise it serves the same + // purpose as low_pc/high_pc pair that covers one + // address point. + + if (high_pc == (::Dwarf_Addr)-1 + && die.tag () != DW_TAG_compile_unit + && die.tag () != DW_TAG_partial_unit) + high_pc = low_pc + 1; + + if (high_pc != (::Dwarf_Addr)-1) + { + if (my_ranges.size () != 0) + wr_message (cat (mc_impact_4, mc_info, mc_error), &wh, + ": both low_pc/high_pc pair and ranges present.\n"); + else + my_ranges.push_back (std::make_pair (low_pc, high_pc)); + } + } + + // If my_ranges is non-empty, check that it's a subset of + // ranges. + if (my_ranges.size () != 0) + { + // xxx Extract this logic to some table. + switch (die.tag ()) + { + /* These PC-ful DIEs should be wholly contained by + PC-ful parental DIE. */ + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + case DW_TAG_entry_point: + case DW_TAG_label: + case DW_TAG_with_stmt: + case DW_TAG_try_block: + case DW_TAG_catch_block: + { + coverage cov1; + WIPE (cov1); + + for (ranges_t::const_iterator it = my_ranges.begin (); + it != my_ranges.end (); ++it) + coverage_add (&cov1, (*it).first, + (*it).second - (*it).first); + + coverage cov2; + WIPE (cov2); + for (ranges_t::const_iterator it = ranges.begin (); + it != ranges.end (); ++it) + coverage_add (&cov2, (*it).first, + (*it).second - (*it).first); + + coverage result; + WIPE (result); + coverage_add_all (&result, &cov1); + coverage_remove_all (&result, &cov2); + + if (result.size > 0) + { + std::string super_wh = where_fmt (&wh_parent); + wr_error (&wh, ": range is not sub-range of %s.\n", + super_wh.c_str ()); + std::string range = cov::format_ranges (cov1); + std::string super_range = cov::format_ranges (cov2); + wr_error (&wh, ": range %s\n", range.c_str ()); + wr_error (&wh_parent, ": range %s\n", super_range.c_str ()); + } + + coverage_free (&result); + coverage_free (&cov2); + } + } + } + + // xxx building the coverage for each die is a waste of time + ranges_t const &use_ranges + = my_ranges.size () > 0 ? my_ranges : ranges; + coverage cov; + WIPE (cov); + + for (ranges_t::const_iterator it = use_ranges.begin (); + it != use_ranges.end (); ++it) + coverage_add (&cov, (*it).first, (*it).second - (*it).first); + + /* + if (use_ranges.size () > 0) + { + std::vector< ::Dwarf_Addr> low_pcs; + std::vector< ::Dwarf_Addr> high_pcs; + + for (ranges_t::const_iterator it = use_ranges.begin (); + it != use_ranges.end (); ++it) + { + low_pcs.push_back (it->first); + high_pcs.push_back (it->second); + } + + std::sort (low_pcs.begin (), low_pcs.end ()); + std::sort (high_pcs.begin (), high_pcs.end ()); + + ::Dwarf_Addr lowest = low_pcs.front (); + ::Dwarf_Addr highest = high_pcs.back (); + assert (highest >= lowest); + coverage_add (&cov, lowest, highest - lowest + 1); + } + */ + + // Now finally look for location attributes and check that + // _their_ PCs form a subset of ranges of this DIE. + for (dwarf::debug_info_entry::attributes_type::const_iterator + at = die.attributes ().begin (); + at != die.attributes ().end (); ++at) + { + dwarf::attr_value const &value = (*at).second; + dwarf::value_space vs = value.what_space (); + + if (vs == dwarf::VS_location) + { + dwarf::location_attr const &loc = value.location (); + if (loc.is_list ()) + { + bool runoff = false; + for (dwarf::location_attr::const_iterator + lt = loc.begin (); lt != loc.end (); ++lt) + { + ::Dwarf_Addr start = (*lt).first.first; //1st insn + ::Dwarf_Addr end = (*lt).first.second; //1st past end + ::Dwarf_Addr length = end - start; + if (length > 0 // skip empty ranges + && !coverage_is_covered (&cov, start, length)) + { + runoff = true; + std::string super_wh = where_fmt (&wh_parent); + wr_error (&wh, ": attribute `%s': " + "range %s runs off DIE scope\n", + dwarf_attr_string ((*at).first), + range_fmt (start, end).c_str ()); + } + } + if (runoff) + { + std::string rangestr = cov::format_ranges (cov); + wr_error (&wh_parent, ": DIE scope is: %s\n", + rangestr.c_str ()); + } + } + } + } + + coverage_free (&cov); + + // Check children recursively. + for (dwarf::debug_info_entry::children_type::const_iterator + jt = die.children ().begin (); + jt != die.children ().end (); ++jt) + (*this) (cu, *jt, use_ranges, + my_ranges.size () > 0 ? wh : wh_parent); + } + } recursively_validate; + + class dwarf::compile_units const &cus = hlctx->dw.compile_units (); + ranges_t r; + r.push_back (std::make_pair (0, -1)); + where wh = WHERE (sec_info, NULL); + for (dwarf::compile_units::const_iterator it = cus.begin (); + it != cus.end (); ++it) + recursively_validate (*it, *it, r, wh); + return true; + } + // XXX more specific class when has it + catch (std::runtime_error &exc) + { + wr_error (NULL, "Error while checking range out of scope: %s.\n", + exc.what ()); + return false; + } +} diff --git a/src/dwarflint.c b/src/dwarflint.c index 9f7c48954..1753e171c 100644 --- a/src/dwarflint.c +++ b/src/dwarflint.c @@ -1289,8 +1289,10 @@ process_file (Elf *elf, const char *fname, bool only_one) else ranges_sound = false; - if (HAS_SEC(loc) && cu_chain != NULL) - check_loc_or_range_structural (&file, SEC(loc), cu_chain, NULL); + if (HAS_SEC(loc) && cu_chain != NULL + && check_loc_or_range_structural (&file, SEC(loc), cu_chain, NULL) + && cu_chain != NULL && hlctx != NULL) + check_range_out_of_scope (hlctx); if (HAS_SEC(aranges)) { diff --git a/src/dwarflint.h b/src/dwarflint.h index c018141a8..ce8ba5936 100644 --- a/src/dwarflint.h +++ b/src/dwarflint.h @@ -31,9 +31,12 @@ #include "dwarflint-coverage.h" #ifdef __cplusplus +# define IF_CPLUSPLUS(X) X +# include extern "C" { #else +# define IF_CPLUSPLUS(X) /*X*/ # include #endif @@ -46,6 +49,7 @@ extern "C" extern void hl_ctx_delete (struct hl_ctx *hlctx); extern bool check_matching_ranges (struct hl_ctx *hlctx); extern bool check_expected_trees (struct hl_ctx *hlctx); + extern bool check_range_out_of_scope (struct hl_ctx *hlctx); /* Functions and data structures describing location in Dwarf. */ @@ -109,7 +113,7 @@ extern "C" (uint64_t)-1, (uint64_t)-1, (uint64_t)-1, \ NULL, NEXT}) - extern const char *where_fmt (const struct where *wh, char *ptr); + extern const char *where_fmt (const struct where *wh, char *ptr IF_CPLUSPLUS (= NULL)); extern void where_fmt_chain (const struct where *wh, const char *severity); extern void where_reset_1 (struct where *wh, uint64_t addr); extern void where_reset_2 (struct where *wh, uint64_t addr); @@ -308,6 +312,8 @@ extern "C" #ifdef __cplusplus } + +extern std::string range_fmt (uint64_t start, uint64_t end); #endif #endif/*DWARFLINT_HL_H*/ -- 2.47.3