/* Check whether PC ranges reported at DIE fall into the containing scope. Copyright (C) 2009,2010,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 the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. 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 a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include "highlevel_check.hh" #include "coverage.hh" #include "pri.hh" #include "check_debug_loc_range.hh" using elfutils::dwarf; namespace { class check_range_out_of_scope : public highlevel_check { typedef std::vector > ranges_t; static void recursively_validate (dwarf::compile_unit const &cu, dwarf::debug_info_entry const &die, ranges_t const &ranges, locus const &wh_parent); public: static checkdescriptor const *descriptor () { static checkdescriptor cd (checkdescriptor::create ("check_range_out_of_scope") .description ( "Check whether PC ranges reported at DIEs fall into the containing scope.\n")); return &cd; } check_range_out_of_scope (checkstack &stack, dwarflint &lint); }; // Register the check. reg reg_range_out_of_scope; const ::Dwarf_Addr noaddr = -1; } check_range_out_of_scope::check_range_out_of_scope (checkstack &stack, dwarflint &lint) : highlevel_check (stack, lint) { try { class dwarf::compile_units_type const &cus = dw.compile_units (); ranges_t r; r.push_back (std::make_pair (0, -1)); section_locus wh (sec_info); for (dwarf::compile_units_type::const_iterator it = cus.begin (); it != cus.end (); ++it) recursively_validate (*it, *it, r, wh); } // XXX more specific class when has it catch (std::runtime_error &exc) { wr_error (section_locus (sec_info)) << "Exception while checking ranges out of scope: " << exc.what () << std::endl; throw check_base::failed (); } } void check_range_out_of_scope::recursively_validate (dwarf::compile_unit const &cu, dwarf::debug_info_entry const &die, ranges_t const &ranges, locus const &wh_parent) { die_locus wh (die); ::Dwarf_Addr low_pc = 0; ::Dwarf_Addr high_pc = ::noaddr; 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 != ::noaddr) { // 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 == ::noaddr && die.tag () != DW_TAG_compile_unit && die.tag () != DW_TAG_partial_unit) high_pc = low_pc + 1; if (high_pc != ::noaddr) { if (my_ranges.size () != 0) wr_message (wh, mc_impact_4 | mc_info | mc_error) << "both low_pc/high_pc pair and ranges present." << std::endl; 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; for (ranges_t::const_iterator it = my_ranges.begin (); it != my_ranges.end (); ++it) cov1.add ((*it).first, (*it).second - (*it).first); coverage cov2; for (ranges_t::const_iterator it = ranges.begin (); it != ranges.end (); ++it) cov2.add ((*it).first, (*it).second - (*it).first); coverage result = cov1 - cov2; if (!result.empty ()) { wr_message (wh, mc_error).id (descriptor ()) << "PC range " << cov::format_ranges (cov1) << " is not a sub-range of containing scope." << std::endl; wr_message (wh_parent, mc_error).when_prev () << "in this context: " << cov::format_ranges (cov2) << std::endl; } } } } // 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; for (ranges_t::const_iterator it = use_ranges.begin (); it != use_ranges.end (); ++it) cov.add ((*it).first, (*it).second - (*it).first); // 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 && !cov.is_covered (start, length)) wr_message (wh, mc_error) .id (descriptor (), runoff) << "attribute `" << elfutils::dwarf::attributes::name ((*at).first) << "': PC range " << pri::range (start, end) << " outside containing scope." << std::endl; } wr_message (wh_parent, mc_error) .when (runoff) << "in this context: " << cov::format_ranges (cov) << '.' << std::endl; } } } // Check children recursively. for (dwarf::debug_info_entry::children_type::const_iterator jt = die.children ().begin (); jt != die.children ().end (); ++jt) recursively_validate (cu, *jt, use_ranges, my_ranges.size () > 0 ? wh : wh_parent); }