]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
dwarflint: Extract .debug_abbrev check
authorPetr Machata <pmachata@redhat.com>
Thu, 22 Oct 2009 15:30:11 +0000 (17:30 +0200)
committerPetr Machata <pmachata@redhat.com>
Wed, 18 Aug 2010 12:55:12 +0000 (14:55 +0200)
src/Makefile.am
src/dwarflint/check_debug_abbrev.cc [new file with mode: 0644]
src/dwarflint/checks-low.cc
src/dwarflint/checks-low.hh
src/dwarflint/coverage.h
src/dwarflint/low.c
src/dwarflint/low.h

index 19eb6982f8d2c92932d39ad363ce1f52d460fa9b..fb61e3ea19d6489217cde025abb22579fdf81cf6 100644 (file)
@@ -91,6 +91,7 @@ dwarflint_SOURCES = dwarfstrings.c \
                    dwarflint/checks-low.cc dwarflint/checks-low.hh \
                    dwarflint/addr-record.cc dwarflint/addr-record.h \
                    dwarflint/checks-high.hh \
+                   dwarflint/check_debug_abbrev.cc \
                    dwarflint/check_matching_ranges.cc \
                    dwarflint/check_range_out_of_scope.cc \
                    dwarflint/check_expected_trees.cc
diff --git a/src/dwarflint/check_debug_abbrev.cc b/src/dwarflint/check_debug_abbrev.cc
new file mode 100644 (file)
index 0000000..17b3295
--- /dev/null
@@ -0,0 +1,426 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+
+   Red Hat elfutils 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; version 2 of the License.
+
+   Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "checks-low.hh"
+#include "dwarfstrings.h"
+
+#include <dwarf.h>
+#include <sstream>
+#include <cassert>
+#include <algorithm>
+
+/* Check that given form may in fact be valid in some CU.  */
+static bool
+check_abbrev_location_form (uint64_t form)
+{
+  switch (form)
+    {
+    case DW_FORM_indirect:
+
+      /* loclistptr */
+    case DW_FORM_data4:
+    case DW_FORM_data8:
+    case DW_FORM_sec_offset: // DWARF 4
+
+      /* block */
+    case DW_FORM_block1:
+    case DW_FORM_block2:
+    case DW_FORM_block4:
+    case DW_FORM_block:
+      return true;
+
+    default:
+      return false;
+    };
+}
+
+void
+abbrev_table_free (struct abbrev_table *abbr)
+{
+  for (struct abbrev_table *it = abbr; it != NULL; )
+    {
+      for (size_t i = 0; i < it->size; ++i)
+       free (it->abbr[i].attribs);
+      free (it->abbr);
+
+      struct abbrev_table *temp = it;
+      it = it->next;
+      free (temp);
+    }
+}
+
+struct abbrev *
+abbrev_table_find_abbrev (struct abbrev_table *abbrevs, uint64_t abbrev_code)
+{
+  size_t a = 0;
+  size_t b = abbrevs->size;
+  struct abbrev *ab = NULL;
+
+  while (a < b)
+    {
+      size_t i = (a + b) / 2;
+      ab = abbrevs->abbr + i;
+
+      if (ab->code > abbrev_code)
+       b = i;
+      else if (ab->code < abbrev_code)
+       a = i + 1;
+      else
+       return ab;
+    }
+
+  return NULL;
+}
+
+bool
+check_debug_abbrev::check_no_abbreviations () const
+{
+  bool ret = abbrevs.begin () == abbrevs.end ();
+  if (ret)
+    {
+      where wh = WHERE (sec_abbrev, NULL);
+      wr_error (&wh, ": no abbreviations.\n");
+    }
+  return ret;
+}
+
+namespace
+{
+  struct cmp_abbrev
+  {
+    bool operator () (abbrev const &a, abbrev const &b) const
+    {
+      return a.code < b.code;
+    }
+  };
+}
+
+check_debug_abbrev::check_debug_abbrev (dwarflint &lint)
+  : _m_sec_abbr (lint.check (_m_sec_abbr))
+{
+  read_ctx ctx;
+  read_ctx_init (&ctx, _m_sec_abbr->sect.data,
+                _m_sec_abbr->file.other_byte_order);
+
+  struct abbrev_table *section = NULL;
+  uint64_t first_attr_off = 0;
+  struct where where = WHERE (sec_abbrev, NULL);
+  where.addr1 = 0;
+
+  while (true)
+    {
+      /* If we get EOF at this point, either the CU was improperly
+        terminated, or there were no data to begin with.  */
+      if (read_ctx_eof (&ctx))
+       {
+         if (!check_no_abbreviations ())
+           wr_error (&where, ": missing zero to mark end-of-table.\n");
+         break;
+       }
+
+      uint64_t abbr_off;
+      uint64_t abbr_code;
+      {
+       uint64_t prev_abbr_off = (uint64_t)-1;
+       uint64_t prev_abbr_code = (uint64_t)-1;
+       uint64_t zero_seq_off = (uint64_t)-1;
+
+       do
+         {
+           abbr_off = read_ctx_get_offset (&ctx);
+           where_reset_2 (&where, abbr_off);
+
+           /* Abbreviation code.  */
+           if (!checked_read_uleb128 (&ctx, &abbr_code, &where, "abbrev code"))
+             throw check_base::failed (""); //xxx
+
+           /* Note: we generally can't tell the difference between
+              empty table and (excessive) padding.  But NUL byte(s)
+              at the very beginning of section are almost certainly
+              the first case.  */
+           if (zero_seq_off == (uint64_t)-1
+               && abbr_code == 0
+               && (prev_abbr_code == 0
+                   || abbrevs.empty ()))
+             zero_seq_off = abbr_off;
+
+           if (abbr_code != 0)
+             break;
+           else
+             section = NULL;
+
+           prev_abbr_code = abbr_code;
+           prev_abbr_off = abbr_off;
+         }
+       while (!read_ctx_eof (&ctx)
+              /* On EOF, shift the offset so that beyond-EOF
+                 end-position is printed for padding warning.
+                 Necessary as our end position is exclusive.  */
+              || ((abbr_off += 1), false));
+
+       if (zero_seq_off != (uint64_t)-1)
+         {
+           struct where wh = WHERE (where.section, NULL);
+           wr_message_padding_0 (mc_abbrevs | mc_header,
+                                 &wh, zero_seq_off, abbr_off);
+         }
+      }
+
+      if (read_ctx_eof (&ctx))
+       {
+         /* It still may have been empty.  */
+         check_no_abbreviations ();
+         break;
+       }
+
+      /* OK, we got some genuine abbreviation.  See if we need to
+        allocate a new section.  */
+      if (section == NULL)
+       {
+         abbrev_table t;
+         WIPE (t);
+         section = &abbrevs.insert (std::make_pair (abbr_off, t)).first->second;
+         section->offset = abbr_off;
+
+         where_reset_1 (&where, abbr_off);
+         where_reset_2 (&where, abbr_off);
+       }
+
+      struct abbrev *original = abbrev_table_find_abbrev (section, abbr_code);
+      if (unlikely (original != NULL))
+       {
+         std::stringstream ss;
+         ss << ": duplicate abbrev code " << abbr_code
+            << "; already defined at " << where_fmt (&original->where) << '.';
+         wr_error (&where, "%s\n", ss.str ().c_str ());
+       }
+
+      struct abbrev fake;
+      struct abbrev *cur;
+      /* Don't actually save this abbrev if it's duplicate.  */
+      if (likely (original == NULL))
+       {
+         REALLOC (section, abbr);
+         cur = section->abbr + section->size++;
+       }
+      else
+       cur = &fake;
+      WIPE (*cur);
+
+      cur->code = abbr_code;
+      cur->where = where;
+
+      /* Abbreviation tag.  */
+      uint64_t abbr_tag;
+      if (!checked_read_uleb128 (&ctx, &abbr_tag, &where, "abbrev tag"))
+       throw check_base::failed (""); //xxx
+
+      if (abbr_tag > DW_TAG_hi_user)
+       {
+         std::stringstream ss;
+         ss << ": invalid abbrev tag 0x" << std::hex << abbr_tag << '.';
+         wr_error (&where, "%s\n", ss.str ().c_str ());
+         throw check_base::failed (""); //xxx
+       }
+      cur->tag = (typeof (cur->tag))abbr_tag;
+
+      /* Abbreviation has_children.  */
+      uint8_t has_children;
+      if (!read_ctx_read_ubyte (&ctx, &has_children))
+       {
+         wr_error (&where, ": can't read abbrev has_children.\n");
+         throw check_base::failed (""); //xxx
+       }
+
+      if (has_children != DW_CHILDREN_no
+         && has_children != DW_CHILDREN_yes)
+       {
+         wr_error (&where,
+                   ": invalid has_children value 0x%x.\n", cur->has_children);
+         throw check_base::failed (""); //xxx
+       }
+      cur->has_children = has_children == DW_CHILDREN_yes;
+
+      bool null_attrib;
+      uint64_t sibling_attr = 0;
+      bool low_pc = false;
+      bool high_pc = false;
+      bool ranges = false;
+      do
+       {
+         uint64_t attr_off = read_ctx_get_offset (&ctx);
+         uint64_t attrib_name, attrib_form;
+         if (first_attr_off == 0)
+           first_attr_off = attr_off;
+         /* Shift to match elfutils reporting.  */
+         where_reset_3 (&where, attr_off - first_attr_off);
+
+         /* Load attribute name and form.  */
+         if (!checked_read_uleb128 (&ctx, &attrib_name, &where,
+                                    "attribute name"))
+           throw check_base::failed (""); //xxx
+
+         if (!checked_read_uleb128 (&ctx, &attrib_form, &where,
+                                    "attribute form"))
+           throw check_base::failed (""); //xxx
+
+         null_attrib = attrib_name == 0 && attrib_form == 0;
+
+         /* Now if both are zero, this was the last attribute.  */
+         if (!null_attrib)
+           {
+             /* Otherwise validate name and form.  */
+             if (attrib_name > DW_AT_hi_user)
+               {
+                 std::stringstream ss;
+                 ss << ": invalid name 0x" << std::hex << attrib_name << '.';
+                 wr_error (&where, "%s\n", ss.str ().c_str ());
+                 throw check_base::failed (""); //xxx
+               }
+
+             if (!attrib_form_valid (attrib_form))
+               {
+                 std::stringstream ss;
+                 ss << ": invalid form 0x" << std::hex << attrib_form << '.';
+                 wr_error (&where, "%s\n", ss.str ().c_str ());
+                 throw check_base::failed (""); //xxx
+               }
+           }
+
+         REALLOC (cur, attribs);
+
+         struct abbrev_attrib *acur = cur->attribs + cur->size++;
+         WIPE (*acur);
+
+         /* We do structural checking of sibling attribute, so make
+            sure our assumptions in actual DIE-loading code are
+            right.  We expect at most one DW_AT_sibling attribute,
+            with form from reference class, but only CU-local, not
+            DW_FORM_ref_addr.  */
+         if (attrib_name == DW_AT_sibling)
+           {
+             if (sibling_attr != 0)
+               {
+                 std::stringstream ss;
+                 ss << ": Another DW_AT_sibling attribute in one abbreviation. "
+                    << "(First was 0x" << std::hex << sibling_attr << ".)";
+                 wr_error (&where, "%s\n", ss.str ().c_str ());
+               }
+             else
+               {
+                 assert (attr_off > 0);
+                 sibling_attr = attr_off;
+
+                 if (!cur->has_children)
+                   wr_message (mc_die_rel | mc_acc_bloat | mc_impact_1,
+                               &where,
+                               ": Excessive DW_AT_sibling attribute at childless abbrev.\n");
+               }
+
+             switch (check_sibling_form (attrib_form))
+               {
+               case -1:
+                 wr_message (mc_die_rel | mc_impact_2, &where,
+                             ": DW_AT_sibling attribute with form DW_FORM_ref_addr.\n");
+                 break;
+
+               case -2:
+                 wr_error (&where,
+                           ": DW_AT_sibling attribute with non-reference form \"%s\".\n",
+                           dwarf_form_string (attrib_form));
+               };
+           }
+         /* Similar for DW_AT_location and friends.  */
+         else if (is_location_attrib (attrib_name))
+           {
+             if (!check_abbrev_location_form (attrib_form))
+               wr_error (&where,
+                         ": location attribute %s with invalid form \"%s\".\n",
+                         dwarf_attr_string (attrib_name),
+                         dwarf_form_string (attrib_form));
+           }
+         /* Similar for DW_AT_ranges.  */
+         else if (attrib_name == DW_AT_ranges
+                  || attrib_name == DW_AT_stmt_list)
+           {
+             if (attrib_form != DW_FORM_data4
+                 && attrib_form != DW_FORM_data8
+                 && attrib_form != DW_FORM_sec_offset
+                 && attrib_form != DW_FORM_indirect)
+               wr_error (&where,
+                         ": %s with invalid form \"%s\".\n",
+                         dwarf_attr_string (attrib_name),
+                         dwarf_form_string (attrib_form));
+             if (attrib_name == DW_AT_ranges)
+               ranges = true;
+           }
+         /* Similar for DW_AT_{low,high}_pc, plus also make sure we
+            don't see high_pc without low_pc.  */
+         else if (attrib_name == DW_AT_low_pc
+                  || attrib_name == DW_AT_high_pc)
+           {
+             if (attrib_form != DW_FORM_addr
+                 && attrib_form != DW_FORM_ref_addr)
+               wr_error (&where,
+                         ": %s with invalid form \"%s\".\n",
+                         dwarf_attr_string (attrib_name),
+                         dwarf_form_string (attrib_form));
+
+             if (attrib_name == DW_AT_low_pc)
+               low_pc = true;
+             else if (attrib_name == DW_AT_high_pc)
+               high_pc = true;
+           }
+
+         acur->name = attrib_name;
+         acur->form = attrib_form;
+         acur->where = where;
+       }
+      while (!null_attrib);
+
+      where_reset_2 (&where, where.addr2); // drop addr 3
+      if (high_pc && !low_pc)
+       wr_error (&where,
+                 ": the abbrev has DW_AT_high_pc"
+                 " without also having DW_AT_low_pc.\n");
+      else if (high_pc && ranges)
+       wr_error (&where,
+                 ": the abbrev has DW_AT_high_pc & DW_AT_low_pc,"
+                 " but also has DW_AT_ranges.\n");
+    }
+
+  abbrev_table *last = NULL;
+  for (abbrev_map::iterator it = abbrevs.begin (); it != abbrevs.end (); ++it)
+    {
+      std::sort (it->second.abbr, it->second.abbr + it->second.size,
+                cmp_abbrev ());
+      if (last != NULL)
+       last->next = &it->second;
+    }
+}
index dd889c61ee16b00aac4a81ed71ae68c9e615460b..17259273cabc133ec99ebeaacfb24da5e69d5856 100644 (file)
@@ -283,23 +283,6 @@ section_base::section_base (dwarflint &lint, section_id secid)
 {
 }
 
-check_debug_abbrev::check_debug_abbrev (dwarflint &lint)
-  : _m_sec_abbr (lint.check (_m_sec_abbr))
-{
-  read_ctx ctx;
-  read_ctx_init (&ctx, _m_sec_abbr->sect.data,
-                _m_sec_abbr->file.other_byte_order);
-
-  /* xxx wrap C routine before proper loading is in place.  */
-  abbrev_table *chain = abbrev_table_load (&ctx);
-  if (chain == NULL)
-    throw check_base::failed (""); // xxx
-  for (abbrev_table *it = chain; it != NULL; it = it->next)
-    abbrevs[it->offset] = *it;
-  // abbrev_table_free (chain); xxx
-  abbrev_chain = chain;
-}
-
 check_debug_info::check_debug_info (dwarflint &lint)
   : _m_sec_info (lint.check (_m_sec_info))
   , _m_sec_abbrev (lint.check (_m_sec_abbrev))
@@ -311,7 +294,8 @@ check_debug_info::check_debug_info (dwarflint &lint)
   /* xxx wrap C routine before proper loading is in place.  */
   cu *chain = check_info_structural
     (&_m_sec_info->file, &_m_sec_info->sect,
-     _m_abbrevs->abbrev_chain, _m_sec_str->sect.data, &cu_cov);
+     &_m_abbrevs->abbrevs.begin ()->second,
+     _m_sec_str->sect.data, &cu_cov);
 
   if (chain == NULL)
     throw check_base::failed (""); // xxx
index 0519a17a29d8d78583c2efdc94461b57916eea4d..9f3655842d013a4a959f08e76db9c6d123988626 100644 (file)
@@ -1,5 +1,5 @@
-#include "checks.hh"
 #include "low.h"
+#include "checks.hh"
 
 class load_sections
   : public check<load_sections>
@@ -35,13 +35,14 @@ class check_debug_abbrev
   : public check<check_debug_abbrev>
 {
   section<sec_abbrev> *_m_sec_abbr;
+  bool check_no_abbreviations () const;
 
 public:
   explicit check_debug_abbrev (dwarflint &lint);
 
   // offset -> abbreviations
-  std::map< ::Dwarf_Off, abbrev_table> abbrevs;
-  struct abbrev_table *abbrev_chain; // xxx
+  typedef std::map< ::Dwarf_Off, abbrev_table> abbrev_map;
+  abbrev_map abbrevs;
 };
 static reg<check_debug_abbrev> reg_debug_abbrev;
 
index 49e413a619613166208e5630fe46fe174a9c1206..c1434855f12638e6462e3e308f5bc6d45203e204 100644 (file)
@@ -108,6 +108,7 @@ bool coverage_find_ranges (struct coverage const *cov,
 #ifdef __cplusplus
 }
 
+#include <string>
 std::string range_fmt (uint64_t start, uint64_t end);
 #endif
 
index 0f748232c05d6a2b329e6b5b155a1f9efdfd95e7..3ceef8c25be978f1e01855d6503dd147cc3edf7a 100644 (file)
@@ -88,7 +88,7 @@ necessary_alignment (uint64_t start, uint64_t length, uint64_t align)
   return address_aligned (start + length, align) && length < align;
 }
 
-static bool
+bool
 checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret,
                      struct where *where, const char *what)
 {
@@ -172,13 +172,13 @@ read_ctx_read_form (struct read_ctx *ctx, struct cu *cu, uint8_t form,
   return false;
 }
 
-static bool
+bool
 attrib_form_valid (uint64_t form)
 {
   return form > 0 && form <= DW_FORM_ref_sig8;
 }
 
-static int
+int
 check_sibling_form (uint64_t form)
 {
   switch (form)
@@ -201,32 +201,7 @@ check_sibling_form (uint64_t form)
     };
 }
 
-/* Check that given form may in fact be valid in some CU.  */
-static bool
-check_abbrev_location_form (uint64_t form)
-{
-  switch (form)
-    {
-    case DW_FORM_indirect:
-
-      /* loclistptr */
-    case DW_FORM_data4:
-    case DW_FORM_data8:
-    case DW_FORM_sec_offset: // DWARF 4
-
-      /* block */
-    case DW_FORM_block1:
-    case DW_FORM_block2:
-    case DW_FORM_block4:
-    case DW_FORM_block:
-      return true;
-
-    default:
-      return false;
-    };
-}
-
-static bool
+bool
 is_location_attrib (uint64_t name)
 {
   switch (name)
@@ -241,353 +216,6 @@ is_location_attrib (uint64_t name)
     }
 }
 
-struct abbrev_table *
-abbrev_table_load (struct read_ctx *ctx)
-{
-  struct abbrev_table *section_chain = NULL;
-  struct abbrev_table *section = NULL;
-  uint64_t first_attr_off = 0;
-  struct where where = WHERE (sec_abbrev, NULL);
-  where.addr1 = 0;
-
-  while (true)
-    {
-      inline bool check_no_abbreviations ()
-      {
-       bool ret = section_chain == NULL;
-       if (ret)
-         wr_error (&WHERE (sec_abbrev, NULL), ": no abbreviations.\n");
-       return ret;
-      }
-
-      /* If we get EOF at this point, either the CU was improperly
-        terminated, or there were no data to begin with.  */
-      if (read_ctx_eof (ctx))
-       {
-         if (!check_no_abbreviations ())
-           wr_error (&where, ": missing zero to mark end-of-table.\n");
-         break;
-       }
-
-      uint64_t abbr_off;
-      uint64_t abbr_code;
-      {
-       uint64_t prev_abbr_off = (uint64_t)-1;
-       uint64_t prev_abbr_code = (uint64_t)-1;
-       uint64_t zero_seq_off = (uint64_t)-1;
-
-       do
-         {
-           abbr_off = read_ctx_get_offset (ctx);
-           where_reset_2 (&where, abbr_off);
-
-           /* Abbreviation code.  */
-           if (!checked_read_uleb128 (ctx, &abbr_code, &where, "abbrev code"))
-             goto free_and_out;
-
-           /* Note: we generally can't tell the difference between
-              empty table and (excessive) padding.  But NUL byte(s)
-              at the very beginning of section are almost certainly
-              the first case.  */
-           if (zero_seq_off == (uint64_t)-1
-               && abbr_code == 0
-               && (prev_abbr_code == 0
-                   || section_chain == NULL))
-             zero_seq_off = abbr_off;
-
-           if (abbr_code != 0)
-             break;
-           else
-             section = NULL;
-
-           prev_abbr_code = abbr_code;
-           prev_abbr_off = abbr_off;
-         }
-       while (!read_ctx_eof (ctx)
-              /* On EOF, shift the offset so that beyond-EOF
-                 end-position is printed for padding warning.
-                 Necessary as our end position is exclusive.  */
-              || ((abbr_off += 1), false));
-
-       if (zero_seq_off != (uint64_t)-1)
-         wr_message_padding_0 (mc_abbrevs | mc_header,
-                               &WHERE (where.section, NULL),
-                               zero_seq_off, abbr_off);
-      }
-
-      if (read_ctx_eof (ctx))
-       {
-         /* It still may have been empty.  */
-         check_no_abbreviations ();
-         break;
-       }
-
-      /* OK, we got some genuine abbreviation.  See if we need to
-        allocate a new section.  */
-      if (section == NULL)
-       {
-         section = xcalloc (1, sizeof (*section));
-         section->offset = abbr_off;
-         section->next = section_chain;
-         section_chain = section;
-
-         where_reset_1 (&where, abbr_off);
-         where_reset_2 (&where, abbr_off);
-       }
-
-      struct abbrev *original = abbrev_table_find_abbrev (section, abbr_code);
-      if (unlikely (original != NULL))
-       {
-         char *site1 = strdup (where_fmt (&original->where, NULL));
-         wr_error (&where, ": duplicate abbrev code %" PRId64
-                   "; already defined at %s.\n", abbr_code, site1);
-         free (site1);
-       }
-
-      struct abbrev fake;
-      struct abbrev *cur;
-      /* Don't actually save this abbrev if it's duplicate.  */
-      if (likely (original == NULL))
-       {
-         REALLOC (section, abbr);
-         cur = section->abbr + section->size++;
-       }
-      else
-       cur = &fake;
-      WIPE (*cur);
-
-      cur->code = abbr_code;
-      cur->where = where;
-
-      /* Abbreviation tag.  */
-      uint64_t abbr_tag;
-      if (!checked_read_uleb128 (ctx, &abbr_tag, &where, "abbrev tag"))
-       goto free_and_out;
-
-      if (abbr_tag > DW_TAG_hi_user)
-       {
-         wr_error (&where, ": invalid abbrev tag 0x%" PRIx64 ".\n", abbr_tag);
-         goto free_and_out;
-       }
-      cur->tag = (typeof (cur->tag))abbr_tag;
-
-      /* Abbreviation has_children.  */
-      uint8_t has_children;
-      if (!read_ctx_read_ubyte (ctx, &has_children))
-       {
-         wr_error (&where, ": can't read abbrev has_children.\n");
-         goto free_and_out;
-       }
-
-      if (has_children != DW_CHILDREN_no
-         && has_children != DW_CHILDREN_yes)
-       {
-         wr_error (&where,
-                   ": invalid has_children value 0x%x.\n", cur->has_children);
-         goto free_and_out;
-       }
-      cur->has_children = has_children == DW_CHILDREN_yes;
-
-      bool null_attrib;
-      uint64_t sibling_attr = 0;
-      bool low_pc = false;
-      bool high_pc = false;
-      bool ranges = false;
-      do
-       {
-         uint64_t attr_off = read_ctx_get_offset (ctx);
-         uint64_t attrib_name, attrib_form;
-         if (first_attr_off == 0)
-           first_attr_off = attr_off;
-         /* Shift to match elfutils reporting.  */
-         where_reset_3 (&where, attr_off - first_attr_off);
-
-         /* Load attribute name and form.  */
-         if (!checked_read_uleb128 (ctx, &attrib_name, &where,
-                                    "attribute name"))
-           goto free_and_out;
-
-         if (!checked_read_uleb128 (ctx, &attrib_form, &where,
-                                    "attribute form"))
-           goto free_and_out;
-
-         null_attrib = attrib_name == 0 && attrib_form == 0;
-
-         /* Now if both are zero, this was the last attribute.  */
-         if (!null_attrib)
-           {
-             /* Otherwise validate name and form.  */
-             if (attrib_name > DW_AT_hi_user)
-               {
-                 wr_error (&where,
-                           ": invalid name 0x%" PRIx64 ".\n", attrib_name);
-                 goto free_and_out;
-               }
-
-             if (!attrib_form_valid (attrib_form))
-               {
-                 wr_error (&where,
-                           ": invalid form 0x%" PRIx64 ".\n", attrib_form);
-                 goto free_and_out;
-               }
-           }
-
-         REALLOC (cur, attribs);
-
-         struct abbrev_attrib *acur = cur->attribs + cur->size++;
-         WIPE (*acur);
-
-         /* We do structural checking of sibling attribute, so make
-            sure our assumptions in actual DIE-loading code are
-            right.  We expect at most one DW_AT_sibling attribute,
-            with form from reference class, but only CU-local, not
-            DW_FORM_ref_addr.  */
-         if (attrib_name == DW_AT_sibling)
-           {
-             if (sibling_attr != 0)
-               wr_error (&where,
-                         ": Another DW_AT_sibling attribute in one abbreviation. "
-                         "(First was 0x%" PRIx64 ".)\n", sibling_attr);
-             else
-               {
-                 assert (attr_off > 0);
-                 sibling_attr = attr_off;
-
-                 if (!cur->has_children)
-                   wr_message (mc_die_rel | mc_acc_bloat | mc_impact_1,
-                               &where,
-                               ": Excessive DW_AT_sibling attribute at childless abbrev.\n");
-               }
-
-             switch (check_sibling_form (attrib_form))
-               {
-               case -1:
-                 wr_message (mc_die_rel | mc_impact_2, &where,
-                             ": DW_AT_sibling attribute with form DW_FORM_ref_addr.\n");
-                 break;
-
-               case -2:
-                 wr_error (&where,
-                           ": DW_AT_sibling attribute with non-reference form \"%s\".\n",
-                           dwarf_form_string (attrib_form));
-               };
-           }
-         /* Similar for DW_AT_location and friends.  */
-         else if (is_location_attrib (attrib_name))
-           {
-             if (!check_abbrev_location_form (attrib_form))
-               wr_error (&where,
-                         ": location attribute %s with invalid form \"%s\".\n",
-                         dwarf_attr_string (attrib_name),
-                         dwarf_form_string (attrib_form));
-           }
-         /* Similar for DW_AT_ranges.  */
-         else if (attrib_name == DW_AT_ranges
-                  || attrib_name == DW_AT_stmt_list)
-           {
-             if (attrib_form != DW_FORM_data4
-                 && attrib_form != DW_FORM_data8
-                 && attrib_form != DW_FORM_sec_offset
-                 && attrib_form != DW_FORM_indirect)
-               wr_error (&where,
-                         ": %s with invalid form \"%s\".\n",
-                         dwarf_attr_string (attrib_name),
-                         dwarf_form_string (attrib_form));
-             if (attrib_name == DW_AT_ranges)
-               ranges = true;
-           }
-         /* Similar for DW_AT_{low,high}_pc, plus also make sure we
-            don't see high_pc without low_pc.  */
-         else if (attrib_name == DW_AT_low_pc
-                  || attrib_name == DW_AT_high_pc)
-           {
-             if (attrib_form != DW_FORM_addr
-                 && attrib_form != DW_FORM_ref_addr)
-               wr_error (&where,
-                         ": %s with invalid form \"%s\".\n",
-                         dwarf_attr_string (attrib_name),
-                         dwarf_form_string (attrib_form));
-
-             if (attrib_name == DW_AT_low_pc)
-               low_pc = true;
-             else if (attrib_name == DW_AT_high_pc)
-               high_pc = true;
-           }
-
-         acur->name = attrib_name;
-         acur->form = attrib_form;
-         acur->where = where;
-       }
-      while (!null_attrib);
-
-      where_reset_2 (&where, where.addr2); // drop addr 3
-      if (high_pc && !low_pc)
-       wr_error (&where,
-                 ": the abbrev has DW_AT_high_pc without also having DW_AT_low_pc.\n");
-      else if (high_pc && ranges)
-       wr_error (&where,
-                 ": the abbrev has DW_AT_high_pc & DW_AT_low_pc, but also has DW_AT_ranges.\n");
-    }
-
-  for (section = section_chain; section != NULL; section = section->next)
-    {
-      int cmp_abbrs (const void *a, const void *b)
-      {
-       struct abbrev *aa = (struct abbrev *)a;
-       struct abbrev *bb = (struct abbrev *)b;
-       return aa->code - bb->code;
-      }
-
-      /* The array is most likely already sorted in the file, but just
-        to be sure...  */
-      qsort (section->abbr, section->size, sizeof (*section->abbr), cmp_abbrs);
-    }
-
-  return section_chain;
-
- free_and_out:
-  abbrev_table_free (section_chain);
-  return NULL;
-}
-
-void
-abbrev_table_free (struct abbrev_table *abbr)
-{
-  for (struct abbrev_table *it = abbr; it != NULL; )
-    {
-      for (size_t i = 0; i < it->size; ++i)
-       free (it->abbr[i].attribs);
-      free (it->abbr);
-
-      struct abbrev_table *temp = it;
-      it = it->next;
-      free (temp);
-    }
-}
-
-struct abbrev *
-abbrev_table_find_abbrev (struct abbrev_table *abbrevs, uint64_t abbrev_code)
-{
-  size_t a = 0;
-  size_t b = abbrevs->size;
-  struct abbrev *ab = NULL;
-
-  while (a < b)
-    {
-      size_t i = (a + b) / 2;
-      ab = abbrevs->abbr + i;
-
-      if (ab->code > abbrev_code)
-       b = i;
-      else if (ab->code < abbrev_code)
-       a = i + 1;
-      else
-       return ab;
-    }
-
-  return NULL;
-}
-
 bool
 found_hole (uint64_t start, uint64_t length, void *data)
 {
index 9a651c339ef90757be4c167c85721b845da92399..a116c0771353717322d40756efab46a0f6665fac 100644 (file)
@@ -102,8 +102,20 @@ extern "C"
   extern bool check_expected_trees (struct hl_ctx *hlctx);
   extern bool elf_file_init (struct elf_file *file, Elf *elf);
 
+  struct abbrev_table
+  {
+    struct abbrev_table *next;
+    struct abbrev *abbr;
+    uint64_t offset;
+    size_t size;
+    size_t alloc;
+    bool used;         /* There are CUs using this table.  */
+    bool skip_check;   /* There were errors during loading one of the
+                          CUs that use this table.  Check for unused
+                          abbrevs should be skipped.  */
+  };
+
   // xxx some of that will go away
-  extern struct abbrev_table * abbrev_table_load (struct read_ctx *ctx);
   extern void abbrev_table_free (struct abbrev_table *abbr);
   extern struct abbrev *abbrev_table_find_abbrev (struct abbrev_table *abbrevs,
                                                  uint64_t abbrev_code);
@@ -163,6 +175,9 @@ extern "C"
                                     struct addr_record *line_tables);
   extern void cu_free (struct cu *cu_chain);
 
+  extern bool attrib_form_valid (uint64_t form);
+  extern int check_sibling_form (uint64_t form);
+  extern bool is_location_attrib (uint64_t name);
 
   struct hole_info
   {
@@ -186,7 +201,15 @@ extern "C"
      DATA->info.data has to be NULL, it is used by the callback.  */
   bool coverage_map_found_hole (uint64_t begin, uint64_t end,
                                struct section_coverage *sco, void *data);
+  bool checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret,
+                            struct where *where, const char *what);
 
+  struct abbrev_attrib
+  {
+    struct where where;
+    uint16_t name;
+    uint8_t form;
+  };
 
   struct abbrev
   {
@@ -194,12 +217,7 @@ extern "C"
     struct where where;
 
     /* Attributes.  */
-    struct abbrev_attrib
-    {
-      struct where where;
-      uint16_t name;
-      uint8_t form;
-    } *attribs;
+    struct abbrev_attrib *attribs;
     size_t size;
     size_t alloc;
 
@@ -213,19 +231,6 @@ extern "C"
     bool used;
   };
 
-  struct abbrev_table
-  {
-    struct abbrev_table *next;
-    struct abbrev *abbr;
-    uint64_t offset;
-    size_t size;
-    size_t alloc;
-    bool used;         /* There are CUs using this table.  */
-    bool skip_check;   /* There were errors during loading one of the
-                          CUs that use this table.  Check for unused
-                          abbrevs should be skipped.  */
-  };
-
   struct cu
   {
     struct cu *next;