]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
dwarflint: Add test for ranges running off the scope
authorPetr Machata <pmachata@redhat.com>
Tue, 6 Oct 2009 16:23:44 +0000 (18:23 +0200)
committerPetr Machata <pmachata@redhat.com>
Wed, 18 Aug 2010 12:55:11 +0000 (14:55 +0200)
src/dwarflint-hl.cc
src/dwarflint.c
src/dwarflint.h

index 2179e2e1b7af4b8df1e9d01515535b9300bba122..f033f6b22fc3294e3b44a942893b61968328d25a 100644 (file)
@@ -37,6 +37,7 @@
 #include <iterator>
 
 #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<std::pair< ::Dwarf_Addr, ::Dwarf_Addr> >
+       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 <dwarf> has it
+  catch (std::runtime_error &exc)
+    {
+      wr_error (NULL, "Error while checking range out of scope: %s.\n",
+               exc.what ());
+      return false;
+    }
+}
index 9f7c489544bab1663b073dc081f305e175151e07..1753e171c31f117c2f7731cedb9336129364a9ca 100644 (file)
@@ -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))
     {
index c018141a8bd0db4c44a27987e25fa77507603203..ce8ba5936efdc78e3167bfcf050b6003c8b503c5 100644 (file)
 #include "dwarflint-coverage.h"
 
 #ifdef __cplusplus
+# define IF_CPLUSPLUS(X) X
+# include <string>
 extern "C"
 {
 #else
+# define IF_CPLUSPLUS(X) /*X*/
 # include <stdbool.h>
 #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*/