Blob Blame History Raw
# Contains DWARF 5 support and includes the following upstream patches:

6b0019cd0c5ae18a59affa49a857b4d610d403e4
a834fccf3c94f78ad6a1b35ae352b1ede183dde4
86408cd826c32229817071bd008d9856cda4aca5
0d1003bc723ba30bfe325bb51aeafe7dbfed6a5c

--- ./tools/debugedit.c.orig	2021-03-30 09:34:25.206392109 +0000
+++ ./tools/debugedit.c	2021-03-30 09:34:40.186365656 +0000
@@ -103,6 +103,8 @@ static bool need_string_replacement = fa
 /* Whether we need to do any updates of the string indexes (DW_FORM_strp)
    in debug_info for string indexes. */
 static bool need_strp_update = false;
+/* Likewise for DW_FORM_line_strp. */
+static bool need_line_strp_update = false;
 /* If the debug_line changes size we will need to update the
    DW_AT_stmt_list attributes indexes in the debug_info. */
 static bool need_stmt_update = false;
@@ -192,7 +194,7 @@ typedef struct
   const char *filename;
   int lastscn;
   size_t phnum;
-  struct strings strings;
+  struct strings debug_str, debug_line_str;
   struct debug_lines lines;
   GElf_Shdr shdr[0];
 } DSO;
@@ -233,7 +235,7 @@ typedef struct
   int shift = 0;			\
   do					\
     {					\
-      c = *ptr++;			\
+      c = *(ptr)++;			\
       ret |= (c & 0x7f) << shift;	\
       shift += 7;			\
     } while (c & 0x80);			\
@@ -251,7 +253,7 @@ typedef struct
       valv >>= 7;			\
       if (valv)				\
 	c |= 0x80;			\
-      *ptr++ = c;			\
+      *(ptr)++ = c;			\
     }					\
   while (valv);				\
 })
@@ -311,7 +313,7 @@ strptr (DSO *dso, int sec, off_t offset)
 }
 
 
-#define read_8(ptr) *ptr++
+#define read_8(ptr) *(ptr)++
 
 #define read_16(ptr) ({					\
   uint16_t ret = do_read_16 (ptr);			\
@@ -328,13 +330,13 @@ strptr (DSO *dso, int sec, off_t offset)
 REL *relptr, *relend;
 int reltype;
 
-#define do_read_32_relocated(ptr) ({			\
-  uint32_t dret = do_read_32 (ptr);			\
+#define do_read_32_relocated(xptr) ({			\
+  uint32_t dret = do_read_32 (xptr);			\
   if (relptr)						\
     {							\
-      while (relptr < relend && relptr->ptr < ptr)	\
+      while (relptr < relend && relptr->ptr < (xptr))	\
 	++relptr;					\
-      if (relptr < relend && relptr->ptr == ptr)	\
+      if (relptr < relend && relptr->ptr == (xptr))	\
 	{						\
 	  if (reltype == SHT_REL)			\
 	    dret += relptr->addend;			\
@@ -433,7 +435,8 @@ typedef struct debug_section
     int sec, relsec;
     REL *relbuf;
     REL *relend;
-    struct debug_section *next; /* Only happens for COMDAT .debug_macro.  */
+    /* Only happens for COMDAT .debug_macro and .debug_types.  */
+    struct debug_section *next;
   } debug_section;
 
 static debug_section debug_sections[] =
@@ -452,6 +455,11 @@ static debug_section debug_sections[] =
 #define DEBUG_TYPES	11
 #define DEBUG_MACRO	12
 #define DEBUG_GDB_SCRIPT	13
+#define DEBUG_RNGLISTS	14
+#define DEBUG_LINE_STR	15
+#define DEBUG_ADDR	16
+#define DEBUG_STR_OFFSETS	17
+#define DEBUG_LOCLISTS	18
     { ".debug_info", NULL, NULL, 0, 0, 0 },
     { ".debug_abbrev", NULL, NULL, 0, 0, 0 },
     { ".debug_line", NULL, NULL, 0, 0, 0 },
@@ -466,6 +474,11 @@ static debug_section debug_sections[] =
     { ".debug_types", NULL, NULL, 0, 0, 0 },
     { ".debug_macro", NULL, NULL, 0, 0, 0 },
     { ".debug_gdb_scripts", NULL, NULL, 0, 0, 0 },
+    { ".debug_rnglists", NULL, NULL, 0, 0, 0 },
+    { ".debug_line_str", NULL, NULL, 0, 0, 0 },
+    { ".debug_addr", NULL, NULL, 0, 0, 0 },
+    { ".debug_str_offsets", NULL, NULL, 0, 0, 0 },
+    { ".debug_loclists", NULL, NULL, 0, 0, 0 },
     { NULL, NULL, NULL, 0, 0, 0 }
   };
 
@@ -542,10 +555,11 @@ setup_relbuf (DSO *dso, debug_section *s
       /* Relocations against section symbols are uninteresting in REL.  */
       if (dso->shdr[i].sh_type == SHT_REL && sym.st_value == 0)
 	continue;
-      /* Only consider relocations against .debug_str, .debug_line
-	 and .debug_abbrev.  */
+      /* Only consider relocations against .debug_str, .debug_line,
+	 .debug_line_str, and .debug_abbrev.  */
       if (sym.st_shndx != debug_sections[DEBUG_STR].sec
 	  && sym.st_shndx != debug_sections[DEBUG_LINE].sec
+	  && sym.st_shndx != debug_sections[DEBUG_LINE_STR].sec
 	  && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec)
 	continue;
       rela.r_addend += sym.st_value;
@@ -754,12 +768,29 @@ no_memory:
 	    }
 	  form = read_uleb128 (ptr);
 	  if (form == 2
-	      || (form > DW_FORM_flag_present && form != DW_FORM_ref_sig8))
+	      || (form > DW_FORM_flag_present
+		  && !(form == DW_FORM_ref_sig8
+		       || form == DW_FORM_data16
+		       || form == DW_FORM_line_strp
+		       || form == DW_FORM_implicit_const
+		       || form == DW_FORM_addrx
+		       || form == DW_FORM_loclistx
+		       || form == DW_FORM_rnglistx
+		       || form == DW_FORM_addrx1
+		       || form == DW_FORM_addrx2
+		       || form == DW_FORM_addrx3
+		       || form == DW_FORM_addrx4)))
 	    {
-	      error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, form);
+	      error (0, 0, "%s: Unknown DWARF DW_FORM_0x%x", dso->filename,
+		     form);
 	      htab_delete (h);
 	      return NULL;
 	    }
+	  if (form == DW_FORM_implicit_const)
+	    {
+	      /* It is SLEB128 but the value is dropped anyway.  */
+	      read_uleb128 (ptr);
+	    }
 
 	  t->attr[t->nattr].attr = attr;
 	  t->attr[t->nattr++].form = form;
@@ -1022,17 +1053,20 @@ string_find_entry (struct strings *strin
    a replacement file string has been recorded for it, otherwise
    returns false.  */
 static bool
-record_file_string_entry_idx (struct strings *strings, size_t old_idx)
+record_file_string_entry_idx (bool line_strp, DSO *dso, size_t old_idx)
 {
+  struct strings *strings = line_strp ? &dso->debug_line_str : &dso->debug_str;
   bool ret = false;
   struct stridxentry *entry = string_find_new_entry (strings, old_idx);
   if (entry != NULL)
     {
-      if (old_idx >= debug_sections[DEBUG_STR].size)
-	error (1, 0, "Bad string pointer index %zd", old_idx);
+      debug_section *sec = &debug_sections[line_strp
+					   ? DEBUG_LINE_STR : DEBUG_STR];
+      if (old_idx >= sec->size)
+	error (1, 0, "Bad string pointer index %zd (%s)", old_idx, sec->name);
 
       Strent *strent;
-      const char *old_str = (char *)debug_sections[DEBUG_STR].data + old_idx;
+      const char *old_str = (char *)sec->data + old_idx;
       const char *file = skip_dir_prefix (old_str, base_dir);
       if (file == NULL)
 	{
@@ -1076,15 +1110,18 @@ record_file_string_entry_idx (struct str
    base_dir with dest_dir, just records the existing string associated
    with the index. */
 static void
-record_existing_string_entry_idx (struct strings *strings, size_t old_idx)
+record_existing_string_entry_idx (bool line_strp, DSO *dso, size_t old_idx)
 {
+  struct strings *strings = line_strp ? &dso->debug_line_str : &dso->debug_str;
   struct stridxentry *entry = string_find_new_entry (strings, old_idx);
   if (entry != NULL)
     {
-      if (old_idx >= debug_sections[DEBUG_STR].size)
-	error (1, 0, "Bad string pointer index %zd", old_idx);
+      debug_section *sec = &debug_sections[line_strp
+					   ? DEBUG_LINE_STR : DEBUG_STR];
+      if (old_idx >= sec->size)
+	error (1, 0, "Bad string pointer index %zd (%s)", old_idx, sec->name);
 
-      const char *str = (char *)debug_sections[DEBUG_STR].data + old_idx;
+      const char *str = (char *)sec->data + old_idx;
       Strent *strent = strtab_add_len (strings->str_tab,
 				       str, strlen (str) + 1);
       if (strent == NULL)
@@ -1217,13 +1254,28 @@ get_line_table (DSO *dso, size_t off, st
 
   /* version */
   t->version = read_16 (ptr);
-  if (t->version != 2 && t->version != 3 && t->version != 4)
+  if (t->version != 2 && t->version != 3 && t->version != 4 && t->version != 5)
     {
       error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
 	     t->version);
       return false;
     }
 
+  if (t->version >= 5)
+    {
+      /* address_size */
+      assert (ptr_size != 0);
+      if (ptr_size != read_8 (ptr))
+	{
+	  error (0, 0, "%s: .debug_line address size differs from .debug_info",
+		 dso->filename);
+	  return false;
+	}
+
+      /* segment_selector_size */
+      (void) read_8 (ptr);
+    }
+
   /* header_length */
   unsigned char *endprol = ptr + 4;
   t->header_length = read_32 (ptr);
@@ -1269,7 +1321,9 @@ static int dirty_elf;
 static void
 dirty_section (unsigned int sec)
 {
-  elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY);
+  for (struct debug_section *secp = &debug_sections[sec]; secp != NULL;
+       secp = secp->next)
+    elf_flagdata (secp->elf_data, ELF_C_SET, ELF_F_DIRTY);
   dirty_elf = 1;
 }
 
@@ -1314,7 +1368,9 @@ edit_dwarf2_line (DSO *dso)
 
   linedata->d_size = dso->lines.debug_lines_len;
   linedata->d_buf = dso->lines.line_buf;
+  debug_sections[DEBUG_LINE].data = linedata->d_buf;
   debug_sections[DEBUG_LINE].size = linedata->d_size;
+  debug_sections[DEBUG_LINE].elf_data = linedata;
 
   /* Make sure the line tables are sorted on the old index. */
   qsort (dso->lines.table, dso->lines.used, sizeof (struct line_table),
@@ -1454,42 +1510,151 @@ edit_dwarf2_line (DSO *dso)
     }
 }
 
-/* Called during phase zero for each debug_line table referenced from
-   .debug_info.  Outputs all source files seen and records any
-   adjustments needed in the debug_list data structures. Returns true
-   if line_table needs to be rewrite either the dir or file paths. */
-static bool
-read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir)
+/* Record or adjust (according to phase) DW_FORM_strp or DW_FORM_line_strp.  */
+static void
+edit_strp (DSO *dso, bool line_strp, unsigned char *ptr, int phase,
+	   bool handled_strp)
 {
-  unsigned char *ptr, *dir;
-  unsigned char **dirt;
-  uint32_t value, dirt_cnt;
-  size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir);
-  struct line_table *table;
+  unsigned char *ptr_orig = ptr;
 
-  if (get_line_table (dso, off, &table) == false
-      || table == NULL)
+  /* In the first pass we collect all strings, in the
+     second we put the new references back (if there are
+     any changes).  */
+  if (phase == 0)
     {
-      if (table != NULL)
-	error (0, 0, ".debug_line offset 0x%x referenced multiple times",
-	       off);
-      return false;
+      /* handled_strp is set for attributes referring to
+	 files. If it is set the string is already
+	 recorded. */
+      if (! handled_strp)
+	{
+	  size_t idx = do_read_32_relocated (ptr);
+	  record_existing_string_entry_idx (line_strp, dso, idx);
+	}
+    }
+  else if (line_strp
+	   ? need_line_strp_update : need_strp_update) /* && phase == 1 */
+    {
+      struct stridxentry *entry;
+      size_t idx, new_idx;
+      struct strings *strings = (line_strp
+				 ? &dso->debug_line_str : &dso->debug_str);
+      idx = do_read_32_relocated (ptr);
+      entry = string_find_entry (strings, idx);
+      new_idx = strent_offset (entry->entry);
+      do_write_32_relocated (ptr, new_idx);
     }
 
-  /* Skip to the directory table. The rest of the header has already
-     been read and checked by get_line_table. */
-  ptr = debug_sections[DEBUG_LINE].data + off;
-  ptr += (4 /* unit len */
-	  + 2 /* version */
-	  + 4 /* header len */
-	  + 1 /* min instr len */
-	  + (table->version >= 4) /* max op per instr, if version >= 4 */
-	  + 1 /* default is stmt */
-	  + 1 /* line base */
-	  + 1 /* line range */
-	  + 1 /* opcode base */
-	  + table->opcode_base - 1); /* opcode len table */
-  dir = ptr;
+  assert (ptr == ptr_orig);
+}
+
+/* Adjust *PTRP after the current *FORMP, update *FORMP for FORM_INDIRECT.  */
+static enum { FORM_OK, FORM_ERROR, FORM_INDIRECT }
+skip_form (DSO *dso, uint32_t *formp, unsigned char **ptrp)
+{
+  size_t len = 0;
+
+  switch (*formp)
+    {
+    case DW_FORM_ref_addr:
+      if (cu_version == 2)
+	*ptrp += ptr_size;
+      else
+	*ptrp += 4;
+      break;
+    case DW_FORM_flag_present:
+    case DW_FORM_implicit_const:
+      break;
+    case DW_FORM_addr:
+      *ptrp += ptr_size;
+      break;
+    case DW_FORM_ref1:
+    case DW_FORM_flag:
+    case DW_FORM_data1:
+    case DW_FORM_strx1:
+    case DW_FORM_addrx1:
+      ++*ptrp;
+      break;
+    case DW_FORM_ref2:
+    case DW_FORM_data2:
+    case DW_FORM_strx2:
+    case DW_FORM_addrx2:
+      *ptrp += 2;
+      break;
+    case DW_FORM_strx3:
+    case DW_FORM_addrx3:
+      *ptrp += 3;
+      break;
+    case DW_FORM_ref4:
+    case DW_FORM_data4:
+    case DW_FORM_strx4:
+    case DW_FORM_addrx4:
+    case DW_FORM_sec_offset:
+      *ptrp += 4;
+      break;
+    case DW_FORM_ref8:
+    case DW_FORM_data8:
+    case DW_FORM_ref_sig8:
+      *ptrp += 8;
+      break;
+    case DW_FORM_data16:
+      *ptrp += 16;
+      break;
+    case DW_FORM_sdata:
+    case DW_FORM_ref_udata:
+    case DW_FORM_udata:
+    case DW_FORM_strx:
+    case DW_FORM_loclistx:
+    case DW_FORM_rnglistx:
+    case DW_FORM_addrx:
+      read_uleb128 (*ptrp);
+      break;
+    case DW_FORM_strp:
+    case DW_FORM_line_strp:
+      *ptrp += 4;
+      break;
+    case DW_FORM_string:
+      *ptrp = (unsigned char *) strchr ((char *)*ptrp, '\0') + 1;
+      break;
+    case DW_FORM_indirect:
+      *formp = read_uleb128 (*ptrp);
+      return FORM_INDIRECT;
+    case DW_FORM_block1:
+      len = *(*ptrp)++;
+      break;
+    case DW_FORM_block2:
+      len = read_16 (*ptrp);
+      *formp = DW_FORM_block1;
+      break;
+    case DW_FORM_block4:
+      len = read_32 (*ptrp);
+      *formp = DW_FORM_block1;
+      break;
+    case DW_FORM_block:
+    case DW_FORM_exprloc:
+      len = read_uleb128 (*ptrp);
+      *formp = DW_FORM_block1;
+      assert (len < UINT_MAX);
+      break;
+    default:
+      error (0, 0, "%s: Unknown DWARF DW_FORM_0x%x", dso->filename, *formp);
+      return FORM_ERROR;
+    }
+
+  if (*formp == DW_FORM_block1)
+    *ptrp += len;
+
+  return FORM_OK;
+}
+
+/* Part of read_dwarf2_line processing DWARF-4.  */
+static bool
+read_dwarf4_line (DSO *dso, unsigned char *ptr, char *comp_dir,
+		  struct line_table *table)
+{
+  unsigned char **dirt;
+  uint32_t value, dirt_cnt;
+  size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir);
+  unsigned char *dir = ptr;
 
   /* dir table: */
   value = 1;
@@ -1622,6 +1787,296 @@ read_dwarf2_line (DSO *dso, uint32_t off
       read_uleb128 (ptr);
     }
 
+  return true;
+}
+
+/* Called by read_dwarf5_line first for directories and then file
+   names as they both have the same format.  */
+static bool
+read_dwarf5_line_entries (DSO *dso, unsigned char **ptrp,
+			  struct line_table *table, int phase,
+			  char ***dirs, int *ndir,
+			  const char *entry_name)
+{
+  /* directory_entry_format_count */
+  /* file_name_entry_format_count */
+  unsigned format_count = read_8 (*ptrp);
+
+  unsigned char *formats = *ptrp;
+
+  /* directory_entry_format */
+  /* file_name_entry_format */
+  for (unsigned formati = 0; formati < format_count; ++formati)
+    {
+      read_uleb128 (*ptrp);
+      read_uleb128 (*ptrp);
+    }
+
+  /* directories_count */
+  /* file_names_count */
+  unsigned entry_count = read_uleb128 (*ptrp);
+
+  bool collecting_dirs = dest_dir && phase == 0 && *dirs == NULL;
+  bool writing_files = dest_dir && phase == 0 && *dirs != NULL;
+  if (collecting_dirs)
+    {
+      *ndir = entry_count;
+      *dirs = malloc (entry_count * sizeof (char *));
+      if (*dirs == NULL)
+	error (1, errno, "%s: Could not allocate debug_line dirs",
+	       dso->filename);
+    }
+
+  /* directories */
+  /* file_names */
+  for (unsigned entryi = 0; entryi < entry_count; ++entryi)
+    {
+      char *dir = NULL;
+      char *file = NULL;;
+      unsigned char *format_ptr = formats;
+      for (unsigned formati = 0; formati < format_count; ++formati)
+	{
+	  unsigned lnct = read_uleb128 (format_ptr);
+	  unsigned form = read_uleb128 (format_ptr);
+	  bool handled_form = false;
+	  bool handled_strp = false;
+	  bool line_strp = form == DW_FORM_line_strp;
+	  if (lnct == DW_LNCT_path)
+	    {
+	      switch (form)
+		{
+		case DW_FORM_strp:
+		case DW_FORM_line_strp:
+		  if (dest_dir && phase == 0)
+		    {
+		      size_t idx = do_read_32_relocated (*ptrp);
+		      if (record_file_string_entry_idx (line_strp, dso, idx))
+			{
+			  if (line_strp)
+			    need_line_strp_update = true;
+			  else
+			    need_strp_update = true;
+			}
+		      handled_strp = true;
+		      if (collecting_dirs || writing_files)
+			{
+			  debug_section *sec = &debug_sections[line_strp
+                                           ? DEBUG_LINE_STR : DEBUG_STR];
+			  if (collecting_dirs)
+			    dir = (char *)sec->data + idx;
+			  if (writing_files)
+			    file = (char *)sec->data + idx;
+			}
+		    }
+		  break;
+		default:
+		  error (0, 0, "%s: Unsupported "
+			 ".debug_line %s %u path DW_FORM_0x%x",
+			 dso->filename, entry_name, entryi, form);
+		  return false;
+		}
+	    }
+	  if (writing_files && lnct == DW_LNCT_directory_index)
+	    {
+	      int dirndx;
+	      switch (form)
+		{
+		case DW_FORM_udata:
+		  handled_form = true;
+		  dirndx = read_uleb128 (*ptrp);
+		  break;
+		case DW_FORM_data1:
+		  dirndx = **ptrp;
+		  break;
+		case DW_FORM_data2:
+		  dirndx = do_read_16 (*ptrp);
+		  break;
+		case DW_FORM_data4:
+		  dirndx = do_read_32 (*ptrp);
+		  break;
+		default:
+		  error (0, 0, "%s: Unsupported "
+			 ".debug_line %s %u dirndx DW_FORM_0x%x",
+			 dso->filename, entry_name, entryi, form);
+		  return false;
+		}
+
+	      if (dirndx > *ndir)
+		{
+		  error (0, 0, "%s: Bad dir number %u in .debug_line %s",
+			 dso->filename, entryi, entry_name);
+		  return false;
+		}
+	      dir = (*dirs)[dirndx];
+	    }
+
+	  switch (form)
+	    {
+	    case DW_FORM_strp:
+	    case DW_FORM_line_strp:
+	      edit_strp (dso, line_strp, *ptrp, phase, handled_strp);
+	      break;
+	    }
+
+	  if (!handled_form)
+	    {
+	      switch (skip_form (dso, &form, ptrp))
+		{
+		case FORM_OK:
+		  break;
+		case FORM_ERROR:
+		  return false;
+		case FORM_INDIRECT:
+		  error (0, 0, "%s: Unsupported "
+			 ".debug_line %s %u DW_FORM_indirect",
+			 dso->filename, entry_name, entryi);
+		  return false;
+		}
+	    }
+	}
+
+      if (collecting_dirs)
+	(*dirs)[entryi] = dir;
+
+      if (writing_files)
+	{
+	  char *comp_dir = (*dirs)[0];
+	  size_t comp_dir_len = strlen(comp_dir);
+	  size_t file_len = strlen (file);
+	  size_t dir_len = strlen (dir);
+
+	  char *s = malloc (comp_dir_len + 1 + file_len + 1 + dir_len + 1);
+	  if (s == NULL)
+	    {
+	      error (0, ENOMEM, "%s: Reading file table", dso->filename);
+	      return false;
+	    }
+	  if (file[0] == '/')
+	    {
+	      memcpy (s, file, file_len + 1);
+	    }
+	  else if (dir[0] == '/')
+	    {
+	      memcpy (s, dir, dir_len);
+	      s[dir_len] = '/';
+	      memcpy (s + dir_len + 1, file, file_len + 1);
+	    }
+	  else
+	    {
+	      char *p = s;
+	      if (comp_dir_len != 0)
+		{
+		  memcpy (s, comp_dir, comp_dir_len);
+		  s[comp_dir_len] = '/';
+		  p += comp_dir_len + 1;
+		}
+	      memcpy (p, dir, dir_len);
+	      p[dir_len] = '/';
+	      memcpy (p + dir_len + 1, file, file_len + 1);
+	    }
+	  canonicalize_path (s, s);
+	  if (list_file_fd != -1)
+	    {
+	      const char *p = NULL;
+	      if (base_dir == NULL)
+		p = s;
+	      else
+		{
+		  p = skip_dir_prefix (s, base_dir);
+		  if (p == NULL && dest_dir != NULL)
+		    p = skip_dir_prefix (s, dest_dir);
+		}
+
+	      if (p)
+		{
+		  size_t size = strlen (p) + 1;
+		  while (size > 0)
+		    {
+		      ssize_t ret = write (list_file_fd, p, size);
+		      if (ret == -1)
+			break;
+		      size -= ret;
+		      p += ret;
+		    }
+		}
+	    }
+
+	  free (s);
+	}
+    }
+
+  return true;
+}
+
+/* Part of read_dwarf2_line processing DWARF-5.  */
+static bool
+read_dwarf5_line (DSO *dso, unsigned char *ptr, struct line_table *table,
+		  int phase)
+{
+  char **dirs = NULL;
+  int ndir;
+  /* Skip header.  */
+  ptr += (4 /* unit len */
+          + 2 /* version */
+          + (table->version < 5 ? 0 : 0
+             + 1 /* address_size */
+             + 1 /* segment_selector*/)
+          + 4 /* header len */
+          + 1 /* min instr len */
+          + (table->version >= 4) /* max op per instr, if version >= 4 */
+          + 1 /* default is stmt */
+          + 1 /* line base */
+          + 1 /* line range */
+          + 1 /* opcode base */
+          + table->opcode_base - 1); /* opcode len table */
+
+  bool retval = (read_dwarf5_line_entries (dso, &ptr, table, phase,
+					   &dirs, &ndir, "directory")
+		 && read_dwarf5_line_entries (dso, &ptr, table, phase,
+					      &dirs, &ndir, "file name"));
+  free (dirs);
+  return retval;
+}
+
+/* Called during phase zero for each debug_line table referenced from
+   .debug_info.  Outputs all source files seen and records any
+   adjustments needed in the debug_list data structures. Returns true
+   if line_table needs to be rewrite either the dir or file paths. */
+static bool
+read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir)
+{
+  unsigned char *ptr;
+  struct line_table *table;
+
+  if (get_line_table (dso, off, &table) == false
+      || table == NULL)
+    return false;
+
+  /* Skip to the directory table. The rest of the header has already
+     been read and checked by get_line_table. */
+  ptr = debug_sections[DEBUG_LINE].data + off;
+  ptr += (4 /* unit len */
+	  + 2 /* version */
+	  + (table->version < 5 ? 0 : 0
+	     + 1 /* address_size */
+	     + 1 /* segment_selector*/)
+	  + 4 /* header len */
+	  + 1 /* min instr len */
+	  + (table->version >= 4) /* max op per instr, if version >= 4 */
+	  + 1 /* default is stmt */
+	  + 1 /* line base */
+	  + 1 /* line range */
+	  + 1 /* opcode base */
+	  + table->opcode_base - 1); /* opcode len table */
+
+  /* DWARF version 5 line tables won't change size. But they might need
+     [line]strp recording/updates. Handle that part later.  */
+  if (table->version < 5)
+    {
+      if (! read_dwarf4_line (dso, ptr, comp_dir, table))
+	return false;
+    }
+
   dso->lines.debug_lines_len += 4 + table->unit_length + table->size_diff;
   return table->replace_dirs || table->replace_files;
 }
@@ -1639,6 +2094,40 @@ find_new_list_offs (struct debug_lines *
   return table->new_idx;
 }
 
+/* Read DW_FORM_strp or DW_FORM_line_strp collecting compilation directory.  */
+static void
+edit_attributes_str_comp_dir (bool line_strp, DSO *dso, unsigned char **ptrp,
+			      int phase, char **comp_dirp, bool *handled_strpp)
+{
+  const char *dir;
+  size_t idx = do_read_32_relocated (*ptrp);
+  /* In phase zero we collect the comp_dir.  */
+  if (phase == 0)
+    {
+      debug_section *sec = &debug_sections[line_strp
+					   ? DEBUG_LINE_STR : DEBUG_STR];
+      if (sec->data == NULL || idx >= sec->size)
+	error (1, 0, "%s: Bad string pointer index %zd for comp_dir (%s)",
+	       dso->filename, idx, sec->name);
+      dir = (char *) sec->data + idx;
+
+      free (*comp_dirp);
+      *comp_dirp = strdup (dir);
+    }
+
+  if (dest_dir != NULL && phase == 0)
+    {
+      if (record_file_string_entry_idx (line_strp, dso, idx))
+	{
+	  if (line_strp)
+	    need_line_strp_update = true;
+	  else
+	    need_strp_update = true;
+	}
+      *handled_strpp = true;
+    }
+}
+
 /* This scans the attributes of one DIE described by the given abbrev_tag.
    PTR points to the data in the debug_info. It will be advanced till all
    abbrev data is consumed. In phase zero data is collected, in phase one
@@ -1657,7 +2146,6 @@ edit_attributes (DSO *dso, unsigned char
   for (i = 0; i < t->nattr; ++i)
     {
       uint32_t form = t->attr[i].form;
-      size_t len = 0;
       while (1)
 	{
 	  /* Whether we already handled a string as file for this
@@ -1743,38 +2231,24 @@ edit_attributes (DSO *dso, unsigned char
 			}
 		    }
 		}
-	      else if (form == DW_FORM_strp &&
-		       debug_sections[DEBUG_STR].data)
-		{
-		  const char *dir;
-		  size_t idx = do_read_32_relocated (ptr);
-		  /* In phase zero we collect the comp_dir.  */
-		  if (phase == 0)
-		    {
-		      if (idx >= debug_sections[DEBUG_STR].size)
-			error (1, 0,
-			       "%s: Bad string pointer index %zd for comp_dir",
-			       dso->filename, idx);
-		      dir = (char *) debug_sections[DEBUG_STR].data + idx;
-
-		      free (comp_dir);
-		      comp_dir = strdup (dir);
-		    }
-
-		  if (dest_dir != NULL && phase == 0)
-		    {
-		      if (record_file_string_entry_idx (&dso->strings, idx))
-			need_strp_update = true;
-		      handled_strp = true;
-		    }
-		}
+	      else if (form == DW_FORM_strp)
+		edit_attributes_str_comp_dir (false /* line_strp */, dso,
+					      &ptr, phase, &comp_dir,
+					      &handled_strp);
+	      else if (form == DW_FORM_line_strp)
+		edit_attributes_str_comp_dir (true /* line_strp */, dso, &ptr,
+					      phase, &comp_dir, &handled_strp);
 	    }
 	  else if ((t->tag == DW_TAG_compile_unit
 		    || t->tag == DW_TAG_partial_unit)
-		   && t->attr[i].attr == DW_AT_name
-		   && form == DW_FORM_strp
-		   && debug_sections[DEBUG_STR].data)
+		   && ((form == DW_FORM_strp
+			&& debug_sections[DEBUG_STR].data)
+		       || (form == DW_FORM_line_strp
+			   && debug_sections[DEBUG_LINE_STR].data))
+		   && t->attr[i].attr == DW_AT_name)
 	    {
+	      bool line_strp = form == DW_FORM_line_strp;
+
 	      /* DW_AT_name is the primary file for this compile
 		 unit. If starting with / it is a full path name.
 		 Note that we don't handle DW_FORM_string in this
@@ -1784,11 +2258,14 @@ edit_attributes (DSO *dso, unsigned char
 	      /* In phase zero we will look for a comp_dir to use.  */
 	      if (phase == 0)
 		{
-		  if (idx >= debug_sections[DEBUG_STR].size)
+		  debug_section *sec = &debug_sections[line_strp
+						       ? DEBUG_LINE_STR
+						       : DEBUG_STR];
+		  if (idx >= sec->size)
 		    error (1, 0,
-			   "%s: Bad string pointer index %zd for unit name",
-			   dso->filename, idx);
-		  char *name = (char *) debug_sections[DEBUG_STR].data + idx;
+			   "%s: Bad string pointer index %zd for unit name (%s)",
+			   dso->filename, idx, sec->name);
+		  char *name = (char *) sec->data + idx;
 		  if (*name == '/' && comp_dir == NULL)
 		    {
 		      char *enddir = strrchr (name, '/');
@@ -1809,107 +2286,37 @@ edit_attributes (DSO *dso, unsigned char
 		 pass (1) stores it (the new index). */
 	      if (dest_dir && phase == 0)
 		{
-		  if (record_file_string_entry_idx (&dso->strings, idx))
-		    need_strp_update = true;
+		  if (record_file_string_entry_idx (line_strp, dso, idx))
+		    {
+		      if (line_strp)
+			need_line_strp_update = true;
+		      else
+			need_strp_update = true;
+		    }
 		  handled_strp = true;
 		}
 	    }
 
 	  switch (form)
 	    {
-	    case DW_FORM_ref_addr:
-	      if (cu_version == 2)
-		ptr += ptr_size;
-	      else
-		ptr += 4;
-	      break;
-	    case DW_FORM_flag_present:
-	      break;
-	    case DW_FORM_addr:
-	      ptr += ptr_size;
-	      break;
-	    case DW_FORM_ref1:
-	    case DW_FORM_flag:
-	    case DW_FORM_data1:
-	      ++ptr;
-	      break;
-	    case DW_FORM_ref2:
-	    case DW_FORM_data2:
-	      ptr += 2;
-	      break;
-	    case DW_FORM_ref4:
-	    case DW_FORM_data4:
-	    case DW_FORM_sec_offset:
-	      ptr += 4;
-	      break;
-	    case DW_FORM_ref8:
-	    case DW_FORM_data8:
-	    case DW_FORM_ref_sig8:
-	      ptr += 8;
-	      break;
-	    case DW_FORM_sdata:
-	    case DW_FORM_ref_udata:
-	    case DW_FORM_udata:
-	      read_uleb128 (ptr);
-	      break;
 	    case DW_FORM_strp:
-	      /* In the first pass we collect all strings, in the
-		 second we put the new references back (if there are
-		 any changes).  */
-	      if (phase == 0)
-		{
-		  /* handled_strp is set for attributes referring to
-		     files. If it is set the string is already
-		     recorded. */
-		  if (! handled_strp)
-		    {
-		      size_t idx = do_read_32_relocated (ptr);
-		      record_existing_string_entry_idx (&dso->strings, idx);
-		    }
-		}
-	      else if (need_strp_update) /* && phase == 1 */
-		{
-		  struct stridxentry *entry;
-		  size_t idx, new_idx;
-		  idx = do_read_32_relocated (ptr);
-		  entry = string_find_entry (&dso->strings, idx);
-		  new_idx = strent_offset (entry->entry);
-		  do_write_32_relocated (ptr, new_idx);
-		}
-	      ptr += 4;
-	      break;
-	    case DW_FORM_string:
-	      ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
-	      break;
-	    case DW_FORM_indirect:
-	      form = read_uleb128 (ptr);
-	      continue;
-	    case DW_FORM_block1:
-	      len = *ptr++;
-	      break;
-	    case DW_FORM_block2:
-	      len = read_16 (ptr);
-	      form = DW_FORM_block1;
+	      edit_strp (dso, false /* line_strp */, ptr, phase, handled_strp);
 	      break;
-	    case DW_FORM_block4:
-	      len = read_32 (ptr);
-	      form = DW_FORM_block1;
+	    case DW_FORM_line_strp:
+	      edit_strp (dso, true /* line_strp */, ptr, phase, handled_strp);
 	      break;
-	    case DW_FORM_block:
-	    case DW_FORM_exprloc:
-	      len = read_uleb128 (ptr);
-	      form = DW_FORM_block1;
-	      assert (len < UINT_MAX);
+	    }
+
+	  switch (skip_form (dso, &form, &ptr))
+	    {
+	    case FORM_OK:
 	      break;
-	    default:
-	      error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename,
-		     form);
+	    case FORM_ERROR:
 	      return NULL;
+	    case FORM_INDIRECT:
+	      continue;
 	    }
 
-	  if (form == DW_FORM_block1)
-	    ptr += len;
-
 	  break;
 	}
     }
@@ -1965,6 +2372,163 @@ line_rel_cmp (const void *a, const void
 }
 
 static int
+edit_info (DSO *dso, int phase, struct debug_section *sec)
+{
+  unsigned char *ptr, *endcu, *endsec;
+  uint32_t value;
+  htab_t abbrev;
+  struct abbrev_tag tag, *t;
+
+  ptr = sec->data;
+  if (ptr == NULL)
+    return 0;
+
+  setup_relbuf(dso, sec, &reltype);
+  endsec = ptr + sec->size;
+  while (ptr < endsec)
+    {
+      unsigned char *cu_start = ptr;
+
+      /* header size, version, unit_type, ptr_size.  */
+      if (ptr + 4 + 2 + 1 + 1 > endsec)
+	{
+	  error (0, 0, "%s: %s CU header too small",
+		 dso->filename, sec->name);
+	  return 1;
+	}
+
+      endcu = ptr + 4;
+      endcu += read_32 (ptr);
+      if (endcu == ptr + 0xffffffff)
+	{
+	  error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+	  return 1;
+	}
+
+      if (endcu > endsec)
+	{
+	  error (0, 0, "%s: %s too small", dso->filename, sec->name);
+	  return 1;
+	}
+
+      cu_version = read_16 (ptr);
+      if (cu_version != 2 && cu_version != 3 && cu_version != 4
+	  && cu_version != 5)
+	{
+	  error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
+		 cu_version);
+	  return 1;
+	}
+
+      int cu_ptr_size = 0;
+
+      if (cu_version >= 5)
+	{
+	  uint8_t unit_type = read_8 (ptr);
+	  if (unit_type != DW_UT_compile && unit_type != DW_UT_partial)
+	    {
+	      error (0, 0, "%s: Unit type %u unhandled", dso->filename,
+		     unit_type);
+	      return 1;
+	    }
+
+	  cu_ptr_size = read_8 (ptr);
+	}
+
+      unsigned char *header_end = (cu_start + 23 + (cu_version < 5 ? 0 : 1));
+      if (header_end > endsec)
+	{
+	  error (0, 0, "%s: %s CU header too small", dso->filename, sec->name);
+	  return 1;
+	}
+
+      value = read_32_relocated (ptr);
+      if (value >= debug_sections[DEBUG_ABBREV].size)
+	{
+	  if (debug_sections[DEBUG_ABBREV].data == NULL)
+	    error (0, 0, "%s: .debug_abbrev not present", dso->filename);
+	  else
+	    error (0, 0, "%s: DWARF CU abbrev offset too large",
+		   dso->filename);
+	  return 1;
+	}
+
+      if (cu_version < 5)
+	cu_ptr_size = read_8 (ptr);
+
+      if (ptr_size == 0)
+	{
+	  ptr_size = cu_ptr_size;
+	  if (ptr_size != 4 && ptr_size != 8)
+	    {
+	      error (0, 0, "%s: Invalid DWARF pointer size %d",
+		     dso->filename, ptr_size);
+	      return 1;
+	    }
+	}
+      else if (cu_ptr_size != ptr_size)
+	{
+	  error (0, 0, "%s: DWARF pointer size differs between CUs",
+		 dso->filename);
+	  return 1;
+	}
+
+      if (sec != &debug_sections[DEBUG_INFO])
+	ptr += 12; /* Skip type_signature and type_offset.  */
+
+      abbrev = read_abbrev (dso,
+			    debug_sections[DEBUG_ABBREV].data + value);
+      if (abbrev == NULL)
+	return 1;
+
+      while (ptr < endcu)
+	{
+	  tag.entry = read_uleb128 (ptr);
+	  if (tag.entry == 0)
+	    continue;
+	  t = htab_find_with_hash (abbrev, &tag, tag.entry);
+	  if (t == NULL)
+	    {
+	      error (0, 0, "%s: Could not find DWARF abbreviation %d",
+		     dso->filename, tag.entry);
+	      htab_delete (abbrev);
+	      return 1;
+	    }
+
+	  ptr = edit_attributes (dso, ptr, t, phase);
+	  if (ptr == NULL)
+	    break;
+	}
+
+      htab_delete (abbrev);
+    }
+
+  return 0;
+}
+
+/* Rebuild .debug_str.  */
+static void
+edit_dwarf2_any_str (DSO *dso, struct strings *strings, debug_section *secp)
+{
+  Strtab *strtab = strings->str_tab;
+  Elf_Data *strdata = secp->elf_data;
+  int strndx = secp->sec;
+  Elf_Scn *strscn = dso->scn[strndx];
+
+  /* Out with the old. */
+  strdata->d_size = 0;
+  /* In with the new. */
+  strdata = elf_newdata (strscn);
+
+  /* We really should check whether we had enough memory,
+     but the old ebl version will just abort on out of
+     memory... */
+  strtab_finalize (strtab, strdata);
+  secp->size = strdata->d_size;
+  strings->str_buf = strdata->d_buf;
+}
+
+static int
 edit_dwarf2 (DSO *dso)
 {
   Elf_Data *data;
@@ -1995,7 +2559,7 @@ edit_dwarf2 (DSO *dso)
 		  struct debug_section *debug_sec = &debug_sections[j];
 		  if (debug_sections[j].data)
 		    {
-		      if (j != DEBUG_MACRO)
+		      if (j != DEBUG_MACRO && j != DEBUG_TYPES)
 			{
 			  error (0, 0, "%s: Found two copies of %s section",
 				 dso->filename, name);
@@ -2003,22 +2567,21 @@ edit_dwarf2 (DSO *dso)
 			}
 		      else
 			{
-			  /* In relocatable files .debug_macro might
-			     appear multiple times as COMDAT
-			     section.  */
+			  /* In relocatable files .debug_macro and .debug_types
+			     might appear multiple times as COMDAT section.  */
 			  struct debug_section *sec;
 			  sec = calloc (sizeof (struct debug_section), 1);
 			  if (sec == NULL)
 			    error (1, errno,
-				   "%s: Could not allocate more macro sections",
-				   dso->filename);
-			  sec->name = ".debug_macro";
+				   "%s: Could not allocate more %s sections",
+				   dso->filename, name);
+			  sec->name = name;
 
-			  struct debug_section *macro_sec = debug_sec;
-			  while (macro_sec->next != NULL)
-			    macro_sec = macro_sec->next;
+			  struct debug_section *multi_sec = debug_sec;
+			  while (multi_sec->next != NULL)
+			    multi_sec = multi_sec->next;
 
-			  macro_sec->next = sec;
+			  multi_sec->next = sec;
 			  debug_sec = sec;
 			}
 		    }
@@ -2055,23 +2618,23 @@ edit_dwarf2 (DSO *dso)
 			  + (dso->shdr[i].sh_type == SHT_RELA),
 			  debug_sections[j].name) == 0)
 	 	{
-		  if (j == DEBUG_MACRO)
+		  if (j == DEBUG_MACRO || j == DEBUG_TYPES)
 		    {
 		      /* Pick the correct one.  */
 		      int rel_target = dso->shdr[i].sh_info;
-		      struct debug_section *macro_sec = &debug_sections[j];
-		      while (macro_sec != NULL)
+		      struct debug_section *multi_sec = &debug_sections[j];
+		      while (multi_sec != NULL)
 			{
-			  if (macro_sec->sec == rel_target)
+			  if (multi_sec->sec == rel_target)
 			    {
-			      macro_sec->relsec = i;
+			      multi_sec->relsec = i;
 			      break;
 			    }
-			  macro_sec = macro_sec->next;
+			  multi_sec = multi_sec->next;
 			}
-		      if (macro_sec == NULL)
-			error (0, 1, "No .debug_macro reloc section: %s",
-			       dso->filename);
+		      if (multi_sec == NULL)
+			error (0, 1, "No %s reloc section: %s",
+			       debug_sections[j].name, dso->filename);
 		    }
 		  else
 		    debug_sections[j].relsec = i;
@@ -2100,388 +2663,338 @@ edit_dwarf2 (DSO *dso)
       return 1;
     }
 
-  if (debug_sections[DEBUG_INFO].data != NULL)
+  if (debug_sections[DEBUG_INFO].data == NULL)
+    return 0;
+
+  unsigned char *ptr, *endsec;
+  int phase;
+  bool info_rel_updated = false;
+  bool types_rel_updated = false;
+  bool macro_rel_updated = false;
+  bool line_rel_updated = false;
+
+  for (phase = 0; phase < 2; phase++)
     {
-      unsigned char *ptr, *endcu, *endsec;
-      uint32_t value;
-      htab_t abbrev;
-      struct abbrev_tag tag, *t;
-      int phase;
-      bool info_rel_updated = false;
-      bool macro_rel_updated = false;
+      /* If we don't need to update anyhing, skip phase 1. */
+      if (phase == 1
+	  && !need_strp_update
+	  && !need_line_strp_update
+	  && !need_string_replacement
+	  && !need_stmt_update)
+	break;
 
-      for (phase = 0; phase < 2; phase++)
-	{
-	  /* If we don't need to update anyhing, skip phase 1. */
-	  if (phase == 1
-	      && !need_strp_update
-	      && !need_string_replacement
-	      && !need_stmt_update)
-	    break;
+      rel_updated = false;
+      if (edit_info (dso, phase, &debug_sections[DEBUG_INFO]))
+	return 1;
 
-	  ptr = debug_sections[DEBUG_INFO].data;
-	  setup_relbuf(dso, &debug_sections[DEBUG_INFO], &reltype);
-	  rel_updated = false;
-	  endsec = ptr + debug_sections[DEBUG_INFO].size;
-	  while (ptr < endsec)
-	    {
-	      if (ptr + 11 > endsec)
-		{
-		  error (0, 0, "%s: .debug_info CU header too small",
-			 dso->filename);
-		  return 1;
-		}
+      /* Remember whether any .debug_info relocations might need
+	 to be updated. */
+      info_rel_updated = rel_updated;
 
-	      endcu = ptr + 4;
-	      endcu += read_32 (ptr);
-	      if (endcu == ptr + 0xffffffff)
-		{
-		  error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
-		  return 1;
-		}
+      rel_updated = false;
+      struct debug_section *types_sec = &debug_sections[DEBUG_TYPES];
+      while (types_sec != NULL)
+	{
+	  if (edit_info (dso, phase, types_sec))
+	    return 1;
+	  types_sec = types_sec->next;
+	}
 
-	      if (endcu > endsec)
-		{
-		  error (0, 0, "%s: .debug_info too small", dso->filename);
-		  return 1;
-		}
+      /* Remember whether any .debug_types relocations might need
+	 to be updated. */
+      types_rel_updated = rel_updated;
 
-	      cu_version = read_16 (ptr);
-	      if (cu_version != 2 && cu_version != 3 && cu_version != 4)
-		{
-		  error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
-			 cu_version);
-		  return 1;
-		}
+      /* We might have to recalculate/rewrite the debug_line
+	 section.  We need to do that before going into phase one
+	 so we have all new offsets.  We do this separately from
+	 scanning the dirs/file names because the DW_AT_stmt_lists
+	 might not be in order or skip some padding we might have
+	 to (re)move. */
+      if (phase == 0 && need_stmt_update)
+	{
+	  edit_dwarf2_line (dso);
 
-	      value = read_32_relocated (ptr);
-	      if (value >= debug_sections[DEBUG_ABBREV].size)
-		{
-		  if (debug_sections[DEBUG_ABBREV].data == NULL)
-		    error (0, 0, "%s: .debug_abbrev not present", dso->filename);
-		  else
-		    error (0, 0, "%s: DWARF CU abbrev offset too large",
-			   dso->filename);
-		  return 1;
-		}
+	  /* The line table programs will be moved
+	     forward/backwards a bit in the new data. Update the
+	     debug_line relocations to the new offsets. */
+	  int rndx = debug_sections[DEBUG_LINE].relsec;
+	  if (rndx != 0)
+	    {
+	      LINE_REL *rbuf;
+	      size_t rels;
+	      Elf_Data *rdata = elf_getdata (dso->scn[rndx], NULL);
+	      int rtype = dso->shdr[rndx].sh_type;
+	      rels = dso->shdr[rndx].sh_size / dso->shdr[rndx].sh_entsize;
+	      rbuf = malloc (rels * sizeof (LINE_REL));
+	      if (rbuf == NULL)
+		error (1, errno, "%s: Could not allocate line relocations",
+		       dso->filename);
 
-	      if (ptr_size == 0)
+	      /* Sort them by offset into section. */
+	      for (size_t i = 0; i < rels; i++)
 		{
-		  ptr_size = read_8 (ptr);
-		  if (ptr_size != 4 && ptr_size != 8)
+		  if (rtype == SHT_RELA)
 		    {
-		      error (0, 0, "%s: Invalid DWARF pointer size %d",
-			     dso->filename, ptr_size);
-		      return 1;
+		      GElf_Rela rela;
+		      if (gelf_getrela (rdata, i, &rela) == NULL)
+			error (1, 0, "Couldn't get relocation: %s",
+			       elf_errmsg (-1));
+		      rbuf[i].r_offset = rela.r_offset;
+		      rbuf[i].ndx = i;
 		    }
-		}
-	      else if (read_8 (ptr) != ptr_size)
-		{
-		  error (0, 0, "%s: DWARF pointer size differs between CUs",
-			 dso->filename);
-		  return 1;
-		}
-
-	      abbrev = read_abbrev (dso,
-				    debug_sections[DEBUG_ABBREV].data + value);
-	      if (abbrev == NULL)
-		return 1;
-
-	      while (ptr < endcu)
-		{
-		  tag.entry = read_uleb128 (ptr);
-		  if (tag.entry == 0)
-		    continue;
-		  t = htab_find_with_hash (abbrev, &tag, tag.entry);
-		  if (t == NULL)
+		  else
 		    {
-		      error (0, 0, "%s: Could not find DWARF abbreviation %d",
-			     dso->filename, tag.entry);
-		      htab_delete (abbrev);
-		      return 1;
+		      GElf_Rel rel;
+		      if (gelf_getrel (rdata, i, &rel) == NULL)
+			error (1, 0, "Couldn't get relocation: %s",
+			       elf_errmsg (-1));
+		      rbuf[i].r_offset = rel.r_offset;
+		      rbuf[i].ndx = i;
 		    }
-
-		  ptr = edit_attributes (dso, ptr, t, phase);
-		  if (ptr == NULL)
-		    break;
 		}
+	      qsort (rbuf, rels, sizeof (LINE_REL), line_rel_cmp);
 
-	      htab_delete (abbrev);
-	    }
-
-	  /* Remember whether any .debug_info relocations might need
-	     to be updated. */
-	  info_rel_updated = rel_updated;
-
-	  /* We might have to recalculate/rewrite the debug_line
-	     section.  We need to do that before going into phase one
-	     so we have all new offsets.  We do this separately from
-	     scanning the dirs/file names because the DW_AT_stmt_lists
-	     might not be in order or skip some padding we might have
-	     to (re)move. */
-	  if (phase == 0 && need_stmt_update)
-	    {
-	      edit_dwarf2_line (dso);
-
-	      /* The line table programs will be moved
-		 forward/backwards a bit in the new data. Update the
-		 debug_line relocations to the new offsets. */
-	      int rndx = debug_sections[DEBUG_LINE].relsec;
-	      if (rndx != 0)
+	      size_t lndx = 0;
+	      for (size_t i = 0; i < rels; i++)
 		{
-		  LINE_REL *rbuf;
-		  size_t rels;
-		  Elf_Data *rdata = elf_getdata (dso->scn[rndx], NULL);
-		  int rtype = dso->shdr[rndx].sh_type;
-		  rels = dso->shdr[rndx].sh_size / dso->shdr[rndx].sh_entsize;
-		  rbuf = malloc (rels * sizeof (LINE_REL));
-		  if (rbuf == NULL)
-		    error (1, errno, "%s: Could not allocate line relocations",
-			   dso->filename);
+		  /* These relocations only happen in ET_REL files
+		     and are section offsets. */
+		  GElf_Addr r_offset;
+		  size_t ndx = rbuf[i].ndx;
 
-		  /* Sort them by offset into section. */
-		  for (size_t i = 0; i < rels; i++)
+		  GElf_Rel rel;
+		  GElf_Rela rela;
+		  if (rtype == SHT_RELA)
 		    {
-		      if (rtype == SHT_RELA)
-			{
-			  GElf_Rela rela;
-			  if (gelf_getrela (rdata, i, &rela) == NULL)
-			    error (1, 0, "Couldn't get relocation: %s",
-				   elf_errmsg (-1));
-			  rbuf[i].r_offset = rela.r_offset;
-			  rbuf[i].ndx = i;
-			}
-		      else
-			{
-			  GElf_Rel rel;
-			  if (gelf_getrel (rdata, i, &rel) == NULL)
-			    error (1, 0, "Couldn't get relocation: %s",
-				   elf_errmsg (-1));
-			  rbuf[i].r_offset = rel.r_offset;
-			  rbuf[i].ndx = i;
-			}
+		      if (gelf_getrela (rdata, ndx, &rela) == NULL)
+			error (1, 0, "Couldn't get relocation: %s",
+			       elf_errmsg (-1));
+		      r_offset = rela.r_offset;
 		    }
-		  qsort (rbuf, rels, sizeof (LINE_REL), line_rel_cmp);
-
-		  size_t lndx = 0;
-		  for (size_t i = 0; i < rels; i++)
+		  else
 		    {
-		      /* These relocations only happen in ET_REL files
-			 and are section offsets. */
-		      GElf_Addr r_offset;
-		      size_t ndx = rbuf[i].ndx;
-
-		      GElf_Rel rel;
-		      GElf_Rela rela;
-		      if (rtype == SHT_RELA)
-			{
-			  if (gelf_getrela (rdata, ndx, &rela) == NULL)
-			    error (1, 0, "Couldn't get relocation: %s",
-				   elf_errmsg (-1));
-			  r_offset = rela.r_offset;
-			}
-		      else
-			{
-			  if (gelf_getrel (rdata, ndx, &rel) == NULL)
-			    error (1, 0, "Couldn't get relocation: %s",
-				   elf_errmsg (-1));
-			  r_offset = rel.r_offset;
-			}
+		      if (gelf_getrel (rdata, ndx, &rel) == NULL)
+			error (1, 0, "Couldn't get relocation: %s",
+			       elf_errmsg (-1));
+		      r_offset = rel.r_offset;
+		    }
 
-		      while (lndx < dso->lines.used
-			     && r_offset > (dso->lines.table[lndx].old_idx
-					    + 4
-					    + dso->lines.table[lndx].unit_length))
-			lndx++;
+		  while (lndx < dso->lines.used
+			 && r_offset > (dso->lines.table[lndx].old_idx
+					+ 4
+					+ dso->lines.table[lndx].unit_length))
+		    lndx++;
 
-		      if (lndx >= dso->lines.used)
-			error (1, 0,
-			       ".debug_line relocation offset out of range");
+		  if (lndx >= dso->lines.used)
+		    error (1, 0,
+			   ".debug_line relocation offset out of range");
 
-		      /* Offset (pointing into the line program) moves
-			 from old to new index including the header
-			 size diff. */
-		      r_offset += (ssize_t)((dso->lines.table[lndx].new_idx
-					     - dso->lines.table[lndx].old_idx)
-					    + dso->lines.table[lndx].size_diff);
+		  /* Offset (pointing into the line program) moves
+		     from old to new index including the header
+		     size diff. */
+		  r_offset += (ssize_t)((dso->lines.table[lndx].new_idx
+					 - dso->lines.table[lndx].old_idx)
+					+ dso->lines.table[lndx].size_diff);
 
-		      if (rtype == SHT_RELA)
-			{
-			  rela.r_offset = r_offset;
-			  if (gelf_update_rela (rdata, ndx, &rela) == 0)
-			    error (1, 0, "Couldn't update relocation: %s",
-				   elf_errmsg (-1));
-			}
-		      else
-			{
-			  rel.r_offset = r_offset;
-			  if (gelf_update_rel (rdata, ndx, &rel) == 0)
-			    error (1, 0, "Couldn't update relocation: %s",
-				   elf_errmsg (-1));
-			}
+		  if (rtype == SHT_RELA)
+		    {
+		      rela.r_offset = r_offset;
+		      if (gelf_update_rela (rdata, ndx, &rela) == 0)
+			error (1, 0, "Couldn't update relocation: %s",
+			       elf_errmsg (-1));
+		    }
+		  else
+		    {
+		      rel.r_offset = r_offset;
+		      if (gelf_update_rel (rdata, ndx, &rel) == 0)
+			error (1, 0, "Couldn't update relocation: %s",
+			       elf_errmsg (-1));
 		    }
-
-		  elf_flagdata (rdata, ELF_C_SET, ELF_F_DIRTY);
-		  free (rbuf);
 		}
+
+	      elf_flagdata (rdata, ELF_C_SET, ELF_F_DIRTY);
+	      free (rbuf);
 	    }
+	}
 
-	  /* The .debug_macro section also contains offsets into the
-	     .debug_str section and references to the .debug_line
-	     tables, so we need to update those as well if we update
-	     the strings or the stmts.  */
-	  if ((need_strp_update || need_stmt_update)
-	      && debug_sections[DEBUG_MACRO].data)
+      /* The .debug_macro section also contains offsets into the
+	 .debug_str section and references to the .debug_line
+	 tables, so we need to update those as well if we update
+	 the strings or the stmts.  */
+      if ((need_strp_update || need_stmt_update)
+	  && debug_sections[DEBUG_MACRO].data)
+	{
+	  /* There might be multiple (COMDAT) .debug_macro sections.  */
+	  struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO];
+	  while (macro_sec != NULL)
 	    {
-	      /* There might be multiple (COMDAT) .debug_macro sections.  */
-	      struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO];
-	      while (macro_sec != NULL)
-		{
-		  setup_relbuf(dso, macro_sec, &reltype);
-		  rel_updated = false;
+	      setup_relbuf(dso, macro_sec, &reltype);
+	      rel_updated = false;
 
-		  ptr = macro_sec->data;
-		  endsec = ptr + macro_sec->size;
-		  int op = 0, macro_version, macro_flags;
-		  int offset_len = 4, line_offset = 0;
+	      ptr = macro_sec->data;
+	      endsec = ptr + macro_sec->size;
+	      int op = 0, macro_version, macro_flags;
+	      int offset_len = 4, line_offset = 0;
 
-		  while (ptr < endsec)
+	      while (ptr < endsec)
+		{
+		  if (!op)
 		    {
-		      if (!op)
-			{
-			  macro_version = read_16 (ptr);
-			  macro_flags = read_8 (ptr);
-			  if (macro_version < 4 || macro_version > 5)
-			    error (1, 0, "unhandled .debug_macro version: %d",
-				   macro_version);
-			  if ((macro_flags & ~2) != 0)
-			    error (1, 0, "unhandled .debug_macro flags: 0x%x",
-				   macro_flags);
-
-			  offset_len = (macro_flags & 0x01) ? 8 : 4;
-			  line_offset = (macro_flags & 0x02) ? 1 : 0;
+		      macro_version = read_16 (ptr);
+		      macro_flags = read_8 (ptr);
+		      if (macro_version < 4 || macro_version > 5)
+			error (1, 0, "unhandled .debug_macro version: %d",
+			       macro_version);
+		      if ((macro_flags & ~2) != 0)
+			error (1, 0, "unhandled .debug_macro flags: 0x%x",
+			       macro_flags);
 
-			  if (offset_len != 4)
-			    error (0, 1,
-				   "Cannot handle 8 byte macro offsets: %s",
-				   dso->filename);
+		      offset_len = (macro_flags & 0x01) ? 8 : 4;
+		      line_offset = (macro_flags & 0x02) ? 1 : 0;
 
-			  /* Update the line_offset if it is there.  */
-			  if (line_offset)
-			    {
-			      if (phase == 0)
-				ptr += offset_len;
-			      else
-				{
-				  size_t idx, new_idx;
-				  idx = do_read_32_relocated (ptr);
-				  new_idx = find_new_list_offs (&dso->lines,
-								idx);
-				  write_32_relocated (ptr, new_idx);
-				}
-			    }
-			}
+		      if (offset_len != 4)
+			error (0, 1,
+			       "Cannot handle 8 byte macro offsets: %s",
+			       dso->filename);
 
-		      op = read_8 (ptr);
-		      if (!op)
-			continue;
-		      switch(op)
+		      /* Update the line_offset if it is there.  */
+		      if (line_offset)
 			{
-			case DW_MACRO_GNU_define:
-			case DW_MACRO_GNU_undef:
-			  read_uleb128 (ptr);
-			  ptr = ((unsigned char *) strchr ((char *) ptr, '\0')
-				 + 1);
-			  break;
-			case DW_MACRO_GNU_start_file:
-			  read_uleb128 (ptr);
-			  read_uleb128 (ptr);
-			  break;
-			case DW_MACRO_GNU_end_file:
-			  break;
-			case DW_MACRO_GNU_define_indirect:
-			case DW_MACRO_GNU_undef_indirect:
-			  read_uleb128 (ptr);
 			  if (phase == 0)
-			    {
-			      size_t idx = read_32_relocated (ptr);
-			      record_existing_string_entry_idx (&dso->strings,
-								idx);
-			    }
+			    ptr += offset_len;
 			  else
 			    {
-			      struct stridxentry *entry;
 			      size_t idx, new_idx;
 			      idx = do_read_32_relocated (ptr);
-			      entry = string_find_entry (&dso->strings, idx);
-			      new_idx = strent_offset (entry->entry);
+			      new_idx = find_new_list_offs (&dso->lines,
+							    idx);
 			      write_32_relocated (ptr, new_idx);
 			    }
-			  break;
-			case DW_MACRO_GNU_transparent_include:
-			  ptr += offset_len;
-			  break;
-			default:
-			  error (1, 0, "Unhandled DW_MACRO op 0x%x", op);
-			  break;
 			}
 		    }
 
-		  if (rel_updated)
-		    macro_rel_updated = true;
-		  macro_sec = macro_sec->next;
+		  op = read_8 (ptr);
+		  if (!op)
+		    continue;
+		  switch(op)
+		    {
+		    case DW_MACRO_GNU_define:
+		    case DW_MACRO_GNU_undef:
+		      read_uleb128 (ptr);
+		      ptr = ((unsigned char *) strchr ((char *) ptr, '\0')
+			     + 1);
+		      break;
+		    case DW_MACRO_GNU_start_file:
+		      read_uleb128 (ptr);
+		      read_uleb128 (ptr);
+		      break;
+		    case DW_MACRO_GNU_end_file:
+		      break;
+		    case DW_MACRO_GNU_define_indirect:
+		    case DW_MACRO_GNU_undef_indirect:
+		      read_uleb128 (ptr);
+		      if (phase == 0)
+			{
+			  size_t idx = read_32_relocated (ptr);
+			  record_existing_string_entry_idx (false, dso, idx);
+			}
+		      else
+			{
+			  struct stridxentry *entry;
+			  size_t idx, new_idx;
+			  idx = do_read_32_relocated (ptr);
+			  entry = string_find_entry (&dso->debug_str, idx);
+			  new_idx = strent_offset (entry->entry);
+			  write_32_relocated (ptr, new_idx);
+			}
+		      break;
+		    case DW_MACRO_GNU_transparent_include:
+		      ptr += offset_len;
+		      break;
+		    default:
+		      error (1, 0, "Unhandled DW_MACRO op 0x%x", op);
+		      break;
+		    }
 		}
-	    }
 
-	  /* Same for the debug_str section. Make sure everything is
-	     in place for phase 1 updating of debug_info
-	     references. */
-	  if (phase == 0 && need_strp_update)
-	    {
-	      Strtab *strtab = dso->strings.str_tab;
-	      Elf_Data *strdata = debug_sections[DEBUG_STR].elf_data;
-	      int strndx = debug_sections[DEBUG_STR].sec;
-	      Elf_Scn *strscn = dso->scn[strndx];
+	      if (rel_updated)
+		macro_rel_updated = true;
+	      macro_sec = macro_sec->next;
+	    }
+	}
 
-	      /* Out with the old. */
-	      strdata->d_size = 0;
-	      /* In with the new. */
-	      strdata = elf_newdata (strscn);
 
-	      /* We really should check whether we had enough memory,
-		 but the old ebl version will just abort on out of
-		 memory... */
-	      strtab_finalize (strtab, strdata);
-	      debug_sections[DEBUG_STR].size = strdata->d_size;
-	      dso->strings.str_buf = strdata->d_buf;
-	    }
+      /* Now handle all the DWARF5 line tables, they contain strp
+	 and/or line_strp entries that need to be registered/rewritten.  */
+      setup_relbuf(dso, &debug_sections[DEBUG_LINE], &reltype);
+      rel_updated = false;
 
+      /* edit_dwarf2_line will have set this up, unless there are no
+	 moved/resized (DWARF4) lines. In which case we can just use
+	 the original section data. new_idx will have been setup
+	 correctly, even if it is the same as old_idx.  */
+      unsigned char *line_buf = (unsigned char *)dso->lines.line_buf;
+      if (line_buf == NULL)
+	line_buf = debug_sections[DEBUG_LINE].data;
+      for (int ldx = 0; ldx < dso->lines.used; ldx++)
+	{
+	  struct line_table *t = &dso->lines.table[ldx];
+	  if (t->version >= 5)
+	    read_dwarf5_line (dso, line_buf + t->new_idx, t, phase);
 	}
+      if (rel_updated)
+	line_rel_updated = true;
 
-      /* After phase 1 we might have rewritten the debug_info with
-	 new strp, strings and/or linep offsets.  */
-      if (need_strp_update || need_string_replacement || need_stmt_update)
-	dirty_section (DEBUG_INFO);
-      if (need_strp_update || need_stmt_update)
-	dirty_section (DEBUG_MACRO);
-      if (need_stmt_update)
-	dirty_section (DEBUG_LINE);
+      /* Same for the debug_str and debug_line_str sections.
+	 Make sure everything is in place for phase 1 updating of debug_info
+	 references. */
+      if (phase == 0 && need_strp_update)
+	edit_dwarf2_any_str (dso, &dso->debug_str,
+			     &debug_sections[DEBUG_STR]);
+      if (phase == 0 && need_line_strp_update)
+	edit_dwarf2_any_str (dso, &dso->debug_line_str,
+			     &debug_sections[DEBUG_LINE_STR]);
+    }
 
-      /* Update any relocations addends we might have touched. */
-      if (info_rel_updated)
-	update_rela_data (dso, &debug_sections[DEBUG_INFO]);
+  /* After phase 1 we might have rewritten the debug_info with
+     new strp, strings and/or linep offsets.  */
+  if (need_strp_update || need_line_strp_update
+      || need_string_replacement || need_stmt_update) {
+    dirty_section (DEBUG_INFO);
+    if (debug_sections[DEBUG_TYPES].data != NULL)
+      dirty_section (DEBUG_TYPES);
+  }
+  if (need_strp_update || need_stmt_update)
+    dirty_section (DEBUG_MACRO);
+  if (need_stmt_update || need_line_strp_update)
+    dirty_section (DEBUG_LINE);
 
-      if (macro_rel_updated)
+  /* Update any relocations addends we might have touched. */
+  if (info_rel_updated)
+    update_rela_data (dso, &debug_sections[DEBUG_INFO]);
+  if (types_rel_updated)
+    {
+      struct debug_section *types_sec = &debug_sections[DEBUG_TYPES];
+      while (types_sec != NULL)
 	{
-	  struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO];
-	  while (macro_sec != NULL)
-	    {
-	      update_rela_data (dso, macro_sec);
-	      macro_sec = macro_sec->next;
-	    }
+	  update_rela_data (dso, types_sec);
+	  types_sec = types_sec->next;
 	}
     }
 
+  if (macro_rel_updated)
+    {
+      struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO];
+      while (macro_sec != NULL)
+	{
+	  update_rela_data (dso, macro_sec);
+	  macro_sec = macro_sec->next;
+	}
+    }
+
+  if (line_rel_updated)
+    update_rela_data (dso, &debug_sections[DEBUG_LINE]);
+
   return 0;
 }
 
@@ -2574,7 +3087,8 @@ fdopen_dso (int fd, const char *name)
     }
 
   dso->filename = (const char *) strdup (name);
-  setup_strings (&dso->strings);
+  setup_strings (&dso->debug_str);
+  setup_strings (&dso->debug_line_str);
   setup_lines (&dso->lines);
   return dso;
 
@@ -2582,7 +3096,8 @@ error_out:
   if (dso)
     {
       free ((char *) dso->filename);
-      destroy_strings (&dso->strings);
+      destroy_strings (&dso->debug_str);
+      destroy_strings (&dso->debug_line_str);
       destroy_lines (&dso->lines);
       free (dso);
     }
@@ -2862,7 +3377,9 @@ main (int argc, char *argv[])
      in elfutils before 0.169 we will have to update and write out all
      section data if any data has changed (when ELF_F_LAYOUT was
      set). https://sourceware.org/bugzilla/show_bug.cgi?id=21199 */
-  bool need_update = need_strp_update || need_stmt_update;
+  bool need_update = (need_strp_update
+		      || need_line_strp_update
+		      || need_stmt_update);
 
 #if !_ELFUTILS_PREREQ (0, 169)
   /* string replacements or build_id updates don't change section size. */
@@ -2934,10 +3451,12 @@ main (int argc, char *argv[])
 	      GElf_Xword sec_size = shdr->sh_size;
 
 	      /* We might have changed the size (and content) of the
-		 debug_str or debug_line section. */
+		 debug_str, debug_line_str or debug_line section. */
 	      size_t secnum = elf_ndxscn (scn);
 	      if (secnum == debug_sections[DEBUG_STR].sec)
 		sec_size = debug_sections[DEBUG_STR].size;
+	      if (secnum == debug_sections[DEBUG_LINE_STR].sec)
+		sec_size = debug_sections[DEBUG_LINE_STR].size;
 	      if (secnum == debug_sections[DEBUG_LINE].sec)
 		sec_size = debug_sections[DEBUG_LINE].size;
 
@@ -3007,7 +3526,8 @@ main (int argc, char *argv[])
   chmod (file, stat_buf.st_mode);
 
   free ((char *) dso->filename);
-  destroy_strings (&dso->strings);
+  destroy_strings (&dso->debug_str);
+  destroy_strings (&dso->debug_line_str);
   destroy_lines (&dso->lines);
   free (dso);
 
@@ -3022,6 +3542,17 @@ main (int argc, char *argv[])
       macro_sec = next;
     }
 
+  /* In case there were multiple (COMDAT) .debug_types sections,
+     free them.  */
+  struct debug_section *types_sec = &debug_sections[DEBUG_TYPES];
+  types_sec = types_sec->next;
+  while (types_sec != NULL)
+    {
+      struct debug_section *next = types_sec->next;
+      free (types_sec);
+      types_sec = next;
+    }
+
   poptFreeContext (optCon);
 
   return 0;