]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Improve location handling
authorPetr Machata <pmachata@redhat.com>
Sat, 17 Jan 2009 08:02:03 +0000 (09:02 +0100)
committerPetr Machata <pmachata@redhat.com>
Sat, 17 Jan 2009 08:02:03 +0000 (09:02 +0100)
* ... and simplify the code that deals with locations a /lot/. vararg
  parameters are now quite rare, hierarchical chaining of locations
  is trivial, DIE references carry their originator with themselves.

src/ChangeLog
src/dwarflint.c

index 72005bc80126eabbcd8fbe21b284e1f7ac88c06c..1dde49cf901a912f73ca9ae59c7af74062946c6e 100644 (file)
@@ -1,3 +1,12 @@
+2009-01-17  Petr Machata  <pmachata@redhat.com>
+
+       * dwarflint.c: Better location handling: references now carry
+       along where did they originate (i.e. which section, offset, etc.),
+       all messages are location-aware.
+       (struct where): New structure that captures location inside the
+       Dwarf file.  Can be chained to achieve chains of "caused by this
+       reference" messages ala GCC.
+
 2009-01-15  Petr Machata  <pmachata@redhat.com>
 
        * dwarflint.c: Implement validation of .debug_loc and references
index c8bfe9722c96657da992dcfc53f07f551d19e4b7..9bb8f4c2ed8d5700e33056da23ef721c36441777 100644 (file)
@@ -58,7 +58,6 @@ const char *argp_program_bug_address = PACKAGE_BUGREPORT;
 /* Definitions of arguments for argp functions.  */
 static const struct argp_option options[] =
 {
-
   { "strict", ARGP_strict, NULL, 0,
     N_("Be extremely strict, flag level 2 features."), 0 },
   { "quiet", 'q', NULL, 0, N_("Do not print anything if successful"), 0 },
@@ -93,6 +92,46 @@ static void
 process_file (int fd, Dwarf *dwarf, const char *fname,
              size_t size, bool only_one);
 
+/* Functions and data structures describing location in Dwarf.  */
+
+enum section_id
+{
+  sec_invalid = 0,
+  sec_info,
+  sec_abbrev,
+  sec_aranges,
+  sec_pubnames,
+  sec_pubtypes,
+  sec_str,
+  sec_loc,
+  sec_locexpr, /* Not a section, but a portion of file that contains a
+                 location expression.  */
+};
+
+struct where
+{
+  enum section_id section;
+  uint64_t addr1; // E.g. a CU offset.
+  uint64_t addr2; // E.g. a DIE address.
+  uint64_t addr3; // E.g. an attribute.
+  struct where *ref; // Related reference, e.g. an abbrev related to given DIE.
+  struct where *next; // Hierarchically superior location.
+};
+
+#define WHERE(SECTION, NEXT)                                           \
+  ((struct where)                                                      \
+    {(SECTION), (uint64_t)-1, (uint64_t)-1, (uint64_t)-1, NULL, NEXT})
+
+static const char *where_fmt (struct where *wh, char *ptr);
+static void where_fmt_chain (struct where *wh, const char *severity);
+static void where_reset_1 (struct where *wh, uint64_t addr);
+static void where_reset_2 (struct where *wh, uint64_t addr);
+static void where_reset_3 (struct where *wh, uint64_t addr);
+
+
+/* Functions and data structures for emitting various types of
+   messages.  */
+
 enum message_category
 {
   mc_none      = 0,
@@ -161,155 +200,98 @@ check_category (enum message_category cat)
   return accept_message (&warning_criteria, cat);
 }
 
-static char fmterr[] = "(fmt error)";
-
-static void __attribute__ ((format (printf, 2, 4)))
-vverror (const char *fmt1, const char *fmt2, va_list ap1, ...)
-{
-  fputs ("error: ", stdout);
-  vprintf (fmt1, ap1);
-
-  va_list ap2;
-  va_start (ap2, ap1);
-  vprintf (fmt2, ap2);
-  va_end (ap2);
-
-  ++error_count;
-}
-
-static void
-verror (const char *format, va_list ap)
-{
-  vverror (format, "%s", ap, "");
-}
-
-static void __attribute__ ((format (printf, 1, 2)))
-wr_error (const char *format, ...)
-{
-  va_list ap;
-  va_start (ap, format);
-  verror (format, ap);
-  va_end (ap);
-}
-
 static void
-vwarning (const char *format, va_list ap)
+wr_verror (struct where *wh, const char *format, va_list ap)
 {
-  fputs ("warning: ", stdout);
+  printf ("error: %s", where_fmt (wh, NULL));
   vprintf (format, ap);
+  where_fmt_chain (wh, "error");
   ++error_count;
 }
 
 static void
-vmessage (enum message_category category, const char *format, va_list ap)
+wr_vwarning (struct where *wh, const char *format, va_list ap)
 {
-  if (accept_message (&warning_criteria, category))
-    {
-      if (accept_message (&error_criteria, category))
-       verror (format, ap);
-      else
-       vwarning (format, ap);
-    }
+  printf ("warning: %s", where_fmt (wh, NULL));
+  vprintf (format, ap);
+  where_fmt_chain (wh, "warning");
+  ++error_count;
 }
 
-static void __attribute__ ((format (printf, 1, 2)))
-wr_warning (const char *format, ...)
+static void __attribute__ ((format (printf, 2, 3)))
+wr_error (struct where *wh, const char *format, ...)
 {
   va_list ap;
   va_start (ap, format);
-  vwarning (format, ap);
+  wr_verror (wh, format, ap);
   va_end (ap);
 }
 
 static void __attribute__ ((format (printf, 2, 3)))
-message (enum message_category category, const char *format, ...)
+wr_warning (struct where *wh, const char *format, ...)
 {
   va_list ap;
   va_start (ap, format);
-  vmessage (category, format, ap);
+  wr_vwarning (wh, format, ap);
   va_end (ap);
 }
 
-static void
-vfmessage (enum message_category category,
-          const char *fmt1, const char *fmt2, va_list ap1, ...)
+static void __attribute__ ((format (printf, 3, 4)))
+wr_message (enum message_category category, struct where *wh,
+            const char *format, ...)
 {
-  if (!accept_message (&warning_criteria, category))
-    return;
-
-  char *buf1, *buf2;
-  if (vasprintf (&buf1, fmt1, ap1) < 0)
-    buf1 = NULL;
-
-  va_list ap2;
-  va_start (ap2, ap1);
-  if (vasprintf (&buf2, fmt2, ap2) < 0)
-    buf2 = NULL;
-  message (category, "%s%s", buf1 ?: fmterr, buf2 ?: fmterr);
-  va_end (ap2);
-
-  free (buf1);
-  free (buf2);
+  va_list ap;
+  va_start (ap, format);
+  if (accept_message (&warning_criteria, category))
+    {
+      if (accept_message (&error_criteria, category))
+       wr_verror (wh, format, ap);
+      else
+       wr_vwarning (wh, format, ap);
+    }
+  va_end (ap);
 }
 
 static void
-format_padding_message (enum message_category category,
-                       uint64_t start, uint64_t end,
-                       char *kind, const char *format, va_list ap)
+wr_format_padding_message (enum message_category category,
+                          struct where *wh,
+                          uint64_t start, uint64_t end, char *kind)
 {
-  vfmessage (category, format,
-            ": 0x%" PRIx64 "..0x%" PRIx64 ": %s.\n",
-            ap, start, end, kind);
+  wr_message (category, wh,
+             ": 0x%" PRIx64 "..0x%" PRIx64 ": %s.\n", start, end, kind);
 }
 
 static void
-format_leb128_message (int st, const char *format, const char *what, va_list ap)
+wr_format_leb128_message (int st, struct where *wh, const char *what)
 {
   enum message_category category = mc_leb128 | mc_acc_bloat | mc_impact_3;
   if (st == 0 || (st > 0 && !accept_message (&warning_criteria, category)))
     return;
 
   if (st < 0)
-    vverror (format, ": can't read %s.\n", ap, what ?: "");
+    wr_error (wh, ": can't read %s.\n", what);
   else if (st > 0)
-    vfmessage (category, format,
-              ": unnecessarily long encoding of %s.\n", ap, what ?: "");
+    wr_message (category, wh, ": unnecessarily long encoding of %s.\n", what);
 }
 
 static void
-vmessage_padding_0 (enum message_category category,
-                   uint64_t start, uint64_t end,
-                   const char *format, va_list ap)
+wr_message_padding_0 (enum message_category category,
+                     struct where *wh,
+                     uint64_t start, uint64_t end)
 {
-  format_padding_message (category | mc_acc_bloat | mc_impact_1,
-                         start, end,
-                         "unnecessary padding with zero bytes",
-                         format, ap);
+  wr_format_padding_message (category | mc_acc_bloat | mc_impact_1,
+                            wh, start, end,
+                            "unnecessary padding with zero bytes");
 }
 
 static void
-message_padding_0 (enum message_category category,
-                  uint64_t start, uint64_t end,
-                  const char *format, ...)
+wr_message_padding_n0 (enum message_category category,
+                      struct where *wh,
+                      uint64_t start, uint64_t end)
 {
-  va_list ap;
-  va_start (ap, format);
-  vmessage_padding_0 (category, start, end, format, ap);
-  va_end (ap);
-}
-
-static void
-message_padding_n0 (enum message_category category,
-                   uint64_t start, uint64_t end,
-                   const char *format, ...)
-{
-  va_list ap;
-  va_start (ap, format);
-  format_padding_message (category | mc_acc_bloat | mc_impact_2,
-                         start, end,
-                         "unreferenced non-zero bytes",
-                         format, ap);
-  va_end (ap);
+  wr_format_padding_message (category | mc_acc_bloat | mc_impact_1,
+                            wh, start, end,
+                            "unreferenced non-zero bytes");
 }
 
 /* True if no message is to be printed if the run is succesful.  */
@@ -359,7 +341,8 @@ main (int argc, char *argv[])
       /* Create an `Elf' descriptor.  */
       Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
       if (elf == NULL)
-       wr_error (gettext ("cannot generate Elf descriptor: %s\n"),
+       wr_error (NULL,
+                 gettext ("cannot generate Elf descriptor: %s\n"),
                  elf_errmsg (-1));
       else
        {
@@ -368,7 +351,8 @@ main (int argc, char *argv[])
          if (dwarf == NULL)
            {
              if (!tolerate_nodebug)
-               wr_error (gettext ("cannot generate Dwarf descriptor: %s\n"),
+               wr_error (NULL,
+                         gettext ("cannot generate Dwarf descriptor: %s\n"),
                          dwarf_errmsg (-1));
            }
          else
@@ -386,12 +370,14 @@ main (int argc, char *argv[])
 
              /* Now we can close the descriptor.  */
              if (dwarf_end (dwarf) != 0)
-               wr_error (gettext ("error while closing Dwarf descriptor: %s\n"),
+               wr_error (NULL,
+                         gettext ("error while closing Dwarf descriptor: %s\n"),
                          dwarf_errmsg (-1));
            }
 
          if (elf_end (elf) != 0)
-           wr_error (gettext ("error while closing Elf descriptor: %s\n"),
+           wr_error (NULL,
+                     gettext ("error while closing Elf descriptor: %s\n"),
                      elf_errmsg (-1));
 
          if (prev_error_count == error_count && !be_quiet)
@@ -453,33 +439,9 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
       }                                                                \
   } while (0)
 
-#define PRI_D_INFO ".debug_info: "
-#define PRI_D_ABBREV ".debug_abbrev: "
-#define PRI_D_ARANGES ".debug_aranges: "
-#define PRI_D_PUBNAMES ".debug_pubnames: "
-#define PRI_D_PUBTYPES ".debug_pubtypes: "
-#define PRI_D_STR ".debug_str: "
-#define PRI_D_LOC ".debug_loc: "
-#define PRI_LOCEXPR "location expression: "
-
 #define PRI_CU "CU 0x%" PRIx64
 #define PRI_DIE "DIE 0x%" PRIx64
-#define PRI_ATTR "attribute 0x%" PRIx64
-#define PRI_ABBR "abbrev 0x%" PRIx64
-#define PRI_ARANGETAB "arange table 0x%" PRIx64
-#define PRI_RECORD "record 0x%" PRIx64
-#define PRI_PUBSET "pubset 0x%" PRIx64 // for pubnames and pubtypes
-
-#define PRI_CU_DIE PRI_CU ", " PRI_DIE
-#define PRI_CU_DIE_ABBR_ATTR PRI_CU_DIE ", " PRI_ABBR ", " PRI_ATTR
-#define PRI_ABBR_ATTR PRI_ABBR ", " PRI_ATTR
-#define PRI_ARANGETAB_CU PRI_ARANGETAB " (for " PRI_CU ")"
-#define PRI_ARANGETAB_CU_RECORD PRI_ARANGETAB_CU ", " PRI_RECORD
-#define PRI_PUBSET_CU PRI_PUBSET " (for " PRI_CU ")"
-#define PRI_PUBSET_CU_RECORD PRI_PUBSET_CU ", " PRI_RECORD
-
 #define PRI_NOT_ENOUGH ": not enough data for %s.\n"
-#define PRI_CAUSE ": caused by this reference.\n"
 
 /* Functions and data structures related to bounds-checked
    reading.  */
@@ -534,9 +496,9 @@ struct abbrev
   /* Attributes.  */
   struct abbrev_attrib
   {
-    uint64_t offset;
     uint16_t name;
     uint8_t form;
+    struct where where;
   } *attribs;
   size_t size;
   size_t alloc;
@@ -583,7 +545,7 @@ static void addr_record_free (struct addr_record *ar);
 struct ref
 {
   uint64_t addr; // Referree address
-  uint64_t who;  // Referrer address
+  struct where who;  // Referrer
 };
 
 struct ref_record
@@ -593,7 +555,7 @@ struct ref_record
   struct ref *refs;
 };
 
-static void ref_record_add (struct ref_record *rr, uint64_t addr, uint64_t who);
+static void ref_record_add (struct ref_record *rr, uint64_t addr, struct where *referrer);
 static void ref_record_free (struct ref_record *rr);
 
 
@@ -614,7 +576,7 @@ struct coverage
 
 struct hole_info
 {
-  const char *section;
+  enum section_id section;
   enum message_category category;
   void *d_buf;
 };
@@ -637,6 +599,7 @@ struct cu
   uint64_t length;
   struct addr_record die_addrs; // Addresses where DIEs begin in this CU.
   struct ref_record die_refs;   // DIE references into other CUs from this CU.
+  struct where where;           // Where was this section defined.
   struct cu *next;
 };
 
@@ -664,8 +627,110 @@ static bool check_aranges_structural (struct read_ctx *ctx,
                                      struct cu *cu_chain);
 static bool check_pub_structural (struct read_ctx *ctx,
                                  struct cu *cu_chain,
-                                 const char *secname);
+                                 enum section_id sec);
+
+
+static const char *where_fmt (struct where *wh, char *ptr)
+{
+  if (wh == NULL)
+    return "";
+
+  static char buf[256];
+
+#define SETUP_ADDR(N)                                                  \
+  char *addr##N##s;                                                    \
+  if (wh->addr##N == (uint64_t)-1)                                     \
+    addr##N##s = NULL;                                                 \
+  else if (asprintf (&addr##N##s, "0x%" PRIx64, wh->addr##N) < 0)      \
+    addr##N##s = "(fmt error)"
+
+  SETUP_ADDR (1);
+  SETUP_ADDR (2);
+  SETUP_ADDR (3);
+#undef SETUP_ADDR
+
+  struct section_info
+  {
+    const char *name;
+    const char *addr1n;
+    const char *addr2n;
+    const char *addr3n;
+  };
+
+  static struct section_info section_names[] = {
+    [sec_info] = {".debug_info", "CU", "DIE", NULL},
+    [sec_abbrev] = {".debug_abbrev", "abbrev table", "abbrev", "attribute"},
+    [sec_aranges] = {".debug_aranges", "arange table", "arange", NULL},
+    [sec_pubnames] = {".debug_pubnames", "pubname table", "record", NULL},
+    [sec_pubtypes] = {".debug_pubtypes", "pubtype table", "record", NULL},
+    [sec_str] = {".debug_str", "offset", NULL, NULL},
+    [sec_loc] = {".debug_loc", "loclist", "offset", NULL},
+    [sec_locexpr] = {"location expression", "offset", NULL, NULL},
+  };
+  assert (wh->section < sizeof (section_names) / sizeof (*section_names));
+  struct section_info *inf = section_names + wh->section;
+  assert (inf->name);
+
+  char *orig = ptr;
+  if (ptr == NULL)
+    ptr = stpcpy (stpcpy (buf, inf->name), addr1s != NULL ? ": " : "");
+
+  assert (addr1s != NULL ? inf->addr1n != NULL : true);
+  assert (addr2s != NULL ? inf->addr2n != NULL : true);
+  assert (addr3s != NULL ? inf->addr3n != NULL : true);
+  assert (addr3s != NULL ? addr2s != NULL : true);
+  assert (addr2s != NULL ? addr1s != NULL : true);
+
+  if (addr1s != NULL)
+    ptr = stpcpy (stpcpy (stpcpy (ptr, inf->addr1n), " "), addr1s);
+  if (addr2s != NULL)
+    ptr = stpcpy (stpcpy (stpcpy (stpcpy (ptr, ", "),
+                                 inf->addr2n), " "), addr2s);
+  if (addr3s != NULL)
+    ptr = stpcpy (stpcpy (stpcpy (stpcpy (ptr, ", "),
+                                 inf->addr3n), " "), addr3s);
+
+  if (wh->ref != NULL)
+    {
+      ptr = stpcpy (ptr, " (");
+      ptr = (char *)where_fmt (wh->ref, ptr);
+      *ptr++ = ')';
+    }
+
+  if (orig == NULL)
+    return buf;
+  else
+    return ptr;
+}
+
+static void
+where_fmt_chain (struct where *wh, const char *severity)
+{
+  if (wh != NULL)
+    for (struct where *it = wh->next; it != NULL; it = it->next)
+      printf ("%s: %s: caused by this reference.\n",
+             severity, where_fmt (it, NULL));
+}
+
+static void
+where_reset_1 (struct where *wh, uint64_t addr)
+{
+  wh->addr1 = addr;
+  wh->addr2 = wh->addr3 = (uint64_t)-1;
+}
+
+static void
+where_reset_2 (struct where *wh, uint64_t addr)
+{
+  wh->addr2 = addr;
+  wh->addr3 = (uint64_t)-1;
+}
 
+static void
+where_reset_3 (struct where *wh, uint64_t addr)
+{
+  wh->addr3 = addr;
+}
 
 static void
 process_file (int fd __attribute__((unused)),
@@ -704,7 +769,7 @@ process_file (int fd __attribute__((unused)),
        invalid_elf:
          /* A "can't happen".  libdw already managed to parse the Elf
             file when constructing the Dwarf object.  */
-         wr_error ("broken Elf");
+         wr_error (NULL, "broken Elf");
          break;
        }
       if (strcmp (scnname, ".debug_pubtypes") == 0)
@@ -724,7 +789,7 @@ process_file (int fd __attribute__((unused)),
     }
   else if (!tolerate_nodebug)
     /* Hard error, not a message.  We can't debug without this.  */
-    wr_error (".debug_abbrev data not found.\n");
+    wr_error (NULL, ".debug_abbrev data not found.\n");
 
   struct cu *cu_chain = NULL;
 
@@ -740,7 +805,7 @@ process_file (int fd __attribute__((unused)),
        }
       else if (!tolerate_nodebug)
        /* Hard error, not a message.  We can't debug without this.  */
-       wr_error (".debug_info data not found.\n");
+       wr_error (NULL, ".debug_info data not found.\n");
     }
 
   if (aranges_data != NULL)
@@ -749,26 +814,26 @@ process_file (int fd __attribute__((unused)),
       check_aranges_structural (&ctx, cu_chain);
     }
   else
-    message (mc_impact_4 | mc_acc_suboptimal | mc_elf,
-            ".debug_aranges data not found.\n");
+    wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf,
+               NULL, ".debug_aranges data not found.\n");
 
   if (pubnames_data != NULL)
     {
       read_ctx_init (&ctx, dwarf, pubnames_data);
-      check_pub_structural (&ctx, cu_chain, PRI_D_PUBNAMES);
+      check_pub_structural (&ctx, cu_chain, sec_pubnames);
     }
   else
-    message (mc_impact_4 | mc_acc_suboptimal | mc_elf,
-            PRI_D_PUBNAMES "data not found.\n");
+    wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf,
+               &WHERE (sec_pubnames, NULL), ": data not found.\n");
 
   if (pubtypes_data != NULL)
     {
       read_ctx_init (&ctx, dwarf, pubtypes_data);
-      check_pub_structural (&ctx, cu_chain, PRI_D_PUBTYPES);
+      check_pub_structural (&ctx, cu_chain, sec_pubtypes);
     }
   else
-    message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_pubtypes,
-            PRI_D_PUBTYPES "data not found.\n");
+    wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_pubtypes,
+               &WHERE (sec_pubtypes, NULL), ": data not found.\n");
 
   cu_free (cu_chain);
   abbrev_table_free (abbrev_chain);
@@ -860,25 +925,14 @@ read_ctx_read_uleb128 (struct read_ctx *ctx, uint64_t *ret)
 }
 
 static bool
-vchecked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret,
-                      const char *format, const char *what, va_list ap)
+wr_checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret,
+                         struct where *wh, const char *what)
 {
   int st = read_ctx_read_uleb128 (ctx, ret);
-  format_leb128_message (st, format, what, ap);
+  wr_format_leb128_message (st, wh, what);
   return st >= 0;
 }
 
-static bool __attribute__ ((format (printf, 3, 5)))
-checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret,
-                     const char *format, const char *what, ...)
-{
-  va_list ap;
-  va_start (ap, what);
-  bool retval = vchecked_read_uleb128 (ctx, ret, format, what, ap);
-  va_end (ap);
-  return retval;
-}
-
 static int
 read_ctx_read_sleb128 (struct read_ctx *ctx, int64_t *ret)
 {
@@ -916,25 +970,14 @@ read_ctx_read_sleb128 (struct read_ctx *ctx, int64_t *ret)
 }
 
 static bool
-vchecked_read_sleb128 (struct read_ctx *ctx, int64_t *ret,
-                      const char *format, const char *what, va_list ap)
+wr_checked_read_sleb128 (struct read_ctx *ctx, int64_t *ret,
+                        struct where *wh, const char *what)
 {
   int st = read_ctx_read_sleb128 (ctx, ret);
-  format_leb128_message (st, format, what, ap);
+  wr_format_leb128_message (st, wh, what);
   return st >= 0;
 }
 
-static bool __attribute__ ((format (printf, 3, 5)))
-checked_read_sleb128 (struct read_ctx *ctx, int64_t *ret,
-                     const char *format, const char *what, ...)
-{
-  va_list ap;
-  va_start (ap, what);
-  bool retval = vchecked_read_sleb128 (ctx, ret, format, what, ap);
-  va_end (ap);
-  return retval;
-}
-
 static bool
 read_ctx_read_2ubyte (struct read_ctx *ctx, uint16_t *ret)
 {
@@ -979,7 +1022,7 @@ read_ctx_read_offset (struct read_ctx *ctx, bool dwarf64, uint64_t *ret)
     return false;
 
   if (ret != NULL)
-    *ret = v;
+    *ret = (uint64_t)v;
   return true;
 }
 
@@ -1131,40 +1174,50 @@ abbrev_table_load (struct read_ctx *ctx)
 {
   struct abbrev_table *section_chain = NULL;
   struct abbrev_table *section = NULL;
-  uint64_t section_off = 0;
+  struct where where = WHERE (sec_abbrev, NULL);
+  where.addr1 = 0;
 
   while (!read_ctx_eof (ctx))
     {
-      uint64_t abbr_off, prev_abbr_off = (uint64_t)-1;
-      uint64_t abbr_code, prev_abbr_code = (uint64_t)-1;
-      uint64_t zero_seq_off = (uint64_t)-1;
+      uint64_t abbr_off;
+      uint64_t abbr_code;
+      {
+       uint64_t prev_abbr_off = (uint64_t)-1;
+       uint64_t prev_abbr_code = (uint64_t)-1;
+       uint64_t zero_seq_off = (uint64_t)-1;
 
-      while (!read_ctx_eof (ctx))
-       {
-         abbr_off = read_ctx_get_offset (ctx);
+       while (!read_ctx_eof (ctx))
+         {
+           abbr_off = read_ctx_get_offset (ctx);
+           where_reset_2 (&where, abbr_off);
 
-         /* Abbreviation code.  */
-         if (!checked_read_uleb128 (ctx, &abbr_code,
-                                    PRI_D_ABBREV PRI_ABBR,
-                                    "abbrev code", abbr_off))
-           goto free_and_out;
+           /* Abbreviation code.  */
+           if (!wr_checked_read_uleb128 (ctx, &abbr_code, &where,
+                                         "abbrev code"))
+             goto free_and_out;
 
-         if (abbr_code == 0 && prev_abbr_code == 0
-             && zero_seq_off == (uint64_t)-1)
-           zero_seq_off = prev_abbr_off;
+           if (abbr_code == 0 && prev_abbr_code == 0
+               && zero_seq_off == (uint64_t)-1)
+             zero_seq_off = prev_abbr_off;
 
-         if (abbr_code != 0)
-           break;
-         else
-           section = NULL;
+           if (abbr_code != 0)
+             break;
+           else
+             section = NULL;
 
-         prev_abbr_code = abbr_code;
-         prev_abbr_off = abbr_off;
-       }
+           prev_abbr_code = abbr_code;
+           prev_abbr_off = abbr_off;
+         }
 
-      if (zero_seq_off != (uint64_t)-1)
-       message_padding_0 (mc_abbrevs, zero_seq_off, prev_abbr_off - 1,
-                          PRI_D_ABBREV PRI_ABBR, section_off);
+       if (zero_seq_off != (uint64_t)-1)
+         {
+           /* Don't report abbrev address, this is section-wide
+              padding.  */
+           struct where wh = WHERE (where.section, NULL);
+           wr_message_padding_0 (mc_abbrevs, &wh,
+                                 zero_seq_off, prev_abbr_off - 1);
+         }
+      }
 
       if (read_ctx_eof (ctx))
        break;
@@ -1175,7 +1228,9 @@ abbrev_table_load (struct read_ctx *ctx)
          section->offset = abbr_off;
          section->next = section_chain;
          section_chain = section;
-         section_off = abbr_off;
+
+         where_reset_1 (&where, abbr_off);
+         where_reset_2 (&where, abbr_off);
        }
       REALLOC (section, abbr);
 
@@ -1186,16 +1241,12 @@ abbrev_table_load (struct read_ctx *ctx)
 
       /* Abbreviation tag.  */
       uint64_t abbr_tag;
-      if (!checked_read_uleb128 (ctx, &abbr_tag,
-                                PRI_D_ABBREV PRI_ABBR,
-                                "abbrev tag", abbr_off))
+      if (!wr_checked_read_uleb128 (ctx, &abbr_tag, &where, "abbrev tag"))
        goto free_and_out;
 
       if (abbr_tag > DW_TAG_hi_user)
        {
-         wr_error (PRI_D_ABBREV PRI_ABBR
-                   ": invalid abbrev tag 0x%" PRIx64 ".\n",
-                   abbr_off, abbr_tag);
+         wr_error (&where, ": invalid abbrev tag 0x%" PRIx64 ".\n", abbr_tag);
          goto free_and_out;
        }
       cur->tag = (typeof (cur->tag))abbr_tag;
@@ -1204,17 +1255,15 @@ abbrev_table_load (struct read_ctx *ctx)
       uint8_t has_children;
       if (!read_ctx_read_ubyte (ctx, &has_children))
        {
-         wr_error (PRI_D_ABBREV PRI_ABBR
-                   ": can't read abbrev has_children.\n", abbr_off);
+         wr_error (&where, ": can't read abbrev has_children.\n");
          goto free_and_out;
        }
 
       if (has_children != DW_CHILDREN_no
          && has_children != DW_CHILDREN_yes)
        {
-         wr_error (PRI_D_ABBREV PRI_ABBR
-                   ": invalid has_children value 0x%x.\n",
-                   abbr_off, cur->has_children);
+         wr_error (&where,
+                   ": invalid has_children value 0x%x.\n", cur->has_children);
          goto free_and_out;
        }
       cur->has_children = has_children == DW_CHILDREN_yes;
@@ -1225,18 +1274,15 @@ abbrev_table_load (struct read_ctx *ctx)
        {
          uint64_t attr_off = read_ctx_get_offset (ctx);
          uint64_t attrib_name, attrib_form;
+         where_reset_3 (&where, attr_off);
 
          /* Load attribute name and form.  */
-         if (!checked_read_uleb128 (ctx, &attrib_name,
-                                    PRI_D_ABBREV PRI_ABBR_ATTR,
-                                    "attribute name",
-                                    abbr_off, attr_off))
+         if (!wr_checked_read_uleb128 (ctx, &attrib_name, &where,
+                                       "attribute name"))
            goto free_and_out;
 
-         if (!checked_read_uleb128 (ctx, &attrib_form,
-                                    PRI_D_ABBREV PRI_ABBR_ATTR,
-                                    "attribute form",
-                                    abbr_off, attr_off))
+         if (!wr_checked_read_uleb128 (ctx, &attrib_form, &where,
+                                       "attribute form"))
            goto free_and_out;
 
          null_attrib = attrib_name == 0 && attrib_form == 0;
@@ -1247,17 +1293,15 @@ abbrev_table_load (struct read_ctx *ctx)
              /* Otherwise validate name and form.  */
              if (attrib_name > DW_AT_hi_user)
                {
-                 wr_error (PRI_D_ABBREV PRI_ABBR_ATTR
-                           ": invalid name 0x%" PRIx64 ".\n",
-                           abbr_off, attr_off, attrib_name);
+                 wr_error (&where,
+                           ": invalid name 0x%" PRIx64 ".\n", attrib_name);
                  goto free_and_out;
                }
 
              if (!attrib_form_valid (attrib_form))
                {
-                 wr_error (PRI_D_ABBREV PRI_ABBR_ATTR
-                           ": invalid form 0x%" PRIx64 ".\n",
-                           abbr_off, attr_off, attrib_form);
+                 wr_error (&where,
+                           ": invalid form 0x%" PRIx64 ".\n", attrib_form);
                  goto free_and_out;
                }
            }
@@ -1275,49 +1319,45 @@ abbrev_table_load (struct read_ctx *ctx)
          if (attrib_name == DW_AT_sibling)
            {
              if (sibling_attr != 0)
-               wr_error (PRI_D_ABBREV PRI_ABBR_ATTR
+               wr_error (&where,
                          ": Another DW_AT_sibling attribute in one abbreviation. "
-                         "(First was 0x%" PRIx64 ".)\n",
-                         abbr_off, attr_off, sibling_attr);
+                         "(First was 0x%" PRIx64 ".)\n", sibling_attr);
              else
                {
                  assert (attr_off > 0);
                  sibling_attr = attr_off;
 
                  if (!cur->has_children)
-                   message (mc_die_rel_sib | mc_acc_bloat | mc_impact_1,
-                            PRI_D_ABBREV PRI_ABBR_ATTR
-                            ": Excessive DW_AT_sibling attribute at childless abbrev.\n",
-                            abbr_off, attr_off);
+                   wr_message (mc_die_rel_sib | mc_acc_bloat | mc_impact_1,
+                               &where,
+                               ": Excessive DW_AT_sibling attribute at childless abbrev.\n");
                }
 
              switch (check_sibling_form (attrib_form))
                {
                case -1:
-                 message (mc_die_rel_sib | mc_impact_2,
-                          PRI_D_ABBREV PRI_ABBR_ATTR
-                          ": DW_AT_sibling attribute with form DW_FORM_ref_addr.\n",
-                          abbr_off, attr_off);
+                 wr_message (mc_die_rel_sib | mc_impact_2, &where,
+                             ": DW_AT_sibling attribute with form DW_FORM_ref_addr.\n");
                  break;
 
                case -2:
-                 wr_error (PRI_D_ABBREV PRI_ABBR_ATTR
+                 wr_error (&where,
                            ": DW_AT_sibling attribute with non-reference form \"%s\".\n",
-                           abbr_off, attr_off, dwarf_form_string (attrib_form));
+                           dwarf_form_string (attrib_form));
                };
            }
          /* Similar for DW_AT_location.  */
          else if (is_location_attrib (attrib_name))
            {
              if (!check_abbrev_location_form (attrib_form))
-               wr_error (PRI_D_ABBREV PRI_ABBR_ATTR
+               wr_error (&where,
                          ": location attribute with invalid form \"%s\".\n",
-                         abbr_off, attr_off, dwarf_form_string (attrib_form));
+                         dwarf_form_string (attrib_form));
            }
 
          acur->name = attrib_name;
          acur->form = attrib_form;
-         acur->offset = attr_off;
+         acur->where = where;
        }
       while (!null_attrib);
     }
@@ -1439,12 +1479,12 @@ addr_record_free (struct addr_record *ar)
 
 
 static void
-ref_record_add (struct ref_record *rr, uint64_t addr, uint64_t who)
+ref_record_add (struct ref_record *rr, uint64_t addr, struct where *referrer)
 {
   REALLOC (rr, refs);
   struct ref *ref = rr->refs + rr->size++;
   ref->addr = addr;
-  ref->who = who;
+  ref->who = *referrer;
 }
 
 static void
@@ -1575,11 +1615,13 @@ found_hole (uint64_t begin, uint64_t end, void *data)
       }
 
   if (all_zeroes)
-    message_padding_0 (info->category, begin, end, info->section);
+    wr_message_padding_0 (info->category, &WHERE (info->section, NULL),
+                         begin, end);
   else
-    /* XXX: This is actually lying in case that the unreferenced
-       portion is composed of sequences of zeroes and non-zeroes.  */
-    message_padding_n0 (info->category, begin, end, info->section);
+    /* XXX: This actually lies when the unreferenced portion is
+       composed of sequences of zeroes and non-zeroes.  */
+    wr_message_padding_n0 (info->category, &WHERE (info->section, NULL),
+                          begin, end);
 }
 
 static void
@@ -1622,9 +1664,8 @@ check_die_references (struct cu *cu,
       struct ref *ref = die_refs->refs + i;
       if (!addr_record_has_addr (&cu->die_addrs, ref->addr))
        {
-         wr_error (PRI_D_INFO PRI_CU_DIE
-                   ": unresolved reference to " PRI_DIE ".\n",
-                   cu->offset, ref->who, ref->addr);
+         wr_error (&ref->who,
+                   ": unresolved reference to " PRI_DIE ".\n", ref->addr);
          retval = false;
        }
     }
@@ -1649,67 +1690,54 @@ check_global_die_references (struct cu *cu_chain)
 
        if (ref_cu == NULL)
          {
-           wr_error (PRI_D_INFO PRI_CU_DIE
-                     ": unresolved (non-CU-local) reference to "
-                     PRI_DIE ".\n",
-                     it->offset, ref->who, ref->addr);
+           wr_error (&ref->who,
+                     ": unresolved (non-CU-local) reference to " PRI_DIE ".\n",
+                     ref->addr);
            retval = false;
          }
        else if (ref_cu == it)
-         message (mc_impact_2 | mc_acc_suboptimal | mc_die_rel_ref,
-                  PRI_D_INFO PRI_CU_DIE
-                  ": local reference to " PRI_DIE " formed as global.\n",
-                  it->offset, ref->who, ref->addr);
+         wr_message (mc_impact_2 | mc_acc_suboptimal | mc_die_rel_ref,
+                     &ref->who,
+                     ": local reference to " PRI_DIE " formed as global.\n",
+                     ref->addr);
       }
 
   return retval;
 }
 
 static bool
-read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
-                bool *dwarf_64p, const char *format, ...)
+wr_read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
+                   bool *dwarf_64p, struct where *wh)
 {
-  bool retval = true;
-
-  va_list ap;
-  va_start (ap, format);
-  char *buf = NULL;
-
   if (size32 == DWARF3_LENGTH_64_BIT)
     {
       if (!read_ctx_read_8ubyte (ctx, sizep))
        {
-         if (vasprintf (&buf, format, ap) < 0)
-           buf = NULL;
-         wr_error ("%s: can't read 64bit CU length.n", buf ?: fmterr);
-         retval = false;
-         goto out;
+         wr_error (wh, ": can't read 64bit CU length.\n");
+         return false;
        }
 
       *dwarf_64p = true;
     }
   else if (size32 >= DWARF3_LENGTH_MIN_ESCAPE_CODE)
     {
-      if (vasprintf (&buf, format, ap) < 0)
-       buf = NULL;
-      wr_error ("%s: unrecognized CU length escape value: %"
-               PRIx32 ".n", buf ?: fmterr, size32);
-      retval = false;
-      goto out;
+      wr_error (wh, ": unrecognized CU length escape value: "
+               "%" PRIx32 ".\n", size32);
+      return false;
     }
   else
-    *sizep = size32;
+    {
+      *sizep = size32;
+      *dwarf_64p = false;
+    }
 
- out:
-  va_end (ap);
-  free (buf);
-  return retval;
+  return true;
 }
 
-static bool __attribute__ ((format (printf, 3, 4)))
-check_zero_padding (struct read_ctx *ctx,
-                   enum message_category category,
-                   const char *format, ...)
+static bool
+wr_check_zero_padding (struct read_ctx *ctx,
+                      enum message_category category,
+                      struct where *wh)
 {
   assert (ctx->ptr != ctx->end);
   const unsigned char *save_ptr = ctx->ptr;
@@ -1720,13 +1748,9 @@ check_zero_padding (struct read_ctx *ctx,
        return false;
       }
 
-  va_list ap;
-  va_start (ap, format);
-  vmessage_padding_0 (category,
-                     (uint64_t)(save_ptr - ctx->begin),
-                     (uint64_t)(ctx->end - ctx->begin),
-                     format, ap);
-  va_end (ap);
+  wr_message_padding_0 (category, wh,
+                       (uint64_t)(save_ptr - ctx->begin),
+                       (uint64_t)(ctx->end - ctx->begin));
   return true;
 }
 
@@ -1763,11 +1787,13 @@ check_debug_info_structural (struct read_ctx *ctx,
   while (!read_ctx_eof (ctx))
     {
       const unsigned char *cu_begin = ctx->ptr;
-      uint64_t cu_off = read_ctx_get_offset (ctx);
+      struct where where = WHERE (sec_info, NULL);
+      where_reset_1 (&where, read_ctx_get_offset (ctx));
 
       struct cu *cur = xcalloc (1, sizeof (*cur));
-      cur->offset = cu_off;
+      cur->offset = where.addr1;
       cur->next = cu_chain;
+      cur->where = where;
       cu_chain = cur;
 
       uint32_t size32;
@@ -1777,23 +1803,20 @@ check_debug_info_structural (struct read_ctx *ctx,
       /* Reading CU header is a bit tricky, because we don't know if
         we have run into (superfluous but allowed) zero padding.  */
       if (!read_ctx_need_data (ctx, 4)
-         && check_zero_padding (ctx, mc_die_other,
-                                PRI_D_INFO PRI_CU, cu_off))
+         && wr_check_zero_padding (ctx, mc_die_other, &where))
        break;
 
       /* CU length.  */
       if (!read_ctx_read_4ubyte (ctx, &size32))
        {
-         wr_error (PRI_D_INFO PRI_CU ": can't read CU length.\n", cu_off);
+         wr_error (&where, ": can't read CU length.\n");
          success = false;
          break;
        }
-      if (size32 == 0 && check_zero_padding (ctx, mc_die_other,
-                                            PRI_D_INFO PRI_CU, cu_off))
+      if (size32 == 0 && wr_check_zero_padding (ctx, mc_die_other, &where))
        break;
 
-      if (!read_size_extra (ctx, size32, &size, &dwarf_64,
-                           PRI_D_INFO PRI_CU, cu_off))
+      if (!wr_read_size_extra (ctx, size32, &size, &dwarf_64, &where))
        {
          success = false;
          break;
@@ -1801,8 +1824,9 @@ check_debug_info_structural (struct read_ctx *ctx,
 
       if (!read_ctx_need_data (ctx, size))
        {
-         wr_error (PRI_D_INFO PRI_CU ": section doesn't have enough data"
-                   " to read CU of size %" PRIx64 ".\n", cu_off, size);
+         wr_error (&where,
+                   ": section doesn't have enough data"
+                   " to read CU of size %" PRIx64 ".\n", size);
          ctx->ptr = ctx->end;
          success = false;
          break;
@@ -1815,8 +1839,8 @@ check_debug_info_structural (struct read_ctx *ctx,
       uint64_t cu_header_size = 2 + (dwarf_64 ? 8 : 4) + 1;
       if (size < cu_header_size)
        {
-         wr_error (PRI_D_INFO PRI_CU ": claimed length of %" PRIx64
-                   " doesn't even cover CU header.\n", cu_off, size);
+         wr_error (&where, ": claimed length of %" PRIx64
+                   " doesn't even cover CU header.\n", size);
          success = false;
          break;
        }
@@ -1827,7 +1851,7 @@ check_debug_info_structural (struct read_ctx *ctx,
          struct read_ctx cu_ctx;
          if (!read_ctx_init_sub (&cu_ctx, ctx, cu_begin, cu_end))
            {
-             wr_error (PRI_D_INFO PRI_CU PRI_NOT_ENOUGH, cu_off, "next CU");
+             wr_error (&where, PRI_NOT_ENOUGH, "next CU");
              success = false;
              break;
            }
@@ -1841,11 +1865,9 @@ check_debug_info_structural (struct read_ctx *ctx,
              break;
            }
          if (cu_ctx.ptr != cu_ctx.end
-             && !check_zero_padding (&cu_ctx, mc_die_other,
-                                     PRI_D_INFO PRI_CU, cu_off))
-           message_padding_n0 (mc_die_other,
-                               read_ctx_get_offset (ctx), size,
-                               PRI_D_INFO PRI_CU, cu_off);
+             && !wr_check_zero_padding (&cu_ctx, mc_die_other, &where))
+           wr_message_padding_n0 (mc_die_other, &where,
+                                  read_ctx_get_offset (ctx), size);
        }
 
       ctx->ptr += size;
@@ -1853,8 +1875,9 @@ check_debug_info_structural (struct read_ctx *ctx,
 
   // Only check this if above we have been successful.
   if (success && ctx->ptr != ctx->end)
-    message (mc_die_other | mc_impact_4,
-            ".debug_info: CU lengths don't exactly match Elf_Data contents.");
+    wr_message (mc_die_other | mc_impact_4,
+               &WHERE (sec_info, NULL),
+               ": CU lengths don't exactly match Elf_Data contents.");
 
   bool references_sound = check_global_die_references (cu_chain);
   ref_record_free (&die_refs);
@@ -1864,7 +1887,7 @@ check_debug_info_structural (struct read_ctx *ctx,
       if (success)
        coverage_find_holes (strings_coverage, found_hole,
                             &((struct hole_info)
-                              {PRI_D_STR, mc_strings, strings->d_buf}));
+                              {sec_str, mc_strings, strings->d_buf}));
       coverage_free (strings_coverage);
     }
 
@@ -1873,7 +1896,7 @@ check_debug_info_structural (struct read_ctx *ctx,
       if (success)
        coverage_find_holes (loc_coverage, found_hole,
                             &((struct hole_info)
-                              {PRI_D_LOC, mc_loc, loc->d_buf}));
+                              {sec_loc, mc_loc, loc->d_buf}));
       coverage_free (loc_coverage);
     }
 
@@ -1912,19 +1935,17 @@ get_location_opcode_operands (uint8_t opcode, uint8_t *op1, uint8_t *op2)
 }
 
 static bool
-skip_form (struct read_ctx *ctx, bool addr_64, uint8_t form, ...)
+skip_form (struct read_ctx *ctx, bool addr_64, uint8_t form,
+          struct where *wh, const char *what)
 {
-  /* XXX fix messages in this function.  */
-  va_list ap;
-  va_start (ap, form);
   switch (form)
     {
     case DW_FORM_addr:
       return read_ctx_read_offset (ctx, addr_64, NULL);
     case DW_FORM_udata:
-      return vchecked_read_uleb128 (ctx, NULL, "", NULL, ap); // here
+      return wr_checked_read_uleb128 (ctx, NULL, wh, what);
     case DW_FORM_sdata:
-      return vchecked_read_sleb128 (ctx, NULL, "", NULL, ap); // here
+      return wr_checked_read_sleb128 (ctx, NULL, wh, what);
     case DW_FORM_data1:
       return read_ctx_read_ubyte (ctx, NULL);
     case DW_FORM_data2:
@@ -1938,45 +1959,43 @@ skip_form (struct read_ctx *ctx, bool addr_64, uint8_t form, ...)
   return false;
 }
 
-/* If it returns false, an error has been written, and the caller
-   should provide "at this point in file"-type message.  */
-static bool
-check_location_expression (struct read_ctx *ctx, bool addr_64)
+static void
+check_location_expression (struct read_ctx *ctx, struct where *wh, bool addr_64)
 {
   while (!read_ctx_eof (ctx))
     {
-      uint64_t op_off = read_ctx_get_offset (ctx);
+      struct where where = WHERE (sec_locexpr, wh);
+      where_reset_1 (&where, read_ctx_get_offset (ctx));
       uint8_t opcode;
       if (!read_ctx_read_ubyte (ctx, &opcode))
        {
-         wr_error (PRI_LOCEXPR PRI_RECORD ": can't read opcode.\n", op_off);
-         return false;
+         wr_error (&where, ": can't read opcode.\n");
+         return;
        }
 
       uint8_t op1, op2;
       if (!get_location_opcode_operands (opcode, &op1, &op2))
        {
-         wr_error (PRI_LOCEXPR PRI_RECORD ": can't decode opcode \"%s\".\n",
-                   op_off, dwarf_locexpr_opcode_string (opcode));
-         return false;
+         wr_error (&where, ": can't decode opcode \"%s\".\n",
+                   dwarf_locexpr_opcode_string (opcode));
+         return;
        }
 
 #define SKIP_FORM(OP, STR)                                             \
-      if (OP != 0 && !skip_form (ctx, addr_64, OP))                    \
+      if (OP != 0                                                      \
+         && !skip_form (ctx, addr_64, OP, &where, STR " operand"))     \
        {                                                               \
-         wr_error (PRI_LOCEXPR PRI_RECORD ": opcode \"%s\""            \
+         wr_error (&where, ": opcode \"%s\""                           \
                    ": can't read " STR " operand (form \"%s\").\n",    \
-                   op_off, dwarf_locexpr_opcode_string (opcode),       \
+                   dwarf_locexpr_opcode_string (opcode),               \
                    dwarf_form_string (OP));                            \
-         return false;                                                 \
+         return;                                                       \
        }
 
       SKIP_FORM (op1, "1st");
       SKIP_FORM (op2, "2nd");
 #undef SKIP_FORM
     }
-
-  return true;
 }
 
 /* If it returns false, an error has been written, and the caller
@@ -1985,7 +2004,8 @@ static bool
 check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
                             struct coverage *loc_coverage,
                             struct addr_record *loc_addrs,
-                            uint64_t addr, bool addr_64)
+                            uint64_t addr, bool addr_64,
+                            struct where *wh)
 {
   if (loc == NULL || loc_coverage == NULL)
     return true;
@@ -1994,7 +2014,7 @@ check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
   read_ctx_init (&ctx, dbg, loc);
   if (!read_ctx_skip (&ctx, addr))
     {
-      wr_error ("invalid reference outside " PRI_D_LOC
+      wr_error (wh, ": invalid reference outside .debug_loc "
                "0x%" PRIx64 ", size only 0x%" PRIx64 ".\n",
                addr, loc->d_size);
       return false;
@@ -2004,8 +2024,10 @@ check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
     {
       if (!addr_record_has_addr (loc_addrs, addr))
        {
-         wr_error (PRI_D_LOC "0x%" PRIx64
-                   ": reference to the middle of location list.\n", addr);
+         /* XXX do it like everywhere else, using addr_records and
+            ref_records..  */
+         wr_error (wh, ": reference to 0x%" PRIx64
+                   " points at the middle of location list.\n", addr);
          return false;
        }
       return true;
@@ -2019,12 +2041,12 @@ check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
   bool overlap = false;
   while (!read_ctx_eof (&ctx))
     {
-      uint64_t off = read_ctx_get_offset (&ctx);
+      struct where where = WHERE (sec_loc, wh);
+      where_reset_1 (&where, read_ctx_get_offset (&ctx));
 
 #define HAVE_OVERLAP                                           \
       do {                                                     \
-       wr_error (PRI_D_LOC PRI_RECORD                          \
-                 ": range definitions overlap.\n", off);       \
+       wr_error (&where, ": range definitions overlap.\n");    \
        retval = false;                                         \
        overlap = true;                                         \
       } while (0)
@@ -2039,8 +2061,7 @@ check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
 
       if (!read_ctx_read_offset (&ctx, addr_64, &begin_addr))
        {
-         wr_error (PRI_D_LOC PRI_RECORD
-                   ": can't read address range beginning.\n", off);
+         wr_error (&where, ": can't read address range beginning.\n");
          return false;
        }
 
@@ -2054,8 +2075,7 @@ check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
 
       if (!read_ctx_read_offset (&ctx, addr_64, &end_addr))
        {
-         wr_error (PRI_D_LOC PRI_RECORD
-                   ": can't read address range ending.\n", off);
+         wr_error (&where, ": can't read address range ending.\n");
          return false;
        }
 
@@ -2072,8 +2092,7 @@ check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
 
          if (!read_ctx_read_2ubyte (&ctx, &len))
            {
-             wr_error (PRI_D_LOC PRI_RECORD
-                       ": can't read length of location expression.\n", off);
+             wr_error (&where, ": can't read length of location expression.\n");
              return false;
            }
 
@@ -2082,18 +2101,12 @@ check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
          if (!read_ctx_init_sub (&expr_ctx, &ctx, ctx.ptr, ctx.ptr + len))
            {
            not_enough:
-             wr_error (PRI_D_LOC PRI_RECORD PRI_NOT_ENOUGH,
-                       off, "location expression");
+             wr_error (&where, PRI_NOT_ENOUGH, "location expression");
              return false;
            }
 
          uint64_t expr_start = read_ctx_get_offset (&ctx);
-         if (!check_location_expression (&expr_ctx, addr_64))
-           {
-             wr_error (PRI_D_LOC PRI_RECORD PRI_NOT_ENOUGH,
-                       off, "location expression");
-             retval = false;
-           }
+         check_location_expression (&expr_ctx, &where, addr_64);
          uint64_t expr_end = read_ctx_get_offset (&ctx);
          if (!overlap
              && !coverage_pristine (loc_coverage,
@@ -2106,8 +2119,7 @@ check_x_location_expression (Dwarf *dbg, Elf_Data *loc,
        }
 #undef HAVE_OVERLAP
 
-      uint64_t fin_off = read_ctx_get_offset (&ctx);
-      coverage_add (loc_coverage, off, fin_off - 1);
+      coverage_add (loc_coverage, where.addr1, read_ctx_get_offset (&ctx) - 1);
       if (done)
        break;
     }
@@ -2137,52 +2149,45 @@ read_die_chain (struct read_ctx *ctx,
                struct coverage *loc_coverage)
 {
   bool got_die = false;
-  const unsigned char *begin = ctx->ptr;
   uint64_t sibling_addr = 0;
   uint64_t die_off, prev_die_off = 0;
   struct abbrev *abbrev, *prev_abbrev = NULL;
+  struct where where = WHERE (sec_info, NULL);
 
   while (!read_ctx_eof (ctx))
     {
+      where = cu->where;
+      where_reset_2 (&where, (die_off = read_ctx_get_offset (ctx)));
       uint64_t abbr_code;
 
       prev_die_off = die_off;
-      die_off = read_ctx_get_offset (ctx);
-      if (!checked_read_uleb128 (ctx, &abbr_code,
-                                PRI_D_INFO PRI_CU_DIE, "abbrev code",
-                                cu->offset, die_off))
+      if (!wr_checked_read_uleb128 (ctx, &abbr_code, &where, "abbrev code"))
        return -1;
 
       /* Check sibling value advertised last time through the loop.  */
       if (sibling_addr != 0)
        {
          if (abbr_code == 0)
-           wr_error (PRI_D_INFO PRI_CU_DIE
-                  ": is the last sibling in chain, but has a DW_AT_sibling attribute.\n",
-                  cu->offset, prev_die_off);
+           wr_error (&where,
+                     ": is the last sibling in chain, but has a DW_AT_sibling attribute.\n");
          else if (sibling_addr != die_off)
-           wr_error (PRI_D_INFO PRI_CU_DIE
-                  ": This DIE should have had its sibling at 0x%"
-                  PRIx64 ", but it's at 0x%" PRIx64 " instead.\n",
-                  cu->offset, prev_die_off, sibling_addr, die_off);
+           wr_error (&where, ": This DIE should have had its sibling at 0x%"
+                     PRIx64 ", but it's at 0x%" PRIx64 " instead.\n",
+                     sibling_addr, die_off);
          sibling_addr = 0;
        }
       else if (prev_abbrev != NULL && prev_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.  */
-       message (mc_die_rel_sib | mc_acc_suboptimal | mc_impact_4,
-                PRI_D_INFO PRI_CU_DIE
-                ": This DIE had children, but no DW_AT_sibling attribute.\n",
-                cu->offset, prev_die_off);
+       wr_message (mc_die_rel_sib | mc_acc_suboptimal | mc_impact_4, &where,
+                   ": This DIE had children, but no DW_AT_sibling attribute.\n");
 
       /* The section ended.  */
       if (read_ctx_eof (ctx) || abbr_code == 0)
        {
          if (abbr_code != 0)
-           wr_error (PRI_D_INFO PRI_CU
-                  ": DIE chain at %p not terminated with DIE with zero abbrev code.\n",
-                  cu->offset, begin);
+           wr_error (&where, ": DIE chain not terminated with DIE with zero abbrev code.\n");
          break;
        }
 
@@ -2193,9 +2198,10 @@ read_die_chain (struct read_ctx *ctx,
       abbrev = abbrev_table_find_abbrev (abbrevs, abbr_code);
       if (abbrev == NULL)
        {
-         wr_error (PRI_D_INFO PRI_CU_DIE ": abbrev section at 0x%" PRIx64
-                " doesn't contain code %" PRIu64 ".\n",
-                cu->offset, die_off, abbrevs->offset, abbr_code);
+         wr_error (&where,
+                   ": abbrev section at 0x%" PRIx64
+                   " doesn't contain code %" PRIu64 ".\n",
+                   abbrevs->offset, abbr_code);
          return -1;
        }
       abbrev->used = true;
@@ -2206,8 +2212,9 @@ read_die_chain (struct read_ctx *ctx,
       for (struct abbrev_attrib *it = abbrev->attribs;
           it->name != 0; ++it)
        {
+         where.ref = &it->where;
 
-         void record_ref (uint64_t addr, uint64_t who, bool local)
+         void record_ref (uint64_t addr, struct where *who, bool local)
          {
            struct ref_record *record = &cu->die_refs;
            if (local)
@@ -2215,9 +2222,9 @@ read_die_chain (struct read_ctx *ctx,
                assert (ctx->end > ctx->begin);
                if (addr > (uint64_t)(ctx->end - ctx->begin))
                  {
-                   wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
-                          ": invalid reference outside the CU: 0x%" PRIx64 ".\n",
-                          cu->offset, die_off, abbrev->code, it->offset, addr);
+                   wr_error (&where,
+                             ": invalid reference outside the CU: 0x%" PRIx64 ".\n",
+                             addr);
                    return;
                  }
 
@@ -2236,18 +2243,14 @@ read_die_chain (struct read_ctx *ctx,
          if (indirect)
            {
              uint64_t value;
-             if (!checked_read_uleb128 (ctx, &value,
-                                        PRI_D_INFO PRI_CU_DIE_ABBR_ATTR,
-                                        "indirect attribute form",
-                                        cu->offset, die_off, abbrev->code,
-                                        it->offset))
+             if (!wr_checked_read_uleb128 (ctx, &value, &where,
+                                           "indirect attribute form"))
                return -1;
 
              if (!attrib_form_valid (value))
                {
-                 wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
-                           ": invalid indirect form 0x%" PRIx64 ".\n",
-                           cu->offset, die_off, abbrev->code, it->offset, value);
+                 wr_error (&where,
+                           ": invalid indirect form 0x%" PRIx64 ".\n", value);
                  return -1;
                }
              form = value;
@@ -2256,16 +2259,13 @@ read_die_chain (struct read_ctx *ctx,
                switch (check_sibling_form (form))
                  {
                  case -1:
-                   message (mc_die_rel_sib | mc_impact_2,
-                            PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
-                            ": DW_AT_sibling attribute with (indirect) form DW_FORM_ref_addr.\n",
-                            cu->offset, die_off, abbrev->code, it->offset);
+                   wr_message (mc_die_rel_sib | mc_impact_2, &where,
+                               ": DW_AT_sibling attribute with (indirect) form DW_FORM_ref_addr.\n");
                    break;
 
                  case -2:
-                   wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
+                   wr_error (&where,
                              ": DW_AT_sibling attribute with non-reference (indirect) form \"%s\".\n",
-                             cu->offset, die_off, abbrev->code, it->offset,
                              dwarf_form_string (value));
                  };
            }
@@ -2280,16 +2280,14 @@ read_die_chain (struct read_ctx *ctx,
                  /* Only print error if it's indirect.  Otherwise we
                     gave diagnostic during abbrev loading.  */
                  if (indirect)
-                   wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
+                   wr_error (&where,
                              ": location attribute with invalid (indirect) form \"%s\".\n",
-                             cu->offset, die_off, abbrev->code, it->offset,
                              dwarf_form_string (form));
                  break;
 
                case -1: /* locptr invalid in this context */
-                 wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
+                 wr_error (&where,
                            ": location attribute with form \"%s\" in %d-bit CU.\n",
-                           cu->offset, die_off, abbrev->code, it->offset,
                            dwarf_form_string (form), (dwarf_64 ? 64 : 32));
                  locptr_64 = !locptr_64;
 
@@ -2307,20 +2305,17 @@ read_die_chain (struct read_ctx *ctx,
                if (!read_ctx_read_offset (ctx, dwarf_64, &addr))
                  {
                  cant_read:
-                   wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
-                          ": can't read attribute value.\n",
-                          cu->offset, die_off, abbrev->code, it->offset);
+                   wr_error (&where, ": can't read attribute value.\n");
                    return -1;
                  }
 
                if (strings == NULL)
-                 wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
-                        ": strp attribute, but no .debug_str section.\n",
-                        cu->offset, die_off, abbrev->code, it->offset);
+                 wr_error (&where,
+                           ": strp attribute, but no .debug_str section.\n");
                else if (addr >= strings->d_size)
-                 wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
-                        ": Invalid offset outside .debug_str: 0x%" PRIx64 ".",
-                        cu->offset, die_off, abbrev->code, it->offset, addr);
+                 wr_error (&where,
+                           ": Invalid offset outside .debug_str: 0x%" PRIx64 ".\n",
+                           addr);
                else
                  {
                    /* Record used part of .debug_str.  */
@@ -2354,7 +2349,7 @@ read_die_chain (struct read_ctx *ctx,
                  goto cant_read;
 
                if (it->form == DW_FORM_ref_addr)
-                 record_ref (addr, die_off, false);
+                 record_ref (addr, &where, false);
 
                /* XXX What are validity criteria for DW_FORM_addr? */
                break;
@@ -2364,17 +2359,14 @@ read_die_chain (struct read_ctx *ctx,
            case DW_FORM_ref_udata:
              {
                uint64_t value;
-               if (!checked_read_uleb128 (ctx, &value,
-                                          PRI_D_INFO PRI_CU_DIE_ABBR_ATTR,
-                                          "attribute value",
-                                          cu->offset, die_off, abbrev->code,
-                                          it->offset))
+               if (!wr_checked_read_uleb128 (ctx, &value, &where,
+                                             "attribute value"))
                  return -1;
 
                if (it->name == DW_AT_sibling)
                  sibling_addr = value;
                else if (it->form == DW_FORM_ref_udata)
-                 record_ref (value, die_off, true);
+                 record_ref (value, &where, true);
                break;
              }
 
@@ -2389,7 +2381,7 @@ read_die_chain (struct read_ctx *ctx,
                if (it->name == DW_AT_sibling)
                  sibling_addr = value;
                else if (it->form == DW_FORM_ref1)
-                 record_ref (value, die_off, true);
+                 record_ref (value, &where, true);
                break;
              }
 
@@ -2403,7 +2395,7 @@ read_die_chain (struct read_ctx *ctx,
                if (it->name == DW_AT_sibling)
                  sibling_addr = value;
                else if (it->form == DW_FORM_ref2)
-                 record_ref (value, die_off, true);
+                 record_ref (value, &where, true);
                break;
              }
 
@@ -2417,15 +2409,11 @@ read_die_chain (struct read_ctx *ctx,
                if (it->name == DW_AT_sibling)
                  sibling_addr = value;
                else if (check_locptr)
-                 {
-                   if (!check_x_location_expression (ctx->dbg, loc,
-                                                     loc_coverage, loc_addrs,
-                                                     value, locptr_64))
-                     wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR PRI_CAUSE,
-                               cu->offset, die_off, abbrev->code, it->offset);
-                 }
+                 check_x_location_expression (ctx->dbg, loc, loc_coverage,
+                                              loc_addrs, value, locptr_64,
+                                              &where);
                else if (it->form == DW_FORM_ref4)
-                 record_ref (value, die_off, true);
+                 record_ref (value, &where, true);
                break;
              }
 
@@ -2439,27 +2427,19 @@ read_die_chain (struct read_ctx *ctx,
                if (it->name == DW_AT_sibling)
                  sibling_addr = value;
                else if (check_locptr)
-                 {
-                   if (!check_x_location_expression (ctx->dbg, loc,
-                                                     loc_coverage, loc_addrs,
-                                                     value, locptr_64))
-                     wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR PRI_CAUSE,
-                               cu->offset, die_off, abbrev->code,
-                               it->offset);
-                 }
+                 check_x_location_expression (ctx->dbg, loc, loc_coverage,
+                                              loc_addrs, value, locptr_64,
+                                              &where);
                else if (it->form == DW_FORM_ref8)
-                 record_ref (value, die_off, true);
+                 record_ref (value, &where, true);
                break;
              }
 
            case DW_FORM_sdata:
              {
                int64_t value;
-               if (!checked_read_sleb128 (ctx, &value,
-                                          PRI_D_INFO PRI_CU_DIE_ABBR_ATTR,
-                                          "attribute value",
-                                          cu->offset, die_off, abbrev->code,
-                                          it->offset))
+               if (!wr_checked_read_sleb128 (ctx, &value, &where,
+                                             "attribute value"))
                  return -1;
                break;
              }
@@ -2484,11 +2464,8 @@ read_die_chain (struct read_ctx *ctx,
              process_DW_FORM_block:
                if (width == 0)
                  {
-                   if (!checked_read_uleb128 (ctx, &length,
-                                              PRI_D_INFO PRI_CU_DIE_ABBR_ATTR,
-                                              "attribute value",
-                                              cu->offset, die_off, abbrev->code,
-                                              it->offset))
+                   if (!wr_checked_read_uleb128 (ctx, &length, &where,
+                                                 "attribute value"))
                      return -1;
                  }
                else if (!read_ctx_read_var (ctx, width, &length))
@@ -2500,15 +2477,11 @@ read_die_chain (struct read_ctx *ctx,
                    if (!read_ctx_init_sub (&sub_ctx, ctx, ctx->ptr,
                                            ctx->ptr + length))
                      {
-                       wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR PRI_NOT_ENOUGH,
-                                 cu->offset, die_off, abbrev->code, it->offset,
-                                 "location expression");
+                       wr_error (&where, PRI_NOT_ENOUGH, "location expression");
                        return -1;
                      }
 
-                   if (!check_location_expression (&sub_ctx, addr_64))
-                     wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR PRI_CAUSE,
-                               cu->offset, die_off, abbrev->code, it->offset);
+                   check_location_expression (&sub_ctx, &where, addr_64);
                  }
 
                if (!read_ctx_skip (ctx, length))
@@ -2518,15 +2491,12 @@ read_die_chain (struct read_ctx *ctx,
              }
 
            case DW_FORM_indirect:
-             wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
-                    ": Indirect form is again indirect.\n",
-                    cu->offset, die_off, abbrev->code, it->offset);
+             wr_error (&where, ": indirect form is again indirect.\n");
              return -1;
 
            default:
-             wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
-                    ": Internal error: unhandled form 0x%x\n",
-                    cu->offset, die_off, abbrev->code, it->offset, it->form);
+             wr_error (&where,
+                       ": internal error: unhandled form 0x%x\n", it->form);
            }
        }
 
@@ -2539,63 +2509,46 @@ read_die_chain (struct read_ctx *ctx,
          if (st == -1)
            return -1;
          else if (st == 0)
-           message (mc_impact_3 | mc_acc_suboptimal | mc_die_rel_child,
-                    PRI_D_INFO PRI_CU_DIE
-                    ": Abbrev has_children, but the chain was empty.\n",
-                    cu->offset, die_off);
+           wr_message (mc_impact_3 | mc_acc_suboptimal | mc_die_rel_child,
+                       &where,
+                       ": Abbrev has_children, but the chain was empty.\n");
        }
     }
 
   if (sibling_addr != 0)
-    wr_error (PRI_D_INFO PRI_CU_DIE
-          ": This DIE should have had its sibling at 0x%"
-          PRIx64 ", but the DIE chain ended.\n",
-          cu->offset, prev_die_off, sibling_addr);
+    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;
 }
 
 static bool
-read_version (struct read_ctx *ctx, bool dwarf_64,
-             uint16_t *versionp, const char *format, ...)
+wr_read_version (struct read_ctx *ctx, bool dwarf_64,
+                uint16_t *versionp, struct where *wh)
 {
   bool retval = read_ctx_read_2ubyte (ctx, versionp);
-  char *buf = NULL;
-  va_list ap;
-  va_start (ap, format);
-
-  if (!retval
-      || *versionp < 2 || *versionp > 3
-      || (*versionp == 2 && dwarf_64))
-    if (vasprintf (&buf, format, ap) < 0)
-      buf = NULL;
 
   if (!retval)
     {
-      wr_error ("%s: can't read version.\n", buf ?: fmterr);
-      retval = false;
-      goto out;
+      wr_error (wh, ": can't read version.\n");
+      return false;
     }
 
   if (*versionp < 2 || *versionp > 3)
     {
-      wr_error ("%s: %s version %d.\n", buf ?: fmterr,
-               (*versionp < 2 ? "invalid" : "unsupported"),
-               *versionp);
-      retval = false;
-      goto out;
+      wr_error (wh, ": %s version %d.\n",
+               (*versionp < 2 ? "invalid" : "unsupported"), *versionp);
+      return false;
     }
 
   if (*versionp == 2 && dwarf_64)
     /* Keep going.  It's a standard violation, but we may still be
        able to read the unit under consideration and do high-level
        checks.  */
-    wr_error ("%s: invalid 64-bit unit in DWARF 2 format.\n", buf ?: fmterr);
+    wr_error (wh, ": invalid 64-bit unit in DWARF 2 format.\n");
 
- out:
-  va_end (ap);
-  free (buf);
-  return retval;
+  return true;
 }
 
 static bool
@@ -2615,27 +2568,27 @@ check_cu_structural (struct read_ctx *ctx,
   uint8_t address_size;
 
   /* Version.  */
-  if (!read_version (ctx, dwarf_64, &version, PRI_D_INFO PRI_CU, cu->offset))
+  if (!wr_read_version (ctx, dwarf_64, &version, &cu->where))
     return false;
 
   /* Abbrev offset.  */
   if (!read_ctx_read_offset (ctx, dwarf_64, &abbrev_offset))
     {
-      wr_error (PRI_D_INFO PRI_CU ": can't read abbrev offset.\n", cu->offset);
+      wr_error (&cu->where, ": can't read abbrev offset.\n");
       return false;
     }
 
   /* Address size.  */
   if (!read_ctx_read_ubyte (ctx, &address_size))
     {
-      wr_error (PRI_D_INFO PRI_CU ": can't read address size.\n", cu->offset);
+      wr_error (&cu->where, ": can't read address size.\n");
       return false;
     }
   if (address_size != 4 && address_size != 8)
     {
-      wr_error (PRI_D_INFO PRI_CU
+      wr_error (&cu->where,
                ": Invalid address size: %d (only 4 or 8 allowed).\n",
-               cu->offset, address_size);
+               address_size);
       return false;
     }
 
@@ -2646,9 +2599,9 @@ check_cu_structural (struct read_ctx *ctx,
 
   if (abbrevs == NULL)
     {
-      wr_error (PRI_D_INFO PRI_CU
+      wr_error (&cu->where,
                ": Couldn't find abbrev section with offset 0x%" PRIx64 ".\n",
-               cu->offset, abbrev_offset);
+               abbrev_offset);
       return false;
     }
 
@@ -2665,10 +2618,9 @@ check_cu_structural (struct read_ctx *ctx,
     {
       for (size_t i = 0; i < abbrevs->size; ++i)
        if (!abbrevs->abbr[i].used)
-         message (mc_impact_3 | mc_acc_bloat | mc_abbrevs,
-                  PRI_D_INFO PRI_CU ": Abbreviation with code %"
-                  PRIu64 " is never used.\n",
-                  cu->offset, abbrevs->abbr[i].code);
+         wr_message (mc_impact_3 | mc_acc_bloat | mc_abbrevs, &cu->where,
+                     ": Abbreviation with code %" PRIu64 " is never used.\n",
+                     abbrevs->abbr[i].code);
 
       if (!check_die_references (cu, &die_loc_refs))
        retval = false;
@@ -2688,7 +2640,8 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
 
   while (!read_ctx_eof (ctx))
     {
-      uint64_t atab_off = read_ctx_get_offset (ctx);
+      struct where where = WHERE (sec_aranges, NULL);
+      where_reset_1 (&where, read_ctx_get_offset (ctx));
       const unsigned char *atab_begin = ctx->ptr;
 
       /* Size.  */
@@ -2697,12 +2650,10 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
       bool dwarf_64;
       if (!read_ctx_read_4ubyte (ctx, &size32))
        {
-         wr_error (PRI_D_ARANGES PRI_ARANGETAB
-                ": can't read table length.\n", atab_off);
+         wr_error (&where, ": can't read table length.\n");
          return false;
        }
-      if (!read_size_extra (ctx, size32, &size, &dwarf_64,
-                           PRI_D_ARANGES PRI_ARANGETAB, atab_off))
+      if (!wr_read_size_extra (ctx, size32, &size, &dwarf_64, &where))
        return false;
 
       struct read_ctx sub_ctx;
@@ -2710,8 +2661,7 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
       if (!read_ctx_init_sub (&sub_ctx, ctx, atab_begin, atab_end))
        {
        not_enough:
-         wr_error (PRI_D_ARANGES PRI_ARANGETAB PRI_NOT_ENOUGH,
-                   atab_off, "next table");
+         wr_error (&where, PRI_NOT_ENOUGH, "next table");
          return false;
        }
 
@@ -2719,8 +2669,7 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
 
       /* Version.  */
       uint16_t version;
-      if (!read_version (&sub_ctx, dwarf_64, &version,
-                        PRI_D_ARANGES PRI_ARANGETAB, atab_off))
+      if (!wr_read_version (&sub_ctx, dwarf_64, &version, &where))
        {
          retval = false;
          goto next;
@@ -2730,32 +2679,28 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
       uint64_t cu_off;
       if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &cu_off))
        {
-         wr_error (PRI_D_ARANGES PRI_ARANGETAB
-                   ": can't read debug info offset.\n", atab_off);
+         wr_error (&where, ": can't read debug info offset.\n");
          retval = false;
          goto next;
        }
-      if (cu_chain != NULL && cu_find_cu (cu_chain, cu_off) == NULL)
-       wr_error (PRI_D_ARANGES PRI_ARANGETAB
-                 ": unresolved reference to " PRI_CU ".\n", atab_off, cu_off);
+      struct cu *cu = NULL;
+      if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_off)) == NULL)
+       wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_off);
+      if (cu != NULL)
+       where.ref = &cu->where;
 
       /* Address size.  */
       uint8_t address_size;
       if (!read_ctx_read_ubyte (&sub_ctx, &address_size))
        {
-         wr_error (PRI_D_ARANGES PRI_ARANGETAB_CU
-                   ": can't read unit address size.\n", atab_off, cu_off);
+         wr_error (&where, ": can't read address size.\n");
          retval = false;
          goto next;
        }
-      if (address_size != 2
-         && address_size != 4
+      if (address_size != 4 /* XXX What values are actually legal?  */
          && address_size != 8)
        {
-         /* XXX Does anyone need e.g. 6 byte addresses?  */
-         wr_error (PRI_D_ARANGES PRI_ARANGETAB_CU
-                   ": invalid address size: %d.\n",
-                   atab_off, cu_off, address_size);
+         wr_error (&where, ": invalid address size: %d.\n", address_size);
          retval = false;
          goto next;
        }
@@ -2764,16 +2709,13 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
       uint8_t segment_size;
       if (!read_ctx_read_ubyte (&sub_ctx, &segment_size))
        {
-         wr_error (PRI_D_ARANGES PRI_ARANGETAB_CU
-                   ": can't read unit segment size.\n", atab_off, cu_off);
+         wr_error (&where, ": can't read unit segment size.\n");
          retval = false;
          goto next;
        }
       if (segment_size != 0)
        {
-         wr_warning (PRI_D_ARANGES PRI_ARANGETAB_CU
-                     ": dwarflint can't handle segment_size != 0.\n",
-                     atab_off, cu_off);
+         wr_warning (&where, ": dwarflint can't handle segment_size != 0.\n");
          retval = false;
          goto next;
        }
@@ -2793,39 +2735,33 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
              uint8_t c;
              if (!read_ctx_read_ubyte (&sub_ctx, &c))
                {
-                 wr_error (PRI_D_ARANGES PRI_ARANGETAB_CU
-                        ": section ends after the header, but before the first entry.\n",
-                        atab_off, cu_off);
+                 wr_error (&where,
+                           ": section ends after the header, but before the first entry.\n");
                  retval = false;
                  goto next;
                }
              if (c != 0)
-               message (mc_impact_2 | mc_aranges,
-                        PRI_D_ARANGES PRI_ARANGETAB_CU
-                        ": non-zero byte at 0x%" PRIx64
-                        " in padding before the first entry.\n",
-                        atab_off, cu_off, read_ctx_get_offset (&sub_ctx));
+               wr_message (mc_impact_2 | mc_aranges, &where,
+                           ": non-zero byte at 0x%" PRIx64
+                           " in padding before the first entry.\n",
+                           read_ctx_get_offset (&sub_ctx));
            }
        }
       assert ((read_ctx_get_offset (&sub_ctx) % tuple_size) == 0);
 
       while (!read_ctx_eof (&sub_ctx))
        {
-         uint64_t tuple_off = read_ctx_get_offset (&sub_ctx);
+         where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
          uint64_t address, length;
          if (!read_ctx_read_var (&sub_ctx, address_size, &address))
            {
-             wr_error (PRI_D_ARANGES PRI_ARANGETAB_CU_RECORD
-                    ": can't read address field.\n",
-                    atab_off, cu_off, tuple_off);
+             wr_error (&where, ": can't read address field.\n");
              retval = false;
              goto next;
            }
          if (!read_ctx_read_var (&sub_ctx, address_size, &length))
            {
-             wr_error (PRI_D_ARANGES PRI_ARANGETAB_CU_RECORD
-                    ": can't read length field.\n",
-                    atab_off, cu_off, tuple_off);
+             wr_error (&where, ": can't read length field.\n");
              retval = false;
              goto next;
            }
@@ -2833,17 +2769,16 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
          if (address == 0 && length == 0)
            break;
 
-         /* Address and length can be validated on high level.  */
+         /* XXX Can address and length can be validated on high level?  */
        }
 
       if (sub_ctx.ptr != sub_ctx.end
-         && !check_zero_padding (&sub_ctx, mc_pubtables,
-                                 PRI_D_ARANGES PRI_ARANGETAB_CU,
-                                 atab_off, cu_off))
+         && !wr_check_zero_padding (&sub_ctx, mc_pubtables,
+                                    &WHERE (where.section, NULL)))
        {
-         message_padding_n0 (mc_pubtables | mc_error,
-                             read_ctx_get_offset (&sub_ctx), size,
-                             PRI_D_ARANGES PRI_ARANGETAB_CU, atab_off, cu_off);
+         wr_message_padding_n0 (mc_pubtables | mc_error,
+                                &WHERE (where.section, NULL),
+                                read_ctx_get_offset (&sub_ctx), size);
          retval = false;
        }
 
@@ -2858,13 +2793,14 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain)
 
 static bool
 check_pub_structural (struct read_ctx *ctx, struct cu *cu_chain,
-                     const char *secname)
+                     enum section_id sec)
 {
   bool retval = true;
 
   while (!read_ctx_eof (ctx))
     {
-      uint64_t set_off = read_ctx_get_offset (ctx);
+      struct where where = WHERE (sec, NULL);
+      where_reset_1 (&where, read_ctx_get_offset (ctx));
       const unsigned char *set_begin = ctx->ptr;
 
       /* Size.  */
@@ -2873,20 +2809,17 @@ check_pub_structural (struct read_ctx *ctx, struct cu *cu_chain,
       bool dwarf_64;
       if (!read_ctx_read_4ubyte (ctx, &size32))
        {
-         wr_error ("%s" PRI_PUBSET
-                   ": can't read set length.\n", secname, set_off);
+         wr_error (&where, ": can't read table length.\n");
          return false;
        }
-      if (!read_size_extra (ctx, size32, &size, &dwarf_64,
-                           "%s" PRI_PUBSET, secname, set_off))
+      if (!wr_read_size_extra (ctx, size32, &size, &dwarf_64, &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))
        {
-         wr_error ("%s" PRI_PUBSET PRI_NOT_ENOUGH,
-                   secname, set_off, "next set");
+         wr_error (&where, PRI_NOT_ENOUGH, "next set");
          return false;
        }
       sub_ctx.ptr = ctx->ptr;
@@ -2895,78 +2828,70 @@ check_pub_structural (struct read_ctx *ctx, struct cu *cu_chain,
       uint16_t version;
       if (!read_ctx_read_2ubyte (&sub_ctx, &version))
        {
-         wr_error ("%s" PRI_PUBSET
-                   ": can't read set version.\n", secname, set_off);
+         wr_error (&where, ": can't read set version.\n");
          retval = false;
          goto next;
        }
       if (version != 2)
        {
-         wr_error ("%s" PRI_PUBSET ": %s set version.\n", secname, set_off,
-                   (version < 2 ? "invalid" : "unsupported"));
+         wr_error (&where, ": %s set version.\n",
+                   version < 2 ? "invalid" : "unsupported");
          retval = false;
          goto next;
        }
 
       /* CU offset.  */
-      uint64_t cu_off;
+      uint64_t cu_off = 0;
       if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &cu_off))
        {
-         wr_error ("%s" PRI_PUBSET
-                   ": can't read debug info offset.\n", secname, set_off);
+         wr_error (&where, ": can't read debug info offset.\n");
          retval = false;
          goto next;
        }
-      struct cu *cu = cu_find_cu (cu_chain, cu_off);
-      if (cu_chain != NULL && cu == NULL)
-       wr_error ("%s" PRI_PUBSET ": unresolved reference to " PRI_CU ".\n",
-                 secname, set_off, cu_off);
+      struct cu *cu = NULL;
+      if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_off)) == NULL)
+       wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_off);
+      if (cu != NULL)
+       where.ref = &cu->where;
 
       /* Covered length.  */
       uint64_t cu_len;
       if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &cu_len))
        {
-         wr_error ("%s" PRI_PUBSET_CU ": can't read debug info offset.\n",
-                   secname, set_off, cu_off);
+         wr_error (&where, ": can't read covered length.\n");
          retval = false;
          goto next;
        }
-      if (cu_chain != NULL && cu_len != cu->length)
+      if (cu != NULL && cu_len != cu->length)
        {
-         wr_error ("%s" PRI_PUBSET_CU
-                   ": the set covers length %" PRId64
-                   " but CU has length %" PRId64 ".\n",
-                   secname, set_off, cu_off, cu_len, cu->length);
+         wr_error (&where,
+                   ": the table covers length %" PRId64
+                   " but CU has length %" PRId64 ".\n", cu_len, cu->length);
          retval = false;
          goto next;
        }
 
-      /* followed by a null-terminated character string
-        representing the name of the object as given by the
-        DW_AT_name attribute of the referenced debugging entry. Each
-        set of names is terminated by an offset field containing zero
-        (and no following string). */
+      /* Records... */
       while (!read_ctx_eof (&sub_ctx))
        {
          uint64_t pair_off = read_ctx_get_offset (&sub_ctx);
+         where_reset_2 (&where, pair_off);
+
          uint64_t offset;
          if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &offset))
            {
-             wr_error ("%s" PRI_PUBSET_CU_RECORD
-                       ": can't read offset field.\n",
-                       secname, set_off, cu_off, pair_off);
+             wr_error (&where, ": can't read offset field.\n");
              retval = false;
              goto next;
            }
          if (offset == 0)
            break;
 
-         if (cu_chain != NULL
+         if (cu != NULL
              && !addr_record_has_addr (&cu->die_addrs, offset + cu->offset))
            {
-             wr_error ("%s" PRI_PUBSET_CU_RECORD
-                       ": unresolved reference to " PRI_DIE ".\n",
-                       secname, set_off, cu_off, pair_off, offset);
+             wr_error (&where,
+                       ": unresolved reference to " PRI_DIE ".\n", offset);
              retval = false;
              goto next;
            }
@@ -2975,9 +2900,7 @@ check_pub_structural (struct read_ctx *ctx, struct cu *cu_chain,
          do
            if (!read_ctx_read_ubyte (&sub_ctx, &c))
              {
-               wr_error ("%s" PRI_PUBSET_CU_RECORD
-                         ": can't read symbol name.\n",
-                         secname, set_off, cu_off, pair_off);
+               wr_error (&where, ": can't read symbol name.\n");
                retval = false;
                goto next;
              }
@@ -2985,12 +2908,10 @@ check_pub_structural (struct read_ctx *ctx, struct cu *cu_chain,
        }
 
       if (sub_ctx.ptr != sub_ctx.end
-         && !check_zero_padding (&sub_ctx, mc_pubtables,
-                                 "%s" PRI_PUBSET, secname, set_off))
+         && !wr_check_zero_padding (&sub_ctx, mc_pubtables, &WHERE (sec, NULL)))
        {
-         message_padding_n0 (mc_pubtables | mc_error,
-                             read_ctx_get_offset (&sub_ctx), size,
-                             "%s" PRI_PUBSET, secname, set_off);
+         wr_message_padding_n0 (mc_pubtables | mc_error, &WHERE (sec, NULL),
+                                read_ctx_get_offset (&sub_ctx), size);
          retval = false;
        }