]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
dwarflint: .debug_line logic in file of its own
authorPetr Machata <pmachata@redhat.com>
Fri, 23 Oct 2009 12:42:25 +0000 (14:42 +0200)
committerPetr Machata <pmachata@redhat.com>
Wed, 18 Aug 2010 12:55:12 +0000 (14:55 +0200)
* ... and reloc code, too

src/Makefile.am
src/dwarflint/check_debug_line.cc [new file with mode: 0644]
src/dwarflint/checks-low.cc
src/dwarflint/low.c
src/dwarflint/low.h
src/dwarflint/reloc.cc [new file with mode: 0644]
src/dwarflint/reloc.h [new file with mode: 0644]
src/dwarflint/where.h

index fb61e3ea19d6489217cde025abb22579fdf81cf6..13d9b3e18a16e3c8319cc5a78d976777a7248faa 100644 (file)
@@ -90,8 +90,10 @@ dwarflint_SOURCES = dwarfstrings.c \
                    dwarflint/checks.cc dwarflint/checks.hh \
                    dwarflint/checks-low.cc dwarflint/checks-low.hh \
                    dwarflint/addr-record.cc dwarflint/addr-record.h \
+                   dwarflint/reloc.cc dwarflint/reloc.h \
                    dwarflint/checks-high.hh \
                    dwarflint/check_debug_abbrev.cc \
+                   dwarflint/check_debug_line.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_line.cc b/src/dwarflint/check_debug_line.cc
new file mode 100644 (file)
index 0000000..a3993e5
--- /dev/null
@@ -0,0 +1,551 @@
+#include "checks-low.hh"
+
+#include <dwarf.h>
+#include "../libdw/known-dwarf.h"
+#include "dwarfstrings.h"
+
+#include <sstream>
+
+namespace
+{
+  class check_debug_line
+    : public check<check_debug_line>
+  {
+    section<sec_line> *_m_sec;
+
+    struct include_directory_t
+    {
+      std::string name;
+      bool used;
+    };
+    typedef std::vector<include_directory_t> include_directories_t;
+    include_directories_t _m_include_directories;
+
+    struct file_t
+    {
+      const char *name;
+      uint64_t dir_idx;
+      bool used;
+    };
+    typedef std::vector<file_t> files_t;
+    files_t _m_files;
+
+  public:
+    explicit check_debug_line (dwarflint &lint)
+      : _m_sec (lint.check (_m_sec))
+    {
+      addr_record line_tables;
+      WIPE (line_tables);
+      if (!check_line_structural (&_m_sec->file,
+                                 &_m_sec->sect,
+                                 &line_tables))
+       throw check_base::failed (""); //xxx
+
+      check_debug_info *info = NULL;
+      info = toplev_check (lint, info);
+      if (info != NULL)
+       for (std::vector<cu>::iterator it = info->cus.begin ();
+            it != info->cus.end (); ++it)
+         for (size_t i = 0; i < it->line_refs.size; ++i)
+           {
+             struct ref *ref = it->line_refs.refs + i;
+             if (!addr_record_has_addr (&line_tables, ref->addr))
+               {
+                 std::stringstream ss;
+                 ss << ": unresolved reference to .debug_line table "
+                    << "0x" << std::hex << ref->addr << ".";
+                 wr_error (&ref->who, "%s\n", ss.str ().c_str ());
+               }
+           }
+      addr_record_free (&line_tables);
+    }
+
+    /* Directory index.  */
+    bool read_directory_index (read_ctx *ctx,
+                              const char *name, uint64_t *ptr,
+                              where *where, bool &retval)
+    {
+      size_t nfile = _m_files.size () + 1;
+      if (!checked_read_uleb128 (ctx, ptr,
+                                where, "directory index"))
+       return false;
+      if (*name == '/' && *ptr != 0)
+       wr_message (mc_impact_2 | mc_line | mc_header, where,
+                   ": file #%zd has absolute pathname,"
+                   " but refers to directory != 0.\n", nfile);
+      if (*ptr > _m_include_directories.size ())
+       /* Not >=, dirs are indexed from 1.  */
+       {
+         std::stringstream ss;
+         ss << ": file #" << nfile
+            << " refers to directory #" << *ptr
+            << ", which wasn't defined.\n";
+         wr_message (mc_impact_4 | mc_line | mc_header, where,
+                     "%s\n", ss.str ().c_str ());
+         /* Consumer might choke on that.  */
+         retval = false;
+       }
+      else if (*ptr != 0)
+       _m_include_directories[*ptr - 1].used = true;
+      return true;
+    }
+
+    bool
+    use_file (uint64_t file_idx, where *where)
+    {
+      if (file_idx == 0 || file_idx > _m_files.size ())
+       {
+         std::stringstream ss;
+         ss << ": DW_LNS_set_file: invalid file index " << file_idx << '.';
+         wr_error (where, "%s\n", ss.str ().c_str ());
+         return false;
+       }
+      else
+       _m_files[file_idx - 1].used = true;
+      return true;
+    }
+
+    bool
+    check_line_structural (struct elf_file *file,
+                          struct sec *sec,
+                          struct addr_record *line_tables);
+  };
+
+  reg<check_debug_line> reg_debug_line;
+}
+
+bool
+check_debug_line::check_line_structural (struct elf_file *file,
+                                        struct sec *sec,
+                                        struct addr_record *line_tables)
+{
+  struct read_ctx ctx;
+  read_ctx_init (&ctx, sec->data, file->other_byte_order);
+  bool retval = true;
+
+  while (!read_ctx_eof (&ctx))
+    {
+      struct where where = WHERE (sec->id, NULL);
+      uint64_t set_offset = read_ctx_get_offset (&ctx);
+      where_reset_1 (&where, set_offset);
+      addr_record_add (line_tables, set_offset);
+      const unsigned char *set_begin = ctx.ptr;
+
+      /* Size.  */
+      uint32_t size32;
+      uint64_t size;
+      int offset_size;
+      if (!read_ctx_read_4ubyte (&ctx, &size32))
+       {
+         wr_error (&where, ": can't read table length.\n");
+         return false;
+       }
+      if (!read_size_extra (&ctx, size32, &size, &offset_size, &where))
+       return false;
+
+      struct read_ctx sub_ctx;
+      const unsigned char *set_end = ctx.ptr + size;
+      if (!read_ctx_init_sub (&sub_ctx, &ctx, set_begin, set_end))
+       {
+       not_enough:
+         wr_error (&where, PRI_NOT_ENOUGH, "next unit");
+         return false;
+       }
+      sub_ctx.ptr = ctx.ptr;
+      sub_ctx.begin = ctx.begin;
+
+      {
+      /* Version.  */
+      uint16_t version;
+      if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+       {
+         wr_error (&where, ": can't read set version.\n");
+       skip:
+         retval = false;
+         goto next;
+       }
+      if (!supported_version (version, 2, &where, 2, 3))
+       goto skip;
+
+      /* Header length.  */
+      uint64_t header_length;
+      if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &header_length))
+       {
+         wr_error (&where, ": can't read attribute value.\n");
+         goto skip;
+       }
+      const unsigned char *program_start = sub_ctx.ptr + header_length;
+
+      /* Minimum instruction length.  */
+      uint8_t minimum_i_length;
+      if (!read_ctx_read_ubyte (&sub_ctx, &minimum_i_length))
+       {
+         wr_error (&where, ": can't read minimum instruction length.\n");
+         goto skip;
+       }
+
+      /* Default value of is_stmt.  */
+      uint8_t default_is_stmt;
+      if (!read_ctx_read_ubyte (&sub_ctx, &default_is_stmt))
+       {
+         wr_error (&where, ": can't read default_is_stmt.\n");
+         goto skip;
+       }
+      /* 7.21: The boolean values "true" and "false" used by the line
+        number information program are encoded as a single byte
+        containing the value 0 for "false," and a non-zero value for
+        "true."  [But give a notice if it's not 0 or 1.]  */
+      if (default_is_stmt != 0
+         && default_is_stmt != 1)
+       wr_message (mc_line | mc_impact_2 | mc_header, &where,
+                   ": default_is_stmt should be 0 or 1, not %ud\n",
+                   default_is_stmt);
+
+      /* Line base.  */
+      int8_t line_base;
+      if (!read_ctx_read_ubyte (&sub_ctx, (uint8_t *)&line_base))
+       {
+         wr_error (&where, ": can't read line_base.\n");
+         goto skip;
+       }
+
+      /* Line range.  */
+      uint8_t line_range;
+      if (!read_ctx_read_ubyte (&sub_ctx, &line_range))
+       {
+         wr_error (&where, ": can't read line_range.\n");
+         goto skip;
+       }
+
+      /* Opcode base.  */
+      uint8_t opcode_base;
+      if (!read_ctx_read_ubyte (&sub_ctx, &opcode_base))
+       {
+         wr_error (&where, ": can't read opcode_base.\n");
+         goto skip;
+       }
+
+      /* Standard opcode lengths.  */
+      if (opcode_base == 0)
+       {
+         wr_error (&where, ": opcode base set to 0.\n");
+         opcode_base = 1; // so that in following, our -1s don't underrun
+       }
+      uint8_t std_opc_lengths[opcode_base - 1]; /* -1, opcodes go from 1.  */
+      for (unsigned i = 0; i < (unsigned)(opcode_base - 1); ++i)
+       if (!read_ctx_read_ubyte (&sub_ctx, std_opc_lengths + i))
+         {
+           wr_error (&where,
+                     ": can't read length of standard opcode #%d.\n", i);
+           goto skip;
+         }
+
+      while (!read_ctx_eof (&sub_ctx))
+       {
+         const char *name = read_ctx_read_str (&sub_ctx);
+         if (name == NULL)
+           {
+             wr_error (&where,
+                       ": can't read name of include directory #%zd.\n",
+                       /* Numbered from 1.  */
+                       _m_include_directories.size () + 1);
+             goto skip;
+           }
+         if (*name == 0)
+           break;
+
+         _m_include_directories.push_back ((include_directory_t){name, false});
+       }
+
+      /* File names.  */
+      while (1)
+       {
+         const char *name = read_ctx_read_str (&sub_ctx);
+         if (name == NULL)
+           {
+             wr_error (&where,
+                       ": can't read name of file #%zd.\n",
+                       _m_files.size () + 1); /* Numbered from 1.  */
+             goto skip;
+           }
+         if (*name == 0)
+           break;
+
+         uint64_t dir_idx;
+         if (!read_directory_index (&sub_ctx, name, &dir_idx, &where, retval))
+           goto skip;
+
+         /* Time of last modification.  */
+         uint64_t timestamp;
+         if (!checked_read_uleb128 (&sub_ctx, &timestamp,
+                                    &where, "timestamp of file entry"))
+           goto skip;
+
+         /* Size of the file.  */
+         uint64_t file_size;
+         if (!checked_read_uleb128 (&sub_ctx, &file_size,
+                                    &where, "file size of file entry"))
+           goto skip;
+
+         _m_files.push_back ((struct file_t){name, dir_idx, false});
+       }
+
+      /* Skip the rest of the header.  */
+      if (sub_ctx.ptr > program_start)
+       {
+         std::stringstream ss;
+         ss << ": header claims that it has a size of 0x"
+            << std::hex << header_length
+            << ", but in fact it has a size of 0x"
+            << sub_ctx.ptr - program_start + header_length << '.';
+
+         wr_error (&where, "%s\n", ss.str ().c_str ());
+         /* Assume that the header lies, and what follows is in
+            fact line number program.  */
+         retval = false;
+       }
+      else if (sub_ctx.ptr < program_start)
+       {
+         struct where wh = WHERE (sec_line, NULL);
+         if (!check_zero_padding (&sub_ctx, cat (mc_line, mc_header), &where))
+           wr_message_padding_n0 (cat (mc_line, mc_header), &wh,
+                                  read_ctx_get_offset (&sub_ctx),
+                                  program_start - sub_ctx.begin);
+         sub_ctx.ptr = program_start;
+       }
+
+      bool terminated = false;
+      bool first_file = true;
+      bool seen_opcode = false;
+      while (!read_ctx_eof (&sub_ctx))
+       {
+         where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
+         uint8_t opcode;
+         if (!read_ctx_read_ubyte (&sub_ctx, &opcode))
+           {
+             wr_error (&where, ": can't read opcode.\n");
+             goto skip;
+           }
+
+         unsigned operands = 0;
+         uint8_t extended = 0;
+         switch (opcode)
+           {
+             /* Extended opcodes.  */
+           case 0:
+             {
+               uint64_t skip_len;
+               if (!checked_read_uleb128 (&sub_ctx, &skip_len, &where,
+                                          "length of extended opcode"))
+                 goto skip;
+               const unsigned char *next = sub_ctx.ptr + skip_len;
+               if (!read_ctx_read_ubyte (&sub_ctx, &extended))
+                 {
+                   wr_error (&where, ": can't read extended opcode.\n");
+                   goto skip;
+                 }
+
+               bool handled = true;
+               switch (extended)
+                 {
+                 case DW_LNE_end_sequence:
+                   terminated = true;
+                   break;
+
+                 case DW_LNE_set_address:
+                   {
+                     uint64_t ctx_offset = read_ctx_get_offset (&sub_ctx);
+                     uint64_t addr;
+                     if (!read_ctx_read_offset (&sub_ctx,
+                                                file->addr_64, &addr))
+                       {
+                         wr_error (&where, ": can't read operand of DW_LNE_set_address.\n");
+                         goto skip;
+                       }
+
+                     struct relocation *rel;
+                     if ((rel = relocation_next (&sec->rel, ctx_offset,
+                                                 &where, skip_mismatched)))
+                       relocate_one (file, &sec->rel, rel,
+                                     file->addr_64 ? 8 : 4,
+                                     &addr, &where, rel_address, NULL);
+                     else if (file->ehdr.e_type == ET_REL)
+                       wr_message (mc_impact_2 | mc_line | mc_reloc, &where,
+                                   PRI_LACK_RELOCATION, "DW_LNE_set_address");
+                     break;
+                   }
+
+                 case DW_LNE_define_file:
+                   {
+                     const char *name;
+                     if ((name = read_ctx_read_str (&sub_ctx)) == NULL)
+                       {
+                         wr_error (&where,
+                                   ": can't read filename operand of DW_LNE_define_file.\n");
+                         goto skip;
+                       }
+                     uint64_t dir_idx;
+                     if (!read_directory_index (&sub_ctx, name, &dir_idx,
+                                                &where, retval))
+                       goto skip;
+                     _m_files.push_back
+                       ((struct file_t){name, dir_idx, false});
+                     operands = 2; /* Skip mtime & size of the file.  */
+                   }
+
+                   /* See if we know about any other standard opcodes.  */
+                 default:
+                   handled = false;
+                   switch (extended)
+                     {
+#define ONE_KNOWN_DW_LNE(NAME, CODE) case CODE: break;
+                       ALL_KNOWN_DW_LNE
+#undef ONE_KNOWN_DW_LNE
+                     default:
+                       /* No we don't, emit a warning.  */
+                       wr_message (mc_impact_2 | mc_line, &where,
+                                   ": unknown extended opcode #%d.\n", extended);
+                     };
+                 };
+
+               if (sub_ctx.ptr > next)
+                 {
+                   std::stringstream ss;
+                   ss << ": opcode claims that it has a size of 0x"
+                      << std::hex << skip_len
+                      << ", but in fact it has a size of 0x"
+                      << (skip_len + (next - sub_ctx.ptr)) << '.';
+                   wr_error (&where, "%s\n", ss.str ().c_str ());
+                   retval = false;
+                 }
+               else if (sub_ctx.ptr < next)
+                 {
+                   if (handled
+                       && !check_zero_padding (&sub_ctx, mc_line, &where))
+                     {
+                       struct where wh = WHERE (sec_line, NULL);
+                       wr_message_padding_n0 (mc_line, &wh,
+                                              read_ctx_get_offset (&sub_ctx),
+                                              next - sub_ctx.begin);
+                     }
+                   sub_ctx.ptr = next;
+                 }
+               break;
+             }
+
+             /* Standard opcodes that need validation or have
+                non-ULEB operands.  */
+           case DW_LNS_fixed_advance_pc:
+             {
+               uint16_t a;
+               if (!read_ctx_read_2ubyte (&sub_ctx, &a))
+                 {
+                   wr_error (&where, ": can't read operand of DW_LNS_fixed_advance_pc.\n");
+                   goto skip;
+                 }
+               break;
+             }
+
+           case DW_LNS_set_file:
+             {
+               uint64_t file_idx;
+               if (!checked_read_uleb128 (&sub_ctx, &file_idx, &where,
+                                          "DW_LNS_set_file operand"))
+                 goto skip;
+               if (!use_file (file_idx, &where))
+                 retval = false;
+               first_file = false;
+             }
+             break;
+
+           case DW_LNS_set_isa:
+             // XXX is it possible to validate this?
+             operands = 1;
+             break;
+
+             /* All the other opcodes.  */
+           default:
+             if (opcode < opcode_base)
+               operands = std_opc_lengths[opcode - 1];
+
+             switch (opcode)
+               {
+#define ONE_KNOWN_DW_LNS(NAME, CODE) case CODE: break;
+                 ALL_KNOWN_DW_LNS
+#undef ONE_KNOWN_DW_LNS
+
+               default:
+                 if (opcode < opcode_base)
+                   wr_message (mc_impact_2 | mc_line, &where,
+                               ": unknown standard opcode #%d.\n", opcode);
+               };
+           };
+
+         for (unsigned i = 0; i < operands; ++i)
+           {
+             uint64_t operand;
+             char buf[128];
+             if (opcode != 0)
+               sprintf (buf, "operand #%d of DW_LNS_%s",
+                        i, dwarf_line_standard_opcode_string (opcode));
+             else
+               sprintf (buf, "operand #%d of extended opcode %d",
+                        i, extended);
+             if (!checked_read_uleb128 (&sub_ctx, &operand, &where, buf))
+               goto skip;
+           }
+
+         if (first_file)
+           {
+             if (!use_file (1, &where))
+               retval = false;
+             first_file = false;
+           }
+
+         if (opcode != 0 || extended != DW_LNE_end_sequence)
+           seen_opcode = true;
+       }
+
+      for (size_t i = 0; i < _m_include_directories.size (); ++i)
+       if (!_m_include_directories[i].used)
+         wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
+                     &where, ": the include #%zd `%s' is not used.\n",
+                     i + 1, _m_include_directories[i].name.c_str ());
+
+      for (size_t i = 0; i < _m_files.size (); ++i)
+       if (!_m_files[i].used)
+         wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
+                     &where, ": the file #%zd `%s' is not used.\n",
+                     i + 1, _m_files[i].name);
+
+      if (!seen_opcode)
+       wr_message (mc_line | mc_acc_bloat | mc_impact_3, &where,
+                   ": empty line number program.\n");
+
+      struct where wh = WHERE (sec_line, NULL);
+      if (!terminated)
+       wr_error (&where,
+                 ": sequence of opcodes not terminated with DW_LNE_end_sequence.\n");
+      else if (sub_ctx.ptr != sub_ctx.end
+              && !check_zero_padding (&sub_ctx, mc_line, &wh))
+       wr_message_padding_n0 (mc_line, &wh,
+                              /*begin*/read_ctx_get_offset (&sub_ctx),
+                              /*end*/sub_ctx.end - sub_ctx.begin);
+      }
+
+      /* XXX overlaps in defined addresses are probably OK, one
+        instruction can be derived from several statements.  But
+        certain flags in table should be consistent in that case,
+        namely is_stmt, basic_block, end_sequence, prologue_end,
+        epilogue_begin, isa.  */
+
+    next:
+      if (!read_ctx_skip (&ctx, size))
+       goto not_enough;
+    }
+
+  if (retval)
+    relocation_skip_rest (&sec->rel, sec->id);
+
+  return retval;
+}
index 17259273cabc133ec99ebeaacfb24da5e69d5856..fbcfaa609c8b86018d0bb579c4ef5e1e0447dd27 100644 (file)
@@ -394,44 +394,3 @@ namespace
   reg<check_debug_pub<sec_pubnames> > reg_debug_pubnames;
   reg<check_debug_pub<sec_pubtypes> > reg_debug_pubtypes;
 }
-
-namespace
-{
-  class check_debug_line
-    : public check<check_debug_line>
-  {
-    section<sec_line> *_m_sec;
-
-  public:
-    explicit check_debug_line (dwarflint &lint)
-      : _m_sec (lint.check (_m_sec))
-    {
-      addr_record line_tables;
-      WIPE (line_tables);
-      if (!check_line_structural (&_m_sec->file,
-                                 &_m_sec->sect,
-                                 &line_tables))
-       throw check_base::failed (""); //xxx
-
-      check_debug_info *info = NULL;
-      info = toplev_check (lint, info);
-      if (info != NULL)
-       for (std::vector<cu>::iterator it = info->cus.begin ();
-            it != info->cus.end (); ++it)
-         for (size_t i = 0; i < it->line_refs.size; ++i)
-           {
-             struct ref *ref = it->line_refs.refs + i;
-             if (!addr_record_has_addr (&line_tables, ref->addr))
-               {
-                 std::stringstream ss;
-                 ss << ": unresolved reference to .debug_line table "
-                    << "0x" << std::hex << ref->addr << ".";
-                 wr_error (&ref->who, "%s\n", ss.str ().c_str ());
-               }
-           }
-      addr_record_free (&line_tables);
-    }
-  };
-
-  reg<check_debug_line> reg_debug_line;
-}
index 3ceef8c25be978f1e01855d6503dd147cc3edf7a..671c2655eb6f4c5666543dc6be2494ca25e2129d 100644 (file)
@@ -42,7 +42,6 @@
 #include <unistd.h>
 
 #include "../libdw/dwarf.h"
-#include "../libdw/known-dwarf.h"
 #include "../libebl/libebl.h"
 #include "dwarfstrings.h"
 #include "low.h"
@@ -62,8 +61,6 @@ check_category (enum message_category cat)
 
 #define PRI_CU "CU 0x%" PRIx64
 #define PRI_DIE "DIE 0x%" PRIx64
-#define PRI_NOT_ENOUGH ": not enough data for %s.\n"
-#define PRI_LACK_RELOCATION ": %s seems to lack a relocation.\n"
 
 
 static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset);
@@ -561,7 +558,7 @@ check_global_die_references (struct cu *cu_chain)
   return retval;
 }
 
-static bool
+bool
 read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
                 int *offset_sizep, struct where *wh)
 {
@@ -590,7 +587,7 @@ read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
   return true;
 }
 
-static bool
+bool
 check_zero_padding (struct read_ctx *ctx,
                    enum message_category category,
                    struct where *wh)
@@ -610,252 +607,6 @@ check_zero_padding (struct read_ctx *ctx,
   return true;
 }
 
-static struct where
-where_from_reloc (struct relocation_data *reloc, struct where *ref)
-{
-  struct where where
-    = WHERE (reloc->type == SHT_REL ? sec_rel : sec_rela, NULL);
-  where_reset_1 (&where, reloc->rel[reloc->index].offset);
-  where.ref = ref;
-  return where;
-}
-
-enum skip_type
-{
-  skip_unref = 0,
-  skip_mismatched = 1,
-  skip_ok,
-};
-
-static struct relocation *
-relocation_next (struct relocation_data *reloc, uint64_t offset,
-                struct where *where, enum skip_type st)
-{
-  if (reloc == NULL || reloc->rel == NULL)
-    return NULL;
-
-  while (reloc->index < reloc->size)
-    {
-      struct relocation *rel = reloc->rel + reloc->index;
-
-      /* This relocation entry is ahead of us.  */
-      if (rel->offset > offset)
-       return NULL;
-
-      reloc->index++;
-
-      if (rel->invalid)
-       continue;
-
-      if (rel->offset < offset)
-       {
-         if (st != skip_ok)
-           {
-             struct where reloc_where = where_from_reloc (reloc, where);
-             where_reset_2 (&reloc_where, rel->offset);
-             void (*w) (const struct where *, const char *, ...) = wr_error;
-             (*w) (&reloc_where,
-                   ((const char *[])
-                   {": relocation targets unreferenced portion of the section.\n",
-                    ": relocation is mismatched.\n"})[st]);
-           }
-         continue;
-       }
-
-      return rel;
-    }
-
-  return NULL;
-}
-
-/* Skip all relocation up to offset, and leave cursor pointing at that
-   relocation, so that next time relocation_next is called, relocation
-   matching that offset is immediately yielded.  */
-static void
-relocation_skip (struct relocation_data *reloc, uint64_t offset,
-                struct where *where, enum skip_type st)
-{
-  if (reloc != NULL && reloc->rel != NULL)
-    relocation_next (reloc, offset - 1, where, st);
-}
-
-/* Skip all the remaining relocations.  */
-static void
-relocation_skip_rest (struct sec *sec)
-{
-  if (sec->rel.rel != NULL)
-    relocation_next (&sec->rel, (uint64_t)-1, &WHERE (sec->id, NULL),
-                    skip_mismatched);
-}
-
-/* SYMPTR may be NULL, otherwise (**SYMPTR) has to yield valid memory
-   location.  When the function returns, (*SYMPTR) is either NULL, in
-   which case we failed or didn't get around to obtain the symbol from
-   symbol table, or non-NULL, in which case the symbol was initialized.  */
-static void
-relocate_one (struct elf_file *file,
-             struct relocation_data *reloc,
-             struct relocation *rel,
-             unsigned width, uint64_t *value, struct where *where,
-             enum section_id offset_into, GElf_Sym **symptr)
-{
-  if (rel->invalid)
-    return;
-
-  struct where reloc_where = where_from_reloc (reloc, where);
-  where_reset_2 (&reloc_where, rel->offset);
-  struct where reloc_ref_where = reloc_where;
-  reloc_ref_where.next = where;
-
-  GElf_Sym symbol_mem, *symbol;
-  if (symptr != NULL)
-    {
-      symbol = *symptr;
-      *symptr = NULL;
-    }
-  else
-    symbol = &symbol_mem;
-
-  if (offset_into == sec_invalid)
-    {
-      wr_message (mc_impact_3 | mc_reloc, &reloc_ref_where,
-                 ": relocates a datum that shouldn't be relocated.\n");
-      return;
-    }
-
-  Elf_Type type = ebl_reloc_simple_type (file->ebl, rel->type);
-
-  unsigned rel_width;
-  switch (type)
-    {
-    case ELF_T_BYTE:
-      rel_width = 1;
-      break;
-
-    case ELF_T_HALF:
-      rel_width = 2;
-      break;
-
-    case ELF_T_WORD:
-    case ELF_T_SWORD:
-      rel_width = 4;
-      break;
-
-    case ELF_T_XWORD:
-    case ELF_T_SXWORD:
-      rel_width = 8;
-      break;
-
-    default:
-      /* This has already been diagnosed during the isolated
-        validation of relocation section.  */
-      return;
-    };
-
-  if (rel_width != width)
-    wr_error (&reloc_ref_where,
-             ": %d-byte relocation relocates %d-byte datum.\n",
-             rel_width, width);
-
-  /* Tolerate that we might have failed to obtain the symbol table.  */
-  if (reloc->symdata != NULL)
-    {
-      symbol = gelf_getsym (reloc->symdata, rel->symndx, symbol);
-      if (symptr != NULL)
-       *symptr = symbol;
-      if (symbol == NULL)
-       {
-         wr_error (&reloc_where,
-                   ": couldn't obtain symbol #%d: %s.\n",
-                   rel->symndx, elf_errmsg (-1));
-         return;
-       }
-
-      uint64_t section_index = symbol->st_shndx;
-      /* XXX We should handle SHN_XINDEX here.  Or, instead, maybe it
-        would be possible to use dwfl, which already does XINDEX
-        translation.  */
-
-      /* For ET_REL files, we do section layout manually.  But we
-        don't update symbol table doing that.  So instead of looking
-        at symbol value, look at section address.  */
-      uint64_t sym_value = symbol->st_value;
-      if (file->ehdr.e_type == ET_REL
-         && ELF64_ST_TYPE (symbol->st_info) == STT_SECTION)
-       {
-         assert (sym_value == 0);
-         sym_value = file->sec[section_index].shdr.sh_addr;
-       }
-
-      /* It's target value, not section offset.  */
-      if (offset_into == rel_value
-         || offset_into == rel_address
-         || offset_into == rel_exec)
-       {
-         /* If a target value is what's expected, then complain if
-            it's not either SHN_ABS, an SHF_ALLOC section, or
-            SHN_UNDEF.  For data forms of address_size, an SHN_UNDEF
-            reloc is acceptable, otherwise reject it.  */
-         if (!(section_index == SHN_ABS
-               || (offset_into == rel_address
-                   && (section_index == SHN_UNDEF
-                       || section_index == SHN_COMMON))))
-           {
-             if (offset_into != rel_address && section_index == SHN_UNDEF)
-               wr_error (&reloc_where,
-                           ": relocation of an address is formed against SHN_UNDEF symbol"
-                           " (symtab index %d).\n", rel->symndx);
-             else
-               {
-                 GElf_Shdr *shdr = &file->sec[section_index].shdr;
-                 if ((shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
-                   wr_message (mc_reloc | mc_impact_3, &reloc_where,
-                               ": associated section %s isn't SHF_ALLOC.\n",
-                               file->sec[section_index].name);
-                 if (offset_into == rel_exec
-                     && (shdr->sh_flags & SHF_EXECINSTR) != SHF_EXECINSTR)
-                   /* This may still be kosher, but it's suspicious.  */
-                   wr_message (mc_reloc | mc_impact_2, &reloc_where,
-                               ": relocation against %s is suspicious, expected executable section.\n",
-                               file->sec[section_index].name);
-               }
-           }
-       }
-      else
-       {
-         enum section_id id;
-         /* If symtab[symndx].st_shndx does not match the expected
-            debug section's index, complain.  */
-         if (section_index >= file->size)
-           wr_error (&reloc_where,
-                     ": invalid associated section #%" PRId64 ".\n",
-                     section_index);
-         else if ((id = file->sec[section_index].id) != offset_into)
-           {
-             char *wh1 = id != sec_invalid
-               ? strdup (where_fmt (&WHERE (id, NULL), NULL))
-               : (char *)file->sec[section_index].name;
-             char *wh2 = strdup (where_fmt (&WHERE (offset_into, NULL), NULL));
-             wr_error (&reloc_where,
-                       ": relocation references section %s, but %s was expected.\n",
-                       wh1, wh2);
-             free (wh2);
-             if (id != sec_invalid)
-               free (wh1);
-           }
-       }
-
-      /* Only do the actual relocation if we have ET_REL files.  For
-        non-ET_REL files, only do the above checking.  */
-      if (file->ehdr.e_type == ET_REL)
-       {
-         *value = rel->addend + sym_value;
-         if (rel_width == 4)
-           *value = *value & (uint64_t)(uint32_t)-1;
-       }
-    }
-}
-
 static enum section_id
 reloc_target (uint8_t form, struct abbrev_attrib *at)
 {
@@ -968,7 +719,7 @@ reloc_target_loc (uint8_t opcode)
   return rel_value;
 }
 
-static bool
+bool
 supported_version (unsigned version,
                   size_t num_supported, struct where *where, ...)
 {
@@ -1855,7 +1606,7 @@ check_info_structural (struct elf_file *file,
                    ": CU lengths don't exactly match Elf_Data contents.");
       else
        /* Did we consume all the relocations?  */
-       relocation_skip_rest (sec);
+       relocation_skip_rest (&sec->rel, sec->id);
 
       /* If we managed to read up everything, now do abbrev usage
         analysis.  */
@@ -2391,7 +2142,7 @@ check_pub_structural (struct elf_file *file,
     }
 
   if (retval)
-    relocation_skip_rest (sec);
+    relocation_skip_rest (&sec->rel, sec->id);
 
   return retval;
 }
@@ -2860,7 +2611,7 @@ check_loc_or_range_structural (struct elf_file *file,
 
   if (retval)
     {
-      relocation_skip_rest (sec);
+      relocation_skip_rest (&sec->rel, sec->id);
 
       /* We check that all CUs have the same address size when building
         the CU chain.  So just take the address size of the first CU in
@@ -3042,494 +2793,3 @@ read_rel (struct elf_file *file,
         sizeof (*sec->rel.rel), &compare);
   return true;
 }
-
-bool
-check_line_structural (struct elf_file *file,
-                      struct sec *sec,
-                      struct addr_record *line_tables)
-{
-  struct read_ctx ctx;
-  read_ctx_init (&ctx, sec->data, file->other_byte_order);
-  bool retval = true;
-
-  while (!read_ctx_eof (&ctx))
-    {
-      struct where where = WHERE (sec->id, NULL);
-      uint64_t set_offset = read_ctx_get_offset (&ctx);
-      where_reset_1 (&where, set_offset);
-      addr_record_add (line_tables, set_offset);
-      const unsigned char *set_begin = ctx.ptr;
-
-      /* Size.  */
-      uint32_t size32;
-      uint64_t size;
-      int offset_size;
-      if (!read_ctx_read_4ubyte (&ctx, &size32))
-       {
-         wr_error (&where, ": can't read table length.\n");
-         return false;
-       }
-      if (!read_size_extra (&ctx, size32, &size, &offset_size, &where))
-       return false;
-
-      struct read_ctx sub_ctx;
-      const unsigned char *set_end = ctx.ptr + size;
-      if (!read_ctx_init_sub (&sub_ctx, &ctx, set_begin, set_end))
-       {
-       not_enough:
-         wr_error (&where, PRI_NOT_ENOUGH, "next unit");
-         return false;
-       }
-      sub_ctx.ptr = ctx.ptr;
-      sub_ctx.begin = ctx.begin;
-
-      {
-      /* Version.  */
-      uint16_t version;
-      if (!read_ctx_read_2ubyte (&sub_ctx, &version))
-       {
-         wr_error (&where, ": can't read set version.\n");
-       skip:
-         retval = false;
-         goto next;
-       }
-      if (!supported_version (version, 2, &where, 2, 3))
-       goto skip;
-
-      /* Header length.  */
-      uint64_t header_length;
-      if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &header_length))
-       {
-         wr_error (&where, ": can't read attribute value.\n");
-         goto skip;
-       }
-      const unsigned char *program_start = sub_ctx.ptr + header_length;
-
-      /* Minimum instruction length.  */
-      uint8_t minimum_i_length;
-      if (!read_ctx_read_ubyte (&sub_ctx, &minimum_i_length))
-       {
-         wr_error (&where, ": can't read minimum instruction length.\n");
-         goto skip;
-       }
-
-      /* Default value of is_stmt.  */
-      uint8_t default_is_stmt;
-      if (!read_ctx_read_ubyte (&sub_ctx, &default_is_stmt))
-       {
-         wr_error (&where, ": can't read default_is_stmt.\n");
-         goto skip;
-       }
-      /* 7.21: The boolean values "true" and "false" used by the line
-        number information program are encoded as a single byte
-        containing the value 0 for "false," and a non-zero value for
-        "true."  [But give a notice if it's not 0 or 1.]  */
-      if (default_is_stmt != 0
-         && default_is_stmt != 1)
-       wr_message (mc_line | mc_impact_2 | mc_header, &where,
-                   ": default_is_stmt should be 0 or 1, not %ud\n",
-                   default_is_stmt);
-
-      /* Line base.  */
-      int8_t line_base;
-      if (!read_ctx_read_ubyte (&sub_ctx, (uint8_t *)&line_base))
-       {
-         wr_error (&where, ": can't read line_base.\n");
-         goto skip;
-       }
-
-      /* Line range.  */
-      uint8_t line_range;
-      if (!read_ctx_read_ubyte (&sub_ctx, &line_range))
-       {
-         wr_error (&where, ": can't read line_range.\n");
-         goto skip;
-       }
-
-      /* Opcode base.  */
-      uint8_t opcode_base;
-      if (!read_ctx_read_ubyte (&sub_ctx, &opcode_base))
-       {
-         wr_error (&where, ": can't read opcode_base.\n");
-         goto skip;
-       }
-
-      /* Standard opcode lengths.  */
-      if (opcode_base == 0)
-       {
-         wr_error (&where, ": opcode base set to 0.\n");
-         opcode_base = 1; // so that in following, our -1s don't underrun
-       }
-      uint8_t std_opc_lengths[opcode_base - 1]; /* -1, opcodes go from 1.  */
-      for (unsigned i = 0; i < (unsigned)(opcode_base - 1); ++i)
-       if (!read_ctx_read_ubyte (&sub_ctx, std_opc_lengths + i))
-         {
-           wr_error (&where,
-                     ": can't read length of standard opcode #%d.\n", i);
-           goto skip;
-         }
-
-      /* Include directories.  */
-      struct include_directory_t
-      {
-       const char *name;
-       bool used;
-      };
-      struct include_directories_t
-      {
-       size_t size;
-       size_t alloc;
-       struct include_directory_t *dirs;
-      } include_directories;
-      WIPE (include_directories);
-
-      while (!read_ctx_eof (&sub_ctx))
-       {
-         const char *name = read_ctx_read_str (&sub_ctx);
-         if (name == NULL)
-           {
-             wr_error (&where,
-                       ": can't read name of include directory #%zd.\n",
-                       include_directories.size + 1); /* Numbered from 1.  */
-             goto skip;
-           }
-         if (*name == 0)
-           break;
-
-         REALLOC (&include_directories, dirs);
-         include_directories.dirs[include_directories.size++] =
-           (struct include_directory_t){name, false};
-       }
-
-      /* File names.  */
-      struct file_t
-      {
-       const char *name;
-       uint64_t dir_idx;
-       bool used;
-      };
-      struct files_t
-      {
-       size_t size;
-       size_t alloc;
-       struct file_t *files;
-      } files;
-      WIPE (files);
-
-      /* Directory index.  */
-      bool read_directory_index (const char *name, uint64_t *ptr)
-      {
-       if (!checked_read_uleb128 (&sub_ctx, ptr,
-                                  &where, "directory index"))
-         return false;
-       if (*name == '/' && *ptr != 0)
-         wr_message (mc_impact_2 | mc_line | mc_header, &where,
-                     ": file #%zd has absolute pathname, but refers to directory != 0.\n",
-                     files.size + 1);
-       if (*ptr > include_directories.size) /* Not >=, dirs indexed from 1.  */
-         {
-           wr_message (mc_impact_4 | mc_line | mc_header, &where,
-                       ": file #%zd refers to directory #%" PRId64 ", which wasn't defined.\n",
-                       files.size + 1, *ptr);
-           /* Consumer might choke on that.  */
-           retval = false;
-         }
-       else if (*ptr != 0)
-         include_directories.dirs[*ptr - 1].used = true;
-       return true;
-      }
-
-      while (1)
-       {
-         const char *name = read_ctx_read_str (&sub_ctx);
-         if (name == NULL)
-           {
-             wr_error (&where,
-                       ": can't read name of file #%zd.\n",
-                       files.size + 1); /* Numbered from 1.  */
-             goto skip;
-           }
-         if (*name == 0)
-           break;
-
-         uint64_t dir_idx;
-         if (!read_directory_index (name, &dir_idx))
-           goto skip;
-
-         /* Time of last modification.  */
-         uint64_t timestamp;
-         if (!checked_read_uleb128 (&sub_ctx, &timestamp,
-                                    &where, "timestamp of file entry"))
-           goto skip;
-
-         /* Size of the file.  */
-         uint64_t file_size;
-         if (!checked_read_uleb128 (&sub_ctx, &file_size,
-                                    &where, "file size of file entry"))
-           goto skip;
-
-         REALLOC (&files, files);
-         files.files[files.size++]
-           = (struct file_t){name, dir_idx, false};
-       }
-
-      /* Skip the rest of the header.  */
-      if (sub_ctx.ptr > program_start)
-       {
-         wr_error (&where,
-                   ": header claims that it has a size of %#" PRIx64
-                   ", but in fact it has a size of %#" PRIx64 ".\n",
-                   header_length, sub_ctx.ptr - program_start + header_length);
-         /* Assume that the header lies, and what follows is in
-            fact line number program.  */
-         retval = false;
-       }
-      else if (sub_ctx.ptr < program_start)
-       {
-         if (!check_zero_padding (&sub_ctx, mc_line | mc_header, &where))
-           wr_message_padding_n0 (mc_line | mc_header, &WHERE (sec_line, NULL),
-                                  read_ctx_get_offset (&sub_ctx),
-                                  program_start - sub_ctx.begin);
-         sub_ctx.ptr = program_start;
-       }
-
-      bool terminated = false;
-      bool first_file = true;
-      bool seen_opcode = false;
-      while (!read_ctx_eof (&sub_ctx))
-       {
-         where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
-         uint8_t opcode;
-         if (!read_ctx_read_ubyte (&sub_ctx, &opcode))
-           {
-             wr_error (&where, ": can't read opcode.\n");
-             goto skip;
-           }
-
-         void use_file (uint64_t file_idx)
-         {
-           if (file_idx == 0 || file_idx > files.size)
-             {
-               wr_error (&where,
-                         ": DW_LNS_set_file: invalid file index %" PRId64 ".\n",
-                         file_idx);
-               retval = false;
-             }
-           else
-             files.files[file_idx - 1].used = true;
-         }
-
-         unsigned operands = 0;
-         uint8_t extended = 0;
-         switch (opcode)
-           {
-             /* Extended opcodes.  */
-           case 0:
-             {
-               uint64_t skip_len;
-               if (!checked_read_uleb128 (&sub_ctx, &skip_len, &where,
-                                          "length of extended opcode"))
-                 goto skip;
-               const unsigned char *next = sub_ctx.ptr + skip_len;
-               if (!read_ctx_read_ubyte (&sub_ctx, &extended))
-                 {
-                   wr_error (&where, ": can't read extended opcode.\n");
-                   goto skip;
-                 }
-
-               bool handled = true;
-               switch (extended)
-                 {
-                 case DW_LNE_end_sequence:
-                   terminated = true;
-                   break;
-
-                 case DW_LNE_set_address:
-                   {
-                     uint64_t ctx_offset = read_ctx_get_offset (&sub_ctx);
-                     uint64_t addr;
-                     if (!read_ctx_read_offset (&sub_ctx,
-                                                file->addr_64, &addr))
-                       {
-                         wr_error (&where, ": can't read operand of DW_LNE_set_address.\n");
-                         goto skip;
-                       }
-
-                     struct relocation *rel;
-                     if ((rel = relocation_next (&sec->rel, ctx_offset,
-                                                 &where, skip_mismatched)))
-                       relocate_one (file, &sec->rel, rel,
-                                     file->addr_64 ? 8 : 4,
-                                     &addr, &where, rel_address, NULL);
-                     else if (file->ehdr.e_type == ET_REL)
-                       wr_message (mc_impact_2 | mc_line | mc_reloc, &where,
-                                   PRI_LACK_RELOCATION, "DW_LNE_set_address");
-                     break;
-                   }
-
-                 case DW_LNE_define_file:
-                   {
-                     const char *name;
-                     if ((name = read_ctx_read_str (&sub_ctx)) == NULL)
-                       {
-                         wr_error (&where,
-                                   ": can't read filename operand of DW_LNE_define_file.\n");
-                         goto skip;
-                       }
-                     uint64_t dir_idx;
-                     if (!read_directory_index (name, &dir_idx))
-                       goto skip;
-                     REALLOC (&files, files);
-                     files.files[files.size++] =
-                       (struct file_t){name, dir_idx, false};
-                     operands = 2; /* Skip mtime & size of the file.  */
-                   }
-
-                   /* See if we know about any other standard opcodes.  */
-                 default:
-                   handled = false;
-                   switch (extended)
-                     {
-#define ONE_KNOWN_DW_LNE(NAME, CODE) case CODE: break;
-                       ALL_KNOWN_DW_LNE
-#undef ONE_KNOWN_DW_LNE
-                     default:
-                       /* No we don't, emit a warning.  */
-                       wr_message (mc_impact_2 | mc_line, &where,
-                                   ": unknown extended opcode #%d.\n", extended);
-                     };
-                 };
-
-               if (sub_ctx.ptr > next)
-                 {
-                   wr_error (&where,
-                             ": opcode claims that it has a size of %#" PRIx64
-                             ", but in fact it has a size of %#" PRIx64 ".\n",
-                             skip_len, skip_len + (next - sub_ctx.ptr));
-                   retval = false;
-                 }
-               else if (sub_ctx.ptr < next)
-                 {
-                   if (handled
-                       && !check_zero_padding (&sub_ctx, mc_line, &where))
-                     wr_message_padding_n0 (mc_line, &WHERE (sec_line, NULL),
-                                            read_ctx_get_offset (&sub_ctx),
-                                            next - sub_ctx.begin);
-                   sub_ctx.ptr = next;
-                 }
-               break;
-             }
-
-             /* Standard opcodes that need validation or have
-                non-ULEB operands.  */
-           case DW_LNS_fixed_advance_pc:
-             {
-               uint16_t a;
-               if (!read_ctx_read_2ubyte (&sub_ctx, &a))
-                 {
-                   wr_error (&where, ": can't read operand of DW_LNS_fixed_advance_pc.\n");
-                   goto skip;
-                 }
-               break;
-             }
-
-           case DW_LNS_set_file:
-             {
-               uint64_t file_idx;
-               if (!checked_read_uleb128 (&sub_ctx, &file_idx, &where,
-                                          "DW_LNS_set_file operand"))
-                 goto skip;
-               use_file (file_idx);
-               first_file = false;
-             }
-             break;
-
-           case DW_LNS_set_isa:
-             // XXX is it possible to validate this?
-             operands = 1;
-             break;
-
-             /* All the other opcodes.  */
-           default:
-             if (opcode < opcode_base)
-               operands = std_opc_lengths[opcode - 1];
-
-             switch (opcode)
-               {
-#define ONE_KNOWN_DW_LNS(NAME, CODE) case CODE: break;
-                 ALL_KNOWN_DW_LNS
-#undef ONE_KNOWN_DW_LNS
-
-               default:
-                 if (opcode < opcode_base)
-                   wr_message (mc_impact_2 | mc_line, &where,
-                               ": unknown standard opcode #%d.\n", opcode);
-               };
-           };
-
-         for (unsigned i = 0; i < operands; ++i)
-           {
-             uint64_t operand;
-             char buf[128];
-             if (opcode != 0)
-               sprintf (buf, "operand #%d of DW_LNS_%s",
-                        i, dwarf_line_standard_opcode_string (opcode));
-             else
-               sprintf (buf, "operand #%d of extended opcode %d",
-                        i, extended);
-             if (!checked_read_uleb128 (&sub_ctx, &operand, &where, buf))
-               goto skip;
-           }
-
-         if (first_file)
-           {
-             use_file (1);
-             first_file = false;
-           }
-
-         if (opcode != 0 || extended != DW_LNE_end_sequence)
-           seen_opcode = true;
-       }
-
-      for (size_t i = 0; i < include_directories.size; ++i)
-       if (!include_directories.dirs[i].used)
-         wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
-                     &where, ": the include #%zd `%s' is not used.\n",
-                     i + 1, include_directories.dirs[i].name);
-
-      for (size_t i = 0; i < files.size; ++i)
-       if (!files.files[i].used)
-         wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
-                     &where, ": the file #%zd `%s' is not used.\n",
-                     i + 1, files.files[i].name);
-
-      if (!seen_opcode)
-       wr_message (mc_line | mc_acc_bloat | mc_impact_3, &where,
-                   ": empty line number program.\n");
-      if (!terminated)
-       wr_error (&where,
-                 ": sequence of opcodes not terminated with DW_LNE_end_sequence.\n");
-      else if (sub_ctx.ptr != sub_ctx.end
-              && !check_zero_padding (&sub_ctx, mc_line,
-                                      &WHERE (sec_line, NULL)))
-       wr_message_padding_n0 (mc_line, &WHERE (sec_line, NULL),
-                              /*begin*/read_ctx_get_offset (&sub_ctx),
-                              /*end*/sub_ctx.end - sub_ctx.begin);
-      }
-
-      /* XXX overlaps in defined addresses are probably OK, one
-        instruction can be derived from several statements.  But
-        certain flags in table should be consistent in that case,
-        namely is_stmt, basic_block, end_sequence, prologue_end,
-        epilogue_begin, isa.  */
-
-    next:
-      if (!read_ctx_skip (&ctx, size))
-       goto not_enough;
-    }
-
-  if (retval)
-    relocation_skip_rest (sec);
-
-  return retval;
-}
index a116c0771353717322d40756efab46a0f6665fac..9b8299c2c7b40c9df957580ce509503b8aba66be 100644 (file)
@@ -23,8 +23,8 @@
    Network licensing program, please visit www.openinventionnetwork.com
    <http://www.openinventionnetwork.com>.  */
 
-#ifndef DWARFLINT_HL_H
-#define DWARFLINT_HL_H
+#ifndef DWARFLINT_LOW_H
+#define DWARFLINT_LOW_H
 
 #include "../libdw/libdw.h"
 #include "../libebl/libebl.h"
@@ -32,6 +32,7 @@
 #include "messages.h"
 #include "readctx.h"
 #include "addr-record.h"
+#include "reloc.h"
 
 #ifdef __cplusplus
 extern "C"
@@ -41,30 +42,6 @@ extern "C"
 #endif
 
   struct hl_ctx;
-  struct relocation
-  {
-    uint64_t offset;
-    uint64_t addend;
-    int symndx;
-    int type;
-    bool invalid;      /* Whether this one relocation should be
-                          ignored.  Necessary so that we don't report
-                          invalid & missing relocation twice.  */
-  };
-
-  struct relocation_data
-  {
-    Elf_Data *symdata;         /* Symbol table associated with this
-                                  relocation section.  */
-    size_t type;               /* SHT_REL or SHT_RELA.  */
-
-    struct relocation *rel;    /* Array of relocations.  May be NULL
-                                  if there are no associated
-                                  relocation data.  */
-    size_t size;
-    size_t alloc;
-    size_t index;              /* Current index. */
-  };
 
   struct sec
   {
@@ -124,6 +101,15 @@ extern "C"
   extern bool address_aligned (uint64_t addr, uint64_t align);
   extern bool necessary_alignment (uint64_t start, uint64_t length,
                                   uint64_t align);
+  extern bool read_size_extra (struct read_ctx *ctx, uint32_t size32,
+                              uint64_t *sizep, int *offset_sizep,
+                              struct where *wh);
+#define PRI_NOT_ENOUGH ": not enough data for %s.\n"
+  extern bool supported_version (unsigned version,
+                                size_t num_supported, struct where *where, ...);
+  extern bool check_zero_padding (struct read_ctx *ctx,
+                                 enum message_category category,
+                                 struct where *wh);
 
   struct section_coverage
   {
@@ -256,4 +242,4 @@ extern "C"
 }
 #endif
 
-#endif/*DWARFLINT_HL_H*/
+#endif/*DWARFLINT_LOW_H*/
diff --git a/src/dwarflint/reloc.cc b/src/dwarflint/reloc.cc
new file mode 100644 (file)
index 0000000..af50a32
--- /dev/null
@@ -0,0 +1,251 @@
+#include "reloc.h"
+#include "messages.h"
+#include "low.h"
+#include <sstream>
+#include <libebl.h>
+#include <cassert>
+
+static struct where
+where_from_reloc (struct relocation_data *reloc, struct where *ref)
+{
+  struct where where
+    = WHERE (reloc->type == SHT_REL ? sec_rel : sec_rela, NULL);
+  where_reset_1 (&where, reloc->rel[reloc->index].offset);
+  where.ref = ref;
+  return where;
+}
+
+relocation *
+relocation_next (relocation_data *reloc, uint64_t offset,
+                struct where *where, enum skip_type st)
+{
+  if (reloc == NULL || reloc->rel == NULL)
+    return NULL;
+
+  while (reloc->index < reloc->size)
+    {
+      struct relocation *rel = reloc->rel + reloc->index;
+
+      /* This relocation entry is ahead of us.  */
+      if (rel->offset > offset)
+       return NULL;
+
+      reloc->index++;
+
+      if (rel->invalid)
+       continue;
+
+      if (rel->offset < offset)
+       {
+         if (st != skip_ok)
+           {
+             struct where reloc_where = where_from_reloc (reloc, where);
+             where_reset_2 (&reloc_where, rel->offset);
+             std::stringstream ss;
+             if (st == skip_unref)
+               ss << ": relocation targets "
+                 "unreferenced portion of the section.";
+             else
+               ss << ": relocation is mismatched.";
+             wr_error (&reloc_where, "%s\n", ss.str ().c_str ());
+           }
+         continue;
+       }
+
+      return rel;
+    }
+
+  return NULL;
+}
+
+/* Skip all relocation up to offset, and leave cursor pointing at that
+   relocation, so that next time relocation_next is called, relocation
+   matching that offset is immediately yielded.  */
+void
+relocation_skip (struct relocation_data *reloc, uint64_t offset,
+                struct where *where, enum skip_type st)
+{
+  if (reloc != NULL && reloc->rel != NULL)
+    relocation_next (reloc, offset - 1, where, st);
+}
+
+/* Skip all the remaining relocations.  */
+void
+relocation_skip_rest (struct relocation_data *reloc,
+                     enum section_id id)
+{
+  if (reloc->rel != NULL)
+    {
+      where wh = WHERE (id, NULL);
+      relocation_next (reloc, (uint64_t)-1, &wh,
+                      skip_mismatched);
+    }
+}
+
+/* SYMPTR may be NULL, otherwise (**SYMPTR) has to yield valid memory
+   location.  When the function returns, (*SYMPTR) is either NULL, in
+   which case we failed or didn't get around to obtain the symbol from
+   symbol table, or non-NULL, in which case the symbol was initialized.  */
+void
+relocate_one (struct elf_file *file,
+             struct relocation_data *reloc,
+             struct relocation *rel,
+             unsigned width, uint64_t *value, struct where *where,
+             enum section_id offset_into, GElf_Sym **symptr)
+{
+  if (rel->invalid)
+    return;
+
+  struct where reloc_where = where_from_reloc (reloc, where);
+  where_reset_2 (&reloc_where, rel->offset);
+  struct where reloc_ref_where = reloc_where;
+  reloc_ref_where.next = where;
+
+  GElf_Sym symbol_mem, *symbol;
+  if (symptr != NULL)
+    {
+      symbol = *symptr;
+      *symptr = NULL;
+    }
+  else
+    symbol = &symbol_mem;
+
+  if (offset_into == sec_invalid)
+    {
+      wr_message (mc_impact_3 | mc_reloc, &reloc_ref_where,
+                 ": relocates a datum that shouldn't be relocated.\n");
+      return;
+    }
+
+  Elf_Type type = ebl_reloc_simple_type (file->ebl, rel->type);
+
+  unsigned rel_width;
+  switch (type)
+    {
+    case ELF_T_BYTE:
+      rel_width = 1;
+      break;
+
+    case ELF_T_HALF:
+      rel_width = 2;
+      break;
+
+    case ELF_T_WORD:
+    case ELF_T_SWORD:
+      rel_width = 4;
+      break;
+
+    case ELF_T_XWORD:
+    case ELF_T_SXWORD:
+      rel_width = 8;
+      break;
+
+    default:
+      /* This has already been diagnosed during the isolated
+        validation of relocation section.  */
+      return;
+    };
+
+  if (rel_width != width)
+    wr_error (&reloc_ref_where,
+             ": %d-byte relocation relocates %d-byte datum.\n",
+             rel_width, width);
+
+  /* Tolerate that we might have failed to obtain the symbol table.  */
+  if (reloc->symdata != NULL)
+    {
+      symbol = gelf_getsym (reloc->symdata, rel->symndx, symbol);
+      if (symptr != NULL)
+       *symptr = symbol;
+      if (symbol == NULL)
+       {
+         wr_error (&reloc_where,
+                   ": couldn't obtain symbol #%d: %s.\n",
+                   rel->symndx, elf_errmsg (-1));
+         return;
+       }
+
+      uint64_t section_index = symbol->st_shndx;
+      /* XXX We should handle SHN_XINDEX here.  Or, instead, maybe it
+        would be possible to use dwfl, which already does XINDEX
+        translation.  */
+
+      /* For ET_REL files, we do section layout manually.  But we
+        don't update symbol table doing that.  So instead of looking
+        at symbol value, look at section address.  */
+      uint64_t sym_value = symbol->st_value;
+      if (file->ehdr.e_type == ET_REL
+         && ELF64_ST_TYPE (symbol->st_info) == STT_SECTION)
+       {
+         assert (sym_value == 0);
+         sym_value = file->sec[section_index].shdr.sh_addr;
+       }
+
+      /* It's target value, not section offset.  */
+      if (offset_into == rel_value
+         || offset_into == rel_address
+         || offset_into == rel_exec)
+       {
+         /* If a target value is what's expected, then complain if
+            it's not either SHN_ABS, an SHF_ALLOC section, or
+            SHN_UNDEF.  For data forms of address_size, an SHN_UNDEF
+            reloc is acceptable, otherwise reject it.  */
+         if (!(section_index == SHN_ABS
+               || (offset_into == rel_address
+                   && (section_index == SHN_UNDEF
+                       || section_index == SHN_COMMON))))
+           {
+             if (offset_into != rel_address && section_index == SHN_UNDEF)
+               wr_error (&reloc_where,
+                           ": relocation of an address is formed against SHN_UNDEF symbol"
+                           " (symtab index %d).\n", rel->symndx);
+             else
+               {
+                 GElf_Shdr *shdr = &file->sec[section_index].shdr;
+                 if ((shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
+                   wr_message (mc_reloc | mc_impact_3, &reloc_where,
+                               ": associated section %s isn't SHF_ALLOC.\n",
+                               file->sec[section_index].name);
+                 if (offset_into == rel_exec
+                     && (shdr->sh_flags & SHF_EXECINSTR) != SHF_EXECINSTR)
+                   /* This may still be kosher, but it's suspicious.  */
+                   wr_message (mc_reloc | mc_impact_2, &reloc_where,
+                               ": relocation against %s is suspicious, expected executable section.\n",
+                               file->sec[section_index].name);
+               }
+           }
+       }
+      else
+       {
+         enum section_id id;
+         /* If symtab[symndx].st_shndx does not match the expected
+            debug section's index, complain.  */
+         if (section_index >= file->size)
+           {
+             std::stringstream ss;
+             ss << ": invalid associated section #" << section_index << ".";
+             wr_error (&reloc_where, "%s\n", ss.str ().c_str ());
+           }
+         else if ((id = file->sec[section_index].id) != offset_into)
+           {
+             std::stringstream ss;
+             ss << ": relocation references section "
+                << (id != sec_invalid
+                    ? where_fmt (WHERE (id, NULL))
+                    : file->sec[section_index].name)
+                << ", but " << where_fmt (WHERE (offset_into, NULL))
+                << " was expected.";
+             wr_error (&reloc_where, "%s\n", ss.str ().c_str ());
+           }
+       }
+
+      /* Only do the actual relocation if we have ET_REL files.  For
+        non-ET_REL files, only do the above checking.  */
+      if (file->ehdr.e_type == ET_REL)
+       {
+         *value = rel->addend + sym_value;
+         if (rel_width == 4)
+           *value = *value & (uint64_t)(uint32_t)-1;
+       }
+    }
+}
diff --git a/src/dwarflint/reloc.h b/src/dwarflint/reloc.h
new file mode 100644 (file)
index 0000000..0804155
--- /dev/null
@@ -0,0 +1,96 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2008,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>.  */
+
+#ifndef DWARFLINT_RELOC_H
+#define DWARFLINT_RELOC_H
+
+#include "where.h"
+#include <libelf.h>
+#include <gelf.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#else
+# include <stdbool.h>
+#endif
+
+  struct elf_file;
+
+  struct relocation
+  {
+    uint64_t offset;
+    uint64_t addend;
+    int symndx;
+    int type;
+    bool invalid;      /* Whether this one relocation should be
+                          ignored.  Necessary so that we don't report
+                          invalid & missing relocation twice.  */
+  };
+
+  struct relocation_data
+  {
+    Elf_Data *symdata;         /* Symbol table associated with this
+                                  relocation section.  */
+    size_t type;               /* SHT_REL or SHT_RELA.  */
+
+    struct relocation *rel;    /* Array of relocations.  May be NULL
+                                  if there are no associated
+                                  relocation data.  */
+    size_t size;
+    size_t alloc;
+    size_t index;              /* Current index. */
+  };
+
+  enum skip_type
+  {
+    skip_unref = 0,
+    skip_mismatched = 1,
+    skip_ok,
+  };
+
+  struct relocation *relocation_next (struct relocation_data *reloc,
+                                     uint64_t offset, struct where *where,
+                                     enum skip_type st);
+
+  void relocation_skip (struct relocation_data *reloc, uint64_t offset,
+                       struct where *where, enum skip_type st);
+
+  void relocation_skip_rest (struct relocation_data *reloc,
+                            enum section_id id);
+
+  void relocate_one (struct elf_file *file,
+                    struct relocation_data *reloc,
+                    struct relocation *rel,
+                    unsigned width, uint64_t *value, struct where *where,
+                    enum section_id offset_into, GElf_Sym **symptr);
+
+#define PRI_LACK_RELOCATION ": %s seems to lack a relocation.\n"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif//DWARFLINT_RELOC_H
index 7aa3ee2ec18489ffde80f27742b604e5aa6e6351..bfa9e31575340c269a83d0bf8182d5efb3693527 100644 (file)
@@ -79,6 +79,12 @@ extern "C"
 
 #ifdef __cplusplus
 }
+
+inline const char *
+where_fmt (where const &wh)
+{
+  return where_fmt (&wh);
+}
 #endif
 
 #endif//DWARFLINT_WHERE_H