libbacktrace patch committed: Add DWARF 5 support

Message ID CAOyqgcUs+5Csn62sB_=iKC99r2oEX_E5dRpcZSEJW0n9-YqiwA@mail.gmail.com
State New
Headers show
Series
  • libbacktrace patch committed: Add DWARF 5 support
Related show

Commit Message

Ian Lance Taylor Dec. 13, 2019, 8:04 p.m.
This patch to libbacktrace adds DWARF 5 support.  I tested this with
GCC 8, GCC tip, and clang 8, using the -gdwarf-5 option.  Bootstrapped
and ran libbacktrace and libgo tests on x86_64-pc-linux-gnu.
Committed to mainline.

Ian

2019-12-13  Ian Lance Taylor  <iant@golang.org>

Add DWARF 5 support.
* dwarf.c (struct attr): Add val field.
(enum attr_val_encoding): Add ATTR_VAL_ADDDRESS_INDEX,
ATTR_VAL_STRING_INDEX, ATTR_VAL_RNGLISTS_INDEX.
(struct line_header): Add addrsize field.
(struct line_header_format): Define.
(struct unit): Add str_offsets_base, addr_base, and rnglists_base
fields.
(read_uint24): New static function.
(read_attribute): Add implicit_val parameter.  Replace dwarf_str
and dwarf_str_size parameters with dwarf_sections parameter.  Add
support for new DWARF 5 forms.  Change all callers.
(resolve_string): New static function.
(resolve_addr_index): Likewise.
(read_abbrevs): Support DW_FORM_implicit_const.
(struct pcrange): Add lowpc_is_addr_index, highpc_is_addr_Index,
and ranges_is_index fields.
(update_pcrange): Support DWARF 5 encodings.
(add_high_low_range): New static function, split out of
add_ranges.
(add_ranges_from_ranges): Likewise.
(add_ranges_from_rnglists): New static function.
(add_ranges): Just call new helper functions.
(find_address_ranges): Use resolve_string for strings, after
reading all attributes.  Handle new DWARF 5 attributes.
(build_address_map): Support DWARF 5 compilation units.
(read_v2_paths): New static function, split out of
read_line_header.
(read_lnct): New static function.
(read_line_header_format_entries): Likewise.
(read_line_header): Add ddata parameter.  Support DWARF 5 line
headers.  Call new helper functions.  Change all callers.
(read_line_program): Use addrsize from line program header.  Don't
special case directory index 0 for DWARF 5.
(read_referenced_name): Use resolve_string.
(read_function_entry): Handle DWARF 5 encodings.  Use
resolve_string.
* internal.h (enum dwarf_section): Add DEBUG_ADDR,
DEBUG_STR_OFFSETS, DEBUG_LINE_STR, DEBUG_RNGLISTS.
* elf.c (dwarf_section_names): Add new section names.
* pecoff.c (dwarf_section_names): Likewise.
* xcoff.c (xcoff_add): Clear dwarf_sections before setting
fields.
* configure.ac: Define HAVE_DWARF5 automake conditional.
* Makefile.am (dwarf5_SOURCES): New variable if HAVE_DWARF5.
(dwarf5_CFLAGS, dwarf5_LDADD): Likewise.
(dwarf5_alloc_SOURCES, dwarf5_alloc_CFLAGS): Likewise.
(dwarf5_alloc_LDADD): Likewise.
(BUILDTESTS): Add dwarf5 tests if HAVE_DWARF5.
(CLEANFILES, clean-local): Define.

Patch

Index: Makefile.am
===================================================================
--- Makefile.am	(revision 279211)
+++ Makefile.am	(working copy)
@@ -385,12 +385,33 @@  BUILDTESTS += ctestg_alloc ctesta_alloc
 
 endif
 
+if HAVE_DWARF5
+
+dwarf5_SOURCES = btest.c testlib.c
+dwarf5_CFLAGS = $(AM_CFLAGS) -gdwarf-5
+dwarf5_LDADD = libbacktrace.la
+
+BUILDTESTS += dwarf5
+
+dwarf5_alloc_SOURCES = $(dwarf5_SOURCES)
+dwarf5_alloc_CFLAGS = $(dwarf5_CFLAGS)
+dwarf5_alloc_LDADD = libbacktrace_alloc.la
+
+BUILDTESTS += dwarf5_alloc
+
+endif
+
 endif NATIVE
 
 check_PROGRAMS += $(BUILDTESTS)
 
 TESTS += $(BUILDTESTS)
 
+CLEANFILES = $(TESTS) *.debug elf_for_test.c edtest2_build.c gen_edtest2_build
+
+clean-local:
+	-rm -rf usr
+
 # We can't use automake's automatic dependency tracking, because it
 # breaks when using bootstrap-lean.  Automatic dependency tracking
 # with GCC bootstrap will cause some of the objects to depend on
Index: configure.ac
===================================================================
--- configure.ac	(revision 279211)
+++ configure.ac	(working copy)
@@ -420,6 +420,17 @@  AC_SUBST(PTHREAD_CFLAGS)
 
 AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes)
 
+dnl Test whether the compiler supports the -gdwarf-5 option.
+AC_CACHE_CHECK([whether -gdwarf-5 is supported],
+[libbacktrace_cv_lib_dwarf5],
+[CFLAGS_hold=$CFLAGS
+CFLAGS="$CFLAGS -gdwarf-5"
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([int i;])],
+[libbacktrace_cv_lib_dwarf5=yes],
+[libbacktrace_cv_lib_dwarf5=no])
+CFLAGS=$CFLAGS_hold])
+AM_CONDITIONAL(HAVE_DWARF5, test "$libbacktrace_cv_lib_dwarf5" = yes)
+
 AC_CHECK_LIB([z], [compress],
     [AC_DEFINE(HAVE_ZLIB, 1, [Define if -lz is available.])])
 AM_CONDITIONAL(HAVE_ZLIB, test "$ac_cv_lib_z_compress" = yes)
Index: dwarf.c
===================================================================
--- dwarf.c	(revision 279211)
+++ dwarf.c	(working copy)
@@ -92,6 +92,8 @@  struct attr
   enum dwarf_attribute name;
   /* The attribute form.  */
   enum dwarf_form form;
+  /* The attribute value, for DW_FORM_implicit_const.  */
+  int64_t val;
 };
 
 /* A single DWARF abbreviation.  */
@@ -133,22 +135,29 @@  enum attr_val_encoding
   ATTR_VAL_NONE,
   /* An address.  */
   ATTR_VAL_ADDRESS,
+  /* An index into the .debug_addr section, whose value is relative to
+   * the DW_AT_addr_base attribute of the compilation unit.  */
+  ATTR_VAL_ADDRESS_INDEX,
   /* A unsigned integer.  */
   ATTR_VAL_UINT,
   /* A sigd integer.  */
   ATTR_VAL_SINT,
   /* A string.  */
   ATTR_VAL_STRING,
+  /* An index into the .debug_str_offsets section.  */
+  ATTR_VAL_STRING_INDEX,
   /* An offset to other data in the containing unit.  */
   ATTR_VAL_REF_UNIT,
-  /* An offset to other data within the .dwarf_info section.  */
+  /* An offset to other data within the .debug_info section.  */
   ATTR_VAL_REF_INFO,
-  /* An offset to other data within the alt .dwarf_info section.  */
+  /* An offset to other data within the alt .debug_info section.  */
   ATTR_VAL_REF_ALT_INFO,
   /* An offset to data in some other section.  */
   ATTR_VAL_REF_SECTION,
   /* A type signature.  */
   ATTR_VAL_REF_TYPE,
+  /* An index into the .debug_rnglists section.  */
+  ATTR_VAL_RNGLISTS_INDEX,
   /* A block of data (not represented).  */
   ATTR_VAL_BLOCK,
   /* An expression (not represented).  */
@@ -163,7 +172,7 @@  struct attr_val
   enum attr_val_encoding encoding;
   union
   {
-    /* ATTR_VAL_ADDRESS, ATTR_VAL_UINT, ATTR_VAL_REF*.  */
+    /* ATTR_VAL_ADDRESS*, ATTR_VAL_UINT, ATTR_VAL_REF*.  */
     uint64_t uint;
     /* ATTR_VAL_SINT.  */
     int64_t sint;
@@ -179,6 +188,8 @@  struct line_header
 {
   /* The version of the line number information.  */
   int version;
+  /* Address size.  */
+  int addrsize;
   /* The minimum instruction length.  */
   unsigned int min_insn_len;
   /* The maximum number of ops per instruction.  */
@@ -201,6 +212,14 @@  struct line_header
   const char **filenames;
 };
 
+/* A format description from a line header.  */
+
+struct line_header_format
+{
+  int lnct;		/* LNCT code.  */
+  enum dwarf_form form;	/* Form of entry data.  */
+};
+
 /* Map a single PC value to a file/line.  We will keep a vector of
    these sorted by PC value.  Each file/line will be correct from the
    PC up to the PC of the next entry if there is one.  We allocate one
@@ -297,6 +316,12 @@  struct unit
   int addrsize;
   /* Offset into line number information.  */
   off_t lineoff;
+  /* Offset of compilation unit in .debug_str_offsets.  */
+  uint64_t str_offsets_base;
+  /* Offset of compilation unit in .debug_addr.  */
+  uint64_t addr_base;
+  /* Offset of compilation unit in .debug_rnglists.  */
+  uint64_t rnglists_base;
   /* Primary source file.  */
   const char *filename;
   /* Compilation command working directory.  */
@@ -483,6 +508,23 @@  read_uint16 (struct dwarf_buf *buf)
     return ((uint16_t) p[1] << 8) | (uint16_t) p[0];
 }
 
+/* Read a 24 bit value from BUF and advance 3 bytes.  */
+
+static uint32_t
+read_uint24 (struct dwarf_buf *buf)
+{
+  const unsigned char *p = buf->buf;
+
+  if (!advance (buf, 3))
+    return 0;
+  if (buf->is_bigendian)
+    return (((uint32_t) p[0] << 16) | ((uint32_t) p[1] << 8)
+	    | (uint32_t) p[2]);
+  else
+    return (((uint32_t) p[2] << 16) | ((uint32_t) p[1] << 8)
+	    | (uint32_t) p[0]);
+}
+
 /* Read a uint32 from BUF and advance 4 bytes.  */
 
 static uint32_t
@@ -709,9 +751,9 @@  free_abbrevs (struct backtrace_state *st
    forms, because we don't care about them.  */
 
 static int
-read_attribute (enum dwarf_form form, struct dwarf_buf *buf,
-		int is_dwarf64, int version, int addrsize,
-		const unsigned char *dwarf_str, size_t dwarf_str_size,
+read_attribute (enum dwarf_form form, uint64_t implicit_val,
+		struct dwarf_buf *buf, int is_dwarf64, int version,
+		int addrsize, const struct dwarf_sections *dwarf_sections,
 		struct dwarf_data *altlink, struct attr_val *val)
 {
   /* Avoid warnings about val.u.FIELD may be used uninitialized if
@@ -744,6 +786,9 @@  read_attribute (enum dwarf_form form, st
       val->encoding = ATTR_VAL_UINT;
       val->u.uint = read_uint64 (buf);
       return 1;
+    case DW_FORM_data16:
+      val->encoding = ATTR_VAL_BLOCK;
+      return advance (buf, 16);
     case DW_FORM_string:
       val->encoding = ATTR_VAL_STRING;
       val->u.string = read_string (buf);
@@ -771,13 +816,29 @@  read_attribute (enum dwarf_form form, st
 	uint64_t offset;
 
 	offset = read_offset (buf, is_dwarf64);
-	if (offset >= dwarf_str_size)
+	if (offset >= dwarf_sections->size[DEBUG_STR])
 	  {
 	    dwarf_buf_error (buf, "DW_FORM_strp out of range");
 	    return 0;
 	  }
 	val->encoding = ATTR_VAL_STRING;
-	val->u.string = (const char *) dwarf_str + offset;
+	val->u.string =
+	  (const char *) dwarf_sections->data[DEBUG_STR] + offset;
+	return 1;
+      }
+    case DW_FORM_line_strp:
+      {
+	uint64_t offset;
+
+	offset = read_offset (buf, is_dwarf64);
+	if (offset >= dwarf_sections->size[DEBUG_LINE_STR])
+	  {
+	    dwarf_buf_error (buf, "DW_FORM_line_strp out of range");
+	    return 0;
+	  }
+	val->encoding = ATTR_VAL_STRING;
+	val->u.string =
+	  (const char *) dwarf_sections->data[DEBUG_LINE_STR] + offset;
 	return 1;
       }
     case DW_FORM_udata:
@@ -816,9 +877,15 @@  read_attribute (enum dwarf_form form, st
 	uint64_t form;
 
 	form = read_uleb128 (buf);
-	return read_attribute ((enum dwarf_form) form, buf, is_dwarf64,
-			       version, addrsize, dwarf_str, dwarf_str_size,
-			       altlink, val);
+	if (form == DW_FORM_implicit_const)
+	  {
+	    dwarf_buf_error (buf,
+			     "DW_FORM_indirect to DW_FORM_implicit_const");
+	    return 0;
+	  }
+	return read_attribute ((enum dwarf_form) form, 0, buf, is_dwarf64,
+			       version, addrsize, dwarf_sections, altlink,
+			       val);
       }
     case DW_FORM_sec_offset:
       val->encoding = ATTR_VAL_REF_SECTION;
@@ -835,6 +902,88 @@  read_attribute (enum dwarf_form form, st
       val->encoding = ATTR_VAL_REF_TYPE;
       val->u.uint = read_uint64 (buf);
       return 1;
+    case DW_FORM_strx: case DW_FORM_strx1: case DW_FORM_strx2:
+    case DW_FORM_strx3: case DW_FORM_strx4:
+      {
+	uint64_t offset;
+
+	switch (form)
+	  {
+	  case DW_FORM_strx:
+	    offset = read_uleb128 (buf);
+	    break;
+	  case DW_FORM_strx1:
+	    offset = read_byte (buf);
+	    break;
+	  case DW_FORM_strx2:
+	    offset = read_uint16 (buf);
+	    break;
+	  case DW_FORM_strx3:
+	    offset = read_uint24 (buf);
+	    break;
+	  case DW_FORM_strx4:
+	    offset = read_uint32 (buf);
+	    break;
+	  default:
+	    /* This case can't happen.  */
+	    return 0;
+	  }
+	val->encoding = ATTR_VAL_STRING_INDEX;
+	val->u.uint = offset;
+	return 1;
+      }
+    case DW_FORM_addrx: case DW_FORM_addrx1: case DW_FORM_addrx2:
+    case DW_FORM_addrx3: case DW_FORM_addrx4:
+      {
+	uint64_t offset;
+
+	switch (form)
+	  {
+	  case DW_FORM_addrx:
+	    offset = read_uleb128 (buf);
+	    break;
+	  case DW_FORM_addrx1:
+	    offset = read_byte (buf);
+	    break;
+	  case DW_FORM_addrx2:
+	    offset = read_uint16 (buf);
+	    break;
+	  case DW_FORM_addrx3:
+	    offset = read_uint24 (buf);
+	    break;
+	  case DW_FORM_addrx4:
+	    offset = read_uint32 (buf);
+	    break;
+	  default:
+	    /* This case can't happen.  */
+	    return 0;
+	  }
+	val->encoding = ATTR_VAL_ADDRESS_INDEX;
+	val->u.uint = offset;
+	return 1;
+      }
+    case DW_FORM_ref_sup4:
+      val->encoding = ATTR_VAL_REF_SECTION;
+      val->u.uint = read_uint32 (buf);
+      return 1;
+    case DW_FORM_ref_sup8:
+      val->encoding = ATTR_VAL_REF_SECTION;
+      val->u.uint = read_uint64 (buf);
+      return 1;
+    case DW_FORM_implicit_const:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = implicit_val;
+      return 1;
+    case DW_FORM_loclistx:
+      /* We don't distinguish this from DW_FORM_sec_offset.  It
+       * shouldn't matter since we don't care about loclists.  */
+      val->encoding = ATTR_VAL_REF_SECTION;
+      val->u.uint = read_uleb128 (buf);
+      return 1;
+    case DW_FORM_rnglistx:
+      val->encoding = ATTR_VAL_RNGLISTS_INDEX;
+      val->u.uint = read_uleb128 (buf);
+      return 1;
     case DW_FORM_GNU_addr_index:
       val->encoding = ATTR_VAL_REF_SECTION;
       val->u.uint = read_uleb128 (buf);
@@ -852,9 +1001,10 @@  read_attribute (enum dwarf_form form, st
 	}
       val->encoding = ATTR_VAL_REF_ALT_INFO;
       return 1;
-    case DW_FORM_GNU_strp_alt:
+    case DW_FORM_strp_sup: case DW_FORM_GNU_strp_alt:
       {
 	uint64_t offset;
+
 	offset = read_offset (buf, is_dwarf64);
 	if (altlink == NULL)
 	  {
@@ -863,7 +1013,7 @@  read_attribute (enum dwarf_form form, st
 	  }
 	if (offset >= altlink->dwarf_sections.size[DEBUG_STR])
 	  {
-	    dwarf_buf_error (buf, "DW_FORM_GNU_strp_alt out of range");
+	    dwarf_buf_error (buf, "DW_FORM_strp_sup out of range");
 	    return 0;
 	  }
 	val->encoding = ATTR_VAL_STRING;
@@ -877,6 +1027,95 @@  read_attribute (enum dwarf_form form, st
     }
 }
 
+/* If we can determine the value of a string attribute, set *STRING to
+   point to the string.  Return 1 on success, 0 on error.  If we don't
+   know the value, we consider that a success, and we don't change
+   *STRING.  An error is only reported for some sort of out of range
+   offset.  */
+
+static int
+resolve_string (const struct dwarf_sections *dwarf_sections, int is_dwarf64,
+		int is_bigendian, uint64_t str_offsets_base,
+		const struct attr_val *val,
+		backtrace_error_callback error_callback, void *data,
+		const char **string)
+{
+  switch (val->encoding)
+    {
+    case ATTR_VAL_STRING:
+      *string = val->u.string;
+      return 1;
+
+    case ATTR_VAL_STRING_INDEX:
+      {
+	uint64_t offset;
+	struct dwarf_buf offset_buf;
+
+	offset = val->u.uint * (is_dwarf64 ? 8 : 4) + str_offsets_base;
+	if (offset + (is_dwarf64 ? 8 : 4)
+	    >= dwarf_sections->size[DEBUG_STR_OFFSETS])
+	  {
+	    error_callback (data, "DW_FORM_strx value out of range", 0);
+	    return 0;
+	  }
+
+	offset_buf.name = ".debug_str_offsets";
+	offset_buf.start = dwarf_sections->data[DEBUG_STR_OFFSETS];
+	offset_buf.buf = dwarf_sections->data[DEBUG_STR_OFFSETS] + offset;
+	offset_buf.left = dwarf_sections->size[DEBUG_STR_OFFSETS] - offset;
+	offset_buf.is_bigendian = is_bigendian;
+	offset_buf.error_callback = error_callback;
+	offset_buf.data = data;
+	offset_buf.reported_underflow = 0;
+
+	offset = read_offset (&offset_buf, is_dwarf64);
+	if (offset >= dwarf_sections->size[DEBUG_STR])
+	  {
+	    dwarf_buf_error (&offset_buf, "DW_FORM_strx offset out of range");
+	    return 0;
+	  }
+	*string = (const char *) dwarf_sections->data[DEBUG_STR] + offset;
+	return 1;
+      }
+
+    default:
+      return 1;
+    }
+}
+
+/* Set *ADDRESS to the real address for a ATTR_VAL_ADDRESS_INDEX.
+   Return 1 on success, 0 on error.  */
+
+static int
+resolve_addr_index (const struct dwarf_sections *dwarf_sections,
+		    uint64_t addr_base, int addrsize, int is_bigendian,
+		    uint64_t addr_index,
+		    backtrace_error_callback error_callback, void *data,
+		    uint64_t *address)
+{
+  uint64_t offset;
+  struct dwarf_buf addr_buf;
+
+  offset = addr_index * addrsize + addr_base;
+  if (offset + addrsize >= dwarf_sections->size[DEBUG_ADDR])
+    {
+      error_callback (data, "DW_FORM_addrx value out of range", 0);
+      return 0;
+    }
+
+  addr_buf.name = ".debug_addr";
+  addr_buf.start = dwarf_sections->data[DEBUG_ADDR];
+  addr_buf.buf = dwarf_sections->data[DEBUG_ADDR] + offset;
+  addr_buf.left = dwarf_sections->size[DEBUG_ADDR] - offset;
+  addr_buf.is_bigendian = is_bigendian;
+  addr_buf.error_callback = error_callback;
+  addr_buf.data = data;
+  addr_buf.reported_underflow = 0;
+
+  *address = read_address (&addr_buf, addrsize);
+  return 1;
+}
+
 /* Compare a unit offset against a unit for bsearch.  */
 
 static int
@@ -1142,7 +1381,13 @@  read_abbrevs (struct backtrace_state *st
       read_byte (&count_buf);
       // Skip attributes.
       while (read_uleb128 (&count_buf) != 0)
-	read_uleb128 (&count_buf);
+	{
+	  uint64_t form;
+
+	  form = read_uleb128 (&count_buf);
+	  if ((enum dwarf_form) form == DW_FORM_implicit_const)
+	    read_sleb128 (&count_buf);
+	}
       // Skip form of last attribute.
       read_uleb128 (&count_buf);
     }
@@ -1185,8 +1430,12 @@  read_abbrevs (struct backtrace_state *st
       num_attrs = 0;
       while (read_uleb128 (&count_buf) != 0)
 	{
+	  uint64_t form;
+
 	  ++num_attrs;
-	  read_uleb128 (&count_buf);
+	  form = read_uleb128 (&count_buf);
+	  if ((enum dwarf_form) form == DW_FORM_implicit_const)
+	    read_sleb128 (&count_buf);
 	}
 
       if (num_attrs == 0)
@@ -1214,6 +1463,10 @@  read_abbrevs (struct backtrace_state *st
 		break;
 	      attrs[num_attrs].name = (enum dwarf_attribute) name;
 	      attrs[num_attrs].form = (enum dwarf_form) form;
+	      if ((enum dwarf_form) form == DW_FORM_implicit_const)
+		attrs[num_attrs].val = read_sleb128 (&abbrev_buf);
+	      else
+		attrs[num_attrs].val = 0;
 	      ++num_attrs;
 	    }
 	}
@@ -1272,11 +1525,14 @@  lookup_abbrev (struct abbrevs *abbrevs,
 struct pcrange {
   uint64_t lowpc;		/* The low PC value.  */
   int have_lowpc;		/* Whether a low PC value was found.  */
+  int lowpc_is_addr_index;	/* Whether lowpc is in .debug_addr.  */
   uint64_t highpc;		/* The high PC value.  */
   int have_highpc;		/* Whether a high PC value was found.  */
   int highpc_is_relative;	/* Whether highpc is relative to lowpc.  */
+  int highpc_is_addr_index;	/* Whether highpc is in .debug_addr.  */
   uint64_t ranges;		/* Offset in ranges section.  */
   int have_ranges;		/* Whether ranges is valid.  */
+  int ranges_is_index;		/* Whether ranges is DW_FORM_rnglistx.  */
 };
 
 /* Update PCRANGE from an attribute value.  */
@@ -1293,6 +1549,12 @@  update_pcrange (const struct attr* attr,
 	  pcrange->lowpc = val->u.uint;
 	  pcrange->have_lowpc = 1;
 	}
+      else if (val->encoding == ATTR_VAL_ADDRESS_INDEX)
+	{
+	  pcrange->lowpc = val->u.uint;
+	  pcrange->have_lowpc = 1;
+	  pcrange->lowpc_is_addr_index = 1;
+	}
       break;
 
     case DW_AT_high_pc:
@@ -1307,6 +1569,12 @@  update_pcrange (const struct attr* attr,
 	  pcrange->have_highpc = 1;
 	  pcrange->highpc_is_relative = 1;
 	}
+      else if (val->encoding == ATTR_VAL_ADDRESS_INDEX)
+	{
+	  pcrange->highpc = val->u.uint;
+	  pcrange->have_highpc = 1;
+	  pcrange->highpc_is_addr_index = 1;
+	}
       break;
 
     case DW_AT_ranges:
@@ -1316,6 +1584,12 @@  update_pcrange (const struct attr* attr,
 	  pcrange->ranges = val->u.uint;
 	  pcrange->have_ranges = 1;
 	}
+      else if (val->encoding == ATTR_VAL_RNGLISTS_INDEX)
+	{
+	  pcrange->ranges = val->u.uint;
+	  pcrange->have_ranges = 1;
+	  pcrange->ranges_is_index = 1;
+	}
       break;
 
     default:
@@ -1323,51 +1597,73 @@  update_pcrange (const struct attr* attr,
     }
 }
 
-/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE.  RDATA is
-   passed to ADD_RANGE, and is either a struct unit * or a struct
-   function *.  VEC is the vector we are adding ranges to, and is
-   either a struct unit_addrs_vector * or a struct function_vector *.
-   Returns 1 on success, 0 on error.  */
+/* Call ADD_RANGE for a low/high PC pair.  Returns 1 on success, 0 on
+  error.  */
 
 static int
-add_ranges (struct backtrace_state *state,
-	    const struct dwarf_sections *dwarf_sections,
-	    uintptr_t base_address, int is_bigendian,
-	    struct unit *u, uint64_t base, const struct pcrange *pcrange,
-	    int (*add_range) (struct backtrace_state *state, void *rdata, 
-			      uint64_t lowpc, uint64_t highpc,
-			      backtrace_error_callback error_callback,
-			      void *data, void *vec),
-	    void *rdata,
-	    backtrace_error_callback error_callback, void *data,
-	    void *vec)
+add_low_high_range (struct backtrace_state *state,
+		    const struct dwarf_sections *dwarf_sections,
+		    uintptr_t base_address, int is_bigendian,
+		    struct unit *u, const struct pcrange *pcrange,
+		    int (*add_range) (struct backtrace_state *state,
+				      void *rdata, uint64_t lowpc,
+				      uint64_t highpc,
+				      backtrace_error_callback error_callback,
+				      void *data, void *vec),
+		    void *rdata,
+		    backtrace_error_callback error_callback, void *data,
+		    void *vec)
 {
-  struct dwarf_buf ranges_buf;
+  uint64_t lowpc;
+  uint64_t highpc;
 
-  if (pcrange->have_lowpc && pcrange->have_highpc)
+  lowpc = pcrange->lowpc;
+  if (pcrange->lowpc_is_addr_index)
     {
-      uint64_t lowpc;
-      uint64_t highpc;
-
-      lowpc = pcrange->lowpc;
-      highpc = pcrange->highpc;
-      if (pcrange->highpc_is_relative)
-	highpc += lowpc;
-
-      /* Add in the base address of the module when recording PC
-	 values, so that we can look up the PC directly.  */
-      lowpc += base_address;
-      highpc += base_address;
-
-      return add_range (state, rdata, lowpc, highpc, error_callback, data,
-			vec);
+      if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize,
+			       is_bigendian, lowpc, error_callback, data,
+			       &lowpc))
+	return 0;
     }
 
-  if (!pcrange->have_ranges)
+  highpc = pcrange->highpc;
+  if (pcrange->highpc_is_addr_index)
     {
-      /* Did not find any address ranges to add.  */
-      return 1;
+      if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize,
+			       is_bigendian, highpc, error_callback, data,
+			       &highpc))
+	return 0;
     }
+  if (pcrange->highpc_is_relative)
+    highpc += lowpc;
+
+  /* Add in the base address of the module when recording PC values,
+     so that we can look up the PC directly.  */
+  lowpc += base_address;
+  highpc += base_address;
+
+  return add_range (state, rdata, lowpc, highpc, error_callback, data, vec);
+}
+
+/* Call ADD_RANGE for each range read from .debug_ranges, as used in
+   DWARF versions 2 through 4.  */
+
+static int
+add_ranges_from_ranges (
+    struct backtrace_state *state,
+    const struct dwarf_sections *dwarf_sections,
+    uintptr_t base_address, int is_bigendian,
+    struct unit *u, uint64_t base,
+    const struct pcrange *pcrange,
+    int (*add_range) (struct backtrace_state *state, void *rdata,
+		      uint64_t lowpc, uint64_t highpc,
+		      backtrace_error_callback error_callback, void *data,
+		      void *vec),
+    void *rdata,
+    backtrace_error_callback error_callback, void *data,
+    void *vec)
+{
+  struct dwarf_buf ranges_buf;
 
   if (pcrange->ranges >= dwarf_sections->size[DEBUG_RANGES])
     {
@@ -1416,6 +1712,220 @@  add_ranges (struct backtrace_state *stat
   return 1;
 }
 
+/* Call ADD_RANGE for each range read from .debug_rnglists, as used in
+   DWARF version 5.  */
+
+static int
+add_ranges_from_rnglists (
+    struct backtrace_state *state,
+    const struct dwarf_sections *dwarf_sections,
+    uintptr_t base_address, int is_bigendian,
+    struct unit *u, uint64_t base,
+    const struct pcrange *pcrange,
+    int (*add_range) (struct backtrace_state *state, void *rdata,
+		      uint64_t lowpc, uint64_t highpc,
+		      backtrace_error_callback error_callback, void *data,
+		      void *vec),
+    void *rdata,
+    backtrace_error_callback error_callback, void *data,
+    void *vec)
+{
+  uint64_t offset;
+  struct dwarf_buf rnglists_buf;
+
+  if (!pcrange->ranges_is_index)
+    offset = pcrange->ranges;
+  else
+    offset = u->rnglists_base + pcrange->ranges * (u->is_dwarf64 ? 8 : 4);
+  if (offset >= dwarf_sections->size[DEBUG_RNGLISTS])
+    {
+      error_callback (data, "rnglists offset out of range", 0);
+      return 0;
+    }
+
+  rnglists_buf.name = ".debug_rnglists";
+  rnglists_buf.start = dwarf_sections->data[DEBUG_RNGLISTS];
+  rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset;
+  rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset;
+  rnglists_buf.is_bigendian = is_bigendian;
+  rnglists_buf.error_callback = error_callback;
+  rnglists_buf.data = data;
+  rnglists_buf.reported_underflow = 0;
+
+  if (pcrange->ranges_is_index)
+    {
+      offset = read_offset (&rnglists_buf, u->is_dwarf64);
+      offset += u->rnglists_base;
+      if (offset >= dwarf_sections->size[DEBUG_RNGLISTS])
+	{
+	  error_callback (data, "rnglists index offset out of range", 0);
+	  return 0;
+	}
+      rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset;
+      rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset;
+    }
+
+  while (1)
+    {
+      unsigned char rle;
+
+      rle = read_byte (&rnglists_buf);
+      if (rle == DW_RLE_end_of_list)
+	break;
+      switch (rle)
+	{
+	case DW_RLE_base_addressx:
+	  {
+	    uint64_t index;
+
+	    index = read_uleb128 (&rnglists_buf);
+	    if (!resolve_addr_index (dwarf_sections, u->addr_base,
+				     u->addrsize, is_bigendian, index,
+				     error_callback, data, &base))
+	      return 0;
+	  }
+	  break;
+
+	case DW_RLE_startx_endx:
+	  {
+	    uint64_t index;
+	    uint64_t low;
+	    uint64_t high;
+
+	    index = read_uleb128 (&rnglists_buf);
+	    if (!resolve_addr_index (dwarf_sections, u->addr_base,
+				     u->addrsize, is_bigendian, index,
+				     error_callback, data, &low))
+	      return 0;
+	    index = read_uleb128 (&rnglists_buf);
+	    if (!resolve_addr_index (dwarf_sections, u->addr_base,
+				     u->addrsize, is_bigendian, index,
+				     error_callback, data, &high))
+	      return 0;
+	    if (!add_range (state, rdata, low + base_address,
+			    high + base_address, error_callback, data,
+			    vec))
+	      return 0;
+	  }
+	  break;
+
+	case DW_RLE_startx_length:
+	  {
+	    uint64_t index;
+	    uint64_t low;
+	    uint64_t length;
+
+	    index = read_uleb128 (&rnglists_buf);
+	    if (!resolve_addr_index (dwarf_sections, u->addr_base,
+				     u->addrsize, is_bigendian, index,
+				     error_callback, data, &low))
+	      return 0;
+	    length = read_uleb128 (&rnglists_buf);
+	    low += base_address;
+	    if (!add_range (state, rdata, low, low + length,
+			    error_callback, data, vec))
+	      return 0;
+	  }
+	  break;
+
+	case DW_RLE_offset_pair:
+	  {
+	    uint64_t low;
+	    uint64_t high;
+
+	    low = read_uleb128 (&rnglists_buf);
+	    high = read_uleb128 (&rnglists_buf);
+	    if (!add_range (state, rdata, low + base + base_address,
+			    high + base + base_address,
+			    error_callback, data, vec))
+	      return 0;
+	  }
+	  break;
+
+	case DW_RLE_base_address:
+	  base = read_address (&rnglists_buf, u->addrsize);
+	  break;
+
+	case DW_RLE_start_end:
+	  {
+	    uint64_t low;
+	    uint64_t high;
+
+	    low = read_address (&rnglists_buf, u->addrsize);
+	    high = read_address (&rnglists_buf, u->addrsize);
+	    if (!add_range (state, rdata, low + base_address,
+			    high + base_address, error_callback, data,
+			    vec))
+	      return 0;
+	  }
+	  break;
+
+	case DW_RLE_start_length:
+	  {
+	    uint64_t low;
+	    uint64_t length;
+
+	    low = read_address (&rnglists_buf, u->addrsize);
+	    length = read_uleb128 (&rnglists_buf);
+	    low += base_address;
+	    if (!add_range (state, rdata, low, low + length,
+			    error_callback, data, vec))
+	      return 0;
+	  }
+	  break;
+
+	default:
+	  dwarf_buf_error (&rnglists_buf, "unrecognized DW_RLE value");
+	  return 0;
+	}
+    }
+
+  if (rnglists_buf.reported_underflow)
+    return 0;
+
+  return 1;
+}
+
+/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE.  RDATA is
+   passed to ADD_RANGE, and is either a struct unit * or a struct
+   function *.  VEC is the vector we are adding ranges to, and is
+   either a struct unit_addrs_vector * or a struct function_vector *.
+   Returns 1 on success, 0 on error.  */
+
+static int
+add_ranges (struct backtrace_state *state,
+	    const struct dwarf_sections *dwarf_sections,
+	    uintptr_t base_address, int is_bigendian,
+	    struct unit *u, uint64_t base, const struct pcrange *pcrange,
+	    int (*add_range) (struct backtrace_state *state, void *rdata, 
+			      uint64_t lowpc, uint64_t highpc,
+			      backtrace_error_callback error_callback,
+			      void *data, void *vec),
+	    void *rdata,
+	    backtrace_error_callback error_callback, void *data,
+	    void *vec)
+{
+  if (pcrange->have_lowpc && pcrange->have_highpc)
+    return add_low_high_range (state, dwarf_sections, base_address,
+			       is_bigendian, u, pcrange, add_range, rdata,
+			       error_callback, data, vec);
+
+  if (!pcrange->have_ranges)
+    {
+      /* Did not find any address ranges to add.  */
+      return 1;
+    }
+
+  if (u->version < 5)
+    return add_ranges_from_ranges (state, dwarf_sections, base_address,
+				   is_bigendian, u, base, pcrange, add_range,
+				   rdata, error_callback, data, vec);
+  else
+    return add_ranges_from_rnglists (state, dwarf_sections, base_address,
+				     is_bigendian, u, base, pcrange, add_range,
+				     rdata, error_callback, data, vec);
+}
+
 /* Find the address range covered by a compilation unit, reading from
    UNIT_BUF and adding values to U.  Returns 1 if all data could be
    read, 0 if there is some error.  */
@@ -1434,6 +1944,10 @@  find_address_ranges (struct backtrace_st
       uint64_t code;
       const struct abbrev *abbrev;
       struct pcrange pcrange;
+      struct attr_val name_val;
+      int have_name_val;
+      struct attr_val comp_dir_val;
+      int have_comp_dir_val;
       size_t i;
 
       code = read_uleb128 (unit_buf);
@@ -1448,15 +1962,17 @@  find_address_ranges (struct backtrace_st
 	*unit_tag = abbrev->tag;
 
       memset (&pcrange, 0, sizeof pcrange);
+      memset (&name_val, 0, sizeof name_val);
+      have_name_val = 0;
+      memset (&comp_dir_val, 0, sizeof comp_dir_val);
+      have_comp_dir_val = 0;
       for (i = 0; i < abbrev->num_attrs; ++i)
 	{
 	  struct attr_val val;
 
-	  if (!read_attribute (abbrev->attrs[i].form, unit_buf,
-			       u->is_dwarf64, u->version, u->addrsize,
-			       dwarf_sections->data[DEBUG_STR],
-			       dwarf_sections->size[DEBUG_STR],
-			       altlink, &val))
+	  if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+			       unit_buf, u->is_dwarf64, u->version,
+			       u->addrsize, dwarf_sections, altlink, &val))
 	    return 0;
 
 	  switch (abbrev->attrs[i].name)
@@ -1473,15 +1989,37 @@  find_address_ranges (struct backtrace_st
 	      break;
 
 	    case DW_AT_name:
-	      if (abbrev->tag == DW_TAG_compile_unit
-		  && val.encoding == ATTR_VAL_STRING)
-		u->filename = val.u.string;
+	      if (abbrev->tag == DW_TAG_compile_unit)
+		{
+		  name_val = val;
+		  have_name_val = 1;
+		}
 	      break;
 
 	    case DW_AT_comp_dir:
+	      if (abbrev->tag == DW_TAG_compile_unit)
+		{
+		  comp_dir_val = val;
+		  have_comp_dir_val = 1;
+		}
+	      break;
+
+	    case DW_AT_str_offsets_base:
+	      if (abbrev->tag == DW_TAG_compile_unit
+		  && val.encoding == ATTR_VAL_REF_SECTION)
+		u->str_offsets_base = val.u.uint;
+	      break;
+
+	    case DW_AT_addr_base:
+	      if (abbrev->tag == DW_TAG_compile_unit
+		  && val.encoding == ATTR_VAL_REF_SECTION)
+		u->addr_base = val.u.uint;
+	      break;
+
+	    case DW_AT_rnglists_base:
 	      if (abbrev->tag == DW_TAG_compile_unit
-		  && val.encoding == ATTR_VAL_STRING)
-		u->comp_dir = val.u.string;
+		  && val.encoding == ATTR_VAL_REF_SECTION)
+		u->rnglists_base = val.u.uint;
 	      break;
 
 	    default:
@@ -1489,6 +2027,23 @@  find_address_ranges (struct backtrace_st
 	    }
 	}
 
+      // Resolve strings after we're sure that we have seen
+      // DW_AT_str_offsets_base.
+      if (have_name_val)
+	{
+	  if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian,
+			       u->str_offsets_base, &name_val,
+			       error_callback, data, &u->filename))
+	    return 0;
+	}
+      if (have_comp_dir_val)
+	{
+	  if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian,
+			       u->str_offsets_base, &comp_dir_val,
+			       error_callback, data, &u->comp_dir))
+	    return 0;
+	}
+
       if (abbrev->tag == DW_TAG_compile_unit
 	  || abbrev->tag == DW_TAG_subprogram)
 	{
@@ -1565,6 +2120,7 @@  build_address_map (struct backtrace_stat
       int is_dwarf64;
       struct dwarf_buf unit_buf;
       int version;
+      int unit_type;
       uint64_t abbrev_offset;
       int addrsize;
       struct unit *u;
@@ -1583,12 +2139,24 @@  build_address_map (struct backtrace_stat
 	goto fail;
 
       version = read_uint16 (&unit_buf);
-      if (version < 2 || version > 4)
+      if (version < 2 || version > 5)
 	{
 	  dwarf_buf_error (&unit_buf, "unrecognized DWARF version");
 	  goto fail;
 	}
 
+      if (version < 5)
+	unit_type = 0;
+      else
+	{
+	  unit_type = read_byte (&unit_buf);
+	  if (unit_type == DW_UT_type || unit_type == DW_UT_split_type)
+	    {
+	      /* This unit doesn't have anything we need.  */
+	      continue;
+	    }
+	}
+
       pu = ((struct unit **)
 	    backtrace_vector_grow (state, sizeof (struct unit *),
 				   error_callback, data, &units));
@@ -1603,6 +2171,11 @@  build_address_map (struct backtrace_stat
       *pu = u;
       ++units_count;
 
+      if (version < 5)
+	addrsize = 0; /* Set below.  */
+      else
+	addrsize = read_byte (&unit_buf);
+
       memset (&u->abbrevs, 0, sizeof u->abbrevs);
       abbrev_offset = read_offset (&unit_buf, is_dwarf64);
       if (!read_abbrevs (state, abbrev_offset,
@@ -1611,7 +2184,21 @@  build_address_map (struct backtrace_stat
 			 is_bigendian, error_callback, data, &u->abbrevs))
 	goto fail;
 
-      addrsize = read_byte (&unit_buf);
+      if (version < 5)
+	addrsize = read_byte (&unit_buf);
+
+      switch (unit_type)
+	{
+	case 0:
+	  break;
+	case DW_UT_compile: case DW_UT_partial:
+	  break;
+	case DW_UT_skeleton: case DW_UT_split_compile:
+	  read_uint64 (&unit_buf); /* dwo_id */
+	  break;
+	default:
+	  break;
+	}
 
       u->low_offset = unit_offset;
       unit_offset += len + (is_dwarf64 ? 12 : 4);
@@ -1720,55 +2307,21 @@  free_line_header (struct backtrace_state
 		  error_callback, data);
 }
 
-/* Read the line header.  Return 1 on success, 0 on failure.  */
+/* Read the directories and file names for a line header for version
+   2, setting fields in HDR.  Return 1 on success, 0 on failure.  */
 
 static int
-read_line_header (struct backtrace_state *state, struct unit *u,
-		  int is_dwarf64, struct dwarf_buf *line_buf,
-		  struct line_header *hdr)
+read_v2_paths (struct backtrace_state *state, struct unit *u,
+	       struct dwarf_buf *hdr_buf, struct line_header *hdr)
 {
-  uint64_t hdrlen;
-  struct dwarf_buf hdr_buf;
   const unsigned char *p;
   const unsigned char *pend;
   size_t i;
 
-  hdr->version = read_uint16 (line_buf);
-  if (hdr->version < 2 || hdr->version > 4)
-    {
-      dwarf_buf_error (line_buf, "unsupported line number version");
-      return 0;
-    }
-
-  hdrlen = read_offset (line_buf, is_dwarf64);
-
-  hdr_buf = *line_buf;
-  hdr_buf.left = hdrlen;
-
-  if (!advance (line_buf, hdrlen))
-    return 0;
-
-  hdr->min_insn_len = read_byte (&hdr_buf);
-  if (hdr->version < 4)
-    hdr->max_ops_per_insn = 1;
-  else
-    hdr->max_ops_per_insn = read_byte (&hdr_buf);
-
-  /* We don't care about default_is_stmt.  */
-  read_byte (&hdr_buf);
-
-  hdr->line_base = read_sbyte (&hdr_buf);
-  hdr->line_range = read_byte (&hdr_buf);
-
-  hdr->opcode_base = read_byte (&hdr_buf);
-  hdr->opcode_lengths = hdr_buf.buf;
-  if (!advance (&hdr_buf, hdr->opcode_base - 1))
-    return 0;
-
   /* Count the number of directory entries.  */
   hdr->dirs_count = 0;
-  p = hdr_buf.buf;
-  pend = p + hdr_buf.left;
+  p = hdr_buf->buf;
+  pend = p + hdr_buf->left;
   while (p < pend && *p != '\0')
     {
       p += strnlen((const char *) p, pend - p) + 1;
@@ -1781,29 +2334,30 @@  read_line_header (struct backtrace_state
       hdr->dirs = ((const char **)
 		   backtrace_alloc (state,
 				    hdr->dirs_count * sizeof (const char *),
-				    line_buf->error_callback, line_buf->data));
+				    hdr_buf->error_callback,
+				    hdr_buf->data));
       if (hdr->dirs == NULL)
 	return 0;
     }
 
   i = 0;
-  while (*hdr_buf.buf != '\0')
+  while (*hdr_buf->buf != '\0')
     {
-      if (hdr_buf.reported_underflow)
+      if (hdr_buf->reported_underflow)
 	return 0;
 
-      hdr->dirs[i] = read_string (&hdr_buf);
+      hdr->dirs[i] = read_string (hdr_buf);
       if (hdr->dirs[i] == NULL)
 	return 0;
       ++i;
     }
-  if (!advance (&hdr_buf, 1))
+  if (!advance (hdr_buf, 1))
     return 0;
 
   /* Count the number of file entries.  */
   hdr->filenames_count = 0;
-  p = hdr_buf.buf;
-  pend = p + hdr_buf.left;
+  p = hdr_buf->buf;
+  pend = p + hdr_buf->left;
   while (p < pend && *p != '\0')
     {
       p += strnlen ((const char *) p, pend - p) + 1;
@@ -1816,23 +2370,23 @@  read_line_header (struct backtrace_state
   hdr->filenames = ((const char **)
 		    backtrace_alloc (state,
 				     hdr->filenames_count * sizeof (char *),
-				     line_buf->error_callback,
-				     line_buf->data));
+				     hdr_buf->error_callback,
+				     hdr_buf->data));
   if (hdr->filenames == NULL)
     return 0;
   i = 0;
-  while (*hdr_buf.buf != '\0')
+  while (*hdr_buf->buf != '\0')
     {
       const char *filename;
       uint64_t dir_index;
 
-      if (hdr_buf.reported_underflow)
+      if (hdr_buf->reported_underflow)
 	return 0;
 
-      filename = read_string (&hdr_buf);
+      filename = read_string (hdr_buf);
       if (filename == NULL)
 	return 0;
-      dir_index = read_uleb128 (&hdr_buf);
+      dir_index = read_uleb128 (hdr_buf);
       if (IS_ABSOLUTE_PATH (filename)
 	  || (dir_index == 0 && u->comp_dir == NULL))
 	hdr->filenames[i] = filename;
@@ -1849,16 +2403,16 @@  read_line_header (struct backtrace_state
 	    dir = hdr->dirs[dir_index - 1];
 	  else
 	    {
-	      dwarf_buf_error (line_buf,
+	      dwarf_buf_error (hdr_buf,
 			       ("invalid directory index in "
 				"line number program header"));
 	      return 0;
 	    }
 	  dir_len = strlen (dir);
 	  filename_len = strlen (filename);
-	  s = ((char *)
-	       backtrace_alloc (state, dir_len + filename_len + 2,
-				line_buf->error_callback, line_buf->data));
+	  s = ((char *) backtrace_alloc (state, dir_len + filename_len + 2,
+					 hdr_buf->error_callback,
+					 hdr_buf->data));
 	  if (s == NULL)
 	    return 0;
 	  memcpy (s, dir, dir_len);
@@ -1871,12 +2425,258 @@  read_line_header (struct backtrace_state
 	}
 
       /* Ignore the modification time and size.  */
-      read_uleb128 (&hdr_buf);
-      read_uleb128 (&hdr_buf);
+      read_uleb128 (hdr_buf);
+      read_uleb128 (hdr_buf);
 
       ++i;
     }
 
+  return 1;
+}
+
+/* Read a single version 5 LNCT entry for a directory or file name in a
+   line header.  Sets *STRING to the resulting name, ignoring other
+   data.  Return 1 on success, 0 on failure.  */
+
+static int
+read_lnct (struct backtrace_state *state, struct dwarf_data *ddata,
+	   struct unit *u, struct dwarf_buf *hdr_buf,
+	   const struct line_header *hdr, size_t formats_count,
+	   const struct line_header_format *formats, const char **string)
+{
+  size_t i;
+  const char *dir;
+  const char *path;
+
+  dir = NULL;
+  path = NULL;
+  for (i = 0; i < formats_count; i++)
+    {
+      struct attr_val val;
+
+      if (!read_attribute (formats[i].form, 0, hdr_buf, u->is_dwarf64,
+			   u->version, hdr->addrsize, &ddata->dwarf_sections,
+			   ddata->altlink, &val))
+	return 0;
+      switch (formats[i].lnct)
+	{
+	case DW_LNCT_path:
+	  if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+			       ddata->is_bigendian, u->str_offsets_base,
+			       &val, hdr_buf->error_callback, hdr_buf->data,
+			       &path))
+	    return 0;
+	  break;
+	case DW_LNCT_directory_index:
+	  if (val.encoding == ATTR_VAL_UINT)
+	    {
+	      if (val.u.uint >= hdr->dirs_count)
+		{
+		  dwarf_buf_error (hdr_buf,
+				   ("invalid directory index in "
+				    "line number program header"));
+		  return 0;
+		}
+	      dir = hdr->dirs[val.u.uint];
+	    }
+	  break;
+	default:
+	  /* We don't care about timestamps or sizes or hashes.  */
+	  break;
+	}
+    }
+
+  if (path == NULL)
+    {
+      dwarf_buf_error (hdr_buf,
+		       "missing file name in line number program header");
+      return 0;
+    }
+
+  if (dir == NULL)
+    *string = path;
+  else
+    {
+      size_t dir_len;
+      size_t path_len;
+      char *s;
+
+      dir_len = strlen (dir);
+      path_len = strlen (path);
+      s = (char *) backtrace_alloc (state, dir_len + path_len + 2,
+				    hdr_buf->error_callback, hdr_buf->data);
+      if (s == NULL)
+	return 0;
+      memcpy (s, dir, dir_len);
+      /* FIXME: If we are on a DOS-based file system, and the
+	 directory or the path name use backslashes, then we should
+	 use a backslash here.  */
+      s[dir_len] = '/';
+      memcpy (s + dir_len + 1, path, path_len + 1);
+      *string = s;
+    }
+
+  return 1;
+}
+
+/* Read a set of DWARF 5 line header format entries, setting *PCOUNT
+   and *PPATHS.  Return 1 on success, 0 on failure.  */
+
+static int
+read_line_header_format_entries (struct backtrace_state *state,
+				 struct dwarf_data *ddata,
+				 struct unit *u,
+				 struct dwarf_buf *hdr_buf,
+				 struct line_header *hdr,
+				 size_t *pcount,
+				 const char ***ppaths)
+{
+  size_t formats_count;
+  struct line_header_format *formats;
+  size_t paths_count;
+  const char **paths;
+  size_t i;
+  int ret;
+
+  formats_count = read_byte (hdr_buf);
+  if (formats_count == 0)
+    formats = NULL;
+  else
+    {
+      formats = ((struct line_header_format *)
+		 backtrace_alloc (state,
+				  (formats_count
+				   * sizeof (struct line_header_format)),
+				  hdr_buf->error_callback,
+				  hdr_buf->data));
+      if (formats == NULL)
+	return 0;
+
+      for (i = 0; i < formats_count; i++)
+	{
+	  formats[i].lnct = (int) read_uleb128(hdr_buf);
+	  formats[i].form = (enum dwarf_form) read_uleb128 (hdr_buf);
+	}
+    }
+
+  paths_count = read_uleb128 (hdr_buf);
+  if (paths_count == 0)
+    {
+      *pcount = 0;
+      *ppaths = NULL;
+      ret = 1;
+      goto exit;
+    }
+
+  paths = ((const char **)
+	   backtrace_alloc (state, paths_count * sizeof (const char *),
+			    hdr_buf->error_callback, hdr_buf->data));
+  if (paths == NULL)
+    {
+      ret = 0;
+      goto exit;
+    }
+  for (i = 0; i < paths_count; i++)
+    {
+      if (!read_lnct (state, ddata, u, hdr_buf, hdr, formats_count,
+		      formats, &paths[i]))
+	{
+	  backtrace_free (state, paths,
+			  paths_count * sizeof (const char *),
+			  hdr_buf->error_callback, hdr_buf->data);
+	  ret = 0;
+	  goto exit;
+	}
+    }
+
+  *pcount = paths_count;
+  *ppaths = paths;
+
+  ret = 1;
+
+ exit:
+  if (formats != NULL)
+    backtrace_free (state, formats,
+		    formats_count * sizeof (struct line_header_format),
+		    hdr_buf->error_callback, hdr_buf->data);
+
+  return  ret;
+}
+
+/* Read the line header.  Return 1 on success, 0 on failure.  */
+
+static int
+read_line_header (struct backtrace_state *state, struct dwarf_data *ddata,
+		  struct unit *u, int is_dwarf64, struct dwarf_buf *line_buf,
+		  struct line_header *hdr)
+{
+  uint64_t hdrlen;
+  struct dwarf_buf hdr_buf;
+
+  hdr->version = read_uint16 (line_buf);
+  if (hdr->version < 2 || hdr->version > 5)
+    {
+      dwarf_buf_error (line_buf, "unsupported line number version");
+      return 0;
+    }
+
+  if (hdr->version < 5)
+    hdr->addrsize = u->addrsize;
+  else
+    {
+      hdr->addrsize = read_byte (line_buf);
+      /* We could support a non-zero segment_selector_size but I doubt
+	 we'll ever see it.  */
+      if (read_byte (line_buf) != 0)
+	{
+	  dwarf_buf_error (line_buf,
+			   "non-zero segment_selector_size not supported");
+	  return 0;
+	}
+    }
+
+  hdrlen = read_offset (line_buf, is_dwarf64);
+
+  hdr_buf = *line_buf;
+  hdr_buf.left = hdrlen;
+
+  if (!advance (line_buf, hdrlen))
+    return 0;
+
+  hdr->min_insn_len = read_byte (&hdr_buf);
+  if (hdr->version < 4)
+    hdr->max_ops_per_insn = 1;
+  else
+    hdr->max_ops_per_insn = read_byte (&hdr_buf);
+
+  /* We don't care about default_is_stmt.  */
+  read_byte (&hdr_buf);
+
+  hdr->line_base = read_sbyte (&hdr_buf);
+  hdr->line_range = read_byte (&hdr_buf);
+
+  hdr->opcode_base = read_byte (&hdr_buf);
+  hdr->opcode_lengths = hdr_buf.buf;
+  if (!advance (&hdr_buf, hdr->opcode_base - 1))
+    return 0;
+
+  if (hdr->version < 5)
+    {
+      if (!read_v2_paths (state, u, &hdr_buf, hdr))
+	return 0;
+    }
+  else
+    {
+      if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr,
+					    &hdr->dirs_count,
+					    &hdr->dirs))
+	return 0;
+      if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr,
+					    &hdr->filenames_count,
+					    &hdr->filenames))
+	return 0;
+    }
+
   if (hdr_buf.reported_underflow)
     return 0;
 
@@ -1942,7 +2742,7 @@  read_line_program (struct backtrace_stat
 	      lineno = 1;
 	      break;
 	    case DW_LNE_set_address:
-	      address = read_address (line_buf, u->addrsize);
+	      address = read_address (line_buf, hdr->addrsize);
 	      break;
 	    case DW_LNE_define_file:
 	      {
@@ -1965,7 +2765,7 @@  read_line_program (struct backtrace_stat
 		    size_t f_len;
 		    char *p;
 
-		    if (dir_index == 0)
+		    if (dir_index == 0 && hdr->version < 5)
 		      dir = u->comp_dir;
 		    else if (dir_index - 1 < hdr->dirs_count)
 		      dir = hdr->dirs[dir_index - 1];
@@ -2129,7 +2929,7 @@  read_line_info (struct backtrace_state *
   len = read_initial_length (&line_buf, &is_dwarf64);
   line_buf.left = len;
 
-  if (!read_line_header (state, u, is_dwarf64, &line_buf, hdr))
+  if (!read_line_header (state, ddata, u, is_dwarf64, &line_buf, hdr))
     goto fail;
 
   if (!read_line_program (state, ddata, u, hdr, &line_buf, &vec))
@@ -2287,11 +3087,9 @@  read_referenced_name (struct dwarf_data
     {
       struct attr_val val;
 
-      if (!read_attribute (abbrev->attrs[i].form, &unit_buf,
-			   u->is_dwarf64, u->version, u->addrsize,
-			   ddata->dwarf_sections.data[DEBUG_STR],
-			   ddata->dwarf_sections.size[DEBUG_STR],
-			   ddata->altlink, &val))
+      if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+			   &unit_buf, u->is_dwarf64, u->version, u->addrsize,
+			   &ddata->dwarf_sections, ddata->altlink, &val))
 	return NULL;
 
       switch (abbrev->attrs[i].name)
@@ -2302,15 +3100,26 @@  read_referenced_name (struct dwarf_data
 	     normally not mangled.  */
 	  if (ret != NULL)
 	    break;
-	  if (val.encoding == ATTR_VAL_STRING)
-	    ret = val.u.string;
+	  if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+			       ddata->is_bigendian, u->str_offsets_base,
+			       &val, error_callback, data, &ret))
+	    return NULL;
 	  break;
 
 	case DW_AT_linkage_name:
 	case DW_AT_MIPS_linkage_name:
 	  /* First name preference: override all.  */
-	  if (val.encoding == ATTR_VAL_STRING)
-	    return val.u.string;
+	  {
+	    const char *s;
+
+	    s = NULL;
+	    if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+				 ddata->is_bigendian, u->str_offsets_base,
+				 &val, error_callback, data, &s))
+	      return NULL;
+	    if (s != NULL)
+	      return s;
+	  }
 	  break;
 
 	case DW_AT_specification:
@@ -2430,19 +3239,28 @@  read_function_entry (struct backtrace_st
 	{
 	  struct attr_val val;
 
-	  if (!read_attribute (abbrev->attrs[i].form, unit_buf,
-			       u->is_dwarf64, u->version, u->addrsize,
-			       ddata->dwarf_sections.data[DEBUG_STR],
-			       ddata->dwarf_sections.size[DEBUG_STR],
+	  if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+			       unit_buf, u->is_dwarf64, u->version,
+			       u->addrsize, &ddata->dwarf_sections,
 			       ddata->altlink, &val))
 	    return 0;
 
 	  /* The compile unit sets the base address for any address
 	     ranges in the function entries.  */
 	  if (abbrev->tag == DW_TAG_compile_unit
-	      && abbrev->attrs[i].name == DW_AT_low_pc
-	      && val.encoding == ATTR_VAL_ADDRESS)
-	    base = val.u.uint;
+	      && abbrev->attrs[i].name == DW_AT_low_pc)
+	    {
+	      if (val.encoding == ATTR_VAL_ADDRESS)
+		base = val.u.uint;
+	      else if (val.encoding == ATTR_VAL_ADDRESS_INDEX)
+		{
+		  if (!resolve_addr_index (&ddata->dwarf_sections,
+					   u->addr_base, u->addrsize,
+					   ddata->is_bigendian, val.u.uint,
+					   error_callback, data, &base))
+		    return 0;
+		}
+	    }
 
 	  if (is_function)
 	    {
@@ -2495,18 +3313,31 @@  read_function_entry (struct backtrace_st
 		  /* Third name preference: don't override.  */
 		  if (function->name != NULL)
 		    break;
-		  if (val.encoding == ATTR_VAL_STRING)
-		    function->name = val.u.string;
+		  if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+				       ddata->is_bigendian,
+				       u->str_offsets_base, &val,
+				       error_callback, data, &function->name))
+		    return 0;
 		  break;
 
 		case DW_AT_linkage_name:
 		case DW_AT_MIPS_linkage_name:
 		  /* First name preference: override all.  */
-		  if (val.encoding == ATTR_VAL_STRING)
-		    {
-		      function->name = val.u.string;
-		      have_linkage_name = 1;
-		    }
+		  {
+		    const char *s;
+
+		    s = NULL;
+		    if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+					 ddata->is_bigendian,
+					 u->str_offsets_base, &val,
+					 error_callback, data, &s))
+		      return 0;
+		    if (s != NULL)
+		      {
+			function->name = s;
+			have_linkage_name = 1;
+		      }
+		  }
 		  break;
 
 		case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges:
Index: elf.c
===================================================================
--- elf.c	(revision 279211)
+++ elf.c	(working copy)
@@ -346,6 +346,10 @@  static const char * const dwarf_section_
   ".debug_abbrev",
   ".debug_ranges",
   ".debug_str",
+  ".debug_addr",
+  ".debug_str_offsets",
+  ".debug_line_str",
+  ".debug_rnglists"
 };
 
 /* Information we gather for the sections we care about.  */
Index: internal.h
===================================================================
--- internal.h	(revision 279211)
+++ internal.h	(working copy)
@@ -295,6 +295,10 @@  enum dwarf_section
   DEBUG_ABBREV,
   DEBUG_RANGES,
   DEBUG_STR,
+  DEBUG_ADDR,
+  DEBUG_STR_OFFSETS,
+  DEBUG_LINE_STR,
+  DEBUG_RNGLISTS,
 
   DEBUG_MAX
 };
Index: pecoff.c
===================================================================
--- pecoff.c	(revision 279211)
+++ pecoff.c	(working copy)
@@ -141,7 +141,11 @@  static const char * const debug_section_
   ".debug_line",
   ".debug_abbrev",
   ".debug_ranges",
-  ".debug_str"
+  ".debug_str",
+  ".debug_addr",
+  ".debug_str_offsets",
+  ".debug_line_str",
+  ".debug_rnglists"
 };
 
 /* Information we gather for the sections we care about.  */
Index: xcoff.c
===================================================================
--- xcoff.c	(revision 279211)
+++ xcoff.c	(working copy)
@@ -1286,6 +1286,8 @@  xcoff_add (struct backtrace_state *state
 			      + (dwsect[i].offset - min_offset));
 	}
 
+      memset (&dwarf_sections, 0, sizeof dwarf_sections);
+
       dwarf_sections.data[DEBUG_INFO] = dwsect[DEBUG_INFO].data;
       dwarf_sections.size[DEBUG_INFO] = dwsect[DEBUG_INFO].size;
 #if BACKTRACE_XCOFF_SIZE == 32