]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
dwarflint: Move read_die_chain over to check_debug_info.cc
authorPetr Machata <pmachata@redhat.com>
Mon, 23 Nov 2009 17:22:56 +0000 (18:22 +0100)
committerPetr Machata <pmachata@redhat.com>
Wed, 18 Aug 2010 12:55:14 +0000 (14:55 +0200)
src/dwarflint/check_debug_info.cc
src/dwarflint/low.c
src/dwarflint/low.h

index cf8c1b7fb464456a762f5c3c33fe159a6548104a..25ae93a2fb4d8b23e3bb4a869d44eddc5090dfe6 100644 (file)
@@ -34,6 +34,8 @@
 #endif
 
 #include <cassert>
+#include <sstream>
+#include "../libdw/dwarf.h"
 
 #include "messages.h"
 #include "low.h"
@@ -224,6 +226,726 @@ namespace
     return ret;
   }
 
+  section_id
+  reloc_target (uint8_t form, struct abbrev_attrib *at)
+  {
+    switch (form)
+      {
+      case DW_FORM_strp:
+       return sec_str;
+
+      case DW_FORM_addr:
+
+       switch (at->name)
+         {
+         case DW_AT_low_pc:
+         case DW_AT_high_pc:
+         case DW_AT_entry_pc:
+           return rel_exec;
+
+         case DW_AT_const_value:
+           /* Appears in some kernel modules.  It's not allowed by the
+              standard, but leave that for high-level checks.  */
+           return rel_address;
+         };
+
+       break;
+
+      case DW_FORM_ref_addr:
+       return sec_info;
+
+      case DW_FORM_data1:
+      case DW_FORM_data2:
+       /* While these are technically legal, they are never used in
+          DWARF sections.  So better mark them as illegal, and have
+          dwarflint flag them.  */
+       return sec_invalid;
+
+      case DW_FORM_data4:
+      case DW_FORM_data8:
+
+       switch (at->name)
+         {
+         case DW_AT_stmt_list:
+           return sec_line;
+
+         case DW_AT_location:
+         case DW_AT_string_length:
+         case DW_AT_return_addr:
+         case DW_AT_data_member_location:
+         case DW_AT_frame_base:
+         case DW_AT_segment:
+         case DW_AT_static_link:
+         case DW_AT_use_location:
+         case DW_AT_vtable_elem_location:
+           return sec_loc;
+
+         case DW_AT_mac_info:
+           return sec_mac;
+
+         case DW_AT_ranges:
+           return sec_ranges;
+         }
+
+       break;
+
+      case DW_FORM_string:
+      case DW_FORM_ref1:
+      case DW_FORM_ref2:
+      case DW_FORM_ref4:
+       /* Shouldn't be relocated.  */
+       return sec_invalid;
+
+      case DW_FORM_sdata:
+      case DW_FORM_udata:
+      case DW_FORM_flag:
+      case DW_FORM_flag_present:
+      case DW_FORM_ref_udata:
+       assert (!"Can't be relocated!");
+
+      case DW_FORM_block1:
+      case DW_FORM_block2:
+      case DW_FORM_block4:
+      case DW_FORM_block:
+       assert (!"Should be handled specially!");
+      };
+
+    std::cout << "XXX don't know how to handle form=" << pri::form (form)
+             << ", at=" << pri::attr (at->name) << std::endl;
+
+    return rel_value;
+  }
+
+  struct value_check_cb_ctx
+  {
+    struct read_ctx *const ctx;
+    struct where *const where;
+    struct cu *const cu;
+    struct ref_record *local_die_refs;
+    Elf_Data *strings;
+    struct coverage *strings_coverage;
+    struct cu_coverage *cu_coverage;
+  };
+
+  typedef void (*value_check_cb_t) (uint64_t addr,
+                                   struct value_check_cb_ctx const *ctx);
+
+  /* Callback for local DIE references.  */
+  void
+  check_die_ref_local (uint64_t addr, struct value_check_cb_ctx const *ctx)
+  {
+    assert (ctx->ctx->end > ctx->ctx->begin);
+    if (addr > (uint64_t)(ctx->ctx->end - ctx->ctx->begin))
+      {
+       wr_error (ctx->where,
+                 ": invalid reference outside the CU: 0x%" PRIx64 ".\n",
+                 addr);
+       return;
+      }
+
+    if (ctx->local_die_refs != NULL)
+      /* Address holds a CU-local reference, so add CU offset
+        to turn it into section offset.  */
+      ref_record_add (ctx->local_die_refs,
+                     addr + ctx->cu->head->offset, ctx->where);
+  }
+
+  /* Callback for global DIE references.  */
+  void
+  check_die_ref_global (uint64_t addr, struct value_check_cb_ctx const *ctx)
+  {
+    ref_record_add (&ctx->cu->die_refs, addr, ctx->where);
+  }
+
+  /* Callback for strp values.  */
+  void
+  check_strp (uint64_t addr, struct value_check_cb_ctx const *ctx)
+  {
+    if (ctx->strings == NULL)
+      wr_error (ctx->where, ": strp attribute, but no .debug_str data.\n");
+    else if (addr >= ctx->strings->d_size)
+      wr_error (ctx->where,
+               ": Invalid offset outside .debug_str: 0x%" PRIx64 ".\n",
+               addr);
+    else
+      {
+       /* Record used part of .debug_str.  */
+       const char *buf = static_cast <const char *> (ctx->strings->d_buf);
+       const char *startp = buf + addr;
+       const char *data_end = buf + ctx->strings->d_size;
+       const char *strp = startp;
+       while (strp < data_end && *strp != 0)
+         ++strp;
+       if (strp == data_end)
+         wr_error (ctx->where,
+                   ": String at .debug_str: 0x%" PRIx64
+                   " is not zero-terminated.\n", addr);
+
+       if (ctx->strings_coverage != NULL)
+         coverage_add (ctx->strings_coverage, addr, strp - startp + 1);
+      }
+  }
+
+  /* Callback for rangeptr values.  */
+  void
+  check_rangeptr (uint64_t value, struct value_check_cb_ctx const *ctx)
+  {
+    if ((value % ctx->cu->head->address_size) != 0)
+      wr_message (mc_ranges | mc_impact_2, ctx->where,
+                 ": rangeptr value %#" PRIx64
+                 " not aligned to CU address size.\n", value);
+    ctx->cu_coverage->need_ranges = true;
+    ref_record_add (&ctx->cu->range_refs, value, ctx->where);
+  }
+
+  /* Callback for lineptr values.  */
+  void
+  check_lineptr (uint64_t value, struct value_check_cb_ctx const *ctx)
+  {
+    ref_record_add (&ctx->cu->line_refs, value, ctx->where);
+  }
+
+  /* Callback for locptr values.  */
+  void
+  check_locptr (uint64_t value, struct value_check_cb_ctx const *ctx)
+  {
+    ref_record_add (&ctx->cu->loc_refs, value, ctx->where);
+  }
+
+  /*
+    Returns:
+    -1 in case of error
+    +0 in case of no error, but the chain only consisted of a
+       terminating zero die.
+    +1 in case some dies were actually loaded
+  */
+  int
+  read_die_chain (dwarf_version_h ver,
+                 struct elf_file *file,
+                 struct read_ctx *ctx,
+                 struct cu *cu,
+                 struct abbrev_table *abbrevs,
+                 Elf_Data *strings,
+                 struct ref_record *local_die_refs,
+                 struct coverage *strings_coverage,
+                 struct relocation_data *reloc,
+                 struct cu_coverage *cu_coverage)
+  {
+    bool got_die = false;
+    uint64_t sibling_addr = 0;
+    uint64_t die_off, prev_die_off = 0;
+    struct abbrev *abbrev = NULL;
+    struct abbrev *prev_abbrev = NULL;
+    struct where where = WHERE (sec_info, NULL);
+
+    struct value_check_cb_ctx cb_ctx = {
+      ctx, &where, cu,
+      local_die_refs,
+      strings, strings_coverage,
+      cu_coverage
+    };
+
+    while (!read_ctx_eof (ctx))
+      {
+       where = cu->head->where;
+       die_off = read_ctx_get_offset (ctx);
+       /* Shift reported DIE offset by CU offset, to match the way
+          readelf reports DIEs.  */
+       where_reset_2 (&where, die_off + cu->head->offset);
+
+       uint64_t abbr_code;
+
+       if (!checked_read_uleb128 (ctx, &abbr_code, &where, "abbrev code"))
+         return -1;
+
+#define DEF_PREV_WHERE                                                 \
+       struct where prev_where = where;                                \
+       where_reset_2 (&prev_where, prev_die_off + cu->head->offset)
+
+       /* Check sibling value advertised last time through the loop.  */
+       if (sibling_addr != 0)
+         {
+           if (abbr_code == 0)
+             wr_error (&where,
+                       ": is the last sibling in chain, "
+                       "but has a DW_AT_sibling attribute.\n");
+           else if (sibling_addr != die_off)
+             {
+               DEF_PREV_WHERE;
+               wr_error (&prev_where,
+                         ": This DIE claims that its sibling is 0x%"
+                         PRIx64 ", but it's actually 0x%" PRIx64 ".\n",
+                         sibling_addr, die_off);
+             }
+           sibling_addr = 0;
+         }
+       else if (abbr_code != 0
+                && abbrev != NULL && abbrev->has_children)
+         {
+           /* Even if it has children, the DIE can't have a sibling
+              attribute if it's the last DIE in chain.  That's the
+              reason we can't simply check this when loading
+              abbrevs.  */
+           DEF_PREV_WHERE;
+           wr_message (mc_die_rel | mc_acc_suboptimal | mc_impact_4, &prev_where,
+                       ": This DIE had children, but no DW_AT_sibling attribute.\n");
+         }
+#undef DEF_PREV_WHERE
+
+       prev_die_off = die_off;
+
+       /* The section ended.  */
+       if (abbr_code == 0)
+         break;
+       if (read_ctx_eof (ctx))
+         {
+           wr_error (&where, ": DIE chain not terminated with DIE with zero abbrev code.\n");
+           break;
+         }
+
+       prev_die_off = die_off;
+       got_die = true;
+       if (dump_die_offsets)
+         fprintf (stderr, "%s: abbrev %" PRId64 "\n",
+                  where_fmt (&where, NULL), abbr_code);
+
+       /* Find the abbrev matching the code.  */
+       prev_abbrev = abbrev;
+       abbrev = abbrev_table_find_abbrev (abbrevs, abbr_code);
+       if (abbrev == NULL)
+         {
+           wr_error (&where,
+                     ": abbrev section at 0x%" PRIx64
+                     " doesn't contain code %" PRIu64 ".\n",
+                     abbrevs->offset, abbr_code);
+           return -1;
+         }
+       abbrev->used = true;
+
+       addr_record_add (&cu->die_addrs, cu->head->offset + die_off);
+
+       uint64_t low_pc = (uint64_t)-1, high_pc = (uint64_t)-1;
+       bool low_pc_relocated = false, high_pc_relocated = false;
+       GElf_Sym low_pc_symbol_mem, *low_pc_symbol = &low_pc_symbol_mem;
+       GElf_Sym high_pc_symbol_mem, *high_pc_symbol = &high_pc_symbol_mem;
+
+       /* Attribute values.  */
+       for (struct abbrev_attrib *it = abbrev->attribs;
+            it->name != 0; ++it)
+         {
+           where.ref = &it->where;
+
+           uint8_t form = it->form;
+           bool indirect = form == DW_FORM_indirect;
+           if (indirect)
+             {
+               uint64_t value;
+               if (!checked_read_uleb128 (ctx, &value, &where,
+                                          "indirect attribute form"))
+                 return -1;
+
+               if (!dwver_form_valid (ver, form))
+                 {
+                   wr_error (&where,
+                             ": invalid indirect form 0x%" PRIx64 ".\n", value);
+                   return -1;
+                 }
+               form = value;
+
+               if (it->name == DW_AT_sibling)
+                 switch (check_sibling_form (ver, form))
+                   {
+                   case -1:
+                     wr_message (mc_die_rel | mc_impact_2, &where,
+                                 ": DW_AT_sibling attribute with (indirect) form DW_FORM_ref_addr.\n");
+                     break;
+
+                   case -2:
+                     wr_error (where)
+                       << "DW_AT_sibling attribute with non-reference (indirect) form \""
+                       << pri::form (value) << "\"." << std::endl;
+                   };
+             }
+
+           value_check_cb_t value_check_cb = NULL;
+
+           /* For checking lineptr, rangeptr, locptr.  */
+           bool check_someptr = false;
+           enum message_category extra_mc = mc_none;
+
+           uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->head->offset;
+           bool type_is_rel = file->ehdr.e_type == ET_REL;
+
+           /* Attribute value.  */
+           uint64_t value = 0;
+
+           /* Whether the value should be relocated first.  Note that
+              relocations are really required only in REL files, so
+              missing relocations are not warned on even with
+              rel_require, unless type_is_rel.  */
+           enum
+           {
+             rel_no,           // don't allow a relocation
+             rel_require,      // require a relocation
+             rel_nonzero,      // require a relocation if value != 0
+           } relocate = rel_no;
+           size_t width = 0;
+
+           /* Point to variable that you want to copy relocated value
+              to.  */
+           uint64_t *valuep = NULL;
+
+           /* Point to variable that you want set to `true' in case the
+              value was relocated.  */
+           bool *relocatedp = NULL;
+
+           /* Point to variable that you want set to symbol that the
+              relocation was made against.  */
+           GElf_Sym **symbolp = NULL;
+
+           /* Setup locptr checking.  */
+           if (is_location_attrib (it->name))
+             {
+               switch (form)
+                 {
+                 case DW_FORM_data8:
+                   if (cu->head->offset_size == 4)
+                     wr_error (where)
+                       << "location attribute with form \""
+                       << pri::form (form) << "\" in 32-bit CU." << std::endl;
+                   /* fall-through */
+
+                 case DW_FORM_data4:
+                 case DW_FORM_sec_offset:
+                   value_check_cb = check_locptr;
+                   extra_mc = mc_loc;
+                   check_someptr = true;
+                   break;
+
+                 case DW_FORM_block1:
+                 case DW_FORM_block2:
+                 case DW_FORM_block4:
+                 case DW_FORM_block:
+                   break;
+
+                 default:
+                   /* Only print error if it's indirect.  Otherwise we
+                      gave diagnostic during abbrev loading.  */
+                   if (indirect)
+                     wr_error (where)
+                       << "location attribute with invalid (indirect) form \""
+                       << pri::form (form) << "\"." << std::endl;
+                 };
+             }
+           /* Setup rangeptr or lineptr checking.  */
+           else if (it->name == DW_AT_ranges
+                    || it->name == DW_AT_stmt_list)
+             switch (form)
+               {
+               case DW_FORM_data8:
+                 if (cu->head->offset_size == 4)
+                   wr_error (where)
+                     << pri::attr (it->name)
+                     << " with form DW_FORM_data8 in 32-bit CU." << std::endl;
+                 /* fall-through */
+
+               case DW_FORM_data4:
+               case DW_FORM_sec_offset:
+                 check_someptr = true;
+                 if (it->name == DW_AT_ranges)
+                   {
+                     value_check_cb = check_rangeptr;
+                     extra_mc = mc_ranges;
+                   }
+                 else
+                   {
+                     assert (it->name == DW_AT_stmt_list);
+                     value_check_cb = check_lineptr;
+                     extra_mc = mc_line;
+                   }
+                 break;
+
+               default:
+                 /* Only print error if it's indirect.  Otherwise we
+                    gave diagnostic during abbrev loading.  */
+                 if (indirect)
+                   wr_error (where)
+                     << pri::attr (it->name)
+                     << " with invalid (indirect) form \"" << pri::form (form)
+                     << "\"." << std::endl;
+               }
+           /* Setup low_pc and high_pc checking.  */
+           else if (it->name == DW_AT_low_pc)
+             {
+               relocatedp = &low_pc_relocated;
+               symbolp = &low_pc_symbol;
+               valuep = &low_pc;
+             }
+           else if (it->name == DW_AT_high_pc)
+             {
+               relocatedp = &high_pc_relocated;
+               symbolp = &high_pc_symbol;
+               valuep = &high_pc;
+             }
+
+           /* Load attribute value and setup per-form checking.  */
+           switch (form)
+             {
+             case DW_FORM_strp:
+               value_check_cb = check_strp;
+             case DW_FORM_sec_offset:
+               if (!read_ctx_read_offset (ctx, cu->head->offset_size == 8, &value))
+                 {
+                 cant_read:
+                   wr_error (where)
+                     << "can't read value of attribute "
+                     << pri::attr (it->name) << '.' << std::endl;
+                   return -1;
+                 }
+
+               relocate = rel_require;
+               width = cu->head->offset_size;
+               break;
+
+             case DW_FORM_string:
+               if (!read_ctx_read_str (ctx))
+                 goto cant_read;
+               break;
+
+             case DW_FORM_ref_addr:
+               value_check_cb = check_die_ref_global;
+               width = cu->head->offset_size;
+
+               if (cu->head->version == 2)
+               case DW_FORM_addr:
+                 width = cu->head->address_size;
+
+               if (!read_ctx_read_offset (ctx, width == 8, &value))
+                 goto cant_read;
+
+               /* In non-rel files, neither addr, nor ref_addr /need/
+                  a relocation.  */
+               relocate = rel_nonzero;
+               break;
+
+             case DW_FORM_ref_udata:
+               value_check_cb = check_die_ref_local;
+             case DW_FORM_udata:
+               if (!checked_read_uleb128 (ctx, &value, &where,
+                                          "attribute value"))
+                 return -1;
+               break;
+
+             case DW_FORM_flag_present:
+               value = 1;
+               break;
+
+             case DW_FORM_ref1:
+               value_check_cb = check_die_ref_local;
+             case DW_FORM_flag:
+             case DW_FORM_data1:
+               if (!read_ctx_read_var (ctx, 1, &value))
+                 goto cant_read;
+               break;
+
+             case DW_FORM_ref2:
+               value_check_cb = check_die_ref_local;
+             case DW_FORM_data2:
+               if (!read_ctx_read_var (ctx, 2, &value))
+                 goto cant_read;
+               break;
+
+             case DW_FORM_data4:
+               if (check_someptr)
+                 {
+                   relocate = rel_require;
+                   width = 4;
+                 }
+               if (false)
+               case DW_FORM_ref4:
+                 value_check_cb = check_die_ref_local;
+               if (!read_ctx_read_var (ctx, 4, &value))
+                 goto cant_read;
+               break;
+
+             case DW_FORM_data8:
+               if (check_someptr)
+                 {
+                   relocate = rel_require;
+                   width = 8;
+                 }
+               if (false)
+               case DW_FORM_ref8:
+                 value_check_cb = check_die_ref_local;
+               if (!read_ctx_read_8ubyte (ctx, &value))
+                 goto cant_read;
+               break;
+
+             case DW_FORM_sdata:
+               {
+                 int64_t value64;
+                 if (!checked_read_sleb128 (ctx, &value64, &where,
+                                            "attribute value"))
+                   return -1;
+                 value = (uint64_t) value64;
+                 break;
+               }
+
+             case DW_FORM_block:
+               {
+                 uint64_t length;
+
+                 if (false)
+                 case DW_FORM_block1:
+                   width = 1;
+
+                 if (false)
+                 case DW_FORM_block2:
+                   width = 2;
+
+                 if (false)
+                 case DW_FORM_block4:
+                   width = 4;
+
+                 if (width == 0)
+                   {
+                     if (!checked_read_uleb128 (ctx, &length, &where,
+                                                "attribute value"))
+                       return -1;
+                   }
+                 else if (!read_ctx_read_var (ctx, width, &length))
+                   goto cant_read;
+
+                 if (is_location_attrib (it->name))
+                   {
+                     uint64_t expr_start
+                       = cu->head->offset + read_ctx_get_offset (ctx);
+                     if (!check_location_expression (file, ctx, cu, expr_start,
+                                                     reloc, length, &where))
+                       return -1;
+                   }
+                 else
+                   /* xxx really skip_mismatched?  We just don't know
+                      how to process these...  */
+                   relocation_skip (reloc,
+                                    read_ctx_get_offset (ctx) + length,
+                                    &where, skip_mismatched);
+
+                 if (!read_ctx_skip (ctx, length))
+                   goto cant_read;
+                 break;
+               }
+
+             case DW_FORM_indirect:
+               wr_error (&where, ": indirect form is again indirect.\n");
+               return -1;
+
+             default:
+               wr_error (&where,
+                         ": internal error: unhandled form 0x%x.\n", form);
+             }
+
+           /* Relocate the value if appropriate.  */
+           struct relocation *rel;
+           if ((rel = relocation_next (reloc, ctx_offset,
+                                       &where, skip_mismatched)))
+             {
+               if (relocate == rel_no)
+                 wr_message (where, cat (mc_impact_4, mc_die_other,
+                                         mc_reloc, extra_mc))
+                   << "unexpected relocation of " << pri::form (form) << '.'
+                   << std::endl;
+
+               relocate_one (file, reloc, rel, width, &value, &where,
+                             reloc_target (form, it), symbolp);
+
+               if (relocatedp != NULL)
+                 *relocatedp = true;
+             }
+           else
+             {
+               if (symbolp != NULL)
+                 WIPE (*symbolp);
+               if (type_is_rel
+                   && (relocate == rel_require
+                       || (relocate == rel_nonzero
+                           && value != 0)))
+                 {
+                   std::stringstream ss;
+                   ss << pri::form (form);
+                   wr_message (where, cat (mc_impact_2, mc_die_other,
+                                           mc_reloc, extra_mc))
+                     << pri::lacks_relocation (ss.str ()) << std::endl;
+                 }
+             }
+
+           /* Dispatch value checking.  */
+           if (it->name == DW_AT_sibling)
+             {
+               /* Full-blown DIE reference checking is too heavy-weight
+                  and not practical (error messages wise) for checking
+                  siblings.  */
+               assert (value_check_cb == check_die_ref_local
+                       || value_check_cb == check_die_ref_global);
+               valuep = &sibling_addr;
+             }
+           else if (value_check_cb != NULL)
+             value_check_cb (value, &cb_ctx);
+
+           /* Store the relocated value.  Note valuep may point to
+              low_pc or high_pc.  */
+           if (valuep != NULL)
+             *valuep = value;
+
+           /* Check PC coverage.  */
+           if (abbrev->tag == DW_TAG_compile_unit
+               || abbrev->tag == DW_TAG_partial_unit)
+             {
+               if (it->name == DW_AT_low_pc)
+                 cu->low_pc = value;
+
+               if (low_pc != (uint64_t)-1 && high_pc != (uint64_t)-1)
+                 coverage_add (&cu_coverage->cov, low_pc, high_pc - low_pc);
+             }
+         }
+       where.ref = NULL;
+
+       if (high_pc != (uint64_t)-1 && low_pc != (uint64_t)-1)
+         {
+           if (high_pc_relocated != low_pc_relocated)
+             wr_message (mc_die_other | mc_impact_2 | mc_reloc, &where,
+                         ": only one of DW_AT_low_pc and DW_AT_high_pc is relocated.\n");
+           else
+             check_range_relocations (mc_die_other, &where,
+                                      file,
+                                      low_pc_symbol, high_pc_symbol,
+                                      "DW_AT_low_pc and DW_AT_high_pc");
+         }
+
+       where.ref = &abbrev->where;
+
+       if (abbrev->has_children)
+         {
+           int st = read_die_chain (ver, file, ctx, cu, abbrevs, strings,
+                                    local_die_refs,
+                                    strings_coverage, reloc,
+                                    cu_coverage);
+           if (st == -1)
+             return -1;
+           else if (st == 0)
+             wr_message (mc_impact_3 | mc_acc_suboptimal | mc_die_rel,
+                         &where,
+                         ": abbrev has_children, but the chain was empty.\n");
+         }
+      }
+
+    if (sibling_addr != 0)
+      wr_error (&where,
+               ": this DIE should have had its sibling at 0x%"
+               PRIx64 ", but the DIE chain ended.\n", sibling_addr);
+
+    return got_die ? 1 : 0;
+  }
   bool
   check_cu_structural (struct elf_file *file,
                       struct read_ctx *ctx,
index 30caedfa991120907d095bc5179219cad6d1bf67..d3b682bbc0860f21d15b0e36368b0ed398662de4 100644 (file)
@@ -60,14 +60,6 @@ static const bool do_range_coverage = false;
 
 static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset);
 
-static bool check_location_expression (struct elf_file *file,
-                                      struct read_ctx *ctx,
-                                      struct cu *cu,
-                                      uint64_t init_off,
-                                      struct relocation_data *reloc,
-                                      size_t length,
-                                      struct where *wh);
-
 bool
 address_aligned (uint64_t addr, uint64_t align)
 {
@@ -97,7 +89,7 @@ checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret,
   return st >= 0;
 }
 
-static bool
+bool
 checked_read_sleb128 (struct read_ctx *ctx, int64_t *ret,
                      struct where *where, const char *what)
 {
@@ -559,96 +551,6 @@ check_zero_padding (struct read_ctx *ctx,
   return true;
 }
 
-static enum section_id
-reloc_target (uint8_t form, struct abbrev_attrib *at)
-{
-  switch (form)
-    {
-    case DW_FORM_strp:
-      return sec_str;
-
-    case DW_FORM_addr:
-
-      switch (at->name)
-       {
-       case DW_AT_low_pc:
-       case DW_AT_high_pc:
-       case DW_AT_entry_pc:
-         return rel_exec;
-
-       case DW_AT_const_value:
-         /* Appears in some kernel modules.  It's not allowed by the
-            standard, but leave that for high-level checks.  */
-         return rel_address;
-       };
-
-      break;
-
-    case DW_FORM_ref_addr:
-      return sec_info;
-
-    case DW_FORM_data1:
-    case DW_FORM_data2:
-      /* While these are technically legal, they are never used in
-        DWARF sections.  So better mark them as illegal, and have
-        dwarflint flag them.  */
-      return sec_invalid;
-
-    case DW_FORM_data4:
-    case DW_FORM_data8:
-
-      switch (at->name)
-       {
-       case DW_AT_stmt_list:
-         return sec_line;
-
-       case DW_AT_location:
-       case DW_AT_string_length:
-       case DW_AT_return_addr:
-       case DW_AT_data_member_location:
-       case DW_AT_frame_base:
-       case DW_AT_segment:
-       case DW_AT_static_link:
-       case DW_AT_use_location:
-       case DW_AT_vtable_elem_location:
-         return sec_loc;
-
-       case DW_AT_mac_info:
-         return sec_mac;
-
-       case DW_AT_ranges:
-         return sec_ranges;
-       }
-
-      break;
-
-    case DW_FORM_string:
-    case DW_FORM_ref1:
-    case DW_FORM_ref2:
-    case DW_FORM_ref4:
-      /* Shouldn't be relocated.  */
-      return sec_invalid;
-
-    case DW_FORM_sdata:
-    case DW_FORM_udata:
-    case DW_FORM_flag:
-    case DW_FORM_flag_present:
-    case DW_FORM_ref_udata:
-      assert (!"Can't be relocated!");
-
-    case DW_FORM_block1:
-    case DW_FORM_block2:
-    case DW_FORM_block4:
-    case DW_FORM_block:
-      assert (!"Should be handled specially!");
-    };
-
-  printf ("XXX don't know how to handle form=%s, at=%s\n",
-         dwarf_form_string (form), dwarf_attr_string (at->name));
-
-  return rel_value;
-}
-
 static enum section_id
 reloc_target_loc (uint8_t opcode)
 {
@@ -695,7 +597,7 @@ supported_version (unsigned version,
   return retval;
 }
 
-static void
+void
 check_range_relocations (enum message_category cat,
                         struct where *where,
                         struct elf_file *file,
@@ -713,630 +615,6 @@ check_range_relocations (enum message_category cat,
                file->sec[end_symbol->st_shndx].name);
 }
 
-struct value_check_cb_ctx
-{
-  struct read_ctx *const ctx;
-  struct where *const where;
-  struct cu *const cu;
-  struct ref_record *local_die_refs;
-  Elf_Data *strings;
-  struct coverage *strings_coverage;
-  struct cu_coverage *cu_coverage;
-};
-
-typedef void (*value_check_cb_t) (uint64_t addr,
-                                 struct value_check_cb_ctx const *ctx);
-
-/* Callback for local DIE references.  */
-static void
-check_die_ref_local (uint64_t addr, struct value_check_cb_ctx const *ctx)
-{
-  assert (ctx->ctx->end > ctx->ctx->begin);
-  if (addr > (uint64_t)(ctx->ctx->end - ctx->ctx->begin))
-    {
-      wr_error (ctx->where,
-               ": invalid reference outside the CU: 0x%" PRIx64 ".\n",
-               addr);
-      return;
-    }
-
-  if (ctx->local_die_refs != NULL)
-    /* Address holds a CU-local reference, so add CU offset
-       to turn it into section offset.  */
-    ref_record_add (ctx->local_die_refs,
-                   addr + ctx->cu->head->offset, ctx->where);
-}
-
-/* Callback for global DIE references.  */
-static void
-check_die_ref_global (uint64_t addr, struct value_check_cb_ctx const *ctx)
-{
-  ref_record_add (&ctx->cu->die_refs, addr, ctx->where);
-}
-
-/* Callback for strp values.  */
-static void
-check_strp (uint64_t addr, struct value_check_cb_ctx const *ctx)
-{
-  if (ctx->strings == NULL)
-    wr_error (ctx->where, ": strp attribute, but no .debug_str data.\n");
-  else if (addr >= ctx->strings->d_size)
-    wr_error (ctx->where,
-             ": Invalid offset outside .debug_str: 0x%" PRIx64 ".\n",
-             addr);
-  else
-    {
-      /* Record used part of .debug_str.  */
-      const char *startp = (const char *)ctx->strings->d_buf + addr;
-      const char *data_end = ctx->strings->d_buf + ctx->strings->d_size;
-      const char *strp = startp;
-      while (strp < data_end && *strp != 0)
-       ++strp;
-      if (strp == data_end)
-       wr_error (ctx->where,
-                 ": String at .debug_str: 0x%" PRIx64
-                 " is not zero-terminated.\n", addr);
-
-      if (ctx->strings_coverage != NULL)
-       coverage_add (ctx->strings_coverage, addr, strp - startp + 1);
-    }
-}
-
-/* Callback for rangeptr values.  */
-static void
-check_rangeptr (uint64_t value, struct value_check_cb_ctx const *ctx)
-{
-  if ((value % ctx->cu->head->address_size) != 0)
-    wr_message (mc_ranges | mc_impact_2, ctx->where,
-               ": rangeptr value %#" PRIx64
-               " not aligned to CU address size.\n", value);
-  ctx->cu_coverage->need_ranges = true;
-  ref_record_add (&ctx->cu->range_refs, value, ctx->where);
-}
-
-/* Callback for lineptr values.  */
-static void
-check_lineptr (uint64_t value, struct value_check_cb_ctx const *ctx)
-{
-  ref_record_add (&ctx->cu->line_refs, value, ctx->where);
-}
-
-/* Callback for locptr values.  */
-static void
-check_locptr (uint64_t value, struct value_check_cb_ctx const *ctx)
-{
-  ref_record_add (&ctx->cu->loc_refs, value, ctx->where);
-}
-
-/*
-  Returns:
-    -1 in case of error
-    +0 in case of no error, but the chain only consisted of a
-       terminating zero die.
-    +1 in case some dies were actually loaded
- */
-int
-read_die_chain (dwarf_version_h ver,
-               struct elf_file *file,
-               struct read_ctx *ctx,
-               struct cu *cu,
-               struct abbrev_table *abbrevs,
-               Elf_Data *strings,
-               struct ref_record *local_die_refs,
-               struct coverage *strings_coverage,
-               struct relocation_data *reloc,
-               struct cu_coverage *cu_coverage)
-{
-  bool got_die = false;
-  uint64_t sibling_addr = 0;
-  uint64_t die_off, prev_die_off = 0;
-  struct abbrev *abbrev = NULL;
-  struct abbrev *prev_abbrev = NULL;
-  struct where where = WHERE (sec_info, NULL);
-
-  struct value_check_cb_ctx cb_ctx = {
-    ctx, &where, cu,
-    local_die_refs,
-    strings, strings_coverage,
-    cu_coverage
-  };
-
-  while (!read_ctx_eof (ctx))
-    {
-      where = cu->head->where;
-      die_off = read_ctx_get_offset (ctx);
-      /* Shift reported DIE offset by CU offset, to match the way
-        readelf reports DIEs.  */
-      where_reset_2 (&where, die_off + cu->head->offset);
-
-      uint64_t abbr_code;
-
-      if (!checked_read_uleb128 (ctx, &abbr_code, &where, "abbrev code"))
-       return -1;
-
-#define DEF_PREV_WHERE                                                 \
-      struct where prev_where = where;                                 \
-      where_reset_2 (&prev_where, prev_die_off + cu->head->offset)
-
-      /* Check sibling value advertised last time through the loop.  */
-      if (sibling_addr != 0)
-       {
-         if (abbr_code == 0)
-           wr_error (&where,
-                     ": is the last sibling in chain, "
-                     "but has a DW_AT_sibling attribute.\n");
-         else if (sibling_addr != die_off)
-           {
-             DEF_PREV_WHERE;
-             wr_error (&prev_where,
-                       ": This DIE claims that its sibling is 0x%"
-                       PRIx64 ", but it's actually 0x%" PRIx64 ".\n",
-                       sibling_addr, die_off);
-           }
-         sibling_addr = 0;
-       }
-      else if (abbr_code != 0
-              && abbrev != NULL && abbrev->has_children)
-       {
-         /* Even if it has children, the DIE can't have a sibling
-            attribute if it's the last DIE in chain.  That's the
-            reason we can't simply check this when loading
-            abbrevs.  */
-         DEF_PREV_WHERE;
-         wr_message (mc_die_rel | mc_acc_suboptimal | mc_impact_4, &prev_where,
-                     ": This DIE had children, but no DW_AT_sibling attribute.\n");
-       }
-#undef DEF_PREV_WHERE
-
-      prev_die_off = die_off;
-
-      /* The section ended.  */
-      if (abbr_code == 0)
-       break;
-      if (read_ctx_eof (ctx))
-       {
-         wr_error (&where, ": DIE chain not terminated with DIE with zero abbrev code.\n");
-         break;
-       }
-
-      prev_die_off = die_off;
-      got_die = true;
-      if (dump_die_offsets)
-       fprintf (stderr, "%s: abbrev %" PRId64 "\n",
-                where_fmt (&where, NULL), abbr_code);
-
-      /* Find the abbrev matching the code.  */
-      prev_abbrev = abbrev;
-      abbrev = abbrev_table_find_abbrev (abbrevs, abbr_code);
-      if (abbrev == NULL)
-       {
-         wr_error (&where,
-                   ": abbrev section at 0x%" PRIx64
-                   " doesn't contain code %" PRIu64 ".\n",
-                   abbrevs->offset, abbr_code);
-         return -1;
-       }
-      abbrev->used = true;
-
-      addr_record_add (&cu->die_addrs, cu->head->offset + die_off);
-
-      uint64_t low_pc = (uint64_t)-1, high_pc = (uint64_t)-1;
-      bool low_pc_relocated = false, high_pc_relocated = false;
-      GElf_Sym low_pc_symbol_mem, *low_pc_symbol = &low_pc_symbol_mem;
-      GElf_Sym high_pc_symbol_mem, *high_pc_symbol = &high_pc_symbol_mem;
-
-      /* Attribute values.  */
-      for (struct abbrev_attrib *it = abbrev->attribs;
-          it->name != 0; ++it)
-       {
-         where.ref = &it->where;
-
-         uint8_t form = it->form;
-         bool indirect = form == DW_FORM_indirect;
-         if (indirect)
-           {
-             uint64_t value;
-             if (!checked_read_uleb128 (ctx, &value, &where,
-                                        "indirect attribute form"))
-               return -1;
-
-             if (!dwver_form_valid (ver, form))
-               {
-                 wr_error (&where,
-                           ": invalid indirect form 0x%" PRIx64 ".\n", value);
-                 return -1;
-               }
-             form = value;
-
-             if (it->name == DW_AT_sibling)
-               switch (check_sibling_form (ver, form))
-                 {
-                 case -1:
-                   wr_message (mc_die_rel | mc_impact_2, &where,
-                               ": DW_AT_sibling attribute with (indirect) form DW_FORM_ref_addr.\n");
-                   break;
-
-                 case -2:
-                   wr_error (&where,
-                             ": DW_AT_sibling attribute with non-reference (indirect) form \"%s\".\n",
-                             dwarf_form_string (value));
-                 };
-           }
-
-         value_check_cb_t value_check_cb = NULL;
-
-         /* For checking lineptr, rangeptr, locptr.  */
-         bool check_someptr = false;
-         enum message_category extra_mc = mc_none;
-
-         uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->head->offset;
-         bool type_is_rel = file->ehdr.e_type == ET_REL;
-
-         /* Attribute value.  */
-         uint64_t value = 0;
-
-         /* Whether the value should be relocated first.  Note that
-            relocations are really required only in REL files, so
-            missing relocations are not warned on even with
-            rel_require, unless type_is_rel.  */
-         enum
-         {
-           rel_no,             // don't allow a relocation
-           rel_require,        // require a relocation
-           rel_nonzero,        // require a relocation if value != 0
-         } relocate = rel_no;
-         size_t width = 0;
-
-         /* Point to variable that you want to copy relocated value
-            to.  */
-         uint64_t *valuep = NULL;
-
-         /* Point to variable that you want set to `true' in case the
-            value was relocated.  */
-         bool *relocatedp = NULL;
-
-         /* Point to variable that you want set to symbol that the
-            relocation was made against.  */
-         GElf_Sym **symbolp = NULL;
-
-         /* Setup locptr checking.  */
-         if (is_location_attrib (it->name))
-           {
-             switch (form)
-               {
-               case DW_FORM_data8:
-                 if (cu->head->offset_size == 4)
-                   wr_error (&where,
-                             ": location attribute with form \"%s\" in 32-bit CU.\n",
-                             dwarf_form_string (form));
-                 /* fall-through */
-
-               case DW_FORM_data4:
-               case DW_FORM_sec_offset:
-                 value_check_cb = check_locptr;
-                 extra_mc = mc_loc;
-                 check_someptr = true;
-                 break;
-
-               case DW_FORM_block1:
-               case DW_FORM_block2:
-               case DW_FORM_block4:
-               case DW_FORM_block:
-                 break;
-
-               default:
-                 /* Only print error if it's indirect.  Otherwise we
-                    gave diagnostic during abbrev loading.  */
-                 if (indirect)
-                   wr_error (&where,
-                             ": location attribute with invalid (indirect) form \"%s\".\n",
-                             dwarf_form_string (form));
-               };
-           }
-         /* Setup rangeptr or lineptr checking.  */
-         else if (it->name == DW_AT_ranges
-                  || it->name == DW_AT_stmt_list)
-           switch (form)
-             {
-             case DW_FORM_data8:
-               if (cu->head->offset_size == 4)
-                 wr_error (&where,
-                           ": %s with form DW_FORM_data8 in 32-bit CU.\n",
-                           dwarf_attr_string (it->name));
-               /* fall-through */
-
-             case DW_FORM_data4:
-             case DW_FORM_sec_offset:
-               check_someptr = true;
-               if (it->name == DW_AT_ranges)
-                 {
-                   value_check_cb = check_rangeptr;
-                   extra_mc = mc_ranges;
-                 }
-               else
-                 {
-                   assert (it->name == DW_AT_stmt_list);
-                   value_check_cb = check_lineptr;
-                   extra_mc = mc_line;
-                 }
-               break;
-
-             default:
-               /* Only print error if it's indirect.  Otherwise we
-                  gave diagnostic during abbrev loading.  */
-               if (indirect)
-                 wr_error (&where,
-                           ": %s with invalid (indirect) form \"%s\".\n",
-                           dwarf_attr_string (it->name),
-                           dwarf_form_string (form));
-             }
-         /* Setup low_pc and high_pc checking.  */
-         else if (it->name == DW_AT_low_pc)
-           {
-             relocatedp = &low_pc_relocated;
-             symbolp = &low_pc_symbol;
-             valuep = &low_pc;
-           }
-         else if (it->name == DW_AT_high_pc)
-           {
-             relocatedp = &high_pc_relocated;
-             symbolp = &high_pc_symbol;
-             valuep = &high_pc;
-           }
-
-         /* Load attribute value and setup per-form checking.  */
-         switch (form)
-           {
-           case DW_FORM_strp:
-             value_check_cb = check_strp;
-           case DW_FORM_sec_offset:
-             if (!read_ctx_read_offset (ctx, cu->head->offset_size == 8, &value))
-               {
-               cant_read:
-                 wr_error (&where, ": can't read value of attribute %s.\n",
-                           dwarf_attr_string (it->name));
-                 return -1;
-               }
-
-             relocate = rel_require;
-             width = cu->head->offset_size;
-             break;
-
-           case DW_FORM_string:
-             if (!read_ctx_read_str (ctx))
-               goto cant_read;
-             break;
-
-           case DW_FORM_ref_addr:
-             value_check_cb = check_die_ref_global;
-             width = cu->head->offset_size;
-
-             if (cu->head->version == 2)
-           case DW_FORM_addr:
-               width = cu->head->address_size;
-
-             if (!read_ctx_read_offset (ctx, width == 8, &value))
-               goto cant_read;
-
-             /* In non-rel files, neither addr, nor ref_addr /need/
-                a relocation.  */
-             relocate = rel_nonzero;
-             break;
-
-           case DW_FORM_ref_udata:
-             value_check_cb = check_die_ref_local;
-           case DW_FORM_udata:
-             if (!checked_read_uleb128 (ctx, &value, &where,
-                                        "attribute value"))
-               return -1;
-             break;
-
-           case DW_FORM_flag_present:
-             value = 1;
-             break;
-
-           case DW_FORM_ref1:
-             value_check_cb = check_die_ref_local;
-           case DW_FORM_flag:
-           case DW_FORM_data1:
-             if (!read_ctx_read_var (ctx, 1, &value))
-               goto cant_read;
-             break;
-
-           case DW_FORM_ref2:
-             value_check_cb = check_die_ref_local;
-           case DW_FORM_data2:
-             if (!read_ctx_read_var (ctx, 2, &value))
-               goto cant_read;
-             break;
-
-           case DW_FORM_data4:
-             if (check_someptr)
-               {
-                 relocate = rel_require;
-                 width = 4;
-               }
-             if (false)
-           case DW_FORM_ref4:
-               value_check_cb = check_die_ref_local;
-             if (!read_ctx_read_var (ctx, 4, &value))
-               goto cant_read;
-             break;
-
-           case DW_FORM_data8:
-             if (check_someptr)
-               {
-                 relocate = rel_require;
-                 width = 8;
-               }
-             if (false)
-           case DW_FORM_ref8:
-               value_check_cb = check_die_ref_local;
-             if (!read_ctx_read_8ubyte (ctx, &value))
-               goto cant_read;
-             break;
-
-           case DW_FORM_sdata:
-             {
-               int64_t value64;
-               if (!checked_read_sleb128 (ctx, &value64, &where,
-                                          "attribute value"))
-                 return -1;
-               value = (uint64_t) value64;
-               break;
-             }
-
-           case DW_FORM_block:
-             {
-               uint64_t length;
-
-               if (false)
-           case DW_FORM_block1:
-                 width = 1;
-
-               if (false)
-           case DW_FORM_block2:
-                 width = 2;
-
-               if (false)
-           case DW_FORM_block4:
-                 width = 4;
-
-               if (width == 0)
-                 {
-                   if (!checked_read_uleb128 (ctx, &length, &where,
-                                              "attribute value"))
-                     return -1;
-                 }
-               else if (!read_ctx_read_var (ctx, width, &length))
-                 goto cant_read;
-
-               if (is_location_attrib (it->name))
-                 {
-                   uint64_t expr_start
-                     = cu->head->offset + read_ctx_get_offset (ctx);
-                   if (!check_location_expression (file, ctx, cu, expr_start,
-                                                   reloc, length, &where))
-                     return -1;
-                 }
-               else
-                 /* xxx really skip_mismatched?  We just don't know
-                    how to process these...  */
-                 relocation_skip (reloc,
-                                  read_ctx_get_offset (ctx) + length,
-                                  &where, skip_mismatched);
-
-               if (!read_ctx_skip (ctx, length))
-                 goto cant_read;
-               break;
-             }
-
-           case DW_FORM_indirect:
-             wr_error (&where, ": indirect form is again indirect.\n");
-             return -1;
-
-           default:
-             wr_error (&where,
-                       ": internal error: unhandled form 0x%x.\n", form);
-           }
-
-         /* Relocate the value if appropriate.  */
-         struct relocation *rel;
-         if ((rel = relocation_next (reloc, ctx_offset,
-                                     &where, skip_mismatched)))
-           {
-             if (relocate == rel_no)
-               wr_message (mc_impact_4 | mc_die_other | mc_reloc | extra_mc,
-                           &where, ": unexpected relocation of %s.\n",
-                           dwarf_form_string (form));
-
-             relocate_one (file, reloc, rel, width, &value, &where,
-                           reloc_target (form, it), symbolp);
-
-             if (relocatedp != NULL)
-               *relocatedp = true;
-           }
-         else
-           {
-             if (symbolp != NULL)
-               WIPE (*symbolp);
-             if (type_is_rel
-                 && (relocate == rel_require
-                     || (relocate == rel_nonzero
-                         && value != 0)))
-               wr_message (mc_impact_2 | mc_die_other | mc_reloc | extra_mc,
-                           &where, PRI_LACK_RELOCATION,
-                           dwarf_form_string (form));
-           }
-
-         /* Dispatch value checking.  */
-         if (it->name == DW_AT_sibling)
-           {
-             /* Full-blown DIE reference checking is too heavy-weight
-                and not practical (error messages wise) for checking
-                siblings.  */
-             assert (value_check_cb == check_die_ref_local
-                     || value_check_cb == check_die_ref_global);
-             valuep = &sibling_addr;
-           }
-         else if (value_check_cb != NULL)
-           value_check_cb (value, &cb_ctx);
-
-         /* Store the relocated value.  Note valuep may point to
-            low_pc or high_pc.  */
-         if (valuep != NULL)
-           *valuep = value;
-
-         /* Check PC coverage.  */
-         if (abbrev->tag == DW_TAG_compile_unit
-             || abbrev->tag == DW_TAG_partial_unit)
-           {
-             if (it->name == DW_AT_low_pc)
-               cu->low_pc = value;
-
-             if (low_pc != (uint64_t)-1 && high_pc != (uint64_t)-1)
-               coverage_add (&cu_coverage->cov, low_pc, high_pc - low_pc);
-           }
-       }
-      where.ref = NULL;
-
-      if (high_pc != (uint64_t)-1 && low_pc != (uint64_t)-1)
-       {
-         if (high_pc_relocated != low_pc_relocated)
-           wr_message (mc_die_other | mc_impact_2 | mc_reloc, &where,
-                       ": only one of DW_AT_low_pc and DW_AT_high_pc is relocated.\n");
-         else
-           check_range_relocations (mc_die_other, &where,
-                                    file,
-                                    low_pc_symbol, high_pc_symbol,
-                                    "DW_AT_low_pc and DW_AT_high_pc");
-       }
-
-      where.ref = &abbrev->where;
-
-      if (abbrev->has_children)
-       {
-         int st = read_die_chain (ver, file, ctx, cu, abbrevs, strings,
-                                  local_die_refs,
-                                  strings_coverage, reloc,
-                                  cu_coverage);
-         if (st == -1)
-           return -1;
-         else if (st == 0)
-           wr_message (mc_impact_3 | mc_acc_suboptimal | mc_die_rel,
-                       &where,
-                       ": abbrev has_children, but the chain was empty.\n");
-       }
-    }
-
-  if (sibling_addr != 0)
-    wr_error (&where,
-             ": this DIE should have had its sibling at 0x%"
-             PRIx64 ", but the DIE chain ended.\n", sibling_addr);
-
-  return got_die ? 1 : 0;
-}
-
 bool
 read_address_size (bool elf_64,
                   struct read_ctx *ctx,
@@ -1930,7 +1208,7 @@ op_read_form (struct elf_file *file,
   return true;
 }
 
-static bool
+bool
 check_location_expression (struct elf_file *file,
                           struct read_ctx *parent_ctx,
                           struct cu *cu,
index d6308c1c8d3b5df7104f1a9f9ce540cf73dd2101..9783f3896a423b5f1b639cb0b7b0585751fb6c53 100644 (file)
@@ -146,16 +146,6 @@ extern "C"
 
   // xxx low-level check entry points, will go away
   struct cu;
-  extern int read_die_chain (dwarf_version_h ver,
-                            struct elf_file *file,
-                            struct read_ctx *ctx,
-                            struct cu *cu,
-                            struct abbrev_table *abbrevs,
-                            Elf_Data *strings,
-                            struct ref_record *local_die_refs,
-                            struct coverage *strings_coverage,
-                            struct relocation_data *reloc,
-                            struct cu_coverage *cu_coverage);
   extern bool check_loc_or_range_structural (struct elf_file *file,
                                             struct sec *sec,
                                             struct cu *cu_chain,
@@ -170,6 +160,20 @@ extern "C"
   extern bool check_line_structural (struct elf_file *file,
                                     struct sec *sec,
                                     struct addr_record *line_tables);
+  extern bool check_location_expression (struct elf_file *file,
+                                        struct read_ctx *parent_ctx,
+                                        struct cu *cu,
+                                        uint64_t init_off,
+                                        struct relocation_data *reloc,
+                                        size_t length,
+                                        struct where *wh);
+  extern void check_range_relocations (enum message_category cat,
+                                      struct where *where,
+                                      struct elf_file *file,
+                                      GElf_Sym *begin_symbol,
+                                      GElf_Sym *end_symbol,
+                                      const char *description);
+
   extern void cu_free (struct cu *cu_chain);
 
   extern int check_sibling_form (dwarf_version_h ver, uint64_t form);
@@ -199,6 +203,8 @@ extern "C"
                                struct section_coverage *sco, void *data);
   bool checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret,
                             struct where *where, const char *what);
+  bool checked_read_sleb128 (struct read_ctx *ctx, int64_t *ret,
+                            struct where *where, const char *what);
 
   struct abbrev_attrib
   {