blob: f60f18f3197e2c60b653fdc1e9afa166f1ceab71 [file] [log] [blame]
/* 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 <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#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<check_range_out_of_scope>
{
typedef std::vector<std::pair< ::Dwarf_Addr, ::Dwarf_Addr> >
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<check_range_out_of_scope> 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<check_range_out_of_scope> (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 <dwarf> 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);
}