[PATCH 2/3] libctf, binutils, include, ld: gettextize and improve error handling

Nick Alcock nick.alcock@oracle.com
Fri Jul 31 19:16:16 GMT 2020


This commit follows on from the earlier commit "libctf, ld, binutils:
add textual error/warning reporting for libctf" and converts every error
in libctf that was reported using ctf_dprintf to use ctf_err_warn
instead, gettextizing them in the process, using N_() where necessary to
avoid doing gettext calls unless an error message is actually generated,
and rephrasing some error messages for ease of translation.

This requires a slight change in the ctf_errwarning_next API: this API
is public but has not been in a release yet, so can still change freely.
The problem is that many errors are emitted at open time (whether
opening of a CTF dict, or opening of a CTF archive): the former of these
throws away its incompletely-initialized ctf_file_t rather than return
it, and the latter has no ctf_file_t at all. So errors and warnings
emitted at open time cannot be stored in the ctf_file_t, and have to go
elsewhere.

We put them in a static local in ctf-subr.c (which is not very
thread-safe: a later commit will improve things here): ctf_err_warn with
a NULL fp adds to this list, and the public interface
ctf_errwarning_next with a NULL fp retrieves from it.

We need a slight exception from the usual iterator rules in this case:
with a NULL fp, there is nowhere to store the ECTF_NEXT_END "error"
which signifies the end of iteration, so we add a new err parameter to
ctf_errwarning_next which is used to report such iteration-related
errors.  (If an fp is provided -- i.e., if not reporting open errors --
this is optional, but even if it's optional it's still an API change.
This is actually useful from a usability POV as well, since
ctf_errwarning_next is usually called when there's been an error, so
overwriting the error code with ECTF_NEXT_END is not very helpful!
So, unusually, ctf_errwarning_next now uses the passed fp for its
error code *only* if no errp pointer is passed in, and leaves it
untouched otherwise.)

ld, objdump and readelf are adapted to call ctf_errwarning_next with a
NULL fp to report open errors where appropriate.

The ctf_err_warn API also has to change, gaining a new error-number
parameter which is used to add the error message corresponding to that
error number into the debug stream when LIBCTF_DEBUG is enabled:
changing this API is easy at this point since we are already touching
all existing calls to gettextize them.  We need this because the debug
stream should contain the errno's message, but the error reported in the
error/warning stream should *not*, because the caller will probably
report it themselves at failure time regardless, and reporting it in
every error message that leads up to it leads to a ridiculous chattering
on failure, which is likely to end up as ridiculous chattering on stderr
(trimmed a bit):

CTF error: `ld/testsuite/ld-ctf/A.c (0): lookup failure for type 3: flags 1: The parent CTF dictionary is unavailable'
CTF error: `ld/testsuite/ld-ctf/A.c (0): struct/union member type hashing error during type hashing for type 80000001, kind 6: The parent CTF dictionary is unavailable'
CTF error: `deduplicating link variable emission failed for ld/testsuite/ld-ctf/A.c: The parent CTF dictionary is unavailable'
ld/.libs/lt-ld-new: warning: CTF linking failed; output will have no CTF section: `The parent CTF dictionary is unavailable'

We only need to be told that the parent CTF dictionary is unavailable
*once*, not over and over again!

errmsgs are still emitted on warning generation, because warnings do not
usually lead to a failure propagated up to the caller and reported
there.

Debug-stream messages are not translated.  If translation is turned on,
there will be a mixture of English and translated messages in the debug
stream, but rather that than burden the translators with debug-only
output.

binutils/ChangeLog
2020-07-27  Nick Alcock  <nick.alcock@oracle.com>

	* objdump.c (dump_ctf_archive_member): Move error-
	reporting...
	(dump_ctf_errs): ... into this separate function.
	(dump_ctf): Call it on open errors.
	* readelf.c (dump_ctf_archive_member): Move error-
	reporting...
	(dump_ctf_errs): ... into this separate function.  Support
	calls with NULL fp. Adjust for new err parameter to
	ctf_errwarning_next.
	(dump_section_as_ctf): Call it on open errors.

include/ChangeLog
2020-07-27  Nick Alcock  <nick.alcock@oracle.com>

	* ctf-api.h (ctf_errwarning_next): New err parameter.

ld/ChangeLog
2020-07-27  Nick Alcock  <nick.alcock@oracle.com>

	* ldlang.c (lang_ctf_errs_warnings): Support calls with NULL fp.
	Adjust for new err parameter to ctf_errwarning_next.  Only
	check for assertion failures when fp is non-NULL.
	(ldlang_open_ctf): Call it on open errors.
	* testsuite/ld-ctf/ctf.exp: Always use the C locale to avoid
	breaking the diags tests.

libctf/ChangeLog
2020-07-27  Nick Alcock  <nick.alcock@oracle.com>

	* ctf-subr.c (open_errors): New list.
	(ctf_err_warn): Calls with NULL fp append to open_errors.  Add err
	parameter, and use it to decorate the debug stream with errmsgs.
	(ctf_err_warn_to_open): Splice errors from a CTF dict into the
	open_errors.
	(ctf_errwarning_next): Calls with NULL fp report from open_errors.
	New err param to report iteration errors (including end-of-iteration)
	when fp is NULL.
	(ctf_assert_fail_internal): Adjust ctf_err_warn call for new err
	parameter: gettextize.
	* ctf-impl.h (ctfo_get_vbytes): Add ctf_file_t parameter.
	(LCTF_VBYTES): Adjust.
	(ctf_err_warn_to_open): New.
	(ctf_err_warn): Adjust.
	(ctf_bundle): Used in only one place: move...
	* ctf-create.c: ... here.
	(enumcmp): Use ctf_err_warn, not ctf_dprintf, passing the err number
	down as needed.  Don't emit the errmsg.  Gettextize.
	(membcmp): Likewise.
	(ctf_add_type_internal): Likewise.
	(ctf_write_mem): Likewise.
	(ctf_compress_write): Likewise.  Report errors writing the header or
	body.
	(ctf_write): Likewise.
	* ctf-archive.c (ctf_arc_write_fd): Use ctf_err_warn, not
	ctf_dprintf, and gettextize, as above.
	(ctf_arc_write): Likewise.
	(ctf_arc_bufopen): Likewise.
	(ctf_arc_open_internal): Likewise.
	* ctf-labels.c (ctf_label_iter): Likewise.
	* ctf-open-bfd.c (ctf_bfdclose): Likewise.
	(ctf_bfdopen): Likewise.
	(ctf_bfdopen_ctfsect): Likewise.
	(ctf_fdopen): Likewise.
	* ctf-string.c (ctf_str_write_strtab): Likewise.
	* ctf-types.c (ctf_type_resolve): Likewise.
	* ctf-open.c (get_vbytes_common): Likewise. Pass down the ctf dict.
	(get_vbytes_v1): Pass down the ctf dict.
	(get_vbytes_v2): Likewise.
	(flip_ctf): Likewise.
	(flip_types): Likewise. Use ctf_err_warn, not ctf_dprintf, and
	gettextize, as above.
	(upgrade_types_v1): Adjust calls.
	(init_types): Use ctf_err_warn, not ctf_dprintf, as above.
	(ctf_bufopen_internal): Likewise. Adjust calls. Transplant errors
	emitted into individual dicts into the open errors if this turns
	out to be a failed open in the end.
	* ctf-dump.c (ctf_dump_format_type): Adjust ctf_err_warn for new err
	argument.  Gettextize.  Don't emit the errmsg.
	(ctf_dump_funcs): Likewise.  Collapse err label into its only case.
	(ctf_dump_type): Likewise.
	* ctf-link.c (ctf_create_per_cu): Adjust ctf_err_warn for new err
	argument.  Gettextize.  Don't emit the errmsg.
	(ctf_link_one_type): Likewise.
	(ctf_link_lazy_open): Likewise.
	(ctf_link_one_input_archive): Likewise.
	(ctf_link_deduplicating_count_inputs): Likewise.
	(ctf_link_deduplicating_open_inputs): Likewise.
	(ctf_link_deduplicating_close_inputs): Likewise.
	(ctf_link_deduplicating): Likewise.
	(ctf_link): Likewise.
	(ctf_link_deduplicating_per_cu): Likewise. Add some missed
	ctf_set_errnos to obscure error cases.
	* ctf-dedup.c (ctf_dedup_rhash_type): Adjust ctf_err_warn for new
	err argument.  Gettextize.  Don't emit the errmsg.
	(ctf_dedup_populate_mappings): Likewise.
	(ctf_dedup_detect_name_ambiguity): Likewise.
	(ctf_dedup_init): Likewise.
	(ctf_dedup_multiple_input_dicts): Likewise.
	(ctf_dedup_conflictify_unshared): Likewise.
	(ctf_dedup): Likewise.
	(ctf_dedup_rwalk_one_output_mapping): Likewise.
	(ctf_dedup_id_to_target): Likewise.
	(ctf_dedup_emit_type): Likewise.
	(ctf_dedup_emit_struct_members): Likewise.
	(ctf_dedup_populate_type_mapping): Likewise.
	(ctf_dedup_populate_type_mappings): Likewise.
	(ctf_dedup_emit): Likewise.
	(ctf_dedup_hash_type): Likewise. Fix a bit of messed-up error
	status setting.
	(ctf_dedup_rwalk_one_output_mapping): Likewise. Don't hide
	unknown-type-kind messages (which signify file corruption).
---
 binutils/objdump.c          |  42 ++++--
 binutils/readelf.c          |  39 +++--
 include/ctf-api.h           |   2 +-
 ld/ldlang.c                 |  63 ++++----
 ld/testsuite/ld-ctf/ctf.exp |  11 ++
 libctf/ctf-archive.c        |  63 ++++----
 libctf/ctf-create.c         |  90 +++++++----
 libctf/ctf-dedup.c          | 290 ++++++++++++++++++------------------
 libctf/ctf-dump.c           |  27 ++--
 libctf/ctf-impl.h           |  17 +--
 libctf/ctf-labels.c         |   6 +-
 libctf/ctf-link.c           | 133 +++++++++--------
 libctf/ctf-open-bfd.c       |  31 ++--
 libctf/ctf-open.c           |  59 ++++----
 libctf/ctf-string.c         |   2 +-
 libctf/ctf-subr.c           |  87 +++++++++--
 libctf/ctf-types.c          |   3 +-
 17 files changed, 547 insertions(+), 418 deletions(-)

diff --git a/binutils/objdump.c b/binutils/objdump.c
index 79ef0518563..c1848961416 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -4071,6 +4071,29 @@ make_ctfsect (const char *name, bfd_byte *data,
   return ctfsect;
 }
 
+/* Dump CTF errors/warnings.  */
+static void
+dump_ctf_errs (ctf_file_t *fp)
+{
+  ctf_next_t *it = NULL;
+  char *errtext;
+  int is_warning;
+  int err;
+
+  /* Dump accumulated errors and warnings.  */
+  while ((errtext = ctf_errwarning_next (fp, &it, &is_warning, &err)) != NULL)
+    {
+      non_fatal (_("%s: `%s'"), is_warning ? _("warning"): _("error"),
+		 errtext);
+      free (errtext);
+    }
+  if (err != ECTF_NEXT_END)
+    {
+      non_fatal (_("CTF error: cannot get CTF errors: `%s'"),
+		 ctf_errmsg (err));
+    }
+}
+
 /* Dump one CTF archive member.  */
 
 static int
@@ -4081,9 +4104,6 @@ dump_ctf_archive_member (ctf_file_t *ctf, const char *name, void *arg)
 			  "Function objects", "Variables", "Types", "Strings",
 			  ""};
   const char **thing;
-  ctf_next_t *it = NULL;
-  char *errtext;
-  int is_warning;
   size_t i;
 
   /* Only print out the name of non-default-named archive members.
@@ -4121,18 +4141,7 @@ dump_ctf_archive_member (ctf_file_t *ctf, const char *name, void *arg)
 	}
     }
 
-  /* Dump accumulated errors and warnings.  */
-  while ((errtext = ctf_errwarning_next (ctf, &it, &is_warning)) != NULL)
-    {
-      non_fatal (_("%s: `%s'"), is_warning ? _("warning"): _("error"),
-		 errtext);
-      free (errtext);
-    }
-  if (ctf_errno (ctf) != ECTF_NEXT_END)
-    {
-      non_fatal (_("CTF error: cannot get CTF errors: `%s'"),
-		 ctf_errmsg (ctf_errno (ctf)));
-    }
+  dump_ctf_errs (ctf);
 
   return 0;
 }
@@ -4162,6 +4171,7 @@ dump_ctf (bfd *abfd, const char *sect_name, const char *parent_name)
   ctfsect = make_ctfsect (sect_name, ctfdata, ctfsize);
   if ((ctfa = ctf_bfdopen_ctfsect (abfd, &ctfsect, &err)) == NULL)
     {
+      dump_ctf_errs (NULL);
       non_fatal (_("CTF open failure: %s"), ctf_errmsg (err));
       bfd_fatal (bfd_get_filename (abfd));
     }
@@ -4171,6 +4181,7 @@ dump_ctf (bfd *abfd, const char *sect_name, const char *parent_name)
       ctfsect = make_ctfsect (parent_name, parentdata, parentsize);
       if ((parenta = ctf_bfdopen_ctfsect (abfd, &ctfsect, &err)) == NULL)
 	{
+	  dump_ctf_errs (NULL);
 	  non_fatal (_("CTF open failure: %s"), ctf_errmsg (err));
 	  bfd_fatal (bfd_get_filename (abfd));
 	}
@@ -4185,6 +4196,7 @@ dump_ctf (bfd *abfd, const char *sect_name, const char *parent_name)
      put CTFs and their parents in archives together.)  */
   if ((parent = ctf_arc_open_by_name (lookparent, NULL, &err)) == NULL)
     {
+      dump_ctf_errs (NULL);
       non_fatal (_("CTF open failure: %s"), ctf_errmsg (err));
       bfd_fatal (bfd_get_filename (abfd));
     }
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 421992d12d9..1466f7519aa 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -14205,6 +14205,26 @@ dump_ctf_indent_lines (ctf_sect_names_t sect ATTRIBUTE_UNUSED,
   return new_s;
 }
 
+/* Dump CTF errors/warnings.  */
+static void
+dump_ctf_errs (ctf_file_t *fp)
+{
+  ctf_next_t *it = NULL;
+  char *errtext;
+  int is_warning;
+  int err;
+
+  /* Dump accumulated errors and warnings.  */
+  while ((errtext = ctf_errwarning_next (fp, &it, &is_warning, &err)) != NULL)
+    {
+      error (_("%s: `%s'"), is_warning ? _("warning"): _("error"),
+	     errtext);
+      free (errtext);
+    }
+  if (err != ECTF_NEXT_END)
+    error (_("CTF error: cannot get CTF errors: `%s'"), ctf_errmsg (err));
+}
+
 /* Dump one CTF archive member.  */
 
 static int
@@ -14215,9 +14235,6 @@ dump_ctf_archive_member (ctf_file_t *ctf, const char *name, void *arg)
 			  "Function objects", "Variables", "Types", "Strings",
 			  ""};
   const char **thing;
-  ctf_next_t *it = NULL;
-  char *errtext;
-  int is_warning;
   size_t i;
   int err = 0;
 
@@ -14258,18 +14275,7 @@ dump_ctf_archive_member (ctf_file_t *ctf, const char *name, void *arg)
     }
 
  out:
-  /* Dump accumulated errors and warnings.  */
-  while ((errtext = ctf_errwarning_next (ctf, &it, &is_warning)) != NULL)
-    {
-      error (_("%s: `%s'\n"), is_warning ? _("warning"): _("error"),
-	     errtext);
-      free (errtext);
-    }
-  if (ctf_errno (ctf) != ECTF_NEXT_END)
-    {
-      error (_("CTF error: cannot get CTF errors: `%s'\n"),
-	     ctf_errmsg (ctf_errno (ctf)));
-    }
+  dump_ctf_errs (ctf);
   return err;
 }
 
@@ -14356,6 +14362,7 @@ dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
 
   if ((ctfa = ctf_arc_bufopen (&ctfsect, symsectp, strsectp, &err)) == NULL)
     {
+      dump_ctf_errs (NULL);
       error (_("CTF open failure: %s\n"), ctf_errmsg (err));
       goto fail;
     }
@@ -14365,6 +14372,7 @@ dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
       if ((parenta = ctf_arc_bufopen (&parentsect, symsectp, strsectp,
 				      &err)) == NULL)
 	{
+	  dump_ctf_errs (NULL);
 	  error (_("CTF open failure: %s\n"), ctf_errmsg (err));
 	  goto fail;
 	}
@@ -14378,6 +14386,7 @@ dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
      put CTFs and their parents in archives together.)  */
   if ((parent = ctf_arc_open_by_name (lookparent, NULL, &err)) == NULL)
     {
+      dump_ctf_errs (NULL);
       error (_("CTF open failure: %s\n"), ctf_errmsg (err));
       goto fail;
     }
diff --git a/include/ctf-api.h b/include/ctf-api.h
index 77ea5cd0ef8..3a8e942f0d8 100644
--- a/include/ctf-api.h
+++ b/include/ctf-api.h
@@ -411,7 +411,7 @@ extern char *ctf_dump (ctf_file_t *, ctf_dump_state_t **state,
    the error/warning list, in order of emission.  Errors and warnings are popped
    after return: the caller must free the returned error-text pointer.  */
 extern char *ctf_errwarning_next (ctf_file_t *, ctf_next_t **,
-				  int *is_warning);
+				  int *is_warning, int *errp);
 
 extern ctf_id_t ctf_add_array (ctf_file_t *, uint32_t,
 			       const ctf_arinfo_t *);
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 1dd17ffff22..dc6f1b40399 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -3671,6 +3671,34 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
 }
 
 #ifdef ENABLE_LIBCTF
+/* Emit CTF errors and warnings.  fp can be NULL to report errors/warnings
+   that happened specifically at CTF open time.  */
+static void
+lang_ctf_errs_warnings (ctf_file_t *fp)
+{
+  ctf_next_t *i = NULL;
+  char *text;
+  int is_warning;
+  int err;
+
+  while ((text = ctf_errwarning_next (fp, &i, &is_warning, &err)) != NULL)
+    {
+      einfo (_("%s: `%s'\n"), is_warning ? _("CTF warning"): _("CTF error"),
+	     text);
+      free (text);
+    }
+  if (err != ECTF_NEXT_END)
+    {
+      einfo (_("CTF error: cannot get CTF errors: `%s'\n"),
+	     ctf_errmsg (err));
+    }
+
+  /* `err' returns errors from the error/warning iterator in particular.
+     These never assert.  But if we have an fp, that could have recorded
+     an assertion failure: assert if it has done so.  */
+  ASSERT (!fp || ctf_errno (fp) != ECTF_INTERNAL);
+}
+
 /* Open the CTF sections in the input files with libctf: if any were opened,
    create a fake input file that we'll write the merged CTF data to later
    on.  */
@@ -3693,9 +3721,12 @@ ldlang_open_ctf (void)
       if ((file->the_ctf = ctf_bfdopen (file->the_bfd, &err)) == NULL)
 	{
 	  if (err != ECTF_NOCTFDATA)
-	    einfo (_("%P: warning: CTF section in %pB not loaded; "
-		     "its types will be discarded: `%s'\n"), file->the_bfd,
+	    {
+	      lang_ctf_errs_warnings (NULL);
+	      einfo (_("%P: warning: CTF section in %pB not loaded; "
+		       "its types will be discarded: `%s'\n"), file->the_bfd,
 		     ctf_errmsg (err));
+	    }
 	  continue;
 	}
 
@@ -3728,29 +3759,6 @@ ldlang_open_ctf (void)
     ctf_close (errfile->the_ctf);
 }
 
-/* Emit CTF errors and warnings.  */
-static void
-lang_ctf_errs_warnings (ctf_file_t *fp)
-{
-  ctf_next_t *i = NULL;
-  char *text;
-  int is_warning;
-
-  while ((text = ctf_errwarning_next (fp, &i, &is_warning)) != NULL)
-    {
-      einfo (_("%s: `%s'\n"), is_warning ? _("CTF warning"): _("CTF error"),
-	     text);
-      free (text);
-    }
-  if (ctf_errno (fp) != ECTF_NEXT_END)
-    {
-      einfo (_("CTF error: cannot get CTF errors: `%s'\n"),
-	     ctf_errmsg (ctf_errno (fp)));
-    }
-
-  ASSERT (ctf_errno (fp) != ECTF_INTERNAL);
-}
-
 /* Merge together CTF sections.  After this, only the symtab-dependent
    function and data object sections need adjustment.  */
 
@@ -3804,6 +3812,7 @@ lang_merge_ctf (void)
 
   if (ctf_link (ctf_output, flags) < 0)
     {
+      lang_ctf_errs_warnings (ctf_output);
       einfo (_("%P: warning: CTF linking failed; "
 	       "output will have no CTF section: `%s'\n"),
 	     ctf_errmsg (ctf_errno (ctf_output)));
@@ -3813,6 +3822,7 @@ lang_merge_ctf (void)
 	  output_sect->flags |= SEC_EXCLUDE;
 	}
     }
+  /* Output any lingering errors that didn't come from ctf_link.  */
   lang_ctf_errs_warnings (ctf_output);
 }
 
@@ -3860,6 +3870,7 @@ lang_write_ctf (int late)
       output_sect->size = output_size;
       output_sect->flags |= SEC_IN_MEMORY | SEC_KEEP;
 
+      lang_ctf_errs_warnings (ctf_output);
       if (!output_sect->contents)
 	{
 	  einfo (_("%P: warning: CTF section emission failed; "
@@ -3868,8 +3879,6 @@ lang_write_ctf (int late)
 	  output_sect->size = 0;
 	  output_sect->flags |= SEC_EXCLUDE;
 	}
-
-      lang_ctf_errs_warnings (ctf_output);
     }
 
   /* This also closes every CTF input file used in the link.  */
diff --git a/ld/testsuite/ld-ctf/ctf.exp b/ld/testsuite/ld-ctf/ctf.exp
index be4c6ed3d6e..6893365f30f 100644
--- a/ld/testsuite/ld-ctf/ctf.exp
+++ b/ld/testsuite/ld-ctf/ctf.exp
@@ -28,6 +28,11 @@ if ![is_elf_format] {
     return 0
 }
 
+if {[info exists env(LC_ALL)]} {
+    set old_lc_all $env(LC_ALL)
+}
+set env(LC_ALL) "C"
+
 set ctf_test_list [lsort [glob -nocomplain $srcdir/$subdir/*.d]]
 
 foreach ctf_test $ctf_test_list {
@@ -39,3 +44,9 @@ foreach ctf_test $ctf_test_list {
     verbose [file rootname $ctf_test]
     run_dump_test [file rootname $ctf_test] { { cc "-gt -fPIC" } }
 }
+
+if {[info exists old_lc_all]} {
+    set env(LC_ALL) $old_lc_all
+} else {
+    unset env(LC_ALL)
+}
diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c
index 87e682c3943..799c3fbf26c 100644
--- a/libctf/ctf-archive.c
+++ b/libctf/ctf-archive.c
@@ -82,19 +82,19 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
   ctf_startoffs = headersz;
   if (lseek (fd, ctf_startoffs - 1, SEEK_SET) < 0)
     {
-      errmsg = "ctf_arc_write(): cannot extend file while writing: %s\n";
+      errmsg = N_("ctf_arc_write(): cannot extend file while writing");
       goto err;
     }
 
   if (write (fd, &dummy, 1) < 0)
     {
-      errmsg = "ctf_arc_write(): cannot extend file while writing: %s\n";
+      errmsg = N_("ctf_arc_write(): cannot extend file while writing");
       goto err;
     }
 
   if ((archdr = arc_mmap_header (fd, headersz)) == NULL)
     {
-      errmsg = "ctf_arc_write(): Cannot mmap(): %s\n";
+      errmsg = N_("ctf_arc_write(): cannot mmap");
       goto err;
     }
 
@@ -128,7 +128,7 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
   nametbl = malloc (namesz);
   if (nametbl == NULL)
     {
-      errmsg = "Error writing named CTF to archive: %s\n";
+      errmsg = N_("ctf_arc_write(): error writing named CTF to archive");
       goto err_unmap;
     }
 
@@ -144,13 +144,13 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
       off = arc_write_one_ctf (ctf_files[i], fd, threshold);
       if ((off < 0) && (off > -ECTF_BASE))
 	{
-	  errmsg = "ctf_arc_write(): Cannot determine file "
-	    "position while writing to archive: %s";
+	  errmsg = N_("ctf_arc_write(): cannot determine file "
+		      "position while writing to archive");
 	  goto err_free;
 	}
       if (off < 0)
 	{
-	  errmsg = "ctf_arc_write(): Cannot write CTF file to archive: %s\n";
+	  errmsg = N_("ctf_arc_write(): cannot write CTF file to archive");
 	  errno = off * -1;
 	  goto err_free;
 	}
@@ -171,8 +171,8 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
 
   if ((nameoffs = lseek (fd, 0, SEEK_CUR)) < 0)
     {
-      errmsg = "ctf_arc_write(): Cannot get current file position "
-	"in archive: %s\n";
+      errmsg = N_("ctf_arc_write(): cannot get current file position "
+		  "in archive");
       goto err_free;
     }
   archdr->ctfa_names = htole64 (nameoffs);
@@ -182,7 +182,7 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
       ssize_t len;
       if ((len = write (fd, np, namesz)) < 0)
 	{
-	  errmsg = "ctf_arc_write(): Cannot write name table to archive: %s\n";
+	  errmsg = N_("ctf_arc_write(): cannot write name table to archive");
 	  goto err_free;
 	}
       namesz -= len;
@@ -201,8 +201,11 @@ err_free:
 err_unmap:
   arc_mmap_unmap (archdr, headersz, NULL);
 err:
-  ctf_dprintf (errmsg, errno < ECTF_BASE ? strerror (errno) :
-	       ctf_errmsg (errno));
+  /* We report errors into the first file in the archive, if any: if this is a
+     zero-file archive, put it in the open-errors stream for lack of anywhere
+     else for it to go.  */
+  ctf_err_warn (ctf_file_cnt > 0 ? ctf_files[0] : NULL, 0, errno, "%s",
+		gettext (errmsg));
   return errno;
 }
 
@@ -213,7 +216,7 @@ err:
 
    Returns 0 on success, or an errno, or an ECTF_* value.  */
 int
-ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
+ctf_arc_write (const char *file, ctf_file_t **ctf_files, size_t ctf_file_cnt,
 	       const char **names, size_t threshold)
 {
   int err;
@@ -221,8 +224,8 @@ ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
 
   if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0)
     {
-      ctf_dprintf ("ctf_arc_write(): cannot create %s: %s\n", file,
-		   strerror (errno));
+      ctf_err_warn (ctf_file_cnt > 0 ? ctf_files[0] : NULL, 0, errno,
+		    _("ctf_arc_write(): cannot create %s"), file);
       return errno;
     }
 
@@ -231,8 +234,8 @@ ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
     goto err_close;
 
   if ((err = close (fd)) < 0)
-    ctf_dprintf ("ctf_arc_write(): Cannot close after writing to archive: "
-		 "%s\n", strerror (errno));
+    ctf_err_warn (ctf_file_cnt > 0 ? ctf_files[0] : NULL, 0, errno,
+		  _("ctf_arc_write(): cannot close after writing to archive"));
   goto err;
 
  err_close:
@@ -404,8 +407,7 @@ ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
       is_archive = 0;
       if ((fp = ctf_bufopen (ctfsect, symsect, strsect, errp)) == NULL)
 	{
-	  ctf_dprintf ("ctf_arc_bufopen(): cannot open CTF: %s\n",
-		       ctf_errmsg (*errp));
+	  ctf_err_warn (NULL, 0, *errp, _("ctf_arc_bufopen(): cannot open CTF"));
 	  return NULL;
 	}
     }
@@ -426,24 +428,24 @@ ctf_arc_open_internal (const char *filename, int *errp)
   libctf_init_debug();
   if ((fd = open (filename, O_RDONLY)) < 0)
     {
-      errmsg = "ctf_arc_open(): cannot open %s: %s\n";
+      errmsg = N_("ctf_arc_open(): cannot open %s");
       goto err;
     }
   if (fstat (fd, &s) < 0)
     {
-      errmsg = "ctf_arc_open(): cannot stat %s: %s\n";
+      errmsg = N_("ctf_arc_open(): cannot stat %s");
       goto err_close;
     }
 
   if ((arc = arc_mmap_file (fd, s.st_size)) == NULL)
     {
-      errmsg = "ctf_arc_open(): Cannot read in %s: %s\n";
+      errmsg = N_("ctf_arc_open(): cannot read in %s");
       goto err_close;
     }
 
   if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
     {
-      errmsg = "ctf_arc_open(): Invalid magic number";
+      errmsg = N_("ctf_arc_open(): %s: invalid magic number");
       errno = ECTF_FMT;
       goto err_unmap;
     }
@@ -462,8 +464,7 @@ err_close:
 err:
   if (errp)
     *errp = errno;
-  ctf_dprintf (errmsg, filename, errno < ECTF_BASE ? strerror (errno) :
-	       ctf_errmsg (errno));
+  ctf_err_warn (NULL, 0, errno, gettext (errmsg), filename);
   return NULL;
 }
 
@@ -872,7 +873,8 @@ static int arc_mmap_writeout (int fd _libctf_unused_, void *header,
     if (msync (header, headersz, MS_ASYNC) < 0)
     {
       if (errmsg)
-	*errmsg = "arc_mmap_writeout(): Cannot sync after writing to %s: %s\n";
+	*errmsg = N_("arc_mmap_writeout(): cannot sync after writing "
+		     "to %s: %s");
       return -1;
     }
     return 0;
@@ -884,7 +886,8 @@ static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg)
   if (munmap (header, headersz) < 0)
     {
       if (errmsg)
-	*errmsg = "arc_mmap_munmap(): Cannot unmap after writing to %s: %s\n";
+	*errmsg = N_("arc_mmap_munmap(): cannot unmap after writing "
+		     "to %s: %s");
       return -1;
     }
     return 0;
@@ -928,8 +931,8 @@ static int arc_mmap_writeout (int fd, void *header, size_t headersz,
   if ((lseek (fd, 0, SEEK_SET)) < 0)
     {
       if (errmsg)
-	*errmsg = "arc_mmap_writeout(): Cannot seek while writing header to "
-	  "%s: %s\n";
+	*errmsg = N_("arc_mmap_writeout(): cannot seek while writing header to "
+		     "%s: %s");
       return -1;
     }
 
@@ -938,7 +941,7 @@ static int arc_mmap_writeout (int fd, void *header, size_t headersz,
       if ((len = write (fd, data, count)) < 0)
 	{
 	  if (errmsg)
-	    *errmsg = "arc_mmap_writeout(): Cannot write header to %s: %s\n";
+	    *errmsg = N_("arc_mmap_writeout(): cannot write header to %s: %s");
 	  return len;
 	}
       if (len == EINTR)
diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index b319476c168..e236606a249 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -1596,6 +1596,13 @@ ctf_add_variable (ctf_file_t *fp, const char *name, ctf_id_t ref)
   return 0;
 }
 
+typedef struct ctf_bundle
+{
+  ctf_file_t *ctb_file;		/* CTF container handle.  */
+  ctf_id_t ctb_type;		/* CTF type identifier.  */
+  ctf_dtdef_t *ctb_dtd;		/* CTF dynamic type definition (if any).  */
+} ctf_bundle_t;
+
 static int
 enumcmp (const char *name, int value, void *arg)
 {
@@ -1604,14 +1611,15 @@ enumcmp (const char *name, int value, void *arg)
 
   if (ctf_enum_value (ctb->ctb_file, ctb->ctb_type, name, &bvalue) < 0)
     {
-      ctf_dprintf ("Conflict due to member %s iteration error: %s.\n", name,
-		   ctf_errmsg (ctf_errno (ctb->ctb_file)));
+      ctf_err_warn (ctb->ctb_file, 0, 0,
+		    _("conflict due to enum %s iteration error"), name);
       return 1;
     }
   if (value != bvalue)
     {
-      ctf_dprintf ("Conflict due to value change: %i versus %i\n",
-		   value, bvalue);
+      ctf_err_warn (ctb->ctb_file, 1, ECTF_CONFLICT,
+		    _("conflict due to enum value change: %i versus %i"),
+		    value, bvalue);
       return 1;
     }
   return 0;
@@ -1640,14 +1648,17 @@ membcmp (const char *name, ctf_id_t type _libctf_unused_, unsigned long offset,
 
   if (ctf_member_info (ctb->ctb_file, ctb->ctb_type, name, &ctm) < 0)
     {
-      ctf_dprintf ("Conflict due to member %s iteration error: %s.\n", name,
-		   ctf_errmsg (ctf_errno (ctb->ctb_file)));
+      ctf_err_warn (ctb->ctb_file, 0, 0,
+		    _("conflict due to struct member %s iteration error"),
+		    name);
       return 1;
     }
   if (ctm.ctm_offset != offset)
     {
-      ctf_dprintf ("Conflict due to member %s offset change: "
-		   "%lx versus %lx\n", name, ctm.ctm_offset, offset);
+      ctf_err_warn (ctb->ctb_file, 1, ECTF_CONFLICT,
+		    _("conflict due to struct member %s offset change: "
+		      "%lx versus %lx"),
+		    name, ctm.ctm_offset, offset);
       return 1;
     }
   return 0;
@@ -1791,8 +1802,10 @@ ctf_add_type_internal (ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type
 	  || (kind != CTF_K_ENUM && kind != CTF_K_STRUCT
 	      && kind != CTF_K_UNION))
 	{
-	  ctf_dprintf ("Conflict for type %s: kinds differ, new: %i; "
-		       "old (ID %lx): %i\n", name, kind, dst_type, dst_kind);
+	  ctf_err_warn (dst_fp, 1, ECTF_CONFLICT,
+			_("ctf_add_file(): conflict for type %s: "
+			  "kinds differ, new: %i; old (ID %lx): %i"),
+			name, kind, dst_type, dst_kind);
 	  return (ctf_set_errno (dst_fp, ECTF_CONFLICT));
 	}
     }
@@ -1937,12 +1950,13 @@ ctf_add_type_internal (ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type
 
 	  if (memcmp (&src_ar, &dst_ar, sizeof (ctf_arinfo_t)))
 	    {
-	      ctf_dprintf ("Conflict for type %s against ID %lx: "
-			   "array info differs, old %lx/%lx/%x; "
-			   "new: %lx/%lx/%x\n", name, dst_type,
-			   src_ar.ctr_contents, src_ar.ctr_index,
-			   src_ar.ctr_nelems, dst_ar.ctr_contents,
-			   dst_ar.ctr_index, dst_ar.ctr_nelems);
+	      ctf_err_warn (dst_fp, 1, ECTF_CONFLICT,
+			    _("conflict for type %s against ID %lx: array info "
+			      "differs, old %lx/%lx/%x; new: %lx/%lx/%x"),
+			    name, dst_type, src_ar.ctr_contents,
+			    src_ar.ctr_index, src_ar.ctr_nelems,
+			    dst_ar.ctr_contents, dst_ar.ctr_index,
+			    dst_ar.ctr_nelems);
 	      return (ctf_set_errno (dst_fp, ECTF_CONFLICT));
 	    }
 	}
@@ -1986,18 +2000,19 @@ ctf_add_type_internal (ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type
 	    if (ctf_type_size (src_fp, src_type) !=
 		ctf_type_size (dst_fp, dst_type))
 	      {
-		ctf_dprintf ("Conflict for type %s against ID %lx: "
-			     "union size differs, old %li, new %li\n",
-			     name, dst_type,
-			     (long) ctf_type_size (src_fp, src_type),
-			     (long) ctf_type_size (dst_fp, dst_type));
+		ctf_err_warn (dst_fp, 1, ECTF_CONFLICT,
+			      _("conflict for type %s against ID %lx: union "
+				"size differs, old %li, new %li"), name,
+			      dst_type, (long) ctf_type_size (src_fp, src_type),
+			      (long) ctf_type_size (dst_fp, dst_type));
 		return (ctf_set_errno (dst_fp, ECTF_CONFLICT));
 	      }
 
 	    if (ctf_member_iter (src_fp, src_type, membcmp, &dst))
 	      {
-		ctf_dprintf ("Conflict for type %s against ID %lx: "
-			     "members differ, see above\n", name, dst_type);
+		ctf_err_warn (dst_fp, 1, ECTF_CONFLICT,
+			      _("conflict for type %s against ID %lx: members "
+				"differ, see above"), name, dst_type);
 		return (ctf_set_errno (dst_fp, ECTF_CONFLICT));
 	      }
 
@@ -2076,8 +2091,9 @@ ctf_add_type_internal (ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type
 	  if (ctf_enum_iter (src_fp, src_type, enumcmp, &dst)
 	      || ctf_enum_iter (dst_fp, dst_type, enumcmp, &src))
 	    {
-	      ctf_dprintf ("Conflict for enum %s against ID %lx: "
-			   "members differ, see above\n", name, dst_type);
+	      ctf_err_warn (dst_fp, 1, ECTF_CONFLICT,
+			    _("conflict for enum %s against ID %lx: members "
+			      "differ, see above"), name, dst_type);
 	      return (ctf_set_errno (dst_fp, ECTF_CONFLICT));
 	    }
 	}
@@ -2200,13 +2216,17 @@ ctf_compress_write (ctf_file_t *fp, int fd)
   compress_len = compressBound (fp->ctf_size);
 
   if ((buf = malloc (compress_len)) == NULL)
-    return (ctf_set_errno (fp, ECTF_ZALLOC));
+    {
+      ctf_err_warn (fp, 0, 0, _("ctf_compress_write: cannot allocate %li bytes"),
+		    (unsigned long) compress_len);
+      return (ctf_set_errno (fp, ECTF_ZALLOC));
+    }
 
   if ((rc = compress (buf, (uLongf *) &compress_len,
 		      fp->ctf_buf, fp->ctf_size)) != Z_OK)
     {
-      ctf_dprintf ("zlib deflate err: %s\n", zError (rc));
       err = ctf_set_errno (fp, ECTF_COMPRESS);
+      ctf_err_warn (fp, 0, 0, _("zlib deflate err: %s"), zError (rc));
       goto ret;
     }
 
@@ -2215,6 +2235,7 @@ ctf_compress_write (ctf_file_t *fp, int fd)
       if ((len = write (fd, hp, header_len)) < 0)
 	{
 	  err = ctf_set_errno (fp, errno);
+	  ctf_err_warn (fp, 0, 0, _("ctf_compress_write: error writing header"));
 	  goto ret;
 	}
       header_len -= len;
@@ -2227,6 +2248,7 @@ ctf_compress_write (ctf_file_t *fp, int fd)
       if ((len = write (fd, bp, compress_len)) < 0)
 	{
 	  err = ctf_set_errno (fp, errno);
+	  ctf_err_warn (fp, 0, 0, _("ctf_compress_write: error writing"));
 	  goto ret;
 	}
       compress_len -= len;
@@ -2260,6 +2282,8 @@ ctf_write_mem (ctf_file_t *fp, size_t *size, size_t threshold)
 		     + sizeof (struct ctf_header))) == NULL)
     {
       ctf_set_errno (fp, ENOMEM);
+      ctf_err_warn (fp, 0, 0, _("ctf_write_mem: cannot allocate %li bytes"),
+		    (unsigned long) (compress_len + sizeof (struct ctf_header)));
       return NULL;
     }
 
@@ -2280,8 +2304,8 @@ ctf_write_mem (ctf_file_t *fp, size_t *size, size_t threshold)
       if ((rc = compress (bp, (uLongf *) &compress_len,
 			  fp->ctf_buf, fp->ctf_size)) != Z_OK)
 	{
-	  ctf_dprintf ("zlib deflate err: %s\n", zError (rc));
 	  ctf_set_errno (fp, ECTF_COMPRESS);
+	  ctf_err_warn (fp, 0, 0, _("zlib deflate err: %s"), zError (rc));
 	  free (buf);
 	  return NULL;
 	}
@@ -2306,7 +2330,10 @@ ctf_write (ctf_file_t *fp, int fd)
   while (resid != 0)
     {
       if ((len = write (fd, buf, resid)) <= 0)
-	return (ctf_set_errno (fp, errno));
+	{
+	  ctf_err_warn (fp, 0, errno, _("ctf_write: error writing header"));
+	  return (ctf_set_errno (fp, errno));
+	}
       resid -= len;
       buf += len;
     }
@@ -2316,7 +2343,10 @@ ctf_write (ctf_file_t *fp, int fd)
   while (resid != 0)
     {
       if ((len = write (fd, buf, resid)) <= 0)
-	return (ctf_set_errno (fp, errno));
+	{
+	  ctf_err_warn (fp, 0, errno, _("ctf_write: error writing"));
+	  return (ctf_set_errno (fp, errno));
+	}
       resid -= len;
       buf += len;
     }
diff --git a/libctf/ctf-dedup.c b/libctf/ctf-dedup.c
index 77b34f4539d..b58b815b84d 100644
--- a/libctf/ctf-dedup.c
+++ b/libctf/ctf-dedup.c
@@ -581,7 +581,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 #define ADD_CITER(citers, hval)						\
   do									\
     {									\
-      whaterr = "updating citers";					\
+      whaterr = N_("error updating citers");				\
       if (!citers)							\
 	if ((citers = ctf_dynset_create (htab_hash_string,		\
 					  ctf_dynset_eq_string,		\
@@ -610,14 +610,13 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 
       if ((hval = intern (fp, strdup (hashbuf))) == NULL)
 	{
-	  ctf_err_warn (fp, 0, "%s (%i): out of memory during forwarding-stub "
-			"hashing for type with GID %p; errno: %s",
-			ctf_link_input_name (input), input_num, type_id,
-			strerror (errno));
+	  ctf_err_warn (fp, 0, 0, _("%s (%i): out of memory during forwarding-"
+				    "stub hashing for type with GID %p"),
+			ctf_link_input_name (input), input_num, type_id);
 	  return NULL;				/* errno is set for us.  */
 	}
 
-      /* In share--duplicated link mode, make sure the origin of this type is
+      /* In share-duplicated link mode, make sure the origin of this type is
 	 recorded, even if this is a type in a parent dict which will not be
 	 directly traversed.  */
       if (d->cd_link_flags & CTF_LINK_SHARE_DUPLICATED
@@ -680,7 +679,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 			    depth);
 	if (ctf_type_encoding (input, type, &ep) < 0)
 	  {
-	    whaterr = "encoding";
+	    whaterr = N_("error getting encoding");
 	    goto err;
 	  }
 	ctf_dedup_sha1_add (&hash, &ep, sizeof (ctf_encoding_t), "encoding",
@@ -699,7 +698,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 				       child_type, flags, depth,
 				       populate_fun)) == NULL)
 	{
-	  whaterr = "referenced type hashing";
+	  whaterr = N_("error doing referenced type hashing");
 	  goto err;
 	}
       ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "referenced type",
@@ -728,7 +727,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 					 child_type, flags, depth,
 					 populate_fun)) == NULL)
 	  {
-	    whaterr = "slice-referenced type hashing";
+	    whaterr = N_("error doing slice-referenced type hashing");
 	    goto err;
 	  }
 	ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "sliced type",
@@ -753,7 +752,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 
 	if (ctf_array_info (input, type, &ar) < 0)
 	  {
-	    whaterr = "array info";
+	    whaterr = N_("error getting array info");
 	    goto err;
 	  }
 
@@ -761,7 +760,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 					 ar.ctr_contents, flags, depth,
 					 populate_fun)) == NULL)
 	  {
-	    whaterr = "array contents type hashing";
+	    whaterr = N_("error doing array contents type hashing");
 	    goto err;
 	  }
 	ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "array contents",
@@ -772,7 +771,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 					 ar.ctr_index, flags, depth,
 					 populate_fun)) == NULL)
 	  {
-	    whaterr = "array index type hashing";
+	    whaterr = N_("error doing array index type hashing");
 	    goto err;
 	  }
 	ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "array index",
@@ -791,7 +790,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 
 	if (ctf_func_type_info (input, type, &fi) < 0)
 	  {
-	    whaterr = "func type info";
+	    whaterr = N_("error getting func type info");
 	    goto err;
 	  }
 
@@ -799,7 +798,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 					 fi.ctc_return, flags, depth,
 					 populate_fun)) == NULL)
 	  {
-	    whaterr = "func return type";
+	    whaterr = N_("error getting func return type");
 	    goto err;
 	  }
 	ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "func return",
@@ -812,14 +811,14 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 
 	if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
 	  {
-	    whaterr = "memory allocation";
+	    whaterr = N_("error doing memory allocation");
 	    goto err;
 	  }
 
 	if (ctf_func_type_args (input, type, fi.ctc_argc, args) < 0)
 	  {
 	    free (args);
-	    whaterr = "func arg type";
+	    whaterr = N_("error getting func arg type");
 	    goto err;
 	  }
 	for (j = 0; j < fi.ctc_argc; j++)
@@ -829,7 +828,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 					     populate_fun)) == NULL)
 	      {
 		free (args);
-		whaterr = "func arg type hashing";
+		whaterr = N_("error doing func arg type hashing");
 		goto err;
 	      }
 	    ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "func arg type",
@@ -854,7 +853,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 	  }
 	if (ctf_errno (input) != ECTF_NEXT_END)
 	  {
-	    whaterr = "enum member iteration";
+	    whaterr = N_("error doing enum member iteration");
 	    goto err;
 	  }
 	break;
@@ -887,7 +886,7 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 					     input_num, membtype, flags, depth,
 					     populate_fun)) == NULL)
 	      {
-		whaterr = "struct/union member type hashing";
+		whaterr = N_("error doing struct/union member type hashing");
 		goto iterr;
 	      }
 
@@ -899,26 +898,26 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
 	  }
 	if (ctf_errno (input) != ECTF_NEXT_END)
 	  {
-	    whaterr = "struct/union member iteration";
+	    whaterr = N_("error doing struct/union member iteration");
 	    goto err;
 	  }
 	break;
       }
     default:
-      whaterr = "unknown type kind";
+      whaterr = N_("error: unknown type kind");
       goto err;
     }
   ctf_sha1_fini (&hash, hashbuf);
 
   if ((hval = intern (fp, strdup (hashbuf))) == NULL)
     {
-      whaterr = "hash internment";
+      whaterr = N_("cannot intern hash");
       goto oom;
     }
 
   /* Populate the citers for this type's subtypes, now the hash for the type
      itself is known.  */
-  whaterr = "citer tracking";
+  whaterr = N_("error tracking citers");
 
   if (citer)
     {
@@ -957,17 +956,15 @@ ctf_dedup_rhash_type (ctf_file_t *fp, ctf_file_t *input, ctf_file_t **inputs,
   ctf_next_destroy (i);
  err:
   ctf_sha1_fini (&hash, NULL);
-  ctf_err_warn (fp, 0, "%s (%i): %s error during type hashing for "
-		"type %lx, kind %i: CTF error: %s; errno: %s",
-		ctf_link_input_name (input), input_num, whaterr, type,
-		kind, ctf_errmsg (ctf_errno (fp)), strerror (errno));
+  ctf_err_warn (fp, 0, 0, _("%s (%i): %s: during type hashing for type %lx, "
+			    "kind %i"), ctf_link_input_name (input),
+		input_num, gettext (whaterr), type, kind);
   return NULL;
  oom:
   ctf_set_errno (fp, errno);
-  ctf_err_warn (fp, 0, "%s (%i): %s error during type hashing for "
-		"type %lx, kind %i: CTF error: %s; errno: %s",
-		ctf_link_input_name (input), input_num, whaterr, type,
-		kind, ctf_errmsg (ctf_errno (fp)), strerror (errno));
+  ctf_err_warn (fp, 0, 0, _("%s (%i): %s: during type hashing for type %lx, "
+			    "kind %i"), ctf_link_input_name (input),
+		input_num, gettext (whaterr), type, kind);
   return NULL;
 }
 
@@ -1034,8 +1031,10 @@ ctf_dedup_hash_type (ctf_file_t *fp, ctf_file_t *input,
 
   if ((tp = ctf_lookup_by_id (&input, type)) == NULL)
     {
-      ctf_err_warn (fp, 0, "%s (%i): lookup failure for type %lx: flags %x",
-		    ctf_link_input_name (input), input_num, type, flags);
+      ctf_set_errno (fp, ctf_errno (input));
+      ctf_err_warn (fp, 0, 0, _("%s (%i): lookup failure for type %lx: "
+				"flags %x"), ctf_link_input_name (input),
+		    input_num, type, flags);
       return NULL;		/* errno is set for us.  */
     }
 
@@ -1106,14 +1105,14 @@ ctf_dedup_hash_type (ctf_file_t *fp, ctf_file_t *input,
 
       if (ctf_dynhash_cinsert (d->cd_type_hashes, type_id, hval) < 0)
 	{
-	  whaterr = "hash caching";
+	  whaterr = N_("error hash caching");
 	  goto oom;
 	}
 
       if (populate_fun (fp, input, inputs, input_num, type, type_id,
 			decorated, hval) < 0)
 	{
-	  whaterr = "population function";
+	  whaterr = N_("error calling population function");
 	  goto err;				/* errno is set for us. */
 	}
     }
@@ -1127,11 +1126,10 @@ ctf_dedup_hash_type (ctf_file_t *fp, ctf_file_t *input,
  oom:
   ctf_set_errno (fp, errno);
  err:
-  ctf_err_warn (fp, 0, "%s (%i): %s error during type hashing, type %lx, "
-		"kind %i: CTF errno: %s; errno: %s",
-		ctf_link_input_name (input), input_num, whaterr, type,
-		kind, ctf_errmsg (ctf_errno (fp)),
-		strerror (errno));
+  ctf_err_warn (fp, 0, 0, _("%s (%i): %s: during type hashing, "
+			    "type %lx, kind %i"),
+		ctf_link_input_name (input), input_num,
+		gettext (whaterr), type, kind);
   return NULL;
 }
 
@@ -1218,7 +1216,7 @@ ctf_dedup_populate_mappings (ctf_file_t *fp, ctf_file_t *input _libctf_unused_,
 	  ctf_id_t bar = CTF_DEDUP_GID_TO_TYPE (one_id);
 	  if (ctf_type_kind_unsliced (foo, bar) != orig_kind)
 	    {
-	      ctf_err_warn (fp, 1, "added wrong kind to output mapping "
+	      ctf_err_warn (fp, 1, 0, "added wrong kind to output mapping "
 			    "for hash %s named %s: %p/%lx from %s is "
 			    "kind %i, but newly-added %p/%lx from %s is "
 			    "kind %i", hval,
@@ -1410,7 +1408,7 @@ ctf_dedup_detect_name_ambiguity (ctf_file_t *fp, ctf_file_t **inputs)
   void *k;
   void *v;
   int err;
-  const char *erm;
+  const char *whaterr;
 
   /* Go through cd_name_counts for all CTF namespaces in turn.  */
 
@@ -1474,7 +1472,7 @@ ctf_dedup_detect_name_ambiguity (ctf_file_t *fp, ctf_file_t **inputs)
 		}
 	      if (err != ECTF_NEXT_END)
 		{
-		  erm = "marking conflicting structs/unions";
+		  whaterr = N_("error marking conflicting structs/unions");
 		  goto iterr;
 		}
 	    }
@@ -1511,7 +1509,7 @@ ctf_dedup_detect_name_ambiguity (ctf_file_t *fp, ctf_file_t **inputs)
 	    }
 	  if (err != ECTF_NEXT_END)
 	    {
-	      erm = "finding commonest conflicting type";
+	      whaterr = N_("error finding commonest conflicting type");
 	      goto iterr;
 	    }
 
@@ -1526,20 +1524,20 @@ ctf_dedup_detect_name_ambiguity (ctf_file_t *fp, ctf_file_t **inputs)
 			   hval, (const char *) k);
 	      if (ctf_dedup_mark_conflicting_hash (fp, hval) < 0)
 		{
-		  erm = "marking hashes as conflicting";
+		  whaterr = N_("error marking hashes as conflicting");
 		  goto err;
 		}
 	    }
 	  if (err != ECTF_NEXT_END)
 	    {
-	      erm = "marking uncommon conflicting types";
+	      whaterr = N_("marking uncommon conflicting types");
 	      goto iterr;
 	    }
 	}
     }
   if (err != ECTF_NEXT_END)
     {
-      erm = "scanning for ambiguous names";
+      whaterr = N_("scanning for ambiguous names");
       goto iterr;
     }
 
@@ -1547,11 +1545,11 @@ ctf_dedup_detect_name_ambiguity (ctf_file_t *fp, ctf_file_t **inputs)
 
  err:
   ctf_next_destroy (i);
-  ctf_err_warn (fp, 0, "%s: %s", erm, ctf_errmsg (ctf_errno (fp)));
-  return -1;
+  ctf_err_warn (fp, 0, 0, "%s", gettext (whaterr));
+  return -1;					/* errno is set for us.  */
 
  iterr:
-  ctf_err_warn (fp, 0, "iteration failed %s: %s", erm, ctf_errmsg (err));
+  ctf_err_warn (fp, 0, err, _("iteration failed: %s"), gettext (whaterr));
   return ctf_set_errno (fp, err);
 
  assert_err:
@@ -1642,8 +1640,8 @@ ctf_dedup_init (ctf_file_t *fp)
   return 0;
 
  oom:
-  ctf_err_warn (fp, 0, "ctf_dedup_init: cannot initialize: "
-		"out of memory.");
+  ctf_err_warn (fp, 0, ENOMEM, _("ctf_dedup_init: cannot initialize: "
+				 "out of memory"));
   return ctf_set_errno (fp, ENOMEM);
 }
 
@@ -1740,10 +1738,9 @@ ctf_dedup_multiple_input_dicts (ctf_file_t *output, ctf_file_t **inputs,
     }
   if ((err != ECTF_NEXT_END) && (err != 0))
     {
-      ctf_err_warn (output, 0, "propagating conflictedness: %s",
-		    ctf_errmsg (err));
-      ctf_set_errno (output, err);
-      return -1;
+      ctf_err_warn (output, 0, err, _("iteration error "
+				      "propagating conflictedness"));
+      return ctf_set_errno (output, err);
     }
 
   if (multiple)
@@ -1836,11 +1833,9 @@ ctf_dedup_conflictify_unshared (ctf_file_t *output, ctf_file_t **inputs)
   err = ctf_errno (output);
   ctf_next_destroy (i);
  iterr:
-  ctf_set_errno (output, err);
   ctf_dynset_destroy (to_mark);
-  ctf_err_warn (output, 0, "conflictifying unshared types: %s",
-		ctf_errmsg (ctf_errno (output)));
-  return -1;
+  ctf_err_warn (output, 0, err, _("conflictifying unshared types"));
+  return ctf_set_errno (output, err);
 }
 
 /* The core deduplicator.  Populate cd_output_mapping in the output ctf_dedup
@@ -1899,9 +1894,10 @@ ctf_dedup (ctf_file_t *output, ctf_file_t **inputs, uint32_t ninputs,
 	}
       if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
 	{
-	  ctf_err_warn (output, 0, "iteration failure computing type "
-			"hashes: %s", ctf_errmsg (ctf_errno (inputs[i])));
-	  return ctf_set_errno (output, ctf_errno (inputs[i]));
+	  ctf_set_errno (output, ctf_errno (inputs[i]));
+	  ctf_err_warn (output, 0, 0, _("iteration failure "
+					"computing type hashes"));
+	  return -1;
 	}
     }
 
@@ -2013,7 +2009,7 @@ ctf_dedup_rwalk_one_output_mapping (ctf_file_t *output,
     hashval = ctf_dynhash_lookup (d->cd_type_hashes, type_id);		\
     if (!ctf_assert (output, hashval))					\
       {									\
-	whaterr = "looking up ID in type hashes";			\
+	whaterr = N_("error looking up ID in type hashes");		\
 	goto errlabel;							\
       }									\
     ctf_dprintf ("ID %i/%lx has hash %s\n", cited_type_input_num, type,	\
@@ -2048,7 +2044,7 @@ ctf_dedup_rwalk_one_output_mapping (ctf_file_t *output,
     case CTF_K_POINTER:
     case CTF_K_SLICE:
       CTF_TYPE_WALK (ctf_type_reference (fp, type), err,
-		     "Referenced type walk");
+		     N_("error during referenced type walk"));
       break;
 
     case CTF_K_ARRAY:
@@ -2057,12 +2053,14 @@ ctf_dedup_rwalk_one_output_mapping (ctf_file_t *output,
 
 	if (ctf_array_info (fp, type, &ar) < 0)
 	  {
-	    whaterr = "array info lookup";
+	    whaterr = N_("error during array info lookup");
 	    goto err_msg;
 	  }
 
-	CTF_TYPE_WALK (ar.ctr_contents, err, "Array contents type walk");
-	CTF_TYPE_WALK (ar.ctr_index, err, "Array index type walk");
+	CTF_TYPE_WALK (ar.ctr_contents, err,
+		       N_("error during array contents type walk"));
+	CTF_TYPE_WALK (ar.ctr_index, err,
+		       N_("error during array index type walk"));
 	break;
       }
 
@@ -2074,27 +2072,29 @@ ctf_dedup_rwalk_one_output_mapping (ctf_file_t *output,
 
 	if (ctf_func_type_info (fp, type, &fi) < 0)
 	  {
-	    whaterr = "func type info lookup";
+	    whaterr = N_("error during func type info lookup");
 	    goto err_msg;
 	  }
 
-	CTF_TYPE_WALK (fi.ctc_return, err, "Func return type walk");
+	CTF_TYPE_WALK (fi.ctc_return, err,
+		       N_("error during func return type walk"));
 
 	if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
 	  {
-	    whaterr = "memory allocation";
+	    whaterr = N_("error doing memory allocation");
 	    goto err_msg;
 	  }
 
 	if (ctf_func_type_args (fp, type, fi.ctc_argc, args) < 0)
 	  {
-	    whaterr = "func arg type lookup";
+	    whaterr = N_("error doing func arg type lookup");
 	    free (args);
 	    goto err_msg;
 	  }
 
 	for (j = 0; j < fi.ctc_argc; j++)
-	  CTF_TYPE_WALK (args[j], err_free_args, "Func arg type walk");
+	  CTF_TYPE_WALK (args[j], err_free_args,
+			 N_("error during Func arg type walk"));
 	free (args);
 	break;
 
@@ -2108,8 +2108,8 @@ ctf_dedup_rwalk_one_output_mapping (ctf_file_t *output,
 	 emitted later, in a separate pass.  */
 	break;
     default:
-      whaterr = "unknown type kind";
-      goto err;
+      whaterr = N_("CTF dict corruption: unknown type kind");
+      goto err_msg;
     }
 
   return visit_fun (hval, output, inputs, ninputs, parents, visited, fp, type,
@@ -2117,8 +2117,8 @@ ctf_dedup_rwalk_one_output_mapping (ctf_file_t *output,
 
  err_msg:
   ctf_set_errno (output, ctf_errno (fp));
-  ctf_err_warn (fp, 0, "%s during type walking in %s at ID %lx: %s", whaterr,
-	       ctf_link_input_name (fp), type, ctf_errmsg (ctf_errno (fp)));
+  ctf_err_warn (output, 0, 0, _("%s in input file %s at type ID %lx"),
+		gettext (whaterr), ctf_link_input_name (fp), type);
  err:
   return -1;
 }
@@ -2157,8 +2157,8 @@ ctf_dedup_rwalk_output_mapping (ctf_file_t *output, ctf_file_t **inputs,
   type_ids = ctf_dynhash_lookup (d->cd_output_mapping, hval);
   if (!type_ids)
     {
-      ctf_err_warn (output, 0, "looked up type kind by nonexistent "
-		    "hash %s.", hval);
+      ctf_err_warn (output, 0, ECTF_INTERNAL,
+		    _("looked up type kind by nonexistent hash %s"), hval);
       return ctf_set_errno (output, ECTF_INTERNAL);
     }
 
@@ -2175,8 +2175,8 @@ ctf_dedup_rwalk_output_mapping (ctf_file_t *output, ctf_file_t **inputs,
       visited = 0;
       if (ctf_dynset_cinsert (already_visited, hval) < 0)
 	{
-	  ctf_err_warn (output, 0, "out of memory tracking already-visited "
-			"types.");
+	  ctf_err_warn (output, 0, ENOMEM,
+			_("out of memory tracking already-visited types"));
 	  return ctf_set_errno (output, ENOMEM);
 	}
     }
@@ -2213,8 +2213,7 @@ ctf_dedup_rwalk_output_mapping (ctf_file_t *output, ctf_file_t **inputs,
     }
   if (err != ECTF_NEXT_END)
     {
-      ctf_err_warn (output, 0, "walking many types with one hash: %s",
-		    ctf_errmsg (err));
+      ctf_err_warn (output, 0, err, _("cannot walk conflicted type"));
       return ctf_set_errno (output, err);
     }
 
@@ -2336,8 +2335,7 @@ ctf_dedup_walk_output_mapping (ctf_file_t *output, ctf_file_t **inputs,
     }
   if (err != ECTF_NEXT_END)
     {
-      ctf_err_warn (output, 0, "recursing over output mapping: %s",
-		    ctf_errmsg (err));
+      ctf_err_warn (output, 0, err, _("cannot recurse over output mapping"));
       ctf_set_errno (output, err);
       goto err;
     }
@@ -2490,8 +2488,8 @@ ctf_dedup_id_to_target (ctf_file_t *output, ctf_file_t *target,
       break;
     case -1:
       ctf_set_errno (err_fp, ctf_errno (output));
-      ctf_err_warn (err_fp, 0, "adding synthetic forward for type %i/%lx: "
-		    "%s", input_num, id, ctf_errmsg (ctf_errno (err_fp)));
+      ctf_err_warn (err_fp, 0, 0, _("cannot add synthetic forward for type "
+				    "%i/%lx"), input_num, id);
       return -1;
     default:
       return emitted_forward;
@@ -2518,10 +2516,10 @@ ctf_dedup_id_to_target (ctf_file_t *output, ctf_file_t *target,
 	case 0: /* No forward needed.  */
 	  break;
 	case -1:
-	  ctf_set_errno (err_fp, ctf_errno (output));
-	  ctf_err_warn (err_fp, 0, "adding synthetic forward for type %i/%lx: "
-			"%s", input_num, id, ctf_errmsg (ctf_errno (err_fp)));
-	  return -1;
+	  ctf_err_warn (err_fp, 0, ctf_errno (output),
+			_("cannot add synthetic forward for type %i/%lx"),
+			input_num, id);
+	  return ctf_set_errno (err_fp, ctf_errno (output));
 	default:
 	  return emitted_forward;
 	}
@@ -2570,7 +2568,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
   ctf_id_t ref;
   ctf_id_t maybe_dup = 0;
   ctf_encoding_t ep;
-  const char *erm;
+  const char *errtype;
   int emission_hashed = 0;
 
   /* We don't want to re-emit something we've already emitted.  */
@@ -2600,10 +2598,10 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
 
 	  if ((target = ctf_create (&err)) == NULL)
 	    {
-	      ctf_err_warn (output, 0, "cannot create per-CU CTF archive "
-			    "for CU %s: %s", ctf_link_input_name (input),
-			    ctf_errmsg (err)); ctf_set_errno (output, err);
-	      return -1;
+	      ctf_err_warn (output, 0, err,
+			    _("cannot create per-CU CTF archive for CU %s"),
+			    ctf_link_input_name (input));
+	      return ctf_set_errno (output, err);
 	    }
 
 	  ctf_import_unref (target, output);
@@ -2621,11 +2619,10 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
   real_input = input;
   if ((tp = ctf_lookup_by_id (&real_input, type)) == NULL)
     {
-      ctf_err_warn (output, 0, "%s: lookup failure for type %lx: %s",
-		    ctf_link_input_name (real_input), type,
-		    ctf_errmsg (ctf_errno (input)));
-      ctf_set_errno (output, ctf_errno (input));
-      return -1;		/* errno is set for us.  */
+      ctf_err_warn (output, 0, ctf_errno (input),
+		    _("%s: lookup failure for type %lx"),
+		    ctf_link_input_name (real_input), type);
+      return ctf_set_errno (output, ctf_errno (input));
     }
 
   name = ctf_strraw (real_input, tp->ctt_name);
@@ -2667,7 +2664,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
       /* This will do nothing if the type to which this forwards already exists,
 	 and will be replaced with such a type if it appears later.  */
 
-      erm = "forward";
+      errtype = _("forward");
       if ((new_type = ctf_add_forward (target, isroot, name,
 				       ctf_type_kind_forwarded (input, type)))
 	  == CTF_ERR)
@@ -2676,7 +2673,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
 
     case CTF_K_FLOAT:
     case CTF_K_INTEGER:
-      erm = "float/int";
+      errtype = _("float/int");
       if (ctf_type_encoding (input, type, &ep) < 0)
 	goto err_input;				/* errno is set for us.  */
       if ((new_type = ctf_add_encoded (target, isroot, name, &ep, kind))
@@ -2687,7 +2684,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
     case CTF_K_ENUM:
       {
 	int val;
-	erm = "enum";
+	errtype = _("enum");
 	if ((new_type = ctf_add_enum (target, isroot, name)) == CTF_ERR)
 	  goto err_input;				/* errno is set for us.  */
 
@@ -2695,10 +2692,11 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
 	  {
 	    if (ctf_add_enumerator (target, new_type, name, val) < 0)
 	      {
-		ctf_err_warn (target, 0, "%s (%i): cannot add enumeration "
-			      "value %s from input type %lx: %s",
+		ctf_err_warn (target, 0, ctf_errno (target),
+			      _("%s (%i): cannot add enumeration value %s "
+				"from input type %lx"),
 			      ctf_link_input_name (input), input_num, name,
-			      type, ctf_errmsg (ctf_errno (target)));
+			      type);
 		ctf_next_destroy (i);
 		return ctf_set_errno (output, ctf_errno (target));
 	      }
@@ -2709,7 +2707,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
       }
 
     case CTF_K_TYPEDEF:
-      erm = "typedef";
+      errtype = _("typedef");
 
       ref = ctf_type_reference (input, type);
       if ((ref = ctf_dedup_id_to_target (output, target, inputs, ninputs,
@@ -2725,7 +2723,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
     case CTF_K_CONST:
     case CTF_K_RESTRICT:
     case CTF_K_POINTER:
-      erm = "pointer or cvr-qual";
+      errtype = _("pointer or cvr-qual");
 
       ref = ctf_type_reference (input, type);
       if ((ref = ctf_dedup_id_to_target (output, target, inputs, ninputs,
@@ -2738,7 +2736,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
       break;
 
     case CTF_K_SLICE:
-      erm = "slice";
+      errtype = _("slice");
 
       if (ctf_type_encoding (input, type, &ep) < 0)
 	goto err_input;				/* errno is set for us.  */
@@ -2757,7 +2755,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
       {
 	ctf_arinfo_t ar;
 
-	erm = "array info";
+	errtype = _("array info");
 	if (ctf_array_info (input, type, &ar) < 0)
 	  goto err_input;
 
@@ -2783,7 +2781,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
 	ctf_id_t *args;
 	uint32_t j;
 
-	erm = "function";
+	errtype = _("function");
 	if (ctf_func_type_info (input, type, &fi) < 0)
 	  goto err_input;
 
@@ -2799,7 +2797,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
 	    goto err_input;
 	  }
 
-	erm = "function args";
+	errtype = _("function args");
 	if (ctf_func_type_args (input, type, fi.ctc_argc, args) < 0)
 	  {
 	    free (args);
@@ -2832,7 +2830,7 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
 	void *out_id;
 	/* Insert the structure itself, so other types can refer to it.  */
 
-	erm = "structure/union";
+	errtype = _("structure/union");
 	if (kind == CTF_K_STRUCT)
 	  new_type = ctf_add_struct_sized (target, isroot, name, size);
 	else
@@ -2850,9 +2848,10 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
 	break;
       }
     default:
-      ctf_err_warn (output, 0, "%s: unknown type kind for input type %lx",
+      ctf_err_warn (output, 0, ECTF_CORRUPT, _("%s: unknown type kind for "
+					       "input type %lx"),
 		    ctf_link_input_name (input), type);
-      return -1;
+      return ctf_set_errno (output, ECTF_CORRUPT);
     }
 
   if (!emission_hashed
@@ -2860,8 +2859,8 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
       && ctf_dynhash_cinsert (target->ctf_dedup.cd_output_emission_hashes,
 			      hval, (void *) (uintptr_t) new_type) < 0)
     {
-      ctf_err_warn (output, 0, "out of memory tracking deduplicated "
-		    "global type IDs");
+      ctf_err_warn (output, 0, ENOMEM, _("out of memory tracking deduplicated "
+					 "global type IDs"));
 	return ctf_set_errno (output, ENOMEM);
     }
 
@@ -2873,22 +2872,23 @@ ctf_dedup_emit_type (const char *hval, ctf_file_t *output, ctf_file_t **inputs,
   return 0;
 
  oom_hash:
-  ctf_err_warn (output, 0, "out of memory creating emission-tracking hashes");
+  ctf_err_warn (output, 0, ENOMEM, _("out of memory creating emission-tracking "
+				     "hashes"));
   return ctf_set_errno (output, ENOMEM);
 
  err_input:
-  ctf_err_warn (output, 0, "%s (%i): while emitting deduplicated %s, error "
-		"getting input type %lx: %s", ctf_link_input_name (input),
-		input_num,erm, type, ctf_errmsg (ctf_errno (input)));
-  ctf_set_errno (output, ctf_errno (input));
-  return -1;
+  ctf_err_warn (output, 0, ctf_errno (input),
+		_("%s (%i): while emitting deduplicated %s, error getting "
+		  "input type %lx"), ctf_link_input_name (input),
+		input_num, errtype, type);
+  return ctf_set_errno (output, ctf_errno (input));
  err_target:
-  ctf_err_warn (output, 0, "%s (%i): while emitting deduplicated %s, error "
-		"emitting target type from input type %lx: %s",
-		ctf_link_input_name (input), input_num, erm, type,
-		ctf_errmsg (ctf_errno (target)));
-  ctf_set_errno (output, ctf_errno (target));
-  return -1;
+  ctf_err_warn (output, 0, ctf_errno (target),
+		_("%s (%i): while emitting deduplicated %s, error emitting "
+		  "target type from input type %lx"),
+		ctf_link_input_name (input), input_num,
+		errtype, type);
+  return ctf_set_errno (output, ctf_errno (target));
 }
 
 /* Traverse the cd_emission_struct_members and emit the members of all
@@ -2978,16 +2978,14 @@ ctf_dedup_emit_struct_members (ctf_file_t *output, ctf_file_t **inputs,
   return 0;
  err_target:
   ctf_next_destroy (i);
-  ctf_err_warn (output, 0, "%s (%i): error emitting members for structure "
-		"type %lx: %s", ctf_link_input_name (input_fp), input_num,
-		err_type, ctf_errmsg (ctf_errno (err_fp)));
-  ctf_set_errno (output, ctf_errno (err_fp));
-  return -1;
+  ctf_err_warn (output, 0, ctf_errno (err_fp),
+		_("%s (%i): error emitting members for structure type %lx"),
+		ctf_link_input_name (input_fp), input_num, err_type);
+  return ctf_set_errno (output, ctf_errno (err_fp));
  iterr:
-  ctf_err_warn (output, 0, "iteration failure emitting structure members: %s",
-		ctf_errmsg (err));
-  ctf_set_errno (output, err);
-  return -1;
+  ctf_err_warn (output, 0, err, _("iteration failure emitting "
+				  "structure members"));
+  return ctf_set_errno (output, err);
 }
 
 /* Populate the type mapping used by the types in one FP (which must be an input
@@ -3052,8 +3050,7 @@ ctf_dedup_populate_type_mapping (ctf_file_t *shared, ctf_file_t *fp,
   return 0;
 
  err:
-  ctf_err_warn (shared, 0, "iteration error populating the type mapping: %s",
-		ctf_errmsg (err));
+  ctf_err_warn (shared, 0, err, _("iteration error populating the type mapping"));
   return ctf_set_errno (shared, err);
 }
 
@@ -3067,8 +3064,8 @@ ctf_dedup_populate_type_mappings (ctf_file_t *output, ctf_file_t **inputs,
 
   if (ctf_dedup_populate_type_mapping (output, output, inputs) < 0)
     {
-      ctf_err_warn (output, 0, "cannot populate type mappings for shared "
-		    "CTF dict: %s", ctf_errmsg (ctf_errno (output)));
+      ctf_err_warn (output, 0, 0, _("cannot populate type mappings for shared "
+				    "CTF dict"));
       return -1;				/* errno is set for us.  */
     }
 
@@ -3076,8 +3073,8 @@ ctf_dedup_populate_type_mappings (ctf_file_t *output, ctf_file_t **inputs,
     {
       if (ctf_dedup_populate_type_mapping (output, inputs[i], inputs) < 0)
 	{
-	  ctf_err_warn (output, 0, "cannot populate type mappings for per-CU "
-			"CTF dict: %s", ctf_errmsg (ctf_errno (inputs[i])));
+	  ctf_err_warn (output, 0, ctf_errno (inputs[i]),
+			_("cannot populate type mappings for per-CU CTF dict"));
 	  return ctf_set_errno (output, ctf_errno (inputs[i]));
 	}
     }
@@ -3129,7 +3126,8 @@ ctf_dedup_emit (ctf_file_t *output, ctf_file_t **inputs, uint32_t ninputs,
 
   if ((outputs = calloc (num_outputs, sizeof (ctf_file_t *))) == NULL)
     {
-      ctf_err_warn (output, 0, "out of memory allocating link outputs array");
+      ctf_err_warn (output, 0, ENOMEM,
+		    _("out of memory allocating link outputs array"));
       ctf_set_errno (output, ENOMEM);
       return NULL;
     }
diff --git a/libctf/ctf-dump.c b/libctf/ctf-dump.c
index 94d6bc6f018..ba358a21b07 100644
--- a/libctf/ctf-dump.c
+++ b/libctf/ctf-dump.c
@@ -174,8 +174,7 @@ ctf_dump_format_type (ctf_file_t *fp, ctf_id_t id, int flag)
  oom:
   ctf_set_errno (fp, errno);
  err:
-  ctf_err_warn (fp, 1, "Cannot format name dumping type 0x%lx: %s", id,
-		ctf_errmsg (ctf_errno (fp)));
+  ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
   free (buf);
   free (str);
   free (bit);
@@ -405,7 +404,6 @@ ctf_dump_funcs (ctf_file_t *fp, ctf_dump_state_t *state)
     {
       char *str;
       char *bit = NULL;
-      const char *err;
       const char *sym_name;
       ctf_funcinfo_t fi;
       ctf_id_t type;
@@ -427,8 +425,11 @@ ctf_dump_funcs (ctf_file_t *fp, ctf_dump_state_t *state)
       /* Return type and all args.  */
       if ((bit = ctf_type_aname (state->cds_fp, type)) == NULL)
 	{
-	  err = "look up return type";
-	  goto err;
+	  ctf_err_warn (fp, 1, ctf_errno (state->cds_fp),
+			_("cannot look up return type dumping function type "
+			  "for symbol 0x%li"), (unsigned long) i);
+	  free (bit);
+	  return -1;			/* errno is set for us.  */
 	}
 
       /* Replace in the returned string, dropping in the function name.  */
@@ -469,13 +470,6 @@ ctf_dump_funcs (ctf_file_t *fp, ctf_dump_state_t *state)
       ctf_dump_append (state, str);
       continue;
 
-    err:
-      ctf_err_warn (fp, 1, "Cannot %s dumping function type for "
-		    "symbol 0x%li: %s", err, (unsigned long) i,
-		    ctf_errmsg (ctf_errno (state->cds_fp)));
-      free (bit);
-      return -1;		/* errno is set for us.  */
-
     oom:
       free (bit);
       return (ctf_set_errno (fp, errno));
@@ -597,13 +591,12 @@ static int
 ctf_dump_type (ctf_id_t id, int flag, void *arg)
 {
   char *str;
-  const char *err;
   ctf_dump_state_t *state = arg;
   ctf_dump_membstate_t membstate = { &str, state->cds_fp };
   size_t len;
 
   if ((str = ctf_dump_format_type (state->cds_fp, id, flag)) == NULL)
-    goto err_nomsg;		/* Error already logged for us.  */
+    goto err;
 
   str = str_append (str, "\n");
   if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
@@ -613,7 +606,8 @@ ctf_dump_type (ctf_id_t id, int flag, void *arg)
 	  ctf_dump_append (state, str);
 	  return 0;
 	}
-      err = "visit members";
+      ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
+		    _("cannot visit members dumping type 0x%lx"), id);
       goto err;
     }
 
@@ -626,9 +620,6 @@ ctf_dump_type (ctf_id_t id, int flag, void *arg)
   return 0;
 
  err:
-  ctf_err_warn (state->cds_fp, 1, "Cannot %s dumping type 0x%lx: %s",
-		err, id, ctf_errmsg (ctf_errno (state->cds_fp)));
- err_nomsg:
   free (str);
   return 0;				/* Swallow the error.  */
 }
diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index faee039adc8..ecd0ef21188 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -130,7 +130,7 @@ typedef struct ctf_fileops
   uint32_t (*ctfo_get_vlen) (uint32_t);
   ssize_t (*ctfo_get_ctt_size) (const ctf_file_t *, const ctf_type_t *,
 				ssize_t *, ssize_t *);
-  ssize_t (*ctfo_get_vbytes) (unsigned short, ssize_t, size_t);
+  ssize_t (*ctfo_get_vbytes) (ctf_file_t *, unsigned short, ssize_t, size_t);
 } ctf_fileops_t;
 
 typedef struct ctf_list
@@ -199,13 +199,6 @@ typedef struct ctf_dvdef
   unsigned long dvd_snapshots;	/* Snapshot count when inserted.  */
 } ctf_dvdef_t;
 
-typedef struct ctf_bundle
-{
-  ctf_file_t *ctb_file;		/* CTF container handle.  */
-  ctf_id_t ctb_type;		/* CTF type identifier.  */
-  ctf_dtdef_t *ctb_dtd;		/* CTF dynamic type definition (if any).  */
-} ctf_bundle_t;
-
 typedef struct ctf_err_warning
 {
   ctf_list_t cew_list;		/* List forward/back pointers.  */
@@ -546,7 +539,7 @@ struct ctf_next
 #define LCTF_INFO_ISROOT(fp, info)	((fp)->ctf_fileops->ctfo_get_root(info))
 #define LCTF_INFO_VLEN(fp, info)	((fp)->ctf_fileops->ctfo_get_vlen(info))
 #define LCTF_VBYTES(fp, kind, size, vlen) \
-  ((fp)->ctf_fileops->ctfo_get_vbytes(kind, size, vlen))
+  ((fp)->ctf_fileops->ctfo_get_vbytes(fp, kind, size, vlen))
 
 #define LCTF_CHILD	0x0001	/* CTF container is a child */
 #define LCTF_RDWR	0x0002	/* CTF container is writable */
@@ -718,8 +711,10 @@ _libctf_printflike_ (1, 2)
 extern void ctf_dprintf (const char *, ...);
 extern void libctf_init_debug (void);
 
-_libctf_printflike_ (3, 4)
-extern void ctf_err_warn (ctf_file_t *, int is_warning, const char *, ...);
+_libctf_printflike_ (4, 5)
+extern void ctf_err_warn (ctf_file_t *, int is_warning, int err,
+			  const char *, ...);
+extern void ctf_err_warn_to_open (ctf_file_t *);
 extern void ctf_assert_fail_internal (ctf_file_t *, const char *,
 				      size_t, const char *);
 extern const char *ctf_link_input_name (ctf_file_t *);
diff --git a/libctf/ctf-labels.c b/libctf/ctf-labels.c
index 53fbdb2dfe0..014a9933e64 100644
--- a/libctf/ctf-labels.c
+++ b/libctf/ctf-labels.c
@@ -80,8 +80,10 @@ ctf_label_iter (ctf_file_t *fp, ctf_label_f *func, void *arg)
     {
       if ((lname = ctf_strraw (fp, ctlp->ctl_label)) == NULL)
 	{
-	  ctf_dprintf ("failed to decode label %u with "
-		       "type %u\n", ctlp->ctl_label, ctlp->ctl_type);
+	  /* Not marked for translation: label code not used yet.  */
+	  ctf_err_warn (fp, 0, ECTF_CORRUPT,
+			"failed to decode label %u with type %u",
+			ctlp->ctl_label, ctlp->ctl_type);
 	  return (ctf_set_errno (fp, ECTF_CORRUPT));
 	}
 
diff --git a/libctf/ctf-link.c b/libctf/ctf-link.c
index f269fe7ac0e..1cda324e65e 100644
--- a/libctf/ctf-link.c
+++ b/libctf/ctf-link.c
@@ -314,9 +314,9 @@ ctf_create_per_cu (ctf_file_t *fp, const char *filename, const char *cuname)
 
       if ((cu_fp = ctf_create (&err)) == NULL)
 	{
-	  ctf_err_warn (fp, 0, "Cannot create per-CU CTF archive for "
-			"CU %s from input file %s: %s", cuname, filename,
-			ctf_errmsg (err));
+	  ctf_err_warn (fp, 0, err, _("cannot create per-CU CTF archive for "
+				      "CU %s from input file %s"),
+			cuname, filename);
 	  ctf_set_errno (fp, err);
 	  return NULL;
 	}
@@ -477,7 +477,8 @@ ctf_link_one_type (ctf_id_t type, int isroot _libctf_unused_, void *arg_)
 
   if (arg->in_fp->ctf_link_flags != CTF_LINK_SHARE_UNCONFLICTED)
     {
-      ctf_err_warn (arg->out_fp, 0, "Share-duplicated mode not yet implemented");
+      ctf_err_warn (arg->out_fp, 0, ECTF_NOTYET,
+		    _("share-duplicated mode not yet implemented"));
       return ctf_set_errno (arg->out_fp, ECTF_NOTYET);
     }
 
@@ -495,9 +496,10 @@ ctf_link_one_type (ctf_id_t type, int isroot _libctf_unused_, void *arg_)
       if (err != ECTF_CONFLICT)
 	{
 	  if (err != ECTF_NONREPRESENTABLE)
-	    ctf_err_warn (arg->out_fp, 1, "Cannot link type %lx from input file %s, "
-			  "CU %s into output link: %s", type, arg->cu_name,
-			 arg->in_file_name, ctf_errmsg (err));
+	    ctf_err_warn (arg->out_fp, 1, 0,
+			  _("cannot link type %lx from input file %s, CU %s "
+			    "into output link"), type, arg->cu_name,
+			  arg->in_file_name);
 	  /* We must ignore this problem or we end up losing future types, then
 	     trying to link the variables in, then exploding.  Better to link as
 	     much as possible.  */
@@ -515,10 +517,11 @@ ctf_link_one_type (ctf_id_t type, int isroot _libctf_unused_, void *arg_)
 
   err = ctf_errno (per_cu_out_fp);
   if (err != ECTF_NONREPRESENTABLE)
-    ctf_err_warn (arg->out_fp, 1, "Cannot link type %lx from input file %s, CU %s "
-		 "into output per-CU CTF archive member %s: %s: skipped", type,
-		 ctf_link_input_name (arg->in_fp), arg->in_file_name,
-		 ctf_link_input_name (per_cu_out_fp), ctf_errmsg (err));
+    ctf_err_warn (arg->out_fp, 1, 0,
+		  _("cannot link type %lx from input file %s, CU %s "
+		    "into output per-CU CTF archive member %s: %s: skipped"),
+		  type, ctf_link_input_name (arg->in_fp), arg->in_file_name,
+		  ctf_link_input_name (per_cu_out_fp), ctf_errmsg (err));
   if (err == ECTF_CONFLICT)
       /* Conflicts are possible at this stage only if a non-ld user has combined
 	 multiple TUs into a single output dictionary.  Even in this case we do not
@@ -633,8 +636,9 @@ ctf_link_one_variable (const char *name, ctf_id_t type, void *arg_)
 
       if (dst_type == 0)
 	{
-	  ctf_err_warn (arg->out_fp, 1, "Type %lx for variable %s in input "
-			"file %s not found: skipped", type, name,
+	  ctf_err_warn (arg->out_fp, 1, 0,
+			_("type %lx for variable %s in input file %s "
+			  "not found: skipped"), type, name,
 			arg->in_file_name);
 	  /* Do not terminate the link: just skip the variable.  */
 	  return 0;
@@ -727,8 +731,8 @@ ctf_link_lazy_open (ctf_file_t *fp, ctf_link_input_t *input)
 #if defined (PIC) || !NOBFD
   input->clin_arc = ctf_open (input->clin_filename, NULL, &err);
 #else
-  ctf_err_warn (fp, 0, "Cannot open %s lazily: %s", input->clin_filename,
-		ctf_errmsg (ECTF_NEEDSBFD));
+  ctf_err_warn (fp, 0, ECTF_NEEDSBFD, _("cannot open %s lazily"),
+		input->clin_filename);
   ctf_set_errno (fp, ECTF_NEEDSBFD);
   return -1;
 #endif
@@ -741,8 +745,8 @@ ctf_link_lazy_open (ctf_file_t *fp, ctf_link_input_t *input)
       if (err == ECTF_NOCTFDATA)
 	return 0;
 
-      ctf_err_warn (fp, 0, "Opening CTF %s failed: %s",
-		    input->clin_filename, ctf_errmsg (err));
+      ctf_err_warn (fp, 0, err, _("opening CTF %s failed"),
+		    input->clin_filename);
       ctf_set_errno (fp, err);
       return -1;
     }
@@ -789,9 +793,10 @@ ctf_link_one_input_archive (void *key, void *value, void *arg_)
 						 &err)) == NULL)
     if (err != ECTF_ARNNAME)
       {
-	ctf_err_warn (arg->out_fp, 0, "Cannot open main archive member in "
-		      "input file %s in the link: skipping: %s",
-		      arg->in_file_name, ctf_errmsg (err));
+	ctf_err_warn (arg->out_fp, 1, 0,
+		      _("cannot open main archive member in input file %s "
+			"in the link: skipping: %s"), arg->in_file_name,
+		      ctf_errmsg (err));
 	goto out;
       }
 
@@ -804,9 +809,9 @@ ctf_link_one_input_archive (void *key, void *value, void *arg_)
   arg->done_parent = 1;
   if (ctf_archive_iter (input->clin_arc, ctf_link_one_input_archive_member,
 			arg) < 0)
-    ctf_err_warn (arg->out_fp, 0, "Cannot traverse archive in input file %s: "
-		  "link cannot continue: %s", arg->in_file_name,
-		  ctf_errmsg (ctf_errno (arg->out_fp)));
+    ctf_err_warn (arg->out_fp, 0, 0, _("cannot traverse archive in input file "
+				       "%s: link cannot continue"),
+		  arg->in_file_name);
   else
     {
       /* The only error indication to the caller is the errno: so ensure that it
@@ -911,8 +916,8 @@ ctf_link_deduplicating_count_inputs (ctf_file_t *fp, ctf_dynhash_t *cu_names,
     }
   if (err != ECTF_NEXT_END)
     {
-      ctf_err_warn (fp, 0, "Iteration error counting deduplicating CTF link "
-		    "inputs: %s", ctf_errmsg (err));
+      ctf_err_warn (fp, 0, err, _("iteration error counting deduplicating "
+				  "CTF link inputs"));
       ctf_set_errno (fp, err);
       return -1;
     }
@@ -1073,8 +1078,8 @@ ctf_link_deduplicating_open_inputs (ctf_file_t *fp, ctf_dynhash_t *cu_names,
  err:
   free (dedup_inputs);
   free (parents_);
-  ctf_err_warn (fp, 0, "Error in deduplicating CTF link input allocation: %s",
-		ctf_errmsg (ctf_errno (fp)));
+  ctf_err_warn (fp, 0, 0, _("error in deduplicating CTF link "
+			    "input allocation"));
   return NULL;
 }
 
@@ -1108,8 +1113,8 @@ ctf_link_deduplicating_close_inputs (ctf_file_t *fp, ctf_dynhash_t *cu_names,
 	}
       if (err != ECTF_NEXT_END)
 	{
-	  ctf_err_warn (fp, 0, "Iteration error in deduplicating link input "
-			"freeing: %s", ctf_errmsg (err));
+	  ctf_err_warn (fp, 0, err, _("iteration error in deduplicating link "
+				      "input freeing"));
 	  ctf_set_errno (fp, err);
 	}
     }
@@ -1186,8 +1191,9 @@ ctf_link_deduplicating_per_cu (ctf_file_t *fp)
 
       if (labs ((long int) ninputs) > 0xfffffffe)
 	{
-	  ctf_err_warn (fp, 0, "Too many inputs in deduplicating link: %li",
-			(long int) ninputs);
+	  ctf_err_warn (fp, 0, EFBIG, _("too many inputs in deduplicating "
+					"link: %li"), (long int) ninputs);
+	  ctf_set_errno (fp, EFBIG);
 	  goto err_open_inputs;
 	}
 
@@ -1209,9 +1215,9 @@ ctf_link_deduplicating_per_cu (ctf_file_t *fp)
 						  &ai, NULL, 0, &err);
 	  if (!only_input->clin_fp)
 	    {
-	      ctf_err_warn (fp, 0, "Cannot open archive %s in CU-mapped CTF "
-			    "link: %s", only_input->clin_filename,
-			    ctf_errmsg (err));
+	      ctf_err_warn (fp, 0, err, _("cannot open archive %s in "
+					  "CU-mapped CTF link"),
+			    only_input->clin_filename);
 	      ctf_set_errno (fp, err);
 	      goto err_open_inputs;
 	    }
@@ -1231,8 +1237,8 @@ ctf_link_deduplicating_per_cu (ctf_file_t *fp)
 					     only_input->clin_fp,
 					     out_name) < 0)
 		{
-		  ctf_err_warn (fp, 0, "Cannot add intermediate files "
-				"to link: %s", ctf_errmsg (ctf_errno (fp)));
+		  ctf_err_warn (fp, 0, 0, _("cannot add intermediate files "
+					    "to link"));
 		  goto err_open_inputs;
 		}
 	      only_input->clin_arc = NULL;
@@ -1255,8 +1261,9 @@ ctf_link_deduplicating_per_cu (ctf_file_t *fp)
 
       if ((out = ctf_create (&err)) == NULL)
 	{
-	  ctf_err_warn (fp, 0, "Cannot create per-CU CTF archive for %s: %s",
-		       out_name, ctf_errmsg (err));
+	  ctf_err_warn (fp, 0, err, _("cannot create per-CU CTF archive "
+				      "for %s"),
+			out_name);
 	  ctf_set_errno (fp, err);
 	  goto err_inputs;
 	}
@@ -1272,17 +1279,18 @@ ctf_link_deduplicating_per_cu (ctf_file_t *fp)
 
       if (ctf_dedup (out, inputs, ninputs, parents, 1) < 0)
 	{
-	  ctf_err_warn (fp, 0, "CU-mapped deduplication failed for %s: %s",
-		       out_name, ctf_errmsg (ctf_errno (out)));
+	  ctf_set_errno (fp, ctf_errno (out));
+	  ctf_err_warn (fp, 0, 0, _("CU-mapped deduplication failed for %s"),
+			out_name);
 	  goto err_inputs;
 	}
 
       if ((outputs = ctf_dedup_emit (out, inputs, ninputs, parents,
 				     &noutputs, 1)) == NULL)
 	{
-	  ctf_err_warn (fp, 0, "CU-mapped deduplicating link type emission "
-			"failed for %s: %s", out_name,
-			ctf_errmsg (ctf_errno (out)));
+	  ctf_set_errno (fp, ctf_errno (out));
+	  ctf_err_warn (fp, 0, 0, _("CU-mapped deduplicating link type emission "
+				     "failed for %s"), out_name);
 	  goto err_inputs;
 	}
       if (!ctf_assert (fp, noutputs == 1))
@@ -1291,9 +1299,9 @@ ctf_link_deduplicating_per_cu (ctf_file_t *fp)
       if (!(fp->ctf_link_flags & CTF_LINK_OMIT_VARIABLES_SECTION)
 	  && ctf_link_deduplicating_variables (out, inputs, ninputs, 1) < 0)
 	{
-	  ctf_err_warn (fp, 0, "CU-mapped deduplicating link variable "
-			"emission failed for %s: %s", out_name,
-			ctf_errmsg (ctf_errno (out)));
+	  ctf_set_errno (fp, ctf_errno (out));
+	  ctf_err_warn (fp, 0, 0, _("CU-mapped deduplicating link variable "
+				    "emission failed for %s"), out_name);
 	  goto err_inputs_outputs;
 	}
 
@@ -1324,8 +1332,7 @@ ctf_link_deduplicating_per_cu (ctf_file_t *fp)
       if (ctf_link_add_ctf_internal (fp, in_arc, NULL,
 				     ctf_cuname (outputs[0])) < 0)
 	{
-	  ctf_err_warn (fp, 0, "Cannot add intermediate files to link: %s",
-			ctf_errmsg (ctf_errno (fp)));
+	  ctf_err_warn (fp, 0, 0, _("cannot add intermediate files to link"));
 	  goto err_outputs;
 	}
 
@@ -1355,8 +1362,8 @@ ctf_link_deduplicating_per_cu (ctf_file_t *fp)
     }
   if (err != ECTF_NEXT_END)
     {
-      ctf_err_warn (fp, 0, "Iteration error in CU-mapped deduplicating "
-		    "link: %s", ctf_errmsg (err));
+      ctf_err_warn (fp, 0, err, _("iteration error in CU-mapped deduplicating "
+				  "link"));
       return ctf_set_errno (fp, err);
     }
 
@@ -1375,8 +1382,7 @@ ctf_link_deduplicating (ctf_file_t *fp)
 
   if (ctf_dedup_atoms_init (fp) < 0)
     {
-      ctf_err_warn (fp, 0, "%s allocating CTF dedup atoms table",
-		    ctf_errmsg (ctf_errno (fp)));
+      ctf_err_warn (fp, 0, 0, _("allocating CTF dedup atoms table"));
       return;					/* Errno is set for us.  */
     }
 
@@ -1396,17 +1402,16 @@ ctf_link_deduplicating (ctf_file_t *fp)
 
   if (ctf_dedup (fp, inputs, ninputs, parents, 0) < 0)
     {
-      ctf_err_warn (fp, 0, "Deduplication failed for %s: %s",
-		    ctf_link_input_name (fp), ctf_errmsg (ctf_errno (fp)));
+      ctf_err_warn (fp, 0, 0, _("deduplication failed for %s"),
+		    ctf_link_input_name (fp));
       goto err;
     }
 
   if ((outputs = ctf_dedup_emit (fp, inputs, ninputs, parents, &noutputs,
 				 0)) == NULL)
     {
-      ctf_err_warn (fp, 0, "Deduplicating link type emission failed "
-		    "for %s: %s", ctf_link_input_name (fp),
-		    ctf_errmsg (ctf_errno (fp)));
+      ctf_err_warn (fp, 0, 0, _("deduplicating link type emission failed "
+				"for %s"), ctf_link_input_name (fp));
       goto err;
     }
 
@@ -1433,8 +1438,8 @@ ctf_link_deduplicating (ctf_file_t *fp)
       continue;
 
     oom_one_output:
-      ctf_err_warn (fp, 0, "Out of memory allocating link outputs");
       ctf_set_errno (fp, ENOMEM);
+      ctf_err_warn (fp, 0, 0, _("out of memory allocating link outputs"));
       free (dynname);
 
       for (; i < noutputs; i++)
@@ -1445,9 +1450,8 @@ ctf_link_deduplicating (ctf_file_t *fp)
   if (!(fp->ctf_link_flags & CTF_LINK_OMIT_VARIABLES_SECTION)
       && ctf_link_deduplicating_variables (fp, inputs, ninputs, 0) < 0)
     {
-      ctf_err_warn (fp, 0, "Deduplicating link variable emission failed for "
-		    "%s: %s", ctf_link_input_name (fp),
-		    ctf_errmsg (ctf_errno (fp)));
+      ctf_err_warn (fp, 0, 0, _("deduplicating link variable emission failed for "
+				"%s"), ctf_link_input_name (fp));
       for (i = 1; i < noutputs; i++)
 	ctf_file_close (outputs[i]);
       goto err;
@@ -1517,8 +1521,7 @@ ctf_link (ctf_file_t *fp, int flags)
 	}
       if (err != ECTF_NEXT_END)
 	{
-	  ctf_err_warn (fp, 1, "Iteration error creating empty CUs: %s",
-			ctf_errmsg (err));
+	  ctf_err_warn (fp, 1, err, _("iteration error creating empty CUs"));
 	  ctf_set_errno (fp, err);
 	  return -1;
 	}
@@ -1823,7 +1826,7 @@ ctf_link_write (ctf_file_t *fp, size_t *size, size_t threshold)
 	free (arg.dynames[i]);
       free (arg.dynames);
     }
-  ctf_err_warn (fp, 0, "Cannot write archive in link: %s failure: %s", errloc,
-		ctf_errmsg (ctf_errno (fp)));
+  ctf_err_warn (fp, 0, 0, _("cannot write archive in link: %s failure"),
+		errloc);
   return NULL;
 }
diff --git a/libctf/ctf-open-bfd.c b/libctf/ctf-open-bfd.c
index 9fcce2fa9cd..5541c9dd4dc 100644
--- a/libctf/ctf-open-bfd.c
+++ b/libctf/ctf-open-bfd.c
@@ -40,7 +40,8 @@ ctf_bfdclose (struct ctf_archive_internal *arci)
 {
   if (arci->ctfi_abfd != NULL)
     if (!bfd_close_all_done (arci->ctfi_abfd))
-      ctf_dprintf ("Cannot close BFD: %s\n", bfd_errmsg (bfd_get_error()));
+      ctf_err_warn (NULL, 0, 0, _("cannot close BFD: %s"),
+		    bfd_errmsg (bfd_get_error ()));
 }
 
 /* Open a CTF file given the specified BFD.  */
@@ -62,8 +63,9 @@ ctf_bfdopen (struct bfd *abfd, int *errp)
 
   if (!bfd_malloc_and_get_section (abfd, ctf_asect, &contents))
     {
-      ctf_dprintf ("ctf_bfdopen(): cannot malloc CTF section: %s\n",
-		   bfd_errmsg (bfd_get_error()));
+      ctf_err_warn (NULL, 0, 0, _("ctf_bfdopen(): cannot malloc "
+				  "CTF section: %s"),
+		    bfd_errmsg (bfd_get_error ()));
       return (ctf_set_open_errno (errp, ECTF_FMT));
     }
 
@@ -112,7 +114,7 @@ ctf_bfdopen_ctfsect (struct bfd *abfd _libctf_unused_,
       symcount = symhdr->sh_size / symhdr->sh_entsize;
       if ((symtab = malloc (symhdr->sh_size)) == NULL)
 	{
-	  bfderrstr = "Cannot malloc symbol table";
+	  bfderrstr = N_("cannot malloc symbol table");
 	  goto err;
 	}
 
@@ -121,7 +123,7 @@ ctf_bfdopen_ctfsect (struct bfd *abfd _libctf_unused_,
       free (isymbuf);
       if (isymbuf == NULL)
 	{
-	  bfderrstr = "Cannot read symbol table";
+	  bfderrstr = N_("cannot read symbol table");
 	  goto err_free_sym;
 	}
 
@@ -135,7 +137,7 @@ ctf_bfdopen_ctfsect (struct bfd *abfd _libctf_unused_,
 	    {
 	      if ((strtab = bfd_elf_get_str_section (abfd, symhdr->sh_link)) == NULL)
 		{
-		  bfderrstr = "Cannot read string table";
+		  bfderrstr = N_("cannot read string table");
 		  goto err_free_sym;
 		}
 	    }
@@ -199,7 +201,7 @@ ctf_bfdopen_ctfsect (struct bfd *abfd _libctf_unused_,
 err: _libctf_unused_;
   if (bfderrstr)
     {
-      ctf_dprintf ("ctf_bfdopen(): %s: %s\n", bfderrstr,
+      ctf_err_warn (NULL, 0, 0, "ctf_bfdopen(): %s: %s", gettext (bfderrstr),
 		   bfd_errmsg (bfd_get_error()));
       ctf_set_open_errno (errp, ECTF_FMT);
     }
@@ -283,18 +285,18 @@ ctf_fdopen (int fd, const char *filename, const char *target, int *errp)
 
   if ((abfd = bfd_fdopenr (filename, target, nfd)) == NULL)
     {
-      ctf_dprintf ("Cannot open BFD from %s: %s\n",
-		   filename ? filename : "(unknown file)",
-		   bfd_errmsg (bfd_get_error()));
+      ctf_err_warn (NULL, 0, 0, _("cannot open BFD from %s: %s"),
+		    filename ? filename : _("(unknown file)"),
+		    bfd_errmsg (bfd_get_error ()));
       return (ctf_set_open_errno (errp, ECTF_FMT));
     }
   bfd_set_cacheable (abfd, 1);
 
   if (!bfd_check_format (abfd, bfd_object))
     {
-      ctf_dprintf ("BFD format problem in %s: %s\n",
-		   filename ? filename : "(unknown file)",
-		   bfd_errmsg (bfd_get_error()));
+      ctf_err_warn (NULL, 0, 0, _("BFD format problem in %s: %s"),
+		    filename ? filename : _("(unknown file)"),
+		    bfd_errmsg (bfd_get_error ()));
       if (bfd_get_error() == bfd_error_file_ambiguously_recognized)
 	return (ctf_set_open_errno (errp, ECTF_BFD_AMBIGUOUS));
       else
@@ -304,7 +306,8 @@ ctf_fdopen (int fd, const char *filename, const char *target, int *errp)
   if ((arci = ctf_bfdopen (abfd, errp)) == NULL)
     {
       if (!bfd_close_all_done (abfd))
-	ctf_dprintf ("Cannot close BFD: %s\n", bfd_errmsg (bfd_get_error()));
+	ctf_err_warn (NULL, 0, 0, _("cannot close BFD: %s"),
+		      bfd_errmsg (bfd_get_error ()));
       return NULL;			/* errno is set for us.  */
     }
   arci->ctfi_bfd_close = ctf_bfdclose;
diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c
index fee7789a308..8c301826167 100644
--- a/libctf/ctf-open.c
+++ b/libctf/ctf-open.c
@@ -141,8 +141,8 @@ get_ctt_size_v2 (const ctf_file_t *fp, const ctf_type_t *tp,
 }
 
 static ssize_t
-get_vbytes_common (unsigned short kind, ssize_t size _libctf_unused_,
-		   size_t vlen)
+get_vbytes_common (ctf_file_t *fp, unsigned short kind,
+		   ssize_t size _libctf_unused_, size_t vlen)
 {
   switch (kind)
     {
@@ -162,13 +162,14 @@ get_vbytes_common (unsigned short kind, ssize_t size _libctf_unused_,
     case CTF_K_RESTRICT:
       return 0;
     default:
-      ctf_dprintf ("detected invalid CTF kind -- %x\n", kind);
-      return ECTF_CORRUPT;
+      ctf_set_errno (fp, ECTF_CORRUPT);
+      ctf_err_warn (fp, 0, 0, _("detected invalid CTF kind: %x"), kind);
+      return -1;
     }
 }
 
 static ssize_t
-get_vbytes_v1 (unsigned short kind, ssize_t size, size_t vlen)
+get_vbytes_v1 (ctf_file_t *fp, unsigned short kind, ssize_t size, size_t vlen)
 {
   switch (kind)
     {
@@ -184,11 +185,11 @@ get_vbytes_v1 (unsigned short kind, ssize_t size, size_t vlen)
 	return (sizeof (ctf_lmember_v1_t) * vlen);
     }
 
-  return (get_vbytes_common (kind, size, vlen));
+  return (get_vbytes_common (fp, kind, size, vlen));
 }
 
 static ssize_t
-get_vbytes_v2 (unsigned short kind, ssize_t size, size_t vlen)
+get_vbytes_v2 (ctf_file_t *fp, unsigned short kind, ssize_t size, size_t vlen)
 {
   switch (kind)
     {
@@ -204,7 +205,7 @@ get_vbytes_v2 (unsigned short kind, ssize_t size, size_t vlen)
 	return (sizeof (ctf_lmember_t) * vlen);
     }
 
-  return (get_vbytes_common (kind, size, vlen));
+  return (get_vbytes_common (fp, kind, size, vlen));
 }
 
 static const ctf_fileops_t ctf_fileops[] = {
@@ -428,11 +429,11 @@ upgrade_types_v1 (ctf_file_t *fp, ctf_header_t *cth)
       unsigned long vlen = CTF_V1_INFO_VLEN (tp->ctt_info);
 
       size = get_ctt_size_v1 (fp, (const ctf_type_t *) tp, NULL, &increment);
-      vbytes = get_vbytes_v1 (kind, size, vlen);
+      vbytes = get_vbytes_v1 (fp, kind, size, vlen);
 
       get_ctt_size_v2_unconverted (fp, (const ctf_type_t *) tp, NULL,
 				   &v2increment);
-      v2bytes = get_vbytes_v2 (kind, size, vlen);
+      v2bytes = get_vbytes_v2 (fp, kind, size, vlen);
 
       if ((vbytes < 0) || (size < 0))
 	return ECTF_CORRUPT;
@@ -485,7 +486,7 @@ upgrade_types_v1 (ctf_file_t *fp, ctf_header_t *cth)
       void *vdata, *v2data;
 
       size = get_ctt_size_v1 (fp, (const ctf_type_t *) tp, NULL, &increment);
-      vbytes = get_vbytes_v1 (kind, size, vlen);
+      vbytes = get_vbytes_v1 (fp, kind, size, vlen);
 
       t2p->ctt_name = tp->ctt_name;
       t2p->ctt_info = CTF_TYPE_INFO (kind, isroot, vlen);
@@ -519,7 +520,7 @@ upgrade_types_v1 (ctf_file_t *fp, ctf_header_t *cth)
 	}
 
       v2size = get_ctt_size_v2 (fp, t2p, NULL, &v2increment);
-      v2bytes = get_vbytes_v2 (kind, v2size, vlen);
+      v2bytes = get_vbytes_v2 (fp, kind, v2size, vlen);
 
       /* Catch out-of-sync get_ctt_size_*().  The count goes wrong if
 	 these are not identical (and having them different makes no
@@ -767,6 +768,7 @@ init_types (ctf_file_t *fp, ctf_header_t *cth)
 
       (void) ctf_get_ctt_size (fp, tp, &size, &increment);
       name = ctf_strptr (fp, tp->ctt_name);
+      /* Cannot fail: shielded by call in loop above.  */
       vbytes = LCTF_VBYTES (fp, kind, size, vlen);
 
       switch (kind)
@@ -905,8 +907,8 @@ init_types (ctf_file_t *fp, ctf_header_t *cth)
 	    return err;
 	  break;
 	default:
-	  ctf_dprintf ("unhandled CTF kind in endianness conversion -- %x\n",
-		       kind);
+	  ctf_err_warn (fp, 0, ECTF_CORRUPT,
+			_("init_types(): unhandled CTF kind: %x"), kind);
 	  return ECTF_CORRUPT;
 	}
 
@@ -1043,7 +1045,7 @@ flip_vars (void *start, size_t len)
    ctf_stype followed by variable data.  */
 
 static int
-flip_types (void *start, size_t len)
+flip_types (ctf_file_t *fp, void *start, size_t len)
 {
   ctf_type_t *t = start;
 
@@ -1056,7 +1058,7 @@ flip_types (void *start, size_t len)
       uint32_t kind = CTF_V2_INFO_KIND (t->ctt_info);
       size_t size = t->ctt_size;
       uint32_t vlen = CTF_V2_INFO_VLEN (t->ctt_info);
-      size_t vbytes = get_vbytes_v2 (kind, size, vlen);
+      size_t vbytes = get_vbytes_v2 (fp, kind, size, vlen);
 
       if (_libctf_unlikely_ (size == CTF_LSIZE_SENT))
 	{
@@ -1181,8 +1183,9 @@ flip_types (void *start, size_t len)
 	    break;
 	  }
 	default:
-	  ctf_dprintf ("unhandled CTF kind in endianness conversion -- %x\n",
-		       kind);
+	  ctf_err_warn (fp, 0, ECTF_CORRUPT,
+			_("unhandled CTF kind in endianness conversion: %x"),
+			kind);
 	  return ECTF_CORRUPT;
 	}
 
@@ -1200,7 +1203,7 @@ flip_types (void *start, size_t len)
    data, this is no real loss.  */
 
 static int
-flip_ctf (ctf_header_t *cth, unsigned char *buf)
+flip_ctf (ctf_file_t *fp, ctf_header_t *cth, unsigned char *buf)
 {
   flip_lbls (buf + cth->cth_lbloff, cth->cth_objtoff - cth->cth_lbloff);
   flip_objts (buf + cth->cth_objtoff, cth->cth_funcoff - cth->cth_objtoff);
@@ -1208,7 +1211,7 @@ flip_ctf (ctf_header_t *cth, unsigned char *buf)
   flip_objts (buf + cth->cth_objtidxoff, cth->cth_funcidxoff - cth->cth_objtidxoff);
   flip_objts (buf + cth->cth_funcidxoff, cth->cth_varoff - cth->cth_funcidxoff);
   flip_vars (buf + cth->cth_varoff, cth->cth_typeoff - cth->cth_varoff);
-  return flip_types (buf + cth->cth_typeoff, cth->cth_stroff - cth->cth_typeoff);
+  return flip_types (fp, buf + cth->cth_typeoff, cth->cth_stroff - cth->cth_typeoff);
 }
 
 /* Set up the ctl hashes in a ctf_file_t.  Called by both writable and
@@ -1376,8 +1379,8 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
 	 info.  We do not support dynamically upgrading such entries (none
 	 should exist in any case, since dwarf2ctf does not create them).  */
 
-      ctf_dprintf ("ctf_bufopen: CTF version %d symsect not "
-		   "supported\n", pp->ctp_version);
+      ctf_err_warn (NULL, 0, 0, _("ctf_bufopen: CTF version %d symsect not "
+				  "supported"), pp->ctp_version);
       return (ctf_set_open_errno (errp, ECTF_NOTSUP));
     }
 
@@ -1470,16 +1473,17 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
 
       if ((rc = uncompress (fp->ctf_base, &dstlen, src, srclen)) != Z_OK)
 	{
-	  ctf_dprintf ("zlib inflate err: %s\n", zError (rc));
+	  ctf_err_warn (NULL, 0, ECTF_DECOMPRESS, _("zlib inflate err: %s"),
+			zError (rc));
 	  err = ECTF_DECOMPRESS;
 	  goto bad;
 	}
 
       if ((size_t) dstlen != fp->ctf_size)
 	{
-	  ctf_dprintf ("zlib inflate short -- got %lu of %lu "
-		       "bytes\n", (unsigned long) dstlen,
-		       (unsigned long) fp->ctf_size);
+	  ctf_err_warn (NULL, 0, ECTF_CORRUPT,
+			_("zlib inflate short: got %lu of %lu bytes"),
+			(unsigned long) dstlen, (unsigned long) fp->ctf_size);
 	  err = ECTF_CORRUPT;
 	  goto bad;
 	}
@@ -1559,7 +1563,7 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
   fp->ctf_syn_ext_strtab = syn_strtab;
 
   if (foreign_endian &&
-      (err = flip_ctf (hp, fp->ctf_buf)) != 0)
+      (err = flip_ctf (fp, hp, fp->ctf_buf)) != 0)
     {
       /* We can be certain that flip_ctf() will have endian-flipped everything
 	 other than the types table when we return.  In particular the header
@@ -1619,6 +1623,7 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
 
 bad:
   ctf_set_open_errno (errp, err);
+  ctf_err_warn_to_open (fp);
   ctf_file_close (fp);
   return NULL;
 }
diff --git a/libctf/ctf-string.c b/libctf/ctf-string.c
index 583679defb4..837f66433c9 100644
--- a/libctf/ctf-string.c
+++ b/libctf/ctf-string.c
@@ -427,7 +427,7 @@ ctf_str_write_strtab (ctf_file_t *fp)
   nullstr = ctf_dynhash_lookup (fp->ctf_str_atoms, "");
   if (!nullstr)
     {
-      ctf_dprintf ("Internal error: null string not found in strtab.\n");
+      ctf_err_warn (fp, 0, ECTF_INTERNAL, _("null string not found in strtab"));
       strtab.cts_strs = NULL;
       return strtab;
     }
diff --git a/libctf/ctf-subr.c b/libctf/ctf-subr.c
index 15865706437..431b6d849f8 100644
--- a/libctf/ctf-subr.c
+++ b/libctf/ctf-subr.c
@@ -193,10 +193,16 @@ void ctf_dprintf (const char *format, ...)
     }
 }
 
-/* Errors and warnings.  */
-_libctf_printflike_ (3, 4)
+/* This needs more attention to thread-safety later on.  */
+static ctf_list_t open_errors;
+
+/* Errors and warnings.  Report the warning or error to the list in FP (or the
+   open errors list if NULL): if ERR is nonzero it is the errno to report to the
+   debug stream instead of that recorded on fp.  */
+_libctf_printflike_ (4, 5)
 extern void
-ctf_err_warn (ctf_file_t *fp, int is_warning, const char *format, ...)
+ctf_err_warn (ctf_file_t *fp, int is_warning, int err,
+	      const char *format, ...)
 {
   va_list alist;
   ctf_err_warning_t *cew;
@@ -219,26 +225,68 @@ ctf_err_warn (ctf_file_t *fp, int is_warning, const char *format, ...)
     }
   va_end (alist);
 
-  ctf_dprintf ("%s: %s\n", is_warning ? "error" : "warning", cew->cew_text);
+  /* Include the error code only if there is one, and if this is not a warning.
+     (Warnings may not have a meaningful error code, since the warning may not
+     lead to unwinding up to the user.)  */
+  if (!is_warning && (err != 0 || (fp && ctf_errno (fp) != 0)))
+    ctf_dprintf ("%s: %s (%s)\n", is_warning ? _("error") : _("warning"),
+		 cew->cew_text, err != 0 ? ctf_errmsg (err)
+		 : ctf_errmsg (ctf_errno (fp)));
+  else
+    ctf_dprintf ("%s: %s\n", is_warning ? _("error") : _("warning"),
+		 cew->cew_text);
+
+  if (fp != NULL)
+    ctf_list_append (&fp->ctf_errs_warnings, cew);
+  else
+    ctf_list_append (&open_errors, cew);
+}
 
-  ctf_list_append (&fp->ctf_errs_warnings, cew);
+/* Move all the errors/warnings from an fp into the open_errors.  */
+void
+ctf_err_warn_to_open (ctf_file_t *fp)
+{
+  ctf_list_splice (&open_errors, &fp->ctf_errs_warnings);
 }
 
 /* Error-warning reporting: an 'iterator' that returns errors and warnings from
    the error/warning list, in order of emission.  Errors and warnings are popped
-   after return: the caller must free the returned error-text pointer.  */
+   after return: the caller must free the returned error-text pointer.
+
+   An fp of NULL returns CTF-open-time errors from the open_errors variable
+   above.
+
+   The treatment of errors from this function itself is somewhat unusual: it
+   will often be called on an error path, so we don't want to overwrite the
+   ctf_errno unless we have no choice.  So, like ctf_bufopen et al, this
+   function takes an errp pointer where errors are reported.  The pointer is
+   optional: if not set, errors are reported via the fp (if non-NULL).  Calls
+   with neither fp nor errp set are mildly problematic because there is no clear
+   way to report end-of-iteration: you just have to assume that a NULL return
+   means the end, and not an iterator error.  */
+
 char *
-ctf_errwarning_next (ctf_file_t *fp, ctf_next_t **it, int *is_warning)
+ctf_errwarning_next (ctf_file_t *fp, ctf_next_t **it, int *is_warning,
+		     int *errp)
 {
   ctf_next_t *i = *it;
   char *ret;
+  ctf_list_t *errlist;
   ctf_err_warning_t *cew;
 
+  if (fp)
+    errlist = &fp->ctf_errs_warnings;
+  else
+    errlist = &open_errors;
+
   if (!i)
     {
       if ((i = ctf_next_create ()) == NULL)
 	{
-	  ctf_set_errno (fp, ENOMEM);
+	  if (errp)
+	    *errp = ENOMEM;
+	  else if (fp)
+	    ctf_set_errno (fp, ENOMEM);
 	  return NULL;
 	}
 
@@ -249,30 +297,39 @@ ctf_errwarning_next (ctf_file_t *fp, ctf_next_t **it, int *is_warning)
 
   if ((void (*) (void)) ctf_errwarning_next != i->ctn_iter_fun)
     {
-      ctf_set_errno (fp, ECTF_NEXT_WRONGFUN);
+      if (errp)
+	*errp = ECTF_NEXT_WRONGFUN;
+      else if (fp)
+	ctf_set_errno (fp, ECTF_NEXT_WRONGFUN);
       return NULL;
     }
 
   if (fp != i->cu.ctn_fp)
     {
-      ctf_set_errno (fp, ECTF_NEXT_WRONGFP);
+      if (errp)
+	*errp = ECTF_NEXT_WRONGFP;
+      else if (fp)
+	ctf_set_errno (fp, ECTF_NEXT_WRONGFP);
       return NULL;
     }
 
-  cew = ctf_list_next (&fp->ctf_errs_warnings);
+  cew = ctf_list_next (errlist);
 
   if (!cew)
     {
       ctf_next_destroy (i);
       *it = NULL;
-      ctf_set_errno (fp, ECTF_NEXT_END);
+      if (errp)
+	*errp = ECTF_NEXT_END;
+      else if (fp)
+	ctf_set_errno (fp, ECTF_NEXT_END);
       return NULL;
     }
 
   if (is_warning)
     *is_warning = cew->cew_is_warning;
   ret = cew->cew_text;
-  ctf_list_delete (&fp->ctf_errs_warnings, cew);
+  ctf_list_delete (errlist, cew);
   free (cew);
   return ret;
 }
@@ -281,7 +338,7 @@ void
 ctf_assert_fail_internal (ctf_file_t *fp, const char *file, size_t line,
 			  const char *exprstr)
 {
-  ctf_err_warn (fp, 0, "%s: %lu: libctf assertion failed: %s", file,
-		(long unsigned int) line, exprstr);
+  ctf_err_warn (fp, 0, ECTF_INTERNAL, _("%s: %lu: libctf assertion failed: %s"),
+		file, (long unsigned int) line, exprstr);
   ctf_set_errno (fp, ECTF_INTERNAL);
 }
diff --git a/libctf/ctf-types.c b/libctf/ctf-types.c
index 4843de38d92..c9fed1558dd 100644
--- a/libctf/ctf-types.c
+++ b/libctf/ctf-types.c
@@ -606,7 +606,8 @@ ctf_type_resolve (ctf_file_t *fp, ctf_id_t type)
 	  if (tp->ctt_type == type || tp->ctt_type == otype
 	      || tp->ctt_type == prev)
 	    {
-	      ctf_dprintf ("type %ld cycle detected\n", otype);
+	      ctf_err_warn (ofp, 0, ECTF_CORRUPT, _("type %lx cycle detected"),
+			    otype);
 	      return (ctf_set_errno (ofp, ECTF_CORRUPT));
 	    }
 	  prev = type;
-- 
2.28.0.248.gcf383e60c9



More information about the Binutils mailing list