public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* libbacktrace: Add support for MiniDebugInfo
@ 2020-09-14 21:12 Ian Lance Taylor
  2020-09-16 11:03 ` Alex Coplan
  0 siblings, 1 reply; 5+ messages in thread
From: Ian Lance Taylor @ 2020-09-14 21:12 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 5181 bytes --]

This patch to libbacktrace adds support for MiniDebugInfo, as
requested in PR 93608.

MiniDebugInfo stores compressed symbol tables for an executable, where
the executable is otherwise stripped.  It is documented at
https://fedoraproject.org/wiki/Features/MiniDebugInfo and
https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html.

Unfortunately, although debug info processors are already required to
handle data compressed using zlib aka gzip in order to handle zdebug
and SHT_COMPRESSED, the MiniDebugInfo implementation choose to instead
compress the debug information with xz which uses LZMA2.  This in
effect forces all debug info processors to add a second decompression
algorithm.  Note to future developers: do not do this.

This libbacktrace implementation includes a new small LZMA
decompressor that just decompresses from one buffer to another.  It is
not heavily optimized, and on my laptop runs at about 78% of the speed
of using liblzma directly.  I think this is a decent tradeoff for
keeping the code reasonably small.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian

PR libbacktrace/93608
Add support for MiniDebugInfo.
* elf.c (struct elf_view): Define.  Replace most uses of
backtrace_view with elf_view.
(elf_get_view): New static functions.  Replace most calls of
backtrace_get_view with elf_get_view.
(elf_release_view): New static functions.  Replace most calls of
backtrace_release_view with elf_release_view.
(elf_uncompress_failed): Rename from elf_zlib_failed.  Change all
callers.
(LZMA_STATES, LZMA_POS_STATES, LZMA_DIST_STATES): Define.
(LZMA_DIST_SLOTS, LZMA_DIST_MODEL_START): Define.
(LZMA_DIST_MODEL_END, LZMA_FULL_DISTANCES): Define.
(LZMA_ALIGN_SIZE, LZMA_LEN_LOW_SYMBOLS): Define.
(LZMA_LEN_MID_SYMBOLS, LZMA_LEN_HIGH_SYMBOLS): Define.
(LZMA_LITERAL_CODERS_MAX, LZMA_LITERAL_CODER_SIZE): Define.
(LZMA_PROB_IS_MATCH_LEN, LZMA_PROB_IS_REP_LEN): Define.
(LZMA_PROB_IS_REP0_LEN, LZMA_PROB_IS_REP1_LEN): Define.
(LZMA_PROB_IS_REP2_LEN, LZMA_PROB_IS_REP0_LONG_LEN): Define.
(LZMA_PROB_DIST_SLOT_LEN, LZMA_PROB_DIST_SPECIAL_LEN): Define.
(LZMA_PROB_DIST_ALIGN_LEN): Define.
(LZMA_PROB_MATCH_LEN_CHOICE_LEN): Define.
(LZMA_PROB_MATCH_LEN_CHOICE2_LEN): Define.
(LZMA_PROB_MATCH_LEN_LOW_LEN): Define.
(LZMA_PROB_MATCH_LEN_MID_LEN): Define.
(LZMA_PROB_MATCH_LEN_HIGH_LEN): Define.
(LZMA_PROB_REP_LEN_CHOICE_LEN): Define.
(LZMA_PROB_REP_LEN_CHOICE2_LEN): Define.
(LZMA_PROB_REP_LEN_LOW_LEN): Define.
(LZMA_PROB_REP_LEN_MID_LEN): Define.
(LZMA_PROB_REP_LEN_HIGH_LEN): Define.
(LZMA_PROB_LITERAL_LEN): Define.
(LZMA_PROB_IS_MATCH_OFFSET, LZMA_PROB_IS_REP_OFFSET): Define.
(LZMA_PROB_IS_REP0_OFFSET, LZMA_PROB_IS_REP1_OFFSET): Define.
(LZMA_PROB_IS_REP2_OFFSET): Define.
(LZMA_PROB_IS_REP0_LONG_OFFSET): Define.
(LZMA_PROB_DIST_SLOT_OFFSET): Define.
(LZMA_PROB_DIST_SPECIAL_OFFSET): Define.
(LZMA_PROB_DIST_ALIGN_OFFSET): Define.
(LZMA_PROB_MATCH_LEN_CHOICE_OFFSET): Define.
(LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET): Define.
(LZMA_PROB_MATCH_LEN_LOW_OFFSET): Define.
(LZMA_PROB_MATCH_LEN_MID_OFFSET): Define.
(LZMA_PROB_MATCH_LEN_HIGH_OFFSET): Define.
(LZMA_PROB_REP_LEN_CHOICE_OFFSET): Define.
(LZMA_PROB_REP_LEN_CHOICE2_OFFSET): Define.
(LZMA_PROB_REP_LEN_LOW_OFFSET): Define.
(LZMA_PROB_REP_LEN_MID_OFFSET): Define.
(LZMA_PROB_REP_LEN_HIGH_OFFSET): Define.
(LZMA_PROB_LITERAL_OFFSET): Define.
(LZMA_PROB_TOTAL_COUNT): Define.
(LZMA_IS_MATCH, LZMA_IS_REP, LZMA_IS_REP0): Define.
(LZMA_IS_REP1, LZMA_IS_REP2, LZMA_IS_REP0_LONG): Define.
(LZMA_DIST_SLOT, LZMA_DIST_SPECIAL, LZMA_DIST_ALIGN): Define.
(LZMA_MATCH_LEN_CHOICE, LZMA_MATCH_LEN_CHOICE2): Define.
(LZMA_MATCH_LEN_LOW, LZMA_MATCH_LEN_MID): Define.
(LZMA_MATCH_LEN_HIGH, LZMA_REP_LEN_CHOICE): Define.
(LZMA_REP_LEN_CHOICE2, LZMA_REP_LEN_LOW): Define.
(LZMA_REP_LEN_MID, LZMA_REP_LEN_HIGH, LZMA_LITERAL): Define.
(elf_lzma_varint): New static function.
(elf_lzma_range_normalize): New static function.
(elf_lzma_bit, elf_lzma_integer): New static functions.
(elf_lzma_reverse_integer): New static function.
(elf_lzma_len, elf_uncompress_lzma_block): New static functions.
(elf_uncompress_lzma): New static function.
(backtrace_uncompress_lzma): New function.
(elf_add): Add memory and memory_size parameters.  Change all
callers.  Look for .gnu_debugdata section, and, if found,
decompress it and use it for symbols and debug info.  Permit the
descriptor parameter to be -1.
* internal.h (backtrace_uncompress_lzma): Declare.
* mtest.c: New file.
* xztest.c: New file.
* configure.ac: Check for nm, xz, and comm programs.  Check for
liblzma library.
(HAVE_MINIDEBUG): Define.
* Makefile.am (mtest_SOURCES): Define.
(mtest_CFLAGS, mtest_LDADD): Define.
(TESTS): Add mtest_minidebug if HAVE_MINIDEBUG.
(%_minidebug): New pattern rule, if HAVE_MINIDEBUG.
(xztest_SOURCES, xztest_CFLAGS, xztest_LDADD): Define.
(xztest_alloc_SOURCES, xztest_alloc_CFLAGS): Define
(xztest_alloc_LDADD): Define.
(BUILDTESTS): Add mtest, xztest, xztest_alloc.
(CLEANFILES): Add files created by minidebug pattern.
(btest.lo): Correct INCDIR reference.
(mtest.lo, xztest.lo, ztest.lo): New targets.
* configure: Renerate.
* config.h.in: Regenerate.
* Makefile.in: Regenerate.

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 96863 bytes --]

diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am
index 6fc07498787..4d349386c9b 100644
--- a/libbacktrace/Makefile.am
+++ b/libbacktrace/Makefile.am
@@ -478,13 +478,61 @@ endif HAVE_DSYMUTIL
 
 endif
 
+mtest_SOURCES = mtest.c testlib.c
+mtest_CFLAGS = $(libbacktrace_TEST_CFLAGS) -O
+mtest_LDADD = libbacktrace.la
+
+BUILDTESTS += mtest
+
+if HAVE_MINIDEBUG
+
+TESTS += mtest_minidebug
+
+%_minidebug: %
+	$(NM) -D $< -P --defined-only | $(AWK) '{ print $$1 }' | sort > $<.dsyms
+	$(NM) $< -P --defined-only | $(AWK) '{ if ($$2 == "T" || $$2 == "t" || $$2 == "D") print $$1 }' | sort > $<.fsyms
+	$(COMM) -13 $<.dsyms $<.fsyms > $<.keepsyms
+	$(OBJCOPY) --only-keep-debug $< $<.dbg
+	$(OBJCOPY) -S --remove-section .gdb_index --remove-section .comment --keep-symbols=$<.keepsyms $<.dbg $<.mdbg
+	$(OBJCOPY) --strip-all --remove-section ..comment $< $<.strip
+	rm -f $<.mdbg.xz
+	$(XZ) $<.mdbg
+	$(OBJCOPY) --add-section .gnu_debugdata=$<.mdbg.xz $<.strip
+	mv $<.strip $@
+
+endif HAVE_MINIDEBUG
+
 endif NATIVE
 
+if HAVE_ELF
+
+xztest_SOURCES = xztest.c testlib.c
+xztest_CFLAGS = $(libbacktrace_TEST_CFLAGS) -DSRCDIR=\"$(srcdir)\"
+xztest_LDADD = libbacktrace.la
+
+xztest_alloc_SOURCES = $(xztest_SOURCES)
+xztest_alloc_CFLAGS = $(xztest_CFLAGS)
+xztest_alloc_LDADD = libbacktrace_alloc.la
+
+if HAVE_LIBLZMA
+xztest_LDADD += -llzma
+xztest_alloc_LDADD += -llzma
+endif
+
+xztest_LDADD += $(CLOCK_GETTIME_LINK)
+xztest_alloc_LDADD += $(CLOCK_GETTIME_LINK)
+
+BUILDTESTS += xztest xztest_alloc
+
+endif HAVE_ELF
+
 check_PROGRAMS += $(BUILDTESTS)
 
 TESTS += $(BUILDTESTS)
 
-CLEANFILES = $(TESTS) *.debug elf_for_test.c edtest2_build.c gen_edtest2_build
+CLEANFILES = \
+	$(TESTS) *.debug elf_for_test.c edtest2_build.c gen_edtest2_build \
+	*.dsyms *.fsyms *.keepsyms *.dbg *.mdbg *.mdbg.xz *.strip
 
 clean-local:
 	-rm -rf usr
@@ -504,7 +552,7 @@ clean-local:
 INCDIR = $(top_srcdir)/../include
 alloc.lo: config.h backtrace.h internal.h
 backtrace.lo: config.h backtrace.h internal.h
-btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h
+btest.lo: $(INCDIR)/filenames.h backtrace.h backtrace-supported.h
 dwarf.lo: config.h $(INCDIR)/dwarf2.h $(INCDIR)/dwarf2.def \
 	$(INCDIR)/filenames.h backtrace.h internal.h
 elf.lo: config.h backtrace.h internal.h
@@ -512,6 +560,7 @@ fileline.lo: config.h backtrace.h internal.h
 macho.lo: config.h backtrace.h internal.h
 mmap.lo: config.h backtrace.h internal.h
 mmapio.lo: config.h backtrace.h internal.h
+mtest.lo: backtrace.h backtrace-supported.h
 nounwind.lo: config.h internal.h
 pecoff.lo: config.h backtrace.h internal.h
 posix.lo: config.h backtrace.h internal.h
@@ -523,5 +572,7 @@ stest.lo: config.h backtrace.h internal.h
 state.lo: config.h backtrace.h backtrace-supported.h internal.h
 unknown.lo: config.h backtrace.h internal.h
 xcoff.lo: config.h backtrace.h internal.h
+xztest.lo: config.h backtrace.h backtrace-supported.h internal.h testlib.h
+ztest.lo: config.h backtrace.h backtrace-supported.h internal.h testlib.h
 
 include $(top_srcdir)/../multilib.am
diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac
index c4b19c4066f..0659ea60484 100644
--- a/libbacktrace/configure.ac
+++ b/libbacktrace/configure.ac
@@ -512,6 +512,21 @@ AC_ARG_VAR(DSYMUTIL, [location of dsymutil])
 AC_CHECK_PROG(DSYMUTIL, dsymutil, dsymutil)
 AM_CONDITIONAL(HAVE_DSYMUTIL, test -n "${DSYMUTIL}")
 
+AC_ARG_VAR(NM, [location of nm])
+AC_CHECK_PROG(NM, nm, nm)
+
+AC_CHECK_PROG(XZ, xz, xz)
+AM_CONDITIONAL(HAVE_XZ, test "$XZ" != "")
+AC_CHECK_PROG(COMM, comm, comm)
+AM_CONDITIONAL(HAVE_COMM, test "$COMM" != "")
+
+AM_CONDITIONAL(HAVE_MINIDEBUG,
+  test "${with_target_subdir}" = "" -a "$FORMAT_FILE" = "elf.lo" -a "${OBJCOPY}" != "" -a "${NM}" != "" -a "${XZ}" != "" -a "${COMM}" != "")
+
+AC_CHECK_LIB([lzma], [lzma_auto_decoder],
+    [AC_DEFINE(HAVE_LIBLZMA, 1, [Define if -llzma is available.])])
+AM_CONDITIONAL(HAVE_LIBLZMA, test "$ac_cv_lib_lzma_lzma_auto_decoder" = yes)
+
 AC_CACHE_CHECK([whether tests can run],
   [libbacktrace_cv_sys_native],
   [AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])],
diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
index 80a00506bd6..dd004708246 100644
--- a/libbacktrace/elf.c
+++ b/libbacktrace/elf.c
@@ -390,6 +390,14 @@ struct elf_syminfo_data
   size_t count;
 };
 
+/* A view that works for either a file or memory.  */
+
+struct elf_view
+{
+  struct backtrace_view view;
+  int release; /* If non-zero, must call backtrace_release_view.  */
+};
+
 /* Information about PowerPC64 ELFv1 .opd section.  */
 
 struct elf_ppc64_opd_data
@@ -401,9 +409,48 @@ struct elf_ppc64_opd_data
   /* Size of the .opd section.  */
   size_t size;
   /* Corresponding section view.  */
-  struct backtrace_view view;
+  struct elf_view view;
 };
 
+/* Create a view of SIZE bytes from DESCRIPTOR/MEMORY at OFFSET.  */
+
+static int
+elf_get_view (struct backtrace_state *state, int descriptor,
+	      const unsigned char *memory, size_t memory_size, off_t offset,
+	      uint64_t size, backtrace_error_callback error_callback,
+	      void *data, struct elf_view *view)
+{
+  if (memory == NULL)
+    {
+      view->release = 1;
+      return backtrace_get_view (state, descriptor, offset, size,
+				 error_callback, data, &view->view);
+    }
+  else
+    {
+      if ((uint64_t) offset + size > (uint64_t) memory_size)
+	{
+	  error_callback (data, "out of range for in-memory file", 0);
+	  return 0;
+	}
+      view->view.data = (const void *) (memory + offset);
+      view->view.base = NULL;
+      view->view.len = size;
+      view->release = 0;
+      return 1;
+    }
+}
+
+/* Release a view read by elf_get_view.  */
+
+static void
+elf_release_view (struct backtrace_state *state, struct elf_view *view,
+		  backtrace_error_callback error_callback, void *data)
+{
+  if (view->release)
+    backtrace_release_view (state, &view->view, error_callback, data);
+}
+
 /* Compute the CRC-32 of BUF/LEN.  This uses the CRC used for
    .gnu_debuglink files.  */
 
@@ -1035,7 +1082,7 @@ elf_open_debugfile_by_debuglink (struct backtrace_state *state,
    when this code is compiled with -g.  */
 
 static void
-elf_zlib_failed(void)
+elf_uncompress_failed(void)
 {
 }
 
@@ -1062,7 +1109,7 @@ elf_zlib_fetch (const unsigned char **ppin, const unsigned char *pinend,
 
   if (unlikely (pinend - pin < 4))
     {
-      elf_zlib_failed ();
+      elf_uncompress_failed ();
       return 0;
     }
 
@@ -1192,7 +1239,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
     {
       if (unlikely (codes[i] >= 16))
 	{
-	  elf_zlib_failed ();
+	  elf_uncompress_failed ();
 	  return 0;
 	}
 
@@ -1229,7 +1276,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
 
       if (unlikely (jcnt > (1U << j)))
 	{
-	  elf_zlib_failed ();
+	  elf_uncompress_failed ();
 	  return 0;
 	}
 
@@ -1249,7 +1296,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
 
 	  if (unlikely ((val & ~HUFFMAN_VALUE_MASK) != 0))
 	    {
-	      elf_zlib_failed ();
+	      elf_uncompress_failed ();
 	      return 0;
 	    }
 
@@ -1265,7 +1312,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
 	    {
 	      if (unlikely (table[ind] != 0))
 		{
-		  elf_zlib_failed ();
+		  elf_uncompress_failed ();
 		  return 0;
 		}
 	      table[ind] = tval;
@@ -1353,7 +1400,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
 	}
       if (unlikely (jcnt != 0))
 	{
-	  elf_zlib_failed ();
+	  elf_uncompress_failed ();
 	  return 0;
 	}
     }
@@ -1406,7 +1453,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
 		  if (unlikely ((next_secondary & HUFFMAN_VALUE_MASK)
 				!= next_secondary))
 		    {
-		      elf_zlib_failed ();
+		      elf_uncompress_failed ();
 		      return 0;
 		    }
 
@@ -1424,7 +1471,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
 		  if (unlikely ((tprimary & (1U << HUFFMAN_SECONDARY_SHIFT))
 				== 0))
 		    {
-		      elf_zlib_failed ();
+		      elf_uncompress_failed ();
 		      return 0;
 		    }
 		  secondary = tprimary & HUFFMAN_VALUE_MASK;
@@ -1432,7 +1479,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
 				    & HUFFMAN_BITS_MASK);
 		  if (unlikely (secondary_bits < j - 8))
 		    {
-		      elf_zlib_failed ();
+		      elf_uncompress_failed ();
 		      return 0;
 		    }
 		}
@@ -1448,7 +1495,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len,
 	    {
 	      if (unlikely (table[secondary + 0x100 + ind] != 0))
 		{
-		  elf_zlib_failed ();
+		  elf_uncompress_failed ();
 		  return 0;
 		}
 	      table[secondary + 0x100 + ind] = tval;
@@ -1664,28 +1711,28 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
       if (unlikely ((pin[0] & 0xf) != 8)) /* 8 is zlib encoding.  */
 	{
 	  /* Unknown compression method.  */
-	  elf_zlib_failed ();
+	  elf_uncompress_failed ();
 	  return 0;
 	}
       if (unlikely ((pin[0] >> 4) > 7))
 	{
 	  /* Window size too large.  Other than this check, we don't
 	     care about the window size.  */
-	  elf_zlib_failed ();
+	  elf_uncompress_failed ();
 	  return 0;
 	}
       if (unlikely ((pin[1] & 0x20) != 0))
 	{
 	  /* Stream expects a predefined dictionary, but we have no
 	     dictionary.  */
-	  elf_zlib_failed ();
+	  elf_uncompress_failed ();
 	  return 0;
 	}
       val = (pin[0] << 8) | pin[1];
       if (unlikely (val % 31 != 0))
 	{
 	  /* Header check failure.  */
-	  elf_zlib_failed ();
+	  elf_uncompress_failed ();
 	  return 0;
 	}
       pin += 2;
@@ -1722,7 +1769,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 	  if (unlikely (type == 3))
 	    {
 	      /* Invalid block type.  */
-	      elf_zlib_failed ();
+	      elf_uncompress_failed ();
 	      return 0;
 	    }
 
@@ -1745,7 +1792,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 	      if (unlikely ((pinend - pin) < 4))
 		{
 		  /* Missing length.  */
-		  elf_zlib_failed ();
+		  elf_uncompress_failed ();
 		  return 0;
 		}
 	      len = pin[0] | (pin[1] << 8);
@@ -1755,14 +1802,14 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 	      if (unlikely (len != lenc))
 		{
 		  /* Corrupt data.  */
-		  elf_zlib_failed ();
+		  elf_uncompress_failed ();
 		  return 0;
 		}
 	      if (unlikely (len > (unsigned int) (pinend - pin)
 			    || len > (unsigned int) (poutend - pout)))
 		{
 		  /* Not enough space in buffers.  */
-		  elf_zlib_failed ();
+		  elf_uncompress_failed ();
 		  return 0;
 		}
 	      memcpy (pout, pin, len);
@@ -1812,7 +1859,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 	      if (unlikely (nlit > 286 || ndist > 30))
 		{
 		  /* Values out of range.  */
-		  elf_zlib_failed ();
+		  elf_uncompress_failed ();
 		  return 0;
 		}
 
@@ -1977,7 +2024,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 		     a secondary table is never necessary.  */
 		  if (unlikely ((t & (1U << HUFFMAN_SECONDARY_SHIFT)) != 0))
 		    {
-		      elf_zlib_failed ();
+		      elf_uncompress_failed ();
 		      return 0;
 		    }
 
@@ -1997,7 +2044,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 
 		      if (unlikely (plen == plenbase))
 			{
-			  elf_zlib_failed ();
+			  elf_uncompress_failed ();
 			  return 0;
 			}
 
@@ -2010,7 +2057,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 		      bits -= 2;
 		      if (unlikely ((unsigned int) (plenend - plen) < c))
 			{
-			  elf_zlib_failed ();
+			  elf_uncompress_failed ();
 			  return 0;
 			}
 
@@ -2045,7 +2092,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 		      bits -= 3;
 		      if (unlikely ((unsigned int) (plenend - plen) < c))
 			{
-			  elf_zlib_failed ();
+			  elf_uncompress_failed ();
 			  return 0;
 			}
 
@@ -2091,7 +2138,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 		      bits -= 7;
 		      if (unlikely ((unsigned int) (plenend - plen) < c))
 			{
-			  elf_zlib_failed ();
+			  elf_uncompress_failed ();
 			  return 0;
 			}
 
@@ -2100,7 +2147,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 		    }
 		  else
 		    {
-		      elf_zlib_failed ();
+		      elf_uncompress_failed ();
 		      return 0;
 		    }
 		}
@@ -2110,7 +2157,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 	      plen = plenbase;
 	      if (unlikely (plen[256] == 0))
 		{
-		  elf_zlib_failed ();
+		  elf_uncompress_failed ();
 		  return 0;
 		}
 
@@ -2162,7 +2209,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 		{
 		  if (unlikely (pout == poutend))
 		    {
-		      elf_zlib_failed ();
+		      elf_uncompress_failed ();
 		      return 0;
 		    }
 
@@ -2191,7 +2238,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 		    len = 258;
 		  else if (unlikely (lit > 285))
 		    {
-		      elf_zlib_failed ();
+		      elf_uncompress_failed ();
 		      return 0;
 		    }
 		  else
@@ -2244,13 +2291,13 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 
 		      if (unlikely (pout == porigout))
 			{
-			  elf_zlib_failed ();
+			  elf_uncompress_failed ();
 			  return 0;
 			}
 
 		      if (unlikely ((unsigned int) (poutend - pout) < len))
 			{
-			  elf_zlib_failed ();
+			  elf_uncompress_failed ();
 			  return 0;
 			}
 
@@ -2259,7 +2306,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 		    }
 		  else if (unlikely (dist > 29))
 		    {
-		      elf_zlib_failed ();
+		      elf_uncompress_failed ();
 		      return 0;
 		    }
 		  else
@@ -2290,13 +2337,13 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
 
 		      if (unlikely ((unsigned int) (pout - porigout) < dist))
 			{
-			  elf_zlib_failed ();
+			  elf_uncompress_failed ();
 			  return 0;
 			}
 
 		      if (unlikely ((unsigned int) (poutend - pout) < len))
 			{
-			  elf_zlib_failed ();
+			  elf_uncompress_failed ();
 			  return 0;
 			}
 
@@ -2326,7 +2373,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table,
   /* We should have filled the output buffer.  */
   if (unlikely (pout != poutend))
     {
-      elf_zlib_failed ();
+      elf_uncompress_failed ();
       return 0;
     }
 
@@ -2453,7 +2500,7 @@ elf_zlib_verify_checksum (const unsigned char *checkbytes,
 
   if (unlikely ((s2 << 16) + s1 != cksum))
     {
-      elf_zlib_failed ();
+      elf_uncompress_failed ();
       return 0;
     }
 
@@ -2608,6 +2655,1311 @@ backtrace_uncompress_zdebug (struct backtrace_state *state,
   return ret;
 }
 
+/* Number of LZMA states.  */
+#define LZMA_STATES (12)
+
+/* Number of LZMA position states.  The pb value of the property byte
+   is the number of bits to include in these states, and the maximum
+   value of pb is 4.  */
+#define LZMA_POS_STATES (16)
+
+/* Number of LZMA distance states.  These are used match distances
+   with a short match length: up to 4 bytes.  */
+#define LZMA_DIST_STATES (4)
+
+/* Number of LZMA distance slots.  LZMA uses six bits to encode larger
+   match lengths, so 1 << 6 possible probabilities.  */
+#define LZMA_DIST_SLOTS (64)
+
+/* LZMA distances 0 to 3 are encoded directly, larger values use a
+   probability model.  */
+#define LZMA_DIST_MODEL_START (4)
+
+/* The LZMA probability model ends at 14.  */
+#define LZMA_DIST_MODEL_END (14)
+
+/* LZMA distance slots for distances less than 127.  */
+#define LZMA_FULL_DISTANCES (128)
+
+/* LZMA uses four alignment bits.  */
+#define LZMA_ALIGN_SIZE (16)
+
+/* LZMA match length is encoded with 4, 5, or 10 bits, some of which
+   are already known.  */
+#define LZMA_LEN_LOW_SYMBOLS (8)
+#define LZMA_LEN_MID_SYMBOLS (8)
+#define LZMA_LEN_HIGH_SYMBOLS (256)
+
+/* LZMA literal encoding.  */
+#define LZMA_LITERAL_CODERS_MAX (16)
+#define LZMA_LITERAL_CODER_SIZE (0x300)
+
+/* LZMA is based on a large set of probabilities, each managed
+   independently.  Each probability is an 11 bit number that we store
+   in a uint16_t.  We use a single large array of probabilities.  */
+
+/* Lengths of entries in the LZMA probabilities array.  The names used
+   here are copied from the Linux kernel implementation.  */
+
+#define LZMA_PROB_IS_MATCH_LEN (LZMA_STATES * LZMA_POS_STATES)
+#define LZMA_PROB_IS_REP_LEN LZMA_STATES
+#define LZMA_PROB_IS_REP0_LEN LZMA_STATES
+#define LZMA_PROB_IS_REP1_LEN LZMA_STATES
+#define LZMA_PROB_IS_REP2_LEN LZMA_STATES
+#define LZMA_PROB_IS_REP0_LONG_LEN (LZMA_STATES * LZMA_POS_STATES)
+#define LZMA_PROB_DIST_SLOT_LEN (LZMA_DIST_STATES * LZMA_DIST_SLOTS)
+#define LZMA_PROB_DIST_SPECIAL_LEN (LZMA_FULL_DISTANCES - LZMA_DIST_MODEL_END)
+#define LZMA_PROB_DIST_ALIGN_LEN LZMA_ALIGN_SIZE
+#define LZMA_PROB_MATCH_LEN_CHOICE_LEN 1
+#define LZMA_PROB_MATCH_LEN_CHOICE2_LEN 1
+#define LZMA_PROB_MATCH_LEN_LOW_LEN (LZMA_POS_STATES * LZMA_LEN_LOW_SYMBOLS)
+#define LZMA_PROB_MATCH_LEN_MID_LEN (LZMA_POS_STATES * LZMA_LEN_MID_SYMBOLS)
+#define LZMA_PROB_MATCH_LEN_HIGH_LEN LZMA_LEN_HIGH_SYMBOLS
+#define LZMA_PROB_REP_LEN_CHOICE_LEN 1
+#define LZMA_PROB_REP_LEN_CHOICE2_LEN 1
+#define LZMA_PROB_REP_LEN_LOW_LEN (LZMA_POS_STATES * LZMA_LEN_LOW_SYMBOLS)
+#define LZMA_PROB_REP_LEN_MID_LEN (LZMA_POS_STATES * LZMA_LEN_MID_SYMBOLS)
+#define LZMA_PROB_REP_LEN_HIGH_LEN LZMA_LEN_HIGH_SYMBOLS
+#define LZMA_PROB_LITERAL_LEN \
+  (LZMA_LITERAL_CODERS_MAX * LZMA_LITERAL_CODER_SIZE)
+
+/* Offsets into the LZMA probabilities array.  This is mechanically
+   generated from the above lengths.  */
+
+#define LZMA_PROB_IS_MATCH_OFFSET 0
+#define LZMA_PROB_IS_REP_OFFSET \
+  (LZMA_PROB_IS_MATCH_OFFSET + LZMA_PROB_IS_MATCH_LEN)
+#define LZMA_PROB_IS_REP0_OFFSET \
+  (LZMA_PROB_IS_REP_OFFSET + LZMA_PROB_IS_REP_LEN)
+#define LZMA_PROB_IS_REP1_OFFSET \
+  (LZMA_PROB_IS_REP0_OFFSET + LZMA_PROB_IS_REP0_LEN)
+#define LZMA_PROB_IS_REP2_OFFSET \
+  (LZMA_PROB_IS_REP1_OFFSET + LZMA_PROB_IS_REP1_LEN)
+#define LZMA_PROB_IS_REP0_LONG_OFFSET \
+  (LZMA_PROB_IS_REP2_OFFSET + LZMA_PROB_IS_REP2_LEN)
+#define LZMA_PROB_DIST_SLOT_OFFSET \
+  (LZMA_PROB_IS_REP0_LONG_OFFSET + LZMA_PROB_IS_REP0_LONG_LEN)
+#define LZMA_PROB_DIST_SPECIAL_OFFSET \
+  (LZMA_PROB_DIST_SLOT_OFFSET + LZMA_PROB_DIST_SLOT_LEN)
+#define LZMA_PROB_DIST_ALIGN_OFFSET \
+  (LZMA_PROB_DIST_SPECIAL_OFFSET + LZMA_PROB_DIST_SPECIAL_LEN)
+#define LZMA_PROB_MATCH_LEN_CHOICE_OFFSET \
+  (LZMA_PROB_DIST_ALIGN_OFFSET + LZMA_PROB_DIST_ALIGN_LEN)
+#define LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET \
+  (LZMA_PROB_MATCH_LEN_CHOICE_OFFSET + LZMA_PROB_MATCH_LEN_CHOICE_LEN)
+#define LZMA_PROB_MATCH_LEN_LOW_OFFSET \
+  (LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET + LZMA_PROB_MATCH_LEN_CHOICE2_LEN)
+#define LZMA_PROB_MATCH_LEN_MID_OFFSET \
+  (LZMA_PROB_MATCH_LEN_LOW_OFFSET + LZMA_PROB_MATCH_LEN_LOW_LEN)
+#define LZMA_PROB_MATCH_LEN_HIGH_OFFSET \
+  (LZMA_PROB_MATCH_LEN_MID_OFFSET + LZMA_PROB_MATCH_LEN_MID_LEN)
+#define LZMA_PROB_REP_LEN_CHOICE_OFFSET \
+  (LZMA_PROB_MATCH_LEN_HIGH_OFFSET + LZMA_PROB_MATCH_LEN_HIGH_LEN)
+#define LZMA_PROB_REP_LEN_CHOICE2_OFFSET \
+  (LZMA_PROB_REP_LEN_CHOICE_OFFSET + LZMA_PROB_REP_LEN_CHOICE_LEN)
+#define LZMA_PROB_REP_LEN_LOW_OFFSET \
+  (LZMA_PROB_REP_LEN_CHOICE2_OFFSET + LZMA_PROB_REP_LEN_CHOICE2_LEN)
+#define LZMA_PROB_REP_LEN_MID_OFFSET \
+  (LZMA_PROB_REP_LEN_LOW_OFFSET + LZMA_PROB_REP_LEN_LOW_LEN)
+#define LZMA_PROB_REP_LEN_HIGH_OFFSET \
+  (LZMA_PROB_REP_LEN_MID_OFFSET + LZMA_PROB_REP_LEN_MID_LEN)
+#define LZMA_PROB_LITERAL_OFFSET \
+  (LZMA_PROB_REP_LEN_HIGH_OFFSET + LZMA_PROB_REP_LEN_HIGH_LEN)
+
+#define LZMA_PROB_TOTAL_COUNT \
+  (LZMA_PROB_LITERAL_OFFSET + LZMA_PROB_LITERAL_LEN)
+
+/* Check that the number of LZMA probabilities is the same as the
+   Linux kernel implementation.  */
+
+#if LZMA_PROB_TOTAL_COUNT != 1846 + (1 << 4) * 0x300
+ #error Wrong number of LZMA probabilities
+#endif
+
+/* Expressions for the offset in the LZMA probabilities array of a
+   specific probability.  */
+
+#define LZMA_IS_MATCH(state, pos) \
+  (LZMA_PROB_IS_MATCH_OFFSET + (state) * LZMA_POS_STATES + (pos))
+#define LZMA_IS_REP(state) \
+  (LZMA_PROB_IS_REP_OFFSET + (state))
+#define LZMA_IS_REP0(state) \
+  (LZMA_PROB_IS_REP0_OFFSET + (state))
+#define LZMA_IS_REP1(state) \
+  (LZMA_PROB_IS_REP1_OFFSET + (state))
+#define LZMA_IS_REP2(state) \
+  (LZMA_PROB_IS_REP2_OFFSET + (state))
+#define LZMA_IS_REP0_LONG(state, pos) \
+  (LZMA_PROB_IS_REP0_LONG_OFFSET + (state) * LZMA_POS_STATES + (pos))
+#define LZMA_DIST_SLOT(dist, slot) \
+  (LZMA_PROB_DIST_SLOT_OFFSET + (dist) * LZMA_DIST_SLOTS + (slot))
+#define LZMA_DIST_SPECIAL(dist) \
+  (LZMA_PROB_DIST_SPECIAL_OFFSET + (dist))
+#define LZMA_DIST_ALIGN(dist) \
+  (LZMA_PROB_DIST_ALIGN_OFFSET + (dist))
+#define LZMA_MATCH_LEN_CHOICE \
+  LZMA_PROB_MATCH_LEN_CHOICE_OFFSET
+#define LZMA_MATCH_LEN_CHOICE2 \
+  LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET
+#define LZMA_MATCH_LEN_LOW(pos, sym) \
+  (LZMA_PROB_MATCH_LEN_LOW_OFFSET + (pos) * LZMA_LEN_LOW_SYMBOLS + (sym))
+#define LZMA_MATCH_LEN_MID(pos, sym) \
+  (LZMA_PROB_MATCH_LEN_MID_OFFSET + (pos) * LZMA_LEN_MID_SYMBOLS + (sym))
+#define LZMA_MATCH_LEN_HIGH(sym) \
+  (LZMA_PROB_MATCH_LEN_HIGH_OFFSET + (sym))
+#define LZMA_REP_LEN_CHOICE \
+  LZMA_PROB_REP_LEN_CHOICE_OFFSET
+#define LZMA_REP_LEN_CHOICE2 \
+  LZMA_PROB_REP_LEN_CHOICE2_OFFSET
+#define LZMA_REP_LEN_LOW(pos, sym) \
+  (LZMA_PROB_REP_LEN_LOW_OFFSET + (pos) * LZMA_LEN_LOW_SYMBOLS + (sym))
+#define LZMA_REP_LEN_MID(pos, sym) \
+  (LZMA_PROB_REP_LEN_MID_OFFSET + (pos) * LZMA_LEN_MID_SYMBOLS + (sym))
+#define LZMA_REP_LEN_HIGH(sym) \
+  (LZMA_PROB_REP_LEN_HIGH_OFFSET + (sym))
+#define LZMA_LITERAL(code, size) \
+  (LZMA_PROB_LITERAL_OFFSET + (code) * LZMA_LITERAL_CODER_SIZE + (size))
+
+/* Read an LZMA varint from BUF, reading and updating *POFFSET,
+   setting *VAL.  Returns 0 on error, 1 on success.  */
+
+static int
+elf_lzma_varint (const unsigned char *compressed, size_t compressed_size,
+		 size_t *poffset, uint64_t *val)
+{
+  size_t off;
+  int i;
+  uint64_t v;
+  unsigned char b;
+
+  off = *poffset;
+  i = 0;
+  v = 0;
+  while (1)
+    {
+      if (unlikely (off >= compressed_size))
+	{
+	  elf_uncompress_failed ();
+	  return 0;
+	}
+      b = compressed[off];
+      v |= (b & 0x7f) << (i * 7);
+      ++off;
+      if ((b & 0x80) == 0)
+	{
+	  *poffset = off;
+	  *val = v;
+	  return 1;
+	}
+      ++i;
+      if (unlikely (i >= 9))
+	{
+	  elf_uncompress_failed ();
+	  return 0;
+	}
+    }
+}
+
+/* Normalize the LZMA range decoder, pulling in an extra input byte if
+   needed.  */
+
+static void
+elf_lzma_range_normalize (const unsigned char *compressed,
+			  size_t compressed_size, size_t *poffset,
+			  uint32_t *prange, uint32_t *pcode)
+{
+  if (*prange < (1U << 24))
+    {
+      if (unlikely (*poffset >= compressed_size))
+	{
+	  /* We assume this will be caught elsewhere.  */
+	  elf_uncompress_failed ();
+	  return;
+	}
+      *prange <<= 8;
+      *pcode <<= 8;
+      *pcode += compressed[*poffset];
+      ++*poffset;
+    }
+}
+
+/* Read and return a single bit from the LZMA stream, reading and
+   updating *PROB.  Each bit comes from the range coder.  */
+
+static int
+elf_lzma_bit (const unsigned char *compressed, size_t compressed_size,
+	      uint16_t *prob, size_t *poffset, uint32_t *prange,
+	      uint32_t *pcode)
+{
+  uint32_t bound;
+
+  elf_lzma_range_normalize (compressed, compressed_size, poffset,
+			    prange, pcode);
+  bound = (*prange >> 11) * (uint32_t) *prob;
+  if (*pcode < bound)
+    {
+      *prange = bound;
+      *prob += ((1U << 11) - *prob) >> 5;
+      return 0;
+    }
+  else
+    {
+      *prange -= bound;
+      *pcode -= bound;
+      *prob -= *prob >> 5;
+      return 1;
+    }
+}
+
+/* Read an integer of size BITS from the LZMA stream, most significant
+   bit first.  The bits are predicted using PROBS.  */
+
+static uint32_t
+elf_lzma_integer (const unsigned char *compressed, size_t compressed_size,
+		  uint16_t *probs, uint32_t bits, size_t *poffset,
+		  uint32_t *prange, uint32_t *pcode)
+{
+  uint32_t sym;
+  uint32_t i;
+
+  sym = 1;
+  for (i = 0; i < bits; i++)
+    {
+      int bit;
+
+      bit = elf_lzma_bit (compressed, compressed_size, probs + sym, poffset,
+			  prange, pcode);
+      sym <<= 1;
+      sym += bit;
+    }
+  return sym - (1 << bits);
+}
+
+/* Read an integer of size BITS from the LZMA stream, least
+   significant bit first.  The bits are predicted using PROBS.  */
+
+static uint32_t
+elf_lzma_reverse_integer (const unsigned char *compressed,
+			  size_t compressed_size, uint16_t *probs,
+			  uint32_t bits, size_t *poffset, uint32_t *prange,
+			  uint32_t *pcode)
+{
+  uint32_t sym;
+  uint32_t val;
+  uint32_t i;
+
+  sym = 1;
+  val = 0;
+  for (i = 0; i < bits; i++)
+    {
+      int bit;
+
+      bit = elf_lzma_bit (compressed, compressed_size, probs + sym, poffset,
+			  prange, pcode);
+      sym <<= 1;
+      sym += bit;
+      val += bit << i;
+    }
+  return val;
+}
+
+/* Read a length from the LZMA stream.  IS_REP picks either LZMA_MATCH
+   or LZMA_REP probabilities.  */
+
+static uint32_t
+elf_lzma_len (const unsigned char *compressed, size_t compressed_size,
+	      uint16_t *probs, int is_rep, unsigned int pos_state,
+	      size_t *poffset, uint32_t *prange, uint32_t *pcode)
+{
+  uint16_t *probs_choice;
+  uint16_t *probs_sym;
+  uint32_t bits;
+  uint32_t len;
+
+  probs_choice = probs + (is_rep
+			  ? LZMA_REP_LEN_CHOICE
+			  : LZMA_MATCH_LEN_CHOICE);
+  if (elf_lzma_bit (compressed, compressed_size, probs_choice, poffset,
+		    prange, pcode))
+    {
+      probs_choice = probs + (is_rep
+			      ? LZMA_REP_LEN_CHOICE2
+			      : LZMA_MATCH_LEN_CHOICE2);
+      if (elf_lzma_bit (compressed, compressed_size, probs_choice,
+			poffset, prange, pcode))
+	{
+	  probs_sym = probs + (is_rep
+			       ? LZMA_REP_LEN_HIGH (0)
+			       : LZMA_MATCH_LEN_HIGH (0));
+	  bits = 8;
+	  len = 2 + 8 + 8;
+	}
+      else
+	{
+	  probs_sym = probs + (is_rep
+			       ? LZMA_REP_LEN_MID (pos_state, 0)
+			       : LZMA_MATCH_LEN_MID (pos_state, 0));
+	  bits = 3;
+	  len = 2 + 8;
+	}
+    }
+  else
+    {
+      probs_sym = probs + (is_rep
+			   ? LZMA_REP_LEN_LOW (pos_state, 0)
+			   : LZMA_MATCH_LEN_LOW (pos_state, 0));
+      bits = 3;
+      len = 2;
+    }
+
+  len += elf_lzma_integer (compressed, compressed_size, probs_sym, bits,
+			   poffset, prange, pcode);
+  return len;
+}
+
+/* Uncompress one LZMA block from a minidebug file.  The compressed
+   data is at COMPRESSED + *POFFSET.  Update *POFFSET.  Store the data
+   into the memory at UNCOMPRESSED, size UNCOMPRESSED_SIZE.  CHECK is
+   the stream flag from the xz header.  Return 1 on successful
+   decompression.  */
+
+static int
+elf_uncompress_lzma_block (const unsigned char *compressed,
+			   size_t compressed_size, unsigned char check,
+			   uint16_t *probs, unsigned char *uncompressed,
+			   size_t uncompressed_size, size_t *poffset)
+{
+  size_t off;
+  size_t block_header_offset;
+  size_t block_header_size;
+  unsigned char block_flags;
+  uint64_t header_compressed_size;
+  uint64_t header_uncompressed_size;
+  unsigned char lzma2_properties;
+  uint32_t computed_crc;
+  uint32_t stream_crc;
+  size_t uncompressed_offset;
+  size_t dict_start_offset;
+  unsigned int lc;
+  unsigned int lp;
+  unsigned int pb;
+  uint32_t range;
+  uint32_t code;
+  uint32_t lstate;
+  uint32_t dist[4];
+
+  off = *poffset;
+  block_header_offset = off;
+
+  /* Block header size is a single byte.  */
+  if (unlikely (off >= compressed_size))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  block_header_size = (compressed[off] + 1) * 4;
+  if (unlikely (off + block_header_size > compressed_size))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* Block flags.  */
+  block_flags = compressed[off + 1];
+  if (unlikely ((block_flags & 0x3c) != 0))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  off += 2;
+
+  /* Optional compressed size.  */
+  header_compressed_size = 0;
+  if ((block_flags & 0x40) != 0)
+    {
+      *poffset = off;
+      if (!elf_lzma_varint (compressed, compressed_size, poffset,
+			    &header_compressed_size))
+	return 0;
+      off = *poffset;
+    }
+
+  /* Optional uncompressed size.  */
+  header_uncompressed_size = 0;
+  if ((block_flags & 0x80) != 0)
+    {
+      *poffset = off;
+      if (!elf_lzma_varint (compressed, compressed_size, poffset,
+			    &header_uncompressed_size))
+	return 0;
+      off = *poffset;
+    }
+
+  /* The recipe for creating a minidebug file is to run the xz program
+     with no arguments, so we expect exactly one filter: lzma2.  */
+
+  if (unlikely ((block_flags & 0x3) != 0))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  if (unlikely (off + 2 >= block_header_offset + block_header_size))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* The filter ID for LZMA2 is 0x21.  */
+  if (unlikely (compressed[off] != 0x21))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  ++off;
+
+  /* The size of the filter properties for LZMA2 is 1.  */
+  if (unlikely (compressed[off] != 1))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  ++off;
+
+  lzma2_properties = compressed[off];
+  ++off;
+
+  if (unlikely (lzma2_properties > 40))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* The properties describe the dictionary size, but we don't care
+     what that is.  */
+
+  /* Block header padding.  */
+  if (unlikely (off + 4 > compressed_size))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  off = (off + 3) &~ (size_t) 3;
+
+  if (unlikely (off + 4 > compressed_size))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* Block header CRC.  */
+  computed_crc = elf_crc32 (0, compressed + block_header_offset,
+			    block_header_size - 4);
+  stream_crc = (compressed[off]
+		| (compressed[off + 1] << 8)
+		| (compressed[off + 2] << 16)
+		| (compressed[off + 3] << 24));
+  if (unlikely (computed_crc != stream_crc))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  off += 4;
+
+  /* Read a sequence of LZMA2 packets.  */
+
+  uncompressed_offset = 0;
+  dict_start_offset = 0;
+  lc = 0;
+  lp = 0;
+  pb = 0;
+  lstate = 0;
+  while (off < compressed_size)
+    {
+      unsigned char control;
+
+      range = 0xffffffff;
+      code = 0;
+
+      control = compressed[off];
+      ++off;
+      if (unlikely (control == 0))
+	{
+	  /* End of packets.  */
+	  break;
+	}
+
+      if (control == 1 || control >= 0xe0)
+	{
+	  /* Reset dictionary to empty.  */
+	  dict_start_offset = uncompressed_offset;
+	}
+
+      if (control < 0x80)
+	{
+	  size_t chunk_size;
+
+	  /* The only valid values here are 1 or 2.  A 1 means to
+	     reset the dictionary (done above).  Then we see an
+	     uncompressed chunk.  */
+
+	  if (unlikely (control > 2))
+	    {
+	      elf_uncompress_failed ();
+	      return 0;
+	    }
+
+	  /* An uncompressed chunk is a two byte size followed by
+	     data.  */
+
+	  if (unlikely (off + 2 > compressed_size))
+	    {
+	      elf_uncompress_failed ();
+	      return 0;
+	    }
+
+	  chunk_size = compressed[off] << 8;
+	  chunk_size += compressed[off + 1];
+	  ++chunk_size;
+
+	  off += 2;
+
+	  if (unlikely (off + chunk_size > compressed_size))
+	    {
+	      elf_uncompress_failed ();
+	      return 0;
+	    }
+	  if (unlikely (uncompressed_offset + chunk_size > uncompressed_size))
+	    {
+	      elf_uncompress_failed ();
+	      return 0;
+	    }
+
+	  memcpy (uncompressed + uncompressed_offset, compressed + off,
+		  chunk_size);
+	  uncompressed_offset += chunk_size;
+	  off += chunk_size;
+	}
+      else
+	{
+	  size_t uncompressed_chunk_start;
+	  size_t uncompressed_chunk_size;
+	  size_t compressed_chunk_size;
+	  size_t limit;
+
+	  /* An LZMA chunk.  This starts with an uncompressed size and
+	     a compressed size.  */
+
+	  if (unlikely (off + 4 >= compressed_size))
+	    {
+	      elf_uncompress_failed ();
+	      return 0;
+	    }
+
+	  uncompressed_chunk_start = uncompressed_offset;
+
+	  uncompressed_chunk_size = (control & 0x1f) << 16;
+	  uncompressed_chunk_size += compressed[off] << 8;
+	  uncompressed_chunk_size += compressed[off + 1];
+	  ++uncompressed_chunk_size;
+
+	  compressed_chunk_size = compressed[off + 2] << 8;
+	  compressed_chunk_size += compressed[off + 3];
+	  ++compressed_chunk_size;
+
+	  off += 4;
+
+	  /* Bit 7 (0x80) is set.
+	     Bits 6 and 5 (0x40 and 0x20) are as follows:
+	     0: don't reset anything
+	     1: reset state
+	     2: reset state, read properties
+	     3: reset state, read properties, reset dictionary (done above) */
+
+	  if (control >= 0xc0)
+	    {
+	      unsigned char props;
+
+	      /* Bit 6 is set, read properties.  */
+
+	      if (unlikely (off >= compressed_size))
+		{
+		  elf_uncompress_failed ();
+		  return 0;
+		}
+	      props = compressed[off];
+	      ++off;
+	      if (unlikely (props > (4 * 5 + 4) * 9 + 8))
+		{
+		  elf_uncompress_failed ();
+		  return 0;
+		}
+	      pb = 0;
+	      while (props >= 9 * 5)
+		{
+		  props -= 9 * 5;
+		  ++pb;
+		}
+	      lp = 0;
+	      while (props > 9)
+		{
+		  props -= 9;
+		  ++lp;
+		}
+	      lc = props;
+	      if (unlikely (lc + lp > 4))
+		{
+		  elf_uncompress_failed ();
+		  return 0;
+		}
+	    }
+
+	  if (control >= 0xa0)
+	    {
+	      size_t i;
+
+	      /* Bit 5 or 6 is set, reset LZMA state.  */
+
+	      lstate = 0;
+	      memset (&dist, 0, sizeof dist);
+	      for (i = 0; i < LZMA_PROB_TOTAL_COUNT; i++)
+		probs[i] = 1 << 10;
+	      range = 0xffffffff;
+	      code = 0;
+	    }
+
+	  /* Read the range code.  */
+
+	  if (unlikely (off + 5 > compressed_size))
+	    {
+	      elf_uncompress_failed ();
+	      return 0;
+	    }
+
+	  /* The byte at compressed[off] is ignored for some
+	     reason.  */
+
+	  code = ((compressed[off + 1] << 24)
+		  + (compressed[off + 2] << 16)
+		  + (compressed[off + 3] << 8)
+		  + compressed[off + 4]);
+	  off += 5;
+
+	  /* This is the main LZMA decode loop.  */
+
+	  limit = off + compressed_chunk_size;
+	  *poffset = off;
+	  while (*poffset < limit)
+	    {
+	      unsigned int pos_state;
+
+	      if (unlikely (uncompressed_offset
+			    == (uncompressed_chunk_start
+				+ uncompressed_chunk_size)))
+		{
+		  /* We've decompressed all the expected bytes.  */
+		  break;
+		}
+
+	      pos_state = ((uncompressed_offset - dict_start_offset)
+			   & ((1 << pb) - 1));
+
+	      if (elf_lzma_bit (compressed, compressed_size,
+				probs + LZMA_IS_MATCH (lstate, pos_state),
+				poffset, &range, &code))
+		{
+		  uint32_t len;
+
+		  if (elf_lzma_bit (compressed, compressed_size,
+				    probs + LZMA_IS_REP (lstate),
+				    poffset, &range, &code))
+		    {
+		      int short_rep;
+		      uint32_t next_dist;
+
+		      /* Repeated match.  */
+
+		      short_rep = 0;
+		      if (elf_lzma_bit (compressed, compressed_size,
+					probs + LZMA_IS_REP0 (lstate),
+					poffset, &range, &code))
+			{
+			  if (elf_lzma_bit (compressed, compressed_size,
+					    probs + LZMA_IS_REP1 (lstate),
+					    poffset, &range, &code))
+			    {
+			      if (elf_lzma_bit (compressed, compressed_size,
+						probs + LZMA_IS_REP2 (lstate),
+						poffset, &range, &code))
+				{
+				  next_dist = dist[3];
+				  dist[3] = dist[2];
+				}
+			      else
+				{
+				  next_dist = dist[2];
+				}
+			      dist[2] = dist[1];
+			    }
+			  else
+			    {
+			      next_dist = dist[1];
+			    }
+
+			  dist[1] = dist[0];
+			  dist[0] = next_dist;
+			}
+		      else
+			{
+			  if (!elf_lzma_bit (compressed, compressed_size,
+					    (probs
+					     + LZMA_IS_REP0_LONG (lstate,
+								  pos_state)),
+					    poffset, &range, &code))
+			    short_rep = 1;
+			}
+
+		      if (lstate < 7)
+			lstate = short_rep ? 9 : 8;
+		      else
+			lstate = 11;
+
+		      if (short_rep)
+			len = 1;
+		      else
+			len = elf_lzma_len (compressed, compressed_size,
+					    probs, 1, pos_state, poffset,
+					    &range, &code);
+		    }
+		  else
+		    {
+		      uint32_t dist_state;
+		      uint32_t dist_slot;
+		      uint16_t *probs_dist;
+
+		      /* Match.  */
+
+		      if (lstate < 7)
+			lstate = 7;
+		      else
+			lstate = 10;
+		      dist[3] = dist[2];
+		      dist[2] = dist[1];
+		      dist[1] = dist[0];
+		      len = elf_lzma_len (compressed, compressed_size,
+					  probs, 0, pos_state, poffset,
+					  &range, &code);
+
+		      if (len < 4 + 2)
+			dist_state = len - 2;
+		      else
+			dist_state = 3;
+		      probs_dist = probs + LZMA_DIST_SLOT (dist_state, 0);
+		      dist_slot = elf_lzma_integer (compressed,
+						    compressed_size,
+						    probs_dist, 6,
+						    poffset, &range,
+						    &code);
+		      if (dist_slot < LZMA_DIST_MODEL_START)
+			dist[0] = dist_slot;
+		      else
+			{
+			  uint32_t limit;
+
+			  limit = (dist_slot >> 1) - 1;
+			  dist[0] = 2 + (dist_slot & 1);
+			  if (dist_slot < LZMA_DIST_MODEL_END)
+			    {
+			      dist[0] <<= limit;
+			      probs_dist = (probs
+					    + LZMA_DIST_SPECIAL(dist[0]
+								- dist_slot
+								- 1));
+			      dist[0] +=
+				elf_lzma_reverse_integer (compressed,
+							  compressed_size,
+							  probs_dist,
+							  limit, poffset,
+							  &range, &code);
+			    }
+			  else
+			    {
+			      uint32_t dist0;
+			      uint32_t i;
+
+			      dist0 = dist[0];
+			      for (i = 0; i < limit - 4; i++)
+				{
+				  uint32_t mask;
+
+				  elf_lzma_range_normalize (compressed,
+							    compressed_size,
+							    poffset,
+							    &range, &code);
+				  range >>= 1;
+				  code -= range;
+				  mask = -(code >> 31);
+				  code += range & mask;
+				  dist0 <<= 1;
+				  dist0 += mask + 1;
+				}
+			      dist0 <<= 4;
+			      probs_dist = probs + LZMA_DIST_ALIGN (0);
+			      dist0 +=
+				elf_lzma_reverse_integer (compressed,
+							  compressed_size,
+							  probs_dist, 4,
+							  poffset,
+							  &range, &code);
+			      dist[0] = dist0;
+			    }
+			}
+		    }
+
+		  if (unlikely (uncompressed_offset
+				- dict_start_offset < dist[0] + 1))
+		    {
+		      elf_uncompress_failed ();
+		      return 0;
+		    }
+		  if (unlikely (uncompressed_offset + len > uncompressed_size))
+		    {
+		      elf_uncompress_failed ();
+		      return 0;
+		    }
+
+		  if (dist[0] == 0)
+		    {
+		      /* A common case, meaning repeat the last
+			 character LEN times.  */
+		      memset (uncompressed + uncompressed_offset,
+			      uncompressed[uncompressed_offset - 1],
+			      len);
+		      uncompressed_offset += len;
+		    }
+		  else if (dist[0] + 1 >= len)
+		    {
+		      memcpy (uncompressed + uncompressed_offset,
+			      uncompressed + uncompressed_offset - dist[0] - 1,
+			      len);
+		      uncompressed_offset += len;
+		    }
+		  else
+		    {
+		      while (len > 0)
+			{
+			  uint32_t copy;
+
+			  copy = len < dist[0] + 1 ? len : dist[0] + 1;
+			  memcpy (uncompressed + uncompressed_offset,
+				  (uncompressed + uncompressed_offset
+				   - dist[0] - 1),
+				  copy);
+			  len -= copy;
+			  uncompressed_offset += copy;
+			}
+		    }
+		}
+	      else
+		{
+		  unsigned char prev;
+		  unsigned char low;
+		  size_t high;
+		  uint16_t *lit_probs;
+		  unsigned int sym;
+
+		  /* Literal value.  */
+
+		  if (uncompressed_offset > 0)
+		    prev = uncompressed[uncompressed_offset - 1];
+		  else
+		    prev = 0;
+		  low = prev >> (8 - lc);
+		  high = (((uncompressed_offset - dict_start_offset)
+			   & ((1 << lp) - 1))
+			  << lc);
+		  lit_probs = probs + LZMA_LITERAL (low + high, 0);
+		  if (lstate < 7)
+		    sym = elf_lzma_integer (compressed, compressed_size,
+					    lit_probs, 8, poffset, &range,
+					    &code);
+		  else
+		    {
+		      unsigned int match;
+		      unsigned int bit;
+		      unsigned int match_bit;
+		      unsigned int idx;
+
+		      sym = 1;
+		      if (uncompressed_offset >= dist[0] + 1)
+			match = uncompressed[uncompressed_offset - dist[0] - 1];
+		      else
+			match = 0;
+		      match <<= 1;
+		      bit = 0x100;
+		      do
+			{
+			  match_bit = match & bit;
+			  match <<= 1;
+			  idx = bit + match_bit + sym;
+			  sym <<= 1;
+			  if (elf_lzma_bit (compressed, compressed_size,
+					    lit_probs + idx, poffset,
+					    &range, &code))
+			    {
+			      ++sym;
+			      bit &= match_bit;
+			    }
+			  else
+			    {
+			      bit &= ~ match_bit;
+			    }
+			}
+		      while (sym < 0x100);
+		    }
+
+		  if (unlikely (uncompressed_offset >= uncompressed_size))
+		    {
+		      elf_uncompress_failed ();
+		      return 0;
+		    }
+
+		  uncompressed[uncompressed_offset] = (unsigned char) sym;
+		  ++uncompressed_offset;
+		  if (lstate <= 3)
+		    lstate = 0;
+		  else if (lstate <= 9)
+		    lstate -= 3;
+		  else
+		    lstate -= 6;
+		}
+	    }
+
+	  elf_lzma_range_normalize (compressed, compressed_size, poffset,
+				    &range, &code);
+
+	  off = *poffset;
+	}
+    }
+
+  /* We have reached the end of the block.  Pad to four byte
+     boundary.  */
+  off = (off + 3) &~ (size_t) 3;
+  if (unlikely (off > compressed_size))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  switch (check)
+    {
+    case 0:
+      /* No check.  */
+      break;
+
+    case 1:
+      /* CRC32 */
+      if (unlikely (off + 4 > compressed_size))
+	{
+	  elf_uncompress_failed ();
+	  return 0;
+	}
+      computed_crc = elf_crc32 (0, uncompressed, uncompressed_offset);
+      stream_crc = (compressed[off]
+		    | (compressed[off + 1] << 8)
+		    | (compressed[off + 2] << 16)
+		    | (compressed[off + 3] << 24));
+      if (computed_crc != stream_crc)
+	{
+	  elf_uncompress_failed ();
+	  return 0;
+	}
+      off += 4;
+      break;
+
+    case 4:
+      /* CRC64.  We don't bother computing a CRC64 checksum.  */
+      if (unlikely (off + 8 > compressed_size))
+	{
+	  elf_uncompress_failed ();
+	  return 0;
+	}
+      off += 8;
+      break;
+
+    case 10:
+      /* SHA.  We don't bother computing a SHA checksum.  */
+      if (unlikely (off + 32 > compressed_size))
+	{
+	  elf_uncompress_failed ();
+	  return 0;
+	}
+      off += 32;
+      break;
+
+    default:
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  *poffset = off;
+
+  return 1;
+}
+
+/* Uncompress LZMA data found in a minidebug file.  The minidebug
+   format is described at
+   https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html.
+   Returns 0 on error, 1 on successful decompression.  For this
+   function we return 0 on failure to decompress, as the calling code
+   will carry on in that case.  */
+
+static int
+elf_uncompress_lzma (struct backtrace_state *state,
+		     const unsigned char *compressed, size_t compressed_size,
+		     backtrace_error_callback error_callback, void *data,
+		     unsigned char **uncompressed, size_t *uncompressed_size)
+{
+  size_t header_size;
+  size_t footer_size;
+  unsigned char check;
+  uint32_t computed_crc;
+  uint32_t stream_crc;
+  size_t offset;
+  size_t index_size;
+  size_t footer_offset;
+  size_t index_offset;
+  uint64_t index_compressed_size;
+  uint64_t index_uncompressed_size;
+  unsigned char *mem;
+  uint16_t *probs;
+  size_t compressed_block_size;
+
+  /* The format starts with a stream header and ends with a stream
+     footer.  */
+  header_size = 12;
+  footer_size = 12;
+  if (unlikely (compressed_size < header_size + footer_size))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* The stream header starts with a magic string.  */
+  if (unlikely (memcmp (compressed, "\375" "7zXZ\0", 6) != 0))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* Next come stream flags.  The first byte is zero, the second byte
+     is the check.  */
+  if (unlikely (compressed[6] != 0))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  check = compressed[7];
+  if (unlikely ((check & 0xf8) != 0))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* Next comes a CRC of the stream flags.  */
+  computed_crc = elf_crc32 (0, compressed + 6, 2);
+  stream_crc = (compressed[8]
+		| (compressed[9] << 8)
+		| (compressed[10] << 16)
+		| (compressed[11] << 24));
+  if (unlikely (computed_crc != stream_crc))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* Now that we've parsed the header, parse the footer, so that we
+     can get the uncompressed size.  */
+
+  /* The footer ends with two magic bytes.  */
+
+  offset = compressed_size;
+  if (unlikely (memcmp (compressed + offset - 2, "YZ", 2) != 0))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  offset -= 2;
+
+  /* Before that are the stream flags, which should be the same as the
+     flags in the header.  */
+  if (unlikely (compressed[offset - 2] != 0
+		|| compressed[offset - 1] != check))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  offset -= 2;
+
+  /* Before that is the size of the index field, which precedes the
+     footer.  */
+  index_size = (compressed[offset - 4]
+		| (compressed[offset - 3] << 8)
+		| (compressed[offset - 2] << 16)
+		| (compressed[offset - 1] << 24));
+  index_size = (index_size + 1) * 4;
+  offset -= 4;
+
+  /* Before that is a footer CRC.  */
+  computed_crc = elf_crc32 (0, compressed + offset, 6);
+  stream_crc = (compressed[offset - 4]
+		| (compressed[offset - 3] << 8)
+		| (compressed[offset - 2] << 16)
+		| (compressed[offset - 1] << 24));
+  if (unlikely (computed_crc != stream_crc))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  offset -= 4;
+
+  /* The index comes just before the footer.  */
+  if (unlikely (offset < index_size + header_size))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  footer_offset = offset;
+  offset -= index_size;
+  index_offset = offset;
+
+  /* The index starts with a zero byte.  */
+  if (unlikely (compressed[offset] != 0))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  ++offset;
+
+  /* Next is the number of blocks.  We expect zero blocks for an empty
+     stream, and otherwise a single block.  */
+  if (unlikely (compressed[offset] == 0))
+    {
+      *uncompressed = NULL;
+      *uncompressed_size = 0;
+      return 1;
+    }
+  if (unlikely (compressed[offset] != 1))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  ++offset;
+
+  /* Next is the compressed size and the uncompressed size.  */
+  if (!elf_lzma_varint (compressed, compressed_size, &offset,
+			&index_compressed_size))
+    return 0;
+  if (!elf_lzma_varint (compressed, compressed_size, &offset,
+			&index_uncompressed_size))
+    return 0;
+
+  /* Pad to a four byte boundary.  */
+  offset = (offset + 3) &~ (size_t) 3;
+
+  /* Next is a CRC of the index.  */
+  computed_crc = elf_crc32 (0, compressed + index_offset,
+			    offset - index_offset);
+  stream_crc = (compressed[offset]
+		| (compressed[offset + 1] << 8)
+		| (compressed[offset + 2] << 16)
+		| (compressed[offset + 3] << 24));
+  if (unlikely (computed_crc != stream_crc))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+  offset += 4;
+
+  /* We should now be back at the footer.  */
+  if (unlikely (offset != footer_offset))
+    {
+      elf_uncompress_failed ();
+      return 0;
+    }
+
+  /* Allocate space to hold the uncompressed data.  If we succeed in
+     uncompressing the LZMA data, we never free this memory.  */
+  mem = (unsigned char *) backtrace_alloc (state, index_uncompressed_size,
+					   error_callback, data);
+  if (unlikely (mem == NULL))
+    return 0;
+  *uncompressed = mem;
+  *uncompressed_size = index_uncompressed_size;
+
+  /* Allocate space for probabilities.  */
+  probs = ((uint16_t *)
+	   backtrace_alloc (state,
+			    LZMA_PROB_TOTAL_COUNT * sizeof (uint16_t),
+			    error_callback, data));
+  if (unlikely (probs == NULL))
+    {
+      backtrace_free (state, mem, index_uncompressed_size, error_callback,
+		      data);
+      return 0;
+    }
+
+  /* Uncompress the block, which follows the header.  */
+  offset = 12;
+  if (!elf_uncompress_lzma_block (compressed, compressed_size, check, probs,
+				  mem, index_uncompressed_size, &offset))
+    {
+      backtrace_free (state, mem, index_uncompressed_size, error_callback,
+		      data);
+      return 0;
+    }
+
+  compressed_block_size = offset - 12;
+  if (unlikely (compressed_block_size
+		!= ((index_compressed_size + 3) &~ (size_t) 3)))
+    {
+      elf_uncompress_failed ();
+      backtrace_free (state, mem, index_uncompressed_size, error_callback,
+		      data);
+      return 0;
+    }
+
+  offset = (offset + 3) &~ (size_t) 3;
+  if (unlikely (offset != index_offset))
+    {
+      elf_uncompress_failed ();
+      backtrace_free (state, mem, index_uncompressed_size, error_callback,
+		      data);
+      return 0;
+    }
+
+  return 1;
+}
+
+/* This function is a hook for testing the LZMA support.  It is only
+   used by tests.  */
+
+int
+backtrace_uncompress_lzma (struct backtrace_state *state,
+			   const unsigned char *compressed,
+			   size_t compressed_size,
+			   backtrace_error_callback error_callback,
+			   void *data, unsigned char **uncompressed,
+			   size_t *uncompressed_size)
+{
+  return elf_uncompress_lzma (state, compressed, compressed_size,
+			      error_callback, data, uncompressed,
+			      uncompressed_size);
+}
+
 /* Add the backtrace data for one ELF file.  Returns 1 on success,
    0 on failure (in both cases descriptor is closed) or -1 if exe
    is non-zero and the ELF file is ET_DYN, which tells the caller that
@@ -2616,23 +3968,24 @@ backtrace_uncompress_zdebug (struct backtrace_state *state,
 
 static int
 elf_add (struct backtrace_state *state, const char *filename, int descriptor,
+	 const unsigned char *memory, size_t memory_size,
 	 uintptr_t base_address, backtrace_error_callback error_callback,
 	 void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf,
 	 struct dwarf_data **fileline_entry, int exe, int debuginfo,
 	 const char *with_buildid_data, uint32_t with_buildid_size)
 {
-  struct backtrace_view ehdr_view;
+  struct elf_view ehdr_view;
   b_elf_ehdr ehdr;
   off_t shoff;
   unsigned int shnum;
   unsigned int shstrndx;
-  struct backtrace_view shdrs_view;
+  struct elf_view shdrs_view;
   int shdrs_view_valid;
   const b_elf_shdr *shdrs;
   const b_elf_shdr *shstrhdr;
   size_t shstr_size;
   off_t shstr_off;
-  struct backtrace_view names_view;
+  struct elf_view names_view;
   int names_view_valid;
   const char *names;
   unsigned int symtab_shndx;
@@ -2640,31 +3993,36 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
   unsigned int i;
   struct debug_section_info sections[DEBUG_MAX];
   struct debug_section_info zsections[DEBUG_MAX];
-  struct backtrace_view symtab_view;
+  struct elf_view symtab_view;
   int symtab_view_valid;
-  struct backtrace_view strtab_view;
+  struct elf_view strtab_view;
   int strtab_view_valid;
-  struct backtrace_view buildid_view;
+  struct elf_view buildid_view;
   int buildid_view_valid;
   const char *buildid_data;
   uint32_t buildid_size;
-  struct backtrace_view debuglink_view;
+  struct elf_view debuglink_view;
   int debuglink_view_valid;
   const char *debuglink_name;
   uint32_t debuglink_crc;
-  struct backtrace_view debugaltlink_view;
+  struct elf_view debugaltlink_view;
   int debugaltlink_view_valid;
   const char *debugaltlink_name;
   const char *debugaltlink_buildid_data;
   uint32_t debugaltlink_buildid_size;
+  struct elf_view gnu_debugdata_view;
+  int gnu_debugdata_view_valid;
+  size_t gnu_debugdata_size;
+  unsigned char *gnu_debugdata_uncompressed;
+  size_t gnu_debugdata_uncompressed_size;
   off_t min_offset;
   off_t max_offset;
   off_t debug_size;
-  struct backtrace_view debug_view;
+  struct elf_view debug_view;
   int debug_view_valid;
   unsigned int using_debug_view;
   uint16_t *zdebug_table;
-  struct backtrace_view split_debug_view[DEBUG_MAX];
+  struct elf_view split_debug_view[DEBUG_MAX];
   unsigned char split_debug_view_valid[DEBUG_MAX];
   struct elf_ppc64_opd_data opd_data, *opd;
   struct dwarf_sections dwarf_sections;
@@ -2689,17 +4047,19 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
   debugaltlink_name = NULL;
   debugaltlink_buildid_data = NULL;
   debugaltlink_buildid_size = 0;
+  gnu_debugdata_view_valid = 0;
+  gnu_debugdata_size = 0;
   debug_view_valid = 0;
   memset (&split_debug_view_valid[0], 0, sizeof split_debug_view_valid);
   opd = NULL;
 
-  if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
-			   data, &ehdr_view))
+  if (!elf_get_view (state, descriptor, memory, memory_size, 0, sizeof ehdr,
+		     error_callback, data, &ehdr_view))
     goto fail;
 
-  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
+  memcpy (&ehdr, ehdr_view.view.data, sizeof ehdr);
 
-  backtrace_release_view (state, &ehdr_view, error_callback, data);
+  elf_release_view (state, &ehdr_view, error_callback, data);
 
   if (ehdr.e_ident[EI_MAG0] != ELFMAG0
       || ehdr.e_ident[EI_MAG1] != ELFMAG1
@@ -2747,14 +4107,14 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
   if ((shnum == 0 || shstrndx == SHN_XINDEX)
       && shoff != 0)
     {
-      struct backtrace_view shdr_view;
+      struct elf_view shdr_view;
       const b_elf_shdr *shdr;
 
-      if (!backtrace_get_view (state, descriptor, shoff, sizeof shdr,
-			       error_callback, data, &shdr_view))
+      if (!elf_get_view (state, descriptor, memory, memory_size, shoff,
+			 sizeof shdr, error_callback, data, &shdr_view))
 	goto fail;
 
-      shdr = (const b_elf_shdr *) shdr_view.data;
+      shdr = (const b_elf_shdr *) shdr_view.view.data;
 
       if (shnum == 0)
 	shnum = shdr->sh_size;
@@ -2778,7 +4138,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	    shstrndx -= 0x100;
 	}
 
-      backtrace_release_view (state, &shdr_view, error_callback, data);
+      elf_release_view (state, &shdr_view, error_callback, data);
     }
 
   if (shnum == 0 || shstrndx == 0)
@@ -2789,12 +4149,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 
   /* Read the section headers, skipping the first one.  */
 
-  if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr),
-			   (shnum - 1) * sizeof (b_elf_shdr),
-			   error_callback, data, &shdrs_view))
+  if (!elf_get_view (state, descriptor, memory, memory_size,
+		     shoff + sizeof (b_elf_shdr),
+		     (shnum - 1) * sizeof (b_elf_shdr),
+		     error_callback, data, &shdrs_view))
     goto fail;
   shdrs_view_valid = 1;
-  shdrs = (const b_elf_shdr *) shdrs_view.data;
+  shdrs = (const b_elf_shdr *) shdrs_view.view.data;
 
   /* Read the section names.  */
 
@@ -2802,11 +4163,11 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
   shstr_size = shstrhdr->sh_size;
   shstr_off = shstrhdr->sh_offset;
 
-  if (!backtrace_get_view (state, descriptor, shstr_off, shstrhdr->sh_size,
-			   error_callback, data, &names_view))
+  if (!elf_get_view (state, descriptor, memory, memory_size, shstr_off,
+		     shstrhdr->sh_size, error_callback, data, &names_view))
     goto fail;
   names_view_valid = 1;
-  names = (const char *) names_view.data;
+  names = (const char *) names_view.view.data;
 
   symtab_shndx = 0;
   dynsym_shndx = 0;
@@ -2871,13 +4232,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	{
 	  const b_elf_note *note;
 
-	  if (!backtrace_get_view (state, descriptor, shdr->sh_offset,
-				   shdr->sh_size, error_callback, data,
-				   &buildid_view))
+	  if (!elf_get_view (state, descriptor, memory, memory_size,
+			     shdr->sh_offset, shdr->sh_size, error_callback,
+			     data, &buildid_view))
 	    goto fail;
 
 	  buildid_view_valid = 1;
-	  note = (const b_elf_note *) buildid_view.data;
+	  note = (const b_elf_note *) buildid_view.view.data;
 	  if (note->type == NT_GNU_BUILD_ID
 	      && note->namesz == 4
 	      && strncmp (note->name, "GNU", 4) == 0
@@ -2905,13 +4266,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	  const char *debuglink_data;
 	  size_t crc_offset;
 
-	  if (!backtrace_get_view (state, descriptor, shdr->sh_offset,
-				   shdr->sh_size, error_callback, data,
-				   &debuglink_view))
+	  if (!elf_get_view (state, descriptor, memory, memory_size,
+			     shdr->sh_offset, shdr->sh_size, error_callback,
+			     data, &debuglink_view))
 	    goto fail;
 
 	  debuglink_view_valid = 1;
-	  debuglink_data = (const char *) debuglink_view.data;
+	  debuglink_data = (const char *) debuglink_view.view.data;
 	  crc_offset = strnlen (debuglink_data, shdr->sh_size);
 	  crc_offset = (crc_offset + 3) & ~3;
 	  if (crc_offset + 4 <= shdr->sh_size)
@@ -2927,13 +4288,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	  const char *debugaltlink_data;
 	  size_t debugaltlink_name_len;
 
-	  if (!backtrace_get_view (state, descriptor, shdr->sh_offset,
-				   shdr->sh_size, error_callback, data,
-				   &debugaltlink_view))
+	  if (!elf_get_view (state, descriptor, memory, memory_size,
+			     shdr->sh_offset, shdr->sh_size, error_callback,
+			     data, &debugaltlink_view))
 	    goto fail;
 
 	  debugaltlink_view_valid = 1;
-	  debugaltlink_data = (const char *) debugaltlink_view.data;
+	  debugaltlink_data = (const char *) debugaltlink_view.view.data;
 	  debugaltlink_name = debugaltlink_data;
 	  debugaltlink_name_len = strnlen (debugaltlink_data, shdr->sh_size);
 	  if (debugaltlink_name_len < shdr->sh_size)
@@ -2947,20 +4308,32 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	    }
 	}
 
+      if (!gnu_debugdata_view_valid
+	  && strcmp (name, ".gnu_debugdata") == 0)
+	{
+	  if (!elf_get_view (state, descriptor, memory, memory_size,
+			     shdr->sh_offset, shdr->sh_size, error_callback,
+			     data, &gnu_debugdata_view))
+	    goto fail;
+
+	  gnu_debugdata_size = shdr->sh_size;
+	  gnu_debugdata_view_valid = 1;
+	}
+
       /* Read the .opd section on PowerPC64 ELFv1.  */
       if (ehdr.e_machine == EM_PPC64
 	  && (ehdr.e_flags & EF_PPC64_ABI) < 2
 	  && shdr->sh_type == SHT_PROGBITS
 	  && strcmp (name, ".opd") == 0)
 	{
-	  if (!backtrace_get_view (state, descriptor, shdr->sh_offset,
-				   shdr->sh_size, error_callback, data,
-				   &opd_data.view))
+	  if (!elf_get_view (state, descriptor, memory, memory_size,
+			     shdr->sh_offset, shdr->sh_size, error_callback,
+			     data, &opd_data.view))
 	    goto fail;
 
 	  opd = &opd_data;
 	  opd->addr = shdr->sh_addr;
-	  opd->data = (const char *) opd_data.view.data;
+	  opd->data = (const char *) opd_data.view.view.data;
 	  opd->size = shdr->sh_size;
 	}
     }
@@ -2984,15 +4357,15 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	}
       strtab_shdr = &shdrs[strtab_shndx - 1];
 
-      if (!backtrace_get_view (state, descriptor, symtab_shdr->sh_offset,
-			       symtab_shdr->sh_size, error_callback, data,
-			       &symtab_view))
+      if (!elf_get_view (state, descriptor, memory, memory_size,
+			 symtab_shdr->sh_offset, symtab_shdr->sh_size,
+			 error_callback, data, &symtab_view))
 	goto fail;
       symtab_view_valid = 1;
 
-      if (!backtrace_get_view (state, descriptor, strtab_shdr->sh_offset,
-			       strtab_shdr->sh_size, error_callback, data,
-			       &strtab_view))
+      if (!elf_get_view (state, descriptor, memory, memory_size,
+			 strtab_shdr->sh_offset, strtab_shdr->sh_size,
+			 error_callback, data, &strtab_view))
 	goto fail;
       strtab_view_valid = 1;
 
@@ -3002,8 +4375,8 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	goto fail;
 
       if (!elf_initialize_syminfo (state, base_address,
-				   symtab_view.data, symtab_shdr->sh_size,
-				   strtab_view.data, strtab_shdr->sh_size,
+				   symtab_view.view.data, symtab_shdr->sh_size,
+				   strtab_view.view.data, strtab_shdr->sh_size,
 				   error_callback, data, sdata, opd))
 	{
 	  backtrace_free (state, sdata, sizeof *sdata, error_callback, data);
@@ -3012,7 +4385,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 
       /* We no longer need the symbol table, but we hold on to the
 	 string table permanently.  */
-      backtrace_release_view (state, &symtab_view, error_callback, data);
+      elf_release_view (state, &symtab_view, error_callback, data);
       symtab_view_valid = 0;
       strtab_view_valid = 0;
 
@@ -3021,9 +4394,9 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
       elf_add_syminfo_data (state, sdata);
     }
 
-  backtrace_release_view (state, &shdrs_view, error_callback, data);
+  elf_release_view (state, &shdrs_view, error_callback, data);
   shdrs_view_valid = 0;
-  backtrace_release_view (state, &names_view, error_callback, data);
+  elf_release_view (state, &names_view, error_callback, data);
   names_view_valid = 0;
 
   /* If the debug info is in a separate file, read that one instead.  */
@@ -3038,19 +4411,17 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	{
 	  int ret;
 
-	  backtrace_release_view (state, &buildid_view, error_callback, data);
+	  elf_release_view (state, &buildid_view, error_callback, data);
 	  if (debuglink_view_valid)
-	    backtrace_release_view (state, &debuglink_view, error_callback,
-				    data);
+	    elf_release_view (state, &debuglink_view, error_callback, data);
 	  if (debugaltlink_view_valid)
-	    backtrace_release_view (state, &debugaltlink_view, error_callback,
-				    data);
-	  ret = elf_add (state, "", d, base_address, error_callback, data,
-			 fileline_fn, found_sym, found_dwarf, NULL, 0, 1, NULL,
-			 0);
+	    elf_release_view (state, &debugaltlink_view, error_callback, data);
+	  ret = elf_add (state, "", d, NULL, 0, base_address, error_callback,
+			 data, fileline_fn, found_sym, found_dwarf, NULL, 0,
+			 1, NULL, 0);
 	  if (ret < 0)
 	    backtrace_close (d, error_callback, data);
-	  else
+	  else if (descriptor >= 0)
 	    backtrace_close (descriptor, error_callback, data);
 	  return ret;
 	}
@@ -3058,13 +4429,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 
   if (buildid_view_valid)
     {
-      backtrace_release_view (state, &buildid_view, error_callback, data);
+      elf_release_view (state, &buildid_view, error_callback, data);
       buildid_view_valid = 0;
     }
 
   if (opd)
     {
-      backtrace_release_view (state, &opd->view, error_callback, data);
+      elf_release_view (state, &opd->view, error_callback, data);
       opd = NULL;
     }
 
@@ -3079,17 +4450,15 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	{
 	  int ret;
 
-	  backtrace_release_view (state, &debuglink_view, error_callback,
-				  data);
+	  elf_release_view (state, &debuglink_view, error_callback, data);
 	  if (debugaltlink_view_valid)
-	    backtrace_release_view (state, &debugaltlink_view, error_callback,
-				    data);
-	  ret = elf_add (state, "", d, base_address, error_callback, data,
-			 fileline_fn, found_sym, found_dwarf, NULL, 0, 1, NULL,
-			 0);
+	    elf_release_view (state, &debugaltlink_view, error_callback, data);
+	  ret = elf_add (state, "", d, NULL, 0, base_address, error_callback,
+			 data, fileline_fn, found_sym, found_dwarf, NULL, 0,
+			 1, NULL, 0);
 	  if (ret < 0)
 	    backtrace_close (d, error_callback, data);
-	  else
+	  else if (descriptor >= 0)
 	    backtrace_close(descriptor, error_callback, data);
 	  return ret;
 	}
@@ -3097,7 +4466,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 
   if (debuglink_view_valid)
     {
-      backtrace_release_view (state, &debuglink_view, error_callback, data);
+      elf_release_view (state, &debuglink_view, error_callback, data);
       debuglink_view_valid = 0;
     }
 
@@ -3112,12 +4481,11 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	{
 	  int ret;
 
-	  ret = elf_add (state, filename, d, base_address, error_callback, data,
-			 fileline_fn, found_sym, found_dwarf, &fileline_altlink,
-			 0, 1, debugaltlink_buildid_data,
-			 debugaltlink_buildid_size);
-	  backtrace_release_view (state, &debugaltlink_view, error_callback,
-				  data);
+	  ret = elf_add (state, filename, d, NULL, 0, base_address,
+			 error_callback, data, fileline_fn, found_sym,
+			 found_dwarf, &fileline_altlink, 0, 1,
+			 debugaltlink_buildid_data, debugaltlink_buildid_size);
+	  elf_release_view (state, &debugaltlink_view, error_callback, data);
 	  debugaltlink_view_valid = 0;
 	  if (ret < 0)
 	    {
@@ -3129,10 +4497,36 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 
   if (debugaltlink_view_valid)
     {
-      backtrace_release_view (state, &debugaltlink_view, error_callback, data);
+      elf_release_view (state, &debugaltlink_view, error_callback, data);
       debugaltlink_view_valid = 0;
     }
 
+  if (gnu_debugdata_view_valid)
+    {
+      int ret;
+
+      ret = elf_uncompress_lzma (state,
+				 ((const unsigned char *)
+				  gnu_debugdata_view.view.data),
+				 gnu_debugdata_size, error_callback, data,
+				 &gnu_debugdata_uncompressed,
+				 &gnu_debugdata_uncompressed_size);
+
+      elf_release_view (state, &gnu_debugdata_view, error_callback, data);
+      gnu_debugdata_view_valid = 0;
+
+      if (ret)
+	{
+	  ret = elf_add (state, filename, -1, gnu_debugdata_uncompressed,
+			 gnu_debugdata_uncompressed_size, base_address,
+			 error_callback, data, fileline_fn, found_sym,
+			 found_dwarf, NULL, 0, 0, NULL, 0);
+	  if (ret >= 0 && descriptor >= 0)
+	    backtrace_close(descriptor, error_callback, data);
+	  return ret;
+	}
+    }
+
   /* Read all the debug sections in a single view, since they are
      probably adjacent in the file.  If any of sections are
      uncompressed, we never release this view.  */
@@ -3165,8 +4559,11 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
     }
   if (min_offset == 0 || max_offset == 0)
     {
-      if (!backtrace_close (descriptor, error_callback, data))
-	goto fail;
+      if (descriptor >= 0)
+	{
+	  if (!backtrace_close (descriptor, error_callback, data))
+	    goto fail;
+	}
       return 1;
     }
 
@@ -3176,9 +4573,9 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
   if (max_offset - min_offset < 0x20000000
       || max_offset - min_offset < debug_size + 0x10000)
     {
-      if (!backtrace_get_view (state, descriptor, min_offset,
-			       max_offset - min_offset,
-			       error_callback, data, &debug_view))
+      if (!elf_get_view (state, descriptor, memory, memory_size, min_offset,
+			 max_offset - min_offset, error_callback, data,
+			 &debug_view))
 	goto fail;
       debug_view_valid = 1;
     }
@@ -3196,24 +4593,28 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	  else
 	    continue;
 
-	  if (!backtrace_get_view (state, descriptor, dsec->offset, dsec->size,
-				   error_callback, data, &split_debug_view[i]))
+	  if (!elf_get_view (state, descriptor, memory, memory_size,
+			     dsec->offset, dsec->size, error_callback, data,
+			     &split_debug_view[i]))
 	    goto fail;
 	  split_debug_view_valid[i] = 1;
 
 	  if (sections[i].size != 0)
 	    sections[i].data = ((const unsigned char *)
-				split_debug_view[i].data);
+				split_debug_view[i].view.data);
 	  else
 	    zsections[i].data = ((const unsigned char *)
-				 split_debug_view[i].data);
+				 split_debug_view[i].view.data);
 	}
     }
 
   /* We've read all we need from the executable.  */
-  if (!backtrace_close (descriptor, error_callback, data))
-    goto fail;
-  descriptor = -1;
+  if (descriptor >= 0)
+    {
+      if (!backtrace_close (descriptor, error_callback, data))
+	goto fail;
+      descriptor = -1;
+    }
 
   using_debug_view = 0;
   if (debug_view_valid)
@@ -3224,7 +4625,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	    sections[i].data = NULL;
 	  else
 	    {
-	      sections[i].data = ((const unsigned char *) debug_view.data
+	      sections[i].data = ((const unsigned char *) debug_view.view.data
 				  + (sections[i].offset - min_offset));
 	      ++using_debug_view;
 	    }
@@ -3232,7 +4633,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	  if (zsections[i].size == 0)
 	    zsections[i].data = NULL;
 	  else
-	    zsections[i].data = ((const unsigned char *) debug_view.data
+	    zsections[i].data = ((const unsigned char *) debug_view.view.data
 				 + (zsections[i].offset - min_offset));
 	}
     }
@@ -3269,8 +4670,8 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 
 	  if (split_debug_view_valid[i])
 	    {
-	      backtrace_release_view (state, &split_debug_view[i],
-				      error_callback, data);
+	      elf_release_view (state, &split_debug_view[i],
+				error_callback, data);
 	      split_debug_view_valid[i] = 0;
 	    }
 	}
@@ -3309,8 +4710,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 	--using_debug_view;
       else if (split_debug_view_valid[i])
 	{
-	  backtrace_release_view (state, &split_debug_view[i],
-				  error_callback, data);
+	  elf_release_view (state, &split_debug_view[i], error_callback, data);
 	  split_debug_view_valid[i] = 0;
 	}
     }
@@ -3321,7 +4721,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 
   if (debug_view_valid && using_debug_view == 0)
     {
-      backtrace_release_view (state, &debug_view, error_callback, data);
+      elf_release_view (state, &debug_view, error_callback, data);
       debug_view_valid = 0;
     }
 
@@ -3344,30 +4744,31 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
 
  fail:
   if (shdrs_view_valid)
-    backtrace_release_view (state, &shdrs_view, error_callback, data);
+    elf_release_view (state, &shdrs_view, error_callback, data);
   if (names_view_valid)
-    backtrace_release_view (state, &names_view, error_callback, data);
+    elf_release_view (state, &names_view, error_callback, data);
   if (symtab_view_valid)
-    backtrace_release_view (state, &symtab_view, error_callback, data);
+    elf_release_view (state, &symtab_view, error_callback, data);
   if (strtab_view_valid)
-    backtrace_release_view (state, &strtab_view, error_callback, data);
+    elf_release_view (state, &strtab_view, error_callback, data);
   if (debuglink_view_valid)
-    backtrace_release_view (state, &debuglink_view, error_callback, data);
+    elf_release_view (state, &debuglink_view, error_callback, data);
   if (debugaltlink_view_valid)
-    backtrace_release_view (state, &debugaltlink_view, error_callback, data);
+    elf_release_view (state, &debugaltlink_view, error_callback, data);
+  if (gnu_debugdata_view_valid)
+    elf_release_view (state, &gnu_debugdata_view, error_callback, data);
   if (buildid_view_valid)
-    backtrace_release_view (state, &buildid_view, error_callback, data);
+    elf_release_view (state, &buildid_view, error_callback, data);
   if (debug_view_valid)
-    backtrace_release_view (state, &debug_view, error_callback, data);
+    elf_release_view (state, &debug_view, error_callback, data);
   for (i = 0; i < (int) DEBUG_MAX; ++i)
     {
       if (split_debug_view_valid[i])
-	backtrace_release_view (state, &split_debug_view[i],
-				error_callback, data);
+	elf_release_view (state, &split_debug_view[i], error_callback, data);
     }
   if (opd)
-    backtrace_release_view (state, &opd->view, error_callback, data);
-  if (descriptor != -1)
+    elf_release_view (state, &opd->view, error_callback, data);
+  if (descriptor >= 0)
     backtrace_close (descriptor, error_callback, data);
   return 0;
 }
@@ -3429,7 +4830,7 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
 	return 0;
     }
 
-  if (elf_add (pd->state, filename, descriptor, info->dlpi_addr,
+  if (elf_add (pd->state, filename, descriptor, NULL, 0, info->dlpi_addr,
 	       pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym,
 	       &found_dwarf, NULL, 0, 0, NULL, 0))
     {
@@ -3458,7 +4859,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
   fileline elf_fileline_fn = elf_nodebug;
   struct phdr_data pd;
 
-  ret = elf_add (state, filename, descriptor, 0, error_callback, data,
+  ret = elf_add (state, filename, descriptor, NULL, 0, 0, error_callback, data,
 		 &elf_fileline_fn, &found_sym, &found_dwarf, NULL, 1, 0, NULL,
 		 0);
   if (!ret)
diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h
index be99bb0c84c..09862337456 100644
--- a/libbacktrace/internal.h
+++ b/libbacktrace/internal.h
@@ -335,4 +335,13 @@ extern int backtrace_uncompress_zdebug (struct backtrace_state *,
 					unsigned char **uncompressed,
 					size_t *uncompressed_size);
 
+/* A test-only hook for elf_uncompress_lzma.  */
+
+extern int backtrace_uncompress_lzma (struct backtrace_state *,
+				      const unsigned char *compressed,
+				      size_t compressed_size,
+				      backtrace_error_callback, void *data,
+				      unsigned char **uncompressed,
+				      size_t *uncompressed_size);
+
 #endif
diff --git a/libbacktrace/mtest.c b/libbacktrace/mtest.c
new file mode 100644
index 00000000000..d90fd1e33cc
--- /dev/null
+++ b/libbacktrace/mtest.c
@@ -0,0 +1,401 @@
+/* mtest.c -- Minidebug test for libbacktrace library
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+/* This program tests using libbacktrace with a program that uses the
+   minidebuginfo format in a .gnu_debugdata section.  See
+   https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html
+   for a bit more information about minidebuginfo.  What is relevant
+   for libbacktrace is that we have just a symbol table, with no debug
+   info, so we should be able to do a function backtrace, but we can't
+   do a file/line backtrace.  */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+
+#include "testlib.h"
+
+static int test1 (void) __attribute__ ((noinline, noclone, unused));
+static int f2 (int) __attribute__ ((noinline, noclone));
+static int f3 (int, int) __attribute__ ((noinline, noclone));
+
+/* Collected PC values.  */
+
+static uintptr_t addrs[20];
+
+/* The backtrace callback function.  This is like callback_one in
+   testlib.c, but it saves the PC also.  */
+
+static int
+callback_mtest (void *vdata, uintptr_t pc, const char *filename, int lineno,
+		const char *function)
+{
+  struct bdata *data = (struct bdata *) vdata;
+
+  if (data->index >= sizeof addrs / sizeof addrs[0])
+    {
+      fprintf (stderr, "callback_mtest: callback called too many times\n");
+      data->failed = 1;
+      return 1;
+    }
+
+  addrs[data->index] = pc;
+
+  return callback_one (vdata, pc, filename, lineno, function);
+}
+
+/* Test the backtrace function with non-inlined functions.  (We don't
+   test with inlined functions because they won't work with minidebug
+   anyhow.)  */
+
+static int
+test1 (void)
+{
+  /* Returning a value here and elsewhere avoids a tailcall which
+     would mess up the backtrace.  */
+  return f2 (__LINE__) + 1;
+}
+
+static int
+f2 (int f1line)
+{
+  return f3 (f1line, __LINE__) + 2;
+}
+
+static int
+f3 (int f1line __attribute__ ((unused)), int f2line __attribute__ ((unused)))
+{
+  struct info all[20];
+  struct bdata data;
+  int i;
+  size_t j;
+
+  data.all = &all[0];
+  data.index = 0;
+  data.max = 20;
+  data.failed = 0;
+
+  i = backtrace_full (state, 0, callback_mtest, error_callback_one, &data);
+
+  if (i != 0)
+    {
+      fprintf (stderr, "test1: unexpected return value %d\n", i);
+      data.failed = 1;
+    }
+
+  if (data.index < 3)
+    {
+      fprintf (stderr,
+	       "test1: not enough frames; got %zu, expected at least 3\n",
+	       data.index);
+      data.failed = 1;
+    }
+
+  /* When using minidebug we don't expect the function name here.  */
+
+  for (j = 0; j < 3 && j < data.index; j++)
+    {
+      if (all[j].function == NULL)
+	{
+	  struct symdata symdata;
+
+	  symdata.name = NULL;
+	  symdata.val = 0;
+	  symdata.size = 0;
+	  symdata.failed = 0;
+
+	  i = backtrace_syminfo (state, addrs[j], callback_three,
+				 error_callback_three, &symdata);
+	  if (i == 0)
+	    {
+	      fprintf (stderr,
+		       ("test1: [%zu], unexpected return value from "
+			"backtrace_syminfo %d\n"),
+		       j, i);
+	      data.failed = 1;
+	    }
+	  else if (symdata.name == NULL)
+	    {
+	      fprintf (stderr, "test1: [%zu]: syminfo did not find name\n", j);
+	      data.failed = 1;
+	    }
+	  else
+	    all[j].function = strdup (symdata.name);
+	}
+    }
+
+  if (all[0].function == NULL)
+    {
+      fprintf (stderr, "test1: [0]: missing function name\n");
+      data.failed = 1;
+    }
+  else if (strcmp (all[0].function, "f3") != 0)
+    {
+      fprintf (stderr, "test1: [0]: got %s expected %s\n",
+	       all[0].function, "f3");
+      data.failed = 1;
+    }
+
+  if (all[1].function == NULL)
+    {
+      fprintf (stderr, "test1: [1]: missing function name\n");
+      data.failed = 1;
+    }
+  else if (strcmp (all[1].function, "f2") != 0)
+    {
+      fprintf (stderr, "test1: [1]: got %s expected %s\n",
+	       all[0].function, "f2");
+      data.failed = 1;
+    }
+
+  if (all[2].function == NULL)
+    {
+      fprintf (stderr, "test1: [2]: missing function name\n");
+      data.failed = 1;
+    }
+  else if (strcmp (all[2].function, "test1") != 0)
+    {
+      fprintf (stderr, "test1: [2]: got %s expected %s\n",
+	       all[0].function, "test1");
+      data.failed = 1;
+    }
+
+  printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");
+
+  if (data.failed)
+    ++failures;
+
+  return failures;
+}
+
+/* Test the backtrace_simple function with non-inlined functions.  */
+
+static int test3 (void) __attribute__ ((noinline, noclone, unused));
+static int f22 (int) __attribute__ ((noinline, noclone));
+static int f23 (int, int) __attribute__ ((noinline, noclone));
+
+static int
+test3 (void)
+{
+  return f22 (__LINE__) + 1;
+}
+
+static int
+f22 (int f1line)
+{
+  return f23 (f1line, __LINE__) + 2;
+}
+
+static int
+f23 (int f1line __attribute__ ((unused)), int f2line __attribute__ ((unused)))
+{
+  uintptr_t addrs[20];
+  struct sdata data;
+  int i;
+
+  data.addrs = &addrs[0];
+  data.index = 0;
+  data.max = 20;
+  data.failed = 0;
+
+  i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
+
+  if (i != 0)
+    {
+      fprintf (stderr, "test3: unexpected return value %d\n", i);
+      data.failed = 1;
+    }
+
+  if (!data.failed)
+    {
+      int j;
+
+      for (j = 0; j < 3; ++j)
+	{
+	  struct symdata symdata;
+
+	  symdata.name = NULL;
+	  symdata.val = 0;
+	  symdata.size = 0;
+	  symdata.failed = 0;
+
+	  i = backtrace_syminfo (state, addrs[j], callback_three,
+				 error_callback_three, &symdata);
+	  if (i == 0)
+	    {
+	      fprintf (stderr,
+		       ("test3: [%d]: unexpected return value "
+			"from backtrace_syminfo %d\n"),
+		       j, i);
+	      symdata.failed = 1;
+	    }
+
+	  if (!symdata.failed)
+	    {
+	      const char *expected;
+
+	      switch (j)
+		{
+		case 0:
+		  expected = "f23";
+		  break;
+		case 1:
+		  expected = "f22";
+		  break;
+		case 2:
+		  expected = "test3";
+		  break;
+		default:
+		  assert (0);
+		}
+
+	      if (symdata.name == NULL)
+		{
+		  fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j);
+		  symdata.failed = 1;
+		}
+	      /* Use strncmp, not strcmp, because GCC might create a
+		 clone.  */
+	      else if (strncmp (symdata.name, expected, strlen (expected))
+		       != 0)
+		{
+		  fprintf (stderr,
+			   ("test3: [%d]: unexpected syminfo name "
+			    "got %s expected %s\n"),
+			   j, symdata.name, expected);
+		  symdata.failed = 1;
+		}
+	    }
+
+	  if (symdata.failed)
+	    data.failed = 1;
+	}
+    }
+
+  printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS");
+
+  if (data.failed)
+    ++failures;
+
+  return failures;
+}
+
+int test5 (void) __attribute__ ((unused));
+
+int global = 1;
+
+int
+test5 (void)
+{
+  struct symdata symdata;
+  int i;
+  uintptr_t addr = (uintptr_t) &global;
+
+  if (sizeof (global) > 1)
+    addr += 1;
+
+  symdata.name = NULL;
+  symdata.val = 0;
+  symdata.size = 0;
+  symdata.failed = 0;
+
+  i = backtrace_syminfo (state, addr, callback_three,
+			 error_callback_three, &symdata);
+  if (i == 0)
+    {
+      fprintf (stderr,
+	       "test5: unexpected return value from backtrace_syminfo %d\n",
+	       i);
+      symdata.failed = 1;
+    }
+
+  if (!symdata.failed)
+    {
+      if (symdata.name == NULL)
+	{
+	  fprintf (stderr, "test5: NULL syminfo name\n");
+	  symdata.failed = 1;
+	}
+      else if (!(strncmp (symdata.name, "global", 6) == 0
+		 && (symdata.name[6] == '\0'|| symdata.name[6] == '.')))
+	{
+	  fprintf (stderr,
+		   "test5: unexpected syminfo name got %s expected %s\n",
+		   symdata.name, "global");
+	  symdata.failed = 1;
+	}
+      else if (symdata.val != (uintptr_t) &global)
+	{
+	  fprintf (stderr,
+		   "test5: unexpected syminfo value got %lx expected %lx\n",
+		   (unsigned long) symdata.val,
+		   (unsigned long) (uintptr_t) &global);
+	  symdata.failed = 1;
+	}
+      else if (symdata.size != sizeof (global))
+	{
+	  fprintf (stderr,
+		   "test5: unexpected syminfo size got %lx expected %lx\n",
+		   (unsigned long) symdata.size,
+		   (unsigned long) sizeof (global));
+	  symdata.failed = 1;
+	}
+    }
+
+  printf ("%s: backtrace_syminfo variable\n",
+	  symdata.failed ? "FAIL" : "PASS");
+
+  if (symdata.failed)
+    ++failures;
+
+  return failures;
+}
+
+int
+main (int argc ATTRIBUTE_UNUSED, char **argv)
+{
+  state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS,
+				  error_callback_create, NULL);
+
+#if BACKTRACE_SUPPORTED
+  test1 ();
+  test3 ();
+#if BACKTRACE_SUPPORTS_DATA
+  test5 ();
+#endif
+#endif
+
+  exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/libbacktrace/xztest.c b/libbacktrace/xztest.c
new file mode 100644
index 00000000000..4fffe99fcf0
--- /dev/null
+++ b/libbacktrace/xztest.c
@@ -0,0 +1,508 @@
+/* xztest.c -- Test for libbacktrace LZMA decoder.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_LIBLZMA
+#include <lzma.h>
+#endif
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+
+#include "internal.h"
+#include "testlib.h"
+
+#ifndef HAVE_CLOCK_GETTIME
+
+typedef int xclockid_t;
+
+static int
+xclock_gettime (xclockid_t id ATTRIBUTE_UNUSED,
+		struct timespec *ts ATTRIBUTE_UNUSED)
+{
+  errno = EINVAL;
+  return -1;
+}
+
+#define clockid_t xclockid_t
+#define clock_gettime xclock_gettime
+#undef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+
+#endif /* !defined(HAVE_CLOCK_GETTIME) */
+
+#ifdef CLOCK_PROCESS_CPUTIME_ID
+#define LIBLZMA_CLOCK_GETTIME_ARG CLOCK_PROCESS_CPUTIME_ID
+#else
+#define LIBLZMA_CLOCK_GETTIME_ARG CLOCK_REALTIME
+#endif
+
+/* Some tests for the local lzma inflation code.  */
+
+struct lzma_test
+{
+  const char *name;
+  const char *uncompressed;
+  size_t uncompressed_len;
+  const char *compressed;
+  size_t compressed_len;
+};
+
+/* Error callback.  */
+
+static void
+error_callback_compress (void *vdata ATTRIBUTE_UNUSED, const char *msg,
+			 int errnum)
+{
+  fprintf (stderr, "%s", msg);
+  if (errnum > 0)
+    fprintf (stderr, ": %s", strerror (errnum));
+  fprintf (stderr, "\n");
+  exit (EXIT_FAILURE);
+}
+
+static const struct lzma_test tests[] =
+{
+  {
+    "empty",
+    "",
+    0,
+    ("\xfd\x37\x7a\x58\x5a\x00\x00\x04\xe6\xd6\xb4\x46\x00\x00\x00\x00"
+     "\x1c\xdf\x44\x21\x1f\xb6\xf3\x7d\x01\x00\x00\x00\x00\x04\x59\x5a"),
+    32,
+  },
+  {
+    "hello",
+    "hello, world\n",
+    0,
+    ("\xfd\x37\x7a\x58\x5a\x00\x00\x04\xe6\xd6\xb4\x46\x02\x00\x21\x01"
+     "\x16\x00\x00\x00\x74\x2f\xe5\xa3\x01\x00\x0c\x68\x65\x6c\x6c\x6f"
+     "\x2c\x20\x77\x6f\x72\x6c\x64\x0a\x00\x00\x00\x00\x7b\x46\x5a\x81"
+     "\xc9\x12\xb8\xea\x00\x01\x25\x0d\x71\x19\xc4\xb6\x1f\xb6\xf3\x7d"
+     "\x01\x00\x00\x00\x00\x04\x59\x5a"),
+    72,
+  },
+  {
+    "goodbye",
+    "goodbye, world",
+    0,
+    ("\xfd\x37\x7a\x58\x5a\x00\x00\x04\xe6\xd6\xb4\x46\x02\x00\x21\x01"
+     "\x16\x00\x00\x00\x74\x2f\xe5\xa3\x01\x00\x0d\x67\x6f\x6f\x64\x62"
+     "\x79\x65\x2c\x20\x77\x6f\x72\x6c\x64\x00\x00\x00\xf6\xf8\xa3\x33"
+     "\x8c\x4e\xc9\x68\x00\x01\x26\x0e\x08\x1b\xe0\x04\x1f\xb6\xf3\x7d"
+     "\x01\x00\x00\x00\x00\x04\x59\x5a"),
+    72,
+  },
+};
+
+/* Test the hand coded samples.  */
+
+static void
+test_samples (struct backtrace_state *state)
+{
+  size_t i;
+
+  for (i = 0; i < sizeof tests / sizeof tests[0]; ++i)
+    {
+      unsigned char *uncompressed;
+      size_t uncompressed_len;
+
+      uncompressed = NULL;
+      uncompressed_len = 0;
+      if (!backtrace_uncompress_lzma (state,
+				      ((const unsigned char *)
+				       tests[i].compressed),
+				      tests[i].compressed_len,
+				      error_callback_compress, NULL,
+				      &uncompressed, &uncompressed_len))
+	{
+	  fprintf (stderr, "test %s: uncompress failed\n", tests[i].name);
+	  ++failures;
+	}
+      else
+	{
+	  size_t v;
+
+	  v = tests[i].uncompressed_len;
+	  if (v == 0)
+	    v = strlen (tests[i].uncompressed);
+	  if (uncompressed_len != v)
+	    {
+	      fprintf (stderr,
+		       "test %s: got uncompressed length %zu, want %zu\n",
+		       tests[i].name, uncompressed_len, v);
+	      ++failures;
+	    }
+	  else if (memcmp (tests[i].uncompressed, uncompressed, v) != 0)
+	    {
+	      size_t j;
+
+	      fprintf (stderr, "test %s: uncompressed data mismatch\n",
+		       tests[i].name);
+	      for (j = 0; j < v; ++j)
+		if (tests[i].uncompressed[j] != uncompressed[j])
+		  fprintf (stderr, "  %zu: got %#x want %#x\n", j,
+			   uncompressed[j], tests[i].uncompressed[j]);
+	      ++failures;
+	    }
+	  else
+	    printf ("PASS: lzma %s\n", tests[i].name);
+
+	  backtrace_free (state, uncompressed, uncompressed_len,
+			  error_callback_compress, NULL);
+	}
+    }
+}
+
+#if HAVE_LIBLZMA
+
+/* Given a set of TRIALS timings, discard the lowest and highest
+   values and return the mean average of the rest.  */
+
+static size_t
+average_time (const size_t *times, size_t trials)
+{
+  size_t imax;
+  size_t max;
+  size_t imin;
+  size_t min;
+  size_t i;
+  size_t sum;
+
+  imin = 0;
+  imax = 0;
+  min = times[0];
+  max = times[0];
+  for (i = 1; i < trials; ++i)
+    {
+      if (times[i] < min)
+	{
+	  imin = i;
+	  min = times[i];
+	}
+      if (times[i] > max)
+	{
+	  imax = i;
+	  max = times[i];
+	}
+    }
+
+  sum = 0;
+  for (i = 0; i < trials; ++i)
+    {
+      if (i != imax && i != imin)
+	sum += times[i];
+    }
+  return sum / (trials - 2);
+}
+
+#endif
+
+/* Test a larger text, if available.  */
+
+static void
+test_large (struct backtrace_state *state ATTRIBUTE_UNUSED)
+{
+#if HAVE_LIBLZMA
+  unsigned char *orig_buf;
+  size_t orig_bufsize;
+  size_t i;
+  lzma_stream initial_stream = LZMA_STREAM_INIT;
+  lzma_stream stream;
+  unsigned char *compressed_buf;
+  size_t compressed_bufsize;
+  unsigned char *uncompressed_buf;
+  size_t uncompressed_bufsize;
+  unsigned char *spare_buf;
+  int r;
+  clockid_t cid;
+  struct timespec ts1;
+  struct timespec ts2;
+  size_t ctime;
+  size_t ztime;
+  const size_t trials = 16;
+  size_t ctimes[16];
+  size_t ztimes[16];
+  static const char * const names[] = {
+    "Isaac.Newton-Opticks.txt",
+    "../libgo/go/testdata/Isaac.Newton-Opticks.txt",
+  };
+
+  orig_buf = NULL;
+  orig_bufsize = 0;
+  uncompressed_buf = NULL;
+  compressed_buf = NULL;
+
+  for (i = 0; i < sizeof names / sizeof names[0]; ++i)
+    {
+      size_t len;
+      char *namebuf;
+      FILE *e;
+      struct stat st;
+      char *rbuf;
+      size_t got;
+
+      len = strlen (SRCDIR) + strlen (names[i]) + 2;
+      namebuf = malloc (len);
+      if (namebuf == NULL)
+	{
+	  perror ("malloc");
+	  goto fail;
+	}
+      snprintf (namebuf, len, "%s/%s", SRCDIR, names[i]);
+      e = fopen (namebuf, "r");
+      free (namebuf);
+      if (e == NULL)
+	continue;
+      if (fstat (fileno (e), &st) < 0)
+	{
+	  perror ("fstat");
+	  fclose (e);
+	  continue;
+	}
+      rbuf = malloc (st.st_size);
+      if (rbuf == NULL)
+	{
+	  perror ("malloc");
+	  goto fail;
+	}
+      got = fread (rbuf, 1, st.st_size, e);
+      fclose (e);
+      if (got > 0)
+	{
+	  orig_buf = (unsigned char *) rbuf;
+	  orig_bufsize = got;
+	  break;
+	}
+      free (rbuf);
+    }
+
+  if (orig_buf == NULL)
+    {
+      /* We couldn't find an input file.  */
+      printf ("UNSUPPORTED: lzma large\n");
+      return;
+    }
+
+  stream = initial_stream;
+  r =  lzma_easy_encoder (&stream, 6, LZMA_CHECK_CRC32);
+  if (r != LZMA_OK)
+    {
+      fprintf (stderr, "lzma_easy_encoder failed: %d\n", r);
+      goto fail;
+    }
+
+  compressed_bufsize = orig_bufsize + 100;
+  compressed_buf = malloc (compressed_bufsize);
+  if (compressed_buf == NULL)
+    {
+      perror ("malloc");
+      goto fail;
+    }
+
+  stream.next_in = orig_buf;
+  stream.avail_in = orig_bufsize;
+  stream.next_out = compressed_buf;
+  stream.avail_out = compressed_bufsize;
+
+  do
+    {
+      r = lzma_code (&stream, LZMA_FINISH);
+      if (r != LZMA_OK && r != LZMA_STREAM_END)
+	{
+	  fprintf (stderr, "lzma_code failed: %d\n", r);
+	  goto fail;
+	}
+    }
+  while (r != LZMA_STREAM_END);
+
+  compressed_bufsize = stream.total_out;
+
+  if (!backtrace_uncompress_lzma (state, (unsigned char *) compressed_buf,
+				  compressed_bufsize,
+				  error_callback_compress, NULL,
+				  &uncompressed_buf, &uncompressed_bufsize))
+    {
+      fprintf (stderr, "lzma large: backtrace_uncompress_lzma failed\n");
+      goto fail;
+    }
+
+  if (uncompressed_bufsize != orig_bufsize)
+    {
+      fprintf (stderr,
+	       "lzma large: got uncompressed length %zu, want %zu\n",
+	       uncompressed_bufsize, orig_bufsize);
+      goto fail;
+    }
+
+  if (memcmp (uncompressed_buf, orig_buf, uncompressed_bufsize) != 0)
+    {
+      fprintf (stderr, "lzma large: uncompressed data mismatch\n");
+      goto fail;
+    }
+
+  printf ("PASS: lzma large\n");
+
+  spare_buf = malloc (orig_bufsize);
+  if (spare_buf == NULL)
+    {
+      perror ("malloc");
+      goto fail;
+    }
+
+  for (i = 0; i < trials; ++i)
+    {
+      cid = LIBLZMA_CLOCK_GETTIME_ARG;
+      if (clock_gettime (cid, &ts1) < 0)
+	{
+	  if (errno == EINVAL)
+	    return;
+	  perror ("clock_gettime");
+	  return;
+	}
+
+      if (!backtrace_uncompress_lzma (state,
+				      (unsigned char *) compressed_buf,
+				      compressed_bufsize,
+				      error_callback_compress, NULL,
+				      &uncompressed_buf,
+				      &uncompressed_bufsize))
+	{
+	  fprintf (stderr,
+		   ("lzma large: "
+		    "benchmark backtrace_uncompress_lzma failed\n"));
+	  return;
+	}
+
+      if (clock_gettime (cid, &ts2) < 0)
+	{
+	  perror ("clock_gettime");
+	  return;
+	}
+
+      ctime = (ts2.tv_sec - ts1.tv_sec) * 1000000000;
+      ctime += ts2.tv_nsec - ts1.tv_nsec;
+      ctimes[i] = ctime;
+
+      stream = initial_stream;
+
+      r = lzma_auto_decoder (&stream, UINT64_MAX, 0);
+      if (r != LZMA_OK)
+	{
+	  fprintf (stderr, "lzma_stream_decoder failed: %d\n", r);
+	  goto fail;
+	}
+
+      stream.next_in = compressed_buf;
+      stream.avail_in = compressed_bufsize;
+      stream.next_out = spare_buf;
+      stream.avail_out = orig_bufsize;
+
+      if (clock_gettime (cid, &ts1) < 0)
+	{
+	  perror("clock_gettime");
+	  return;
+	}
+
+      do
+	{
+	  r = lzma_code (&stream, LZMA_FINISH);
+	  if (r != LZMA_OK && r != LZMA_STREAM_END)
+	    {
+	      fprintf (stderr, "lzma_code failed: %d\n", r);
+	      goto fail;
+	    }
+	}
+      while (r != LZMA_STREAM_END);
+
+      if (clock_gettime (cid, &ts2) < 0)
+	{
+	  perror ("clock_gettime");
+	  return;
+	}
+
+      ztime = (ts2.tv_sec - ts1.tv_sec) * 1000000000;
+      ztime += ts2.tv_nsec - ts1.tv_nsec;
+      ztimes[i] = ztime;
+    }
+
+  /* Toss the highest and lowest times and average the rest.  */
+  ctime = average_time (ctimes, trials);
+  ztime = average_time (ztimes, trials);
+
+  printf ("backtrace: %zu ns\n", ctime);
+  printf ("liblzma  : %zu ns\n", ztime);
+  printf ("ratio    : %g\n", (double) ztime / (double) ctime);
+
+  return;
+
+ fail:
+  printf ("FAIL: lzma large\n");
+  ++failures;
+
+  if (orig_buf != NULL)
+    free (orig_buf);
+  if (compressed_buf != NULL)
+    free (compressed_buf);
+  if (uncompressed_buf != NULL)
+    free (uncompressed_buf);
+
+#else /* !HAVE_LIBLZMA */
+
+ printf ("UNSUPPORTED: lzma large\n");
+
+#endif /* !HAVE_LIBLZMA */
+}
+
+int
+main (int argc ATTRIBUTE_UNUSED, char **argv)
+{
+  struct backtrace_state *state;
+
+  state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS,
+				  error_callback_create, NULL);
+
+  test_samples (state);
+  test_large (state);
+
+  exit (failures != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: libbacktrace: Add support for MiniDebugInfo
  2020-09-14 21:12 libbacktrace: Add support for MiniDebugInfo Ian Lance Taylor
@ 2020-09-16 11:03 ` Alex Coplan
  2020-09-16 14:26   ` Ian Lance Taylor
  0 siblings, 1 reply; 5+ messages in thread
From: Alex Coplan @ 2020-09-16 11:03 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: gcc-patches

Hi Ian,

On 14/09/2020 14:12, Ian Lance Taylor via Gcc-patches wrote:
> This patch to libbacktrace adds support for MiniDebugInfo, as
> requested in PR 93608.

This appears to introduce a failure in the libbacktrace testsuite
(observed on both x86 and aarch64):

../gcc/libbacktrace/../test-driver: line 107:  7905 Segmentation fault      (core dumped) "$@" > $log_file 2>&1
FAIL: mtest_minidebug

> 
> MiniDebugInfo stores compressed symbol tables for an executable, where
> the executable is otherwise stripped.  It is documented at
> https://fedoraproject.org/wiki/Features/MiniDebugInfo and
> https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html.
> 
> Unfortunately, although debug info processors are already required to
> handle data compressed using zlib aka gzip in order to handle zdebug
> and SHT_COMPRESSED, the MiniDebugInfo implementation choose to instead
> compress the debug information with xz which uses LZMA2.  This in
> effect forces all debug info processors to add a second decompression
> algorithm.  Note to future developers: do not do this.
> 
> This libbacktrace implementation includes a new small LZMA
> decompressor that just decompresses from one buffer to another.  It is
> not heavily optimized, and on my laptop runs at about 78% of the speed
> of using liblzma directly.  I think this is a decent tradeoff for
> keeping the code reasonably small.
> 
> Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
> to mainline.
> 
> Ian

Thanks,
Alex

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: libbacktrace: Add support for MiniDebugInfo
  2020-09-16 11:03 ` Alex Coplan
@ 2020-09-16 14:26   ` Ian Lance Taylor
  2020-09-16 15:54     ` Alex Coplan
  0 siblings, 1 reply; 5+ messages in thread
From: Ian Lance Taylor @ 2020-09-16 14:26 UTC (permalink / raw)
  To: Alex Coplan; +Cc: gcc-patches

On Wed, Sep 16, 2020, 4:03 AM Alex Coplan <alex.coplan@arm.com> wrote:

> Hi Ian,
>
> On 14/09/2020 14:12, Ian Lance Taylor via Gcc-patches wrote:
> > This patch to libbacktrace adds support for MiniDebugInfo, as
> > requested in PR 93608.
>
> This appears to introduce a failure in the libbacktrace testsuite
> (observed on both x86 and aarch64):
>
> ../gcc/libbacktrace/../test-driver: line 107:  7905 Segmentation fault
>   (core dumped) "$@" > $log_file 2>&1
> FAIL: mtest_minidebug



I tested on x86 without seeing anything like this.  Can you give me any
more details?  Thanks.

Ian




> > MiniDebugInfo stores compressed symbol tables for an executable, where
> > the executable is otherwise stripped.  It is documented at
> > https://fedoraproject.org/wiki/Features/MiniDebugInfo and
> > https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html.
> >
> > Unfortunately, although debug info processors are already required to
> > handle data compressed using zlib aka gzip in order to handle zdebug
> > and SHT_COMPRESSED, the MiniDebugInfo implementation choose to instead
> > compress the debug information with xz which uses LZMA2.  This in
> > effect forces all debug info processors to add a second decompression
> > algorithm.  Note to future developers: do not do this.
> >
> > This libbacktrace implementation includes a new small LZMA
> > decompressor that just decompresses from one buffer to another.  It is
> > not heavily optimized, and on my laptop runs at about 78% of the speed
> > of using liblzma directly.  I think this is a decent tradeoff for
> > keeping the code reasonably small.
> >
> > Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
> > to mainline.
> >
> > Ian
>
> Thanks,
> Alex
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: libbacktrace: Add support for MiniDebugInfo
  2020-09-16 14:26   ` Ian Lance Taylor
@ 2020-09-16 15:54     ` Alex Coplan
  2020-09-17  0:04       ` Ian Lance Taylor
  0 siblings, 1 reply; 5+ messages in thread
From: Alex Coplan @ 2020-09-16 15:54 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: gcc-patches

On 16/09/2020 07:26, Ian Lance Taylor wrote:
> On Wed, Sep 16, 2020, 4:03 AM Alex Coplan <alex.coplan@arm.com> wrote:
> 
> > Hi Ian,
> >
> > On 14/09/2020 14:12, Ian Lance Taylor via Gcc-patches wrote:
> > > This patch to libbacktrace adds support for MiniDebugInfo, as
> > > requested in PR 93608.
> >
> > This appears to introduce a failure in the libbacktrace testsuite
> > (observed on both x86 and aarch64):
> >
> > ../gcc/libbacktrace/../test-driver: line 107:  7905 Segmentation fault
> >   (core dumped) "$@" > $log_file 2>&1
> > FAIL: mtest_minidebug
> 
> 
> 
> I tested on x86 without seeing anything like this.  Can you give me any
> more details?  Thanks.

Sure. On an Ubuntu 18.04 / x86-64 system with current trunk, configuring
with:

~/toolchain/src/gcc/configure \
  --prefix=`pwd` \
  --enable-languages=c,c++ \
  --disable-multilib \
  --disable-bootstrap

running `make && make check-libbacktrace` gives the failure described
above. Here is the contents of libbacktrace/test-suite.log:

=====================================================
package-unused version-unused: ./test-suite.log
=====================================================

# TOTAL: 33
# PASS:  32
# SKIP:  0
# XFAIL: 0
# FAIL:  1
# XPASS: 0
# ERROR: 0

.. contents:: :depth: 2

FAIL: mtest_minidebug
=====================

no debug info in ELF executable
no debug info in ELF executable
no debug info in ELF executable
no debug info in ELF executable
no debug info in ELF executable
no debug info in ELF executable
no debug info in ELF executable
test1: not enough frames; got 0, expected at least 3
test1: [0]: got  expected f3
test1: [1]: got  expected f2
FAIL mtest_minidebug (exit status: 139)

---

Let me know if you need any more info.

Thanks,
Alex

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: libbacktrace: Add support for MiniDebugInfo
  2020-09-16 15:54     ` Alex Coplan
@ 2020-09-17  0:04       ` Ian Lance Taylor
  0 siblings, 0 replies; 5+ messages in thread
From: Ian Lance Taylor @ 2020-09-17  0:04 UTC (permalink / raw)
  To: Alex Coplan; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2603 bytes --]

On Wed, Sep 16, 2020 at 8:54 AM Alex Coplan <alex.coplan@arm.com> wrote:
>
> On 16/09/2020 07:26, Ian Lance Taylor wrote:
> > On Wed, Sep 16, 2020, 4:03 AM Alex Coplan <alex.coplan@arm.com> wrote:
> >
> > > Hi Ian,
> > >
> > > On 14/09/2020 14:12, Ian Lance Taylor via Gcc-patches wrote:
> > > > This patch to libbacktrace adds support for MiniDebugInfo, as
> > > > requested in PR 93608.
> > >
> > > This appears to introduce a failure in the libbacktrace testsuite
> > > (observed on both x86 and aarch64):
> > >
> > > ../gcc/libbacktrace/../test-driver: line 107:  7905 Segmentation fault
> > >   (core dumped) "$@" > $log_file 2>&1
> > > FAIL: mtest_minidebug
> >
> >
> >
> > I tested on x86 without seeing anything like this.  Can you give me any
> > more details?  Thanks.
>
> Sure. On an Ubuntu 18.04 / x86-64 system with current trunk, configuring
> with:
>
> ~/toolchain/src/gcc/configure \
>   --prefix=`pwd` \
>   --enable-languages=c,c++ \
>   --disable-multilib \
>   --disable-bootstrap
>
> running `make && make check-libbacktrace` gives the failure described
> above. Here is the contents of libbacktrace/test-suite.log:
>
> =====================================================
> package-unused version-unused: ./test-suite.log
> =====================================================
>
> # TOTAL: 33
> # PASS:  32
> # SKIP:  0
> # XFAIL: 0
> # FAIL:  1
> # XPASS: 0
> # ERROR: 0
>
> .. contents:: :depth: 2
>
> FAIL: mtest_minidebug
> =====================
>
> no debug info in ELF executable
> no debug info in ELF executable
> no debug info in ELF executable
> no debug info in ELF executable
> no debug info in ELF executable
> no debug info in ELF executable
> no debug info in ELF executable
> test1: not enough frames; got 0, expected at least 3
> test1: [0]: got  expected f3
> test1: [1]: got  expected f2
> FAIL mtest_minidebug (exit status: 139)
>
> ---
>
> Let me know if you need any more info.


Thanks.  I think the problem arises when libbacktrace is unable to
find debug info for any shared library involved in the link.

This patch should fix it.  Bootstrapped and ran libbacktrace tests on
x86_64-pc-linux-gnu and aarch64-linux-gnu.  Committed to mainline.

Ian

PR libbacktrace/97080
* fileline.c (backtrace_syminfo_to_full_callback): New function.
(backtrace_syminfo_to_full_error_callback): New function.
* elf.c (elf_nodebug): Call syminfo_fn if possible.
* internal.h (struct backtrace_call_full): Define.
(backtrace_syminfo_to_full_callback): Declare.
(backtrace_syminfo_to_full_error_callback): Declare.
* mtest.c (f3): Only check all[i] if data.index permits.

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 6346 bytes --]

diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
index dd004708246..941f820d944 100644
--- a/libbacktrace/elf.c
+++ b/libbacktrace/elf.c
@@ -547,18 +547,6 @@ elf_crc32_file (struct backtrace_state *state, int descriptor,
   return ret;
 }
 
-/* A dummy callback function used when we can't find any debug info.  */
-
-static int
-elf_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED,
-	     uintptr_t pc ATTRIBUTE_UNUSED,
-	     backtrace_full_callback callback ATTRIBUTE_UNUSED,
-	     backtrace_error_callback error_callback, void *data)
-{
-  error_callback (data, "no debug info in ELF executable", -1);
-  return 0;
-}
-
 /* A dummy callback function used when we can't find a symbol
    table.  */
 
@@ -571,6 +559,33 @@ elf_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED,
   error_callback (data, "no symbol table in ELF executable", -1);
 }
 
+/* A callback function used when we can't find any debug info.  */
+
+static int
+elf_nodebug (struct backtrace_state *state, uintptr_t pc,
+	     backtrace_full_callback callback,
+	     backtrace_error_callback error_callback, void *data)
+{
+  if (state->syminfo_fn != NULL && state->syminfo_fn != elf_nosyms)
+    {
+      struct backtrace_call_full bdata;
+
+      /* Fetch symbol information so that we can least get the
+	 function name.  */
+
+      bdata.full_callback = callback;
+      bdata.full_error_callback = error_callback;
+      bdata.full_data = data;
+      bdata.ret = 0;
+      state->syminfo_fn (state, pc, backtrace_syminfo_to_full_callback,
+			 backtrace_syminfo_to_full_error_callback, &bdata);
+      return bdata.ret;
+    }
+
+  error_callback (data, "no debug info in ELF executable", -1);
+  return 0;
+}
+
 /* Compare struct elf_symbol for qsort.  */
 
 static int
diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c
index be62b9899c5..cd1e10dd58c 100644
--- a/libbacktrace/fileline.c
+++ b/libbacktrace/fileline.c
@@ -317,3 +317,30 @@ backtrace_syminfo (struct backtrace_state *state, uintptr_t pc,
   state->syminfo_fn (state, pc, callback, error_callback, data);
   return 1;
 }
+
+/* A backtrace_syminfo_callback that can call into a
+   backtrace_full_callback, used when we have a symbol table but no
+   debug info.  */
+
+void
+backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
+				    const char *symname,
+				    uintptr_t symval ATTRIBUTE_UNUSED,
+				    uintptr_t symsize ATTRIBUTE_UNUSED)
+{
+  struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
+
+  bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, symname);
+}
+
+/* An error callback that corresponds to
+   backtrace_syminfo_to_full_callback.  */
+
+void
+backtrace_syminfo_to_full_error_callback (void *data, const char *msg,
+					  int errnum)
+{
+  struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
+
+  bdata->full_error_callback (bdata->full_data, msg, errnum);
+}
diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h
index 09862337456..047a700c0ce 100644
--- a/libbacktrace/internal.h
+++ b/libbacktrace/internal.h
@@ -326,6 +326,31 @@ extern int backtrace_dwarf_add (struct backtrace_state *state,
 				void *data, fileline *fileline_fn,
 				struct dwarf_data **fileline_entry);
 
+/* A data structure to pass to backtrace_syminfo_to_full.  */
+
+struct backtrace_call_full
+{
+  backtrace_full_callback full_callback;
+  backtrace_error_callback full_error_callback;
+  void *full_data;
+  int ret;
+};
+
+/* A backtrace_syminfo_callback that can call into a
+   backtrace_full_callback, used when we have a symbol table but no
+   debug info.  */
+
+extern void backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
+						const char *symname,
+						uintptr_t symval,
+						uintptr_t symsize);
+
+/* An error callback that corresponds to
+   backtrace_syminfo_to_full_callback.  */
+
+extern void backtrace_syminfo_to_full_error_callback (void *, const char *,
+						      int);
+
 /* A test-only hook for elf_uncompress_zdebug.  */
 
 extern int backtrace_uncompress_zdebug (struct backtrace_state *,
diff --git a/libbacktrace/mtest.c b/libbacktrace/mtest.c
index d90fd1e33cc..d73c98d44f8 100644
--- a/libbacktrace/mtest.c
+++ b/libbacktrace/mtest.c
@@ -156,40 +156,49 @@ f3 (int f1line __attribute__ ((unused)), int f2line __attribute__ ((unused)))
 	}
     }
 
-  if (all[0].function == NULL)
+  if (data.index > 0)
     {
-      fprintf (stderr, "test1: [0]: missing function name\n");
-      data.failed = 1;
-    }
-  else if (strcmp (all[0].function, "f3") != 0)
-    {
-      fprintf (stderr, "test1: [0]: got %s expected %s\n",
-	       all[0].function, "f3");
-      data.failed = 1;
+      if (all[0].function == NULL)
+	{
+	  fprintf (stderr, "test1: [0]: missing function name\n");
+	  data.failed = 1;
+	}
+      else if (strcmp (all[0].function, "f3") != 0)
+	{
+	  fprintf (stderr, "test1: [0]: got %s expected %s\n",
+		   all[0].function, "f3");
+	  data.failed = 1;
+	}
     }
 
-  if (all[1].function == NULL)
-    {
-      fprintf (stderr, "test1: [1]: missing function name\n");
-      data.failed = 1;
-    }
-  else if (strcmp (all[1].function, "f2") != 0)
+  if (data.index > 1)
     {
-      fprintf (stderr, "test1: [1]: got %s expected %s\n",
-	       all[0].function, "f2");
-      data.failed = 1;
+      if (all[1].function == NULL)
+	{
+	  fprintf (stderr, "test1: [1]: missing function name\n");
+	  data.failed = 1;
+	}
+      else if (strcmp (all[1].function, "f2") != 0)
+	{
+	  fprintf (stderr, "test1: [1]: got %s expected %s\n",
+		   all[0].function, "f2");
+	  data.failed = 1;
+	}
     }
 
-  if (all[2].function == NULL)
-    {
-      fprintf (stderr, "test1: [2]: missing function name\n");
-      data.failed = 1;
-    }
-  else if (strcmp (all[2].function, "test1") != 0)
+  if (data.index > 2)
     {
-      fprintf (stderr, "test1: [2]: got %s expected %s\n",
-	       all[0].function, "test1");
-      data.failed = 1;
+      if (all[2].function == NULL)
+	{
+	  fprintf (stderr, "test1: [2]: missing function name\n");
+	  data.failed = 1;
+	}
+      else if (strcmp (all[2].function, "test1") != 0)
+	{
+	  fprintf (stderr, "test1: [2]: got %s expected %s\n",
+		   all[0].function, "test1");
+	  data.failed = 1;
+	}
     }
 
   printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-09-17  0:05 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-14 21:12 libbacktrace: Add support for MiniDebugInfo Ian Lance Taylor
2020-09-16 11:03 ` Alex Coplan
2020-09-16 14:26   ` Ian Lance Taylor
2020-09-16 15:54     ` Alex Coplan
2020-09-17  0:04       ` Ian Lance Taylor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).