Igor Gnatenko 082d5d
From ddb7c4abddd746d7ec354fb89be16a2411a92cba Mon Sep 17 00:00:00 2001
Panu Matilainen 5b4d98
From: Mark Wielaard <mark@klomp.org>
Igor Gnatenko 082d5d
Date: Mon, 27 Feb 2017 16:28:18 +0100
Igor Gnatenko 082d5d
Subject: [PATCH 13/49] debugedit: Support String/Line table rewriting for
Panu Matilainen 5b4d98
 larger/smaller paths.
Panu Matilainen 5b4d98
Panu Matilainen 5b4d98
debugedit --base to --dest rewriting of debug source file paths only
Panu Matilainen 5b4d98
supported dest paths that were smaller or equal than the base path
Panu Matilainen 5b4d98
(and the size should differ more than 1 character for correct debug lines).
Panu Matilainen 5b4d98
All paths were changed "in place". Which could in theory mess up debug str
Panu Matilainen 5b4d98
sharing.
Panu Matilainen 5b4d98
Panu Matilainen 5b4d98
This rewrite supports base and dest strings of any size (some limitations,
Panu Matilainen 5b4d98
see below). This is done by reconstructing the debug_str and debug_line
Panu Matilainen 5b4d98
tables and updating the references in the debug_info attributes pointing
Panu Matilainen 5b4d98
to these tables. Plus, if necessary (only for ET_REL kernel modules),
Panu Matilainen 5b4d98
updating any relocations for the debug_info and debug_line sections.
Panu Matilainen 5b4d98
Panu Matilainen 5b4d98
This has the nice benefit of merging any duplicate strings in the
Panu Matilainen 5b4d98
debug_str table which might resulting on slightly smaller files.
Panu Matilainen 5b4d98
kernel modules are ET_REL files that often contain a lot of duplicate
Panu Matilainen 5b4d98
strings.
Panu Matilainen 5b4d98
Panu Matilainen 5b4d98
The rewrite uses elfutils (either libebl or libdw) to reconstruct the
Panu Matilainen 5b4d98
debug_str table. Since we are changing some section sizes now we cannot
Panu Matilainen 5b4d98
just use mmap and rawdata to poke the values, but need to read in and
Panu Matilainen 5b4d98
write out the changed sections. This does take a bit more memory because
Panu Matilainen 5b4d98
we now also need to keep track of all string/line references.
Panu Matilainen 5b4d98
Panu Matilainen 5b4d98
There are still some limitations (already in the original debugedit)
Panu Matilainen 5b4d98
not fixed by this rewrite:
Panu Matilainen 5b4d98
- DW_AT_comp_dir in .debug_info using DW_FORM_string can not be made
Panu Matilainen 5b4d98
  larger. We only warn about that now instead of failing. The only
Panu Matilainen 5b4d98
  producer of DW_FORM_string comp_dirs is binutils gas. It seems simpler
Panu Matilainen 5b4d98
  to fix gas than to try to support resizing the debug_info section.
Panu Matilainen 5b4d98
- A DW_AT_name on a DW_TAG_compile_unit is only rewritten for DW_FORM_strp
Panu Matilainen 5b4d98
  not for DW_FORM_string. Probably no problem in practice since this
Panu Matilainen 5b4d98
  wasn't supported originally either.
Panu Matilainen 5b4d98
- The debug_line program isn't scanned for DW_LNE_define_file which
Panu Matilainen 5b4d98
  could in theory define an absolute path that might need rewriting.
Panu Matilainen 5b4d98
  Again probably not a problem because this wasn't supported before
Panu Matilainen 5b4d98
  and there are no know producers for this construct.
Panu Matilainen 5b4d98
Panu Matilainen 5b4d98
To support the upcoming DWARFv5 in gcc 7 (not on by default), we will
Panu Matilainen 5b4d98
need to add support for the new debug_line format and scan the new
Panu Matilainen 5b4d98
debug_macro section that can have references to the debug_str table.
Panu Matilainen 5b4d98
Panu Matilainen 5b4d98
Signed-off-by: Mark Wielaard <mark@klomp.org>
Igor Gnatenko 082d5d
(cherry picked from commit 88989572fff1f31e0c4f972a6895585e4742ef4b)
Panu Matilainen 5b4d98
---
Panu Matilainen 5b4d98
 Makefile.am       |    8 +-
Panu Matilainen 5b4d98
 configure.ac      |    6 +
Panu Matilainen 5b4d98
 tools/debugedit.c | 1569 ++++++++++++++++++++++++++++++++++++++++++++---------
Panu Matilainen 5b4d98
 3 files changed, 1330 insertions(+), 253 deletions(-)
Panu Matilainen 5b4d98
Panu Matilainen 5b4d98
diff --git a/Makefile.am b/Makefile.am
Igor Gnatenko 082d5d
index 6b37b5898..1b77730aa 100644
Panu Matilainen 5b4d98
--- a/Makefile.am
Panu Matilainen 5b4d98
+++ b/Makefile.am
Panu Matilainen 5b4d98
@@ -156,13 +156,18 @@ rpm2archive_LDADD +=	@WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@ @WITH_ARCHIV
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 if LIBELF
Panu Matilainen 5b4d98
 if LIBDWARF
Panu Matilainen 5b4d98
+if LIBDW
Panu Matilainen 5b4d98
 rpmconfig_SCRIPTS += scripts/find-debuginfo.sh
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 rpmlibexec_PROGRAMS +=	debugedit
Panu Matilainen 5b4d98
 debugedit_SOURCES =	tools/debugedit.c tools/hashtab.c tools/hashtab.h
Panu Matilainen 5b4d98
 debugedit_LDADD =	rpmio/librpmio.la
Panu Matilainen 5b4d98
 debugedit_LDADD +=	@WITH_LIBELF_LIB@ @WITH_POPT_LIB@
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
+if HAVE_LIBDW_STRTAB
Panu Matilainen 5b4d98
+debugedit_LDADD +=	@WITH_LIBDW_LIB@
Panu Matilainen 5b4d98
+else
Panu Matilainen 5b4d98
+debugedit_LDADD +=	@WITH_LIBDW_LIB@ -lebl
Panu Matilainen 5b4d98
+endif
Panu Matilainen 5b4d98
 rpmlibexec_PROGRAMS +=	elfdeps
Panu Matilainen 5b4d98
 elfdeps_SOURCES =	tools/elfdeps.c
Panu Matilainen 5b4d98
 elfdeps_LDADD =		rpmio/librpmio.la
Panu Matilainen 5b4d98
@@ -173,6 +178,7 @@ sepdebugcrcfix_SOURCES = tools/sepdebugcrcfix.c
Panu Matilainen 5b4d98
 sepdebugcrcfix_LDADD =	@WITH_LIBELF_LIB@
Panu Matilainen 5b4d98
 endif
Panu Matilainen 5b4d98
 endif
Panu Matilainen 5b4d98
+endif
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 rpmlibexec_PROGRAMS +=	rpmdeps
Panu Matilainen 5b4d98
 rpmdeps_SOURCES =	tools/rpmdeps.c
Panu Matilainen 5b4d98
diff --git a/configure.ac b/configure.ac
Igor Gnatenko 082d5d
index 9596a97b3..e6362535b 100644
Panu Matilainen 5b4d98
--- a/configure.ac
Panu Matilainen 5b4d98
+++ b/configure.ac
Igor Gnatenko 082d5d
@@ -362,18 +362,24 @@ AM_CONDITIONAL(WITH_ARCHIVE,[test "$with_archive" = yes])
Panu Matilainen 5b4d98
 #=================
Panu Matilainen 5b4d98
 # Check for elfutils libdw library with dwelf_elf_gnu_build_id.
Panu Matilainen 5b4d98
 WITH_LIBDW_LIB=
Panu Matilainen 5b4d98
+HAVE_LIBDW_STRTAB=
Panu Matilainen 5b4d98
 AS_IF([test "$WITH_LIBELF" = yes],[
Panu Matilainen 5b4d98
   AC_CHECK_HEADERS([elfutils/libdwelf.h],[
Panu Matilainen 5b4d98
+    # dwelf_elf_gnu_build_id was introduced in elfutils 0.159
Panu Matilainen 5b4d98
     AC_CHECK_LIB(dw, dwelf_elf_gnu_build_id, [
Panu Matilainen 5b4d98
       AC_DEFINE(HAVE_LIBDW, 1,
Panu Matilainen 5b4d98
                 [Define to 1 if you have elfutils libdw library])
Panu Matilainen 5b4d98
       WITH_LIBDW_LIB="-ldw"
Panu Matilainen 5b4d98
       WITH_LIBDW=yes
Panu Matilainen 5b4d98
+      # If possible we also want the strtab functions from elfutils 0.167.
Panu Matilainen 5b4d98
+      # But we can fall back on the (unsupported) ebl alternatives if not.
Panu Matilainen 5b4d98
+      AC_CHECK_LIB(dw, dwelf_strtab_init, [HAVE_LIBDW_STRTAB=yes])
Panu Matilainen 5b4d98
     ])
Panu Matilainen 5b4d98
   ])
Panu Matilainen 5b4d98
 ])
Panu Matilainen 5b4d98
 AC_SUBST(WITH_LIBDW_LIB)
Panu Matilainen 5b4d98
 AM_CONDITIONAL(LIBDW,[test "$WITH_LIBDW" = yes])
Panu Matilainen 5b4d98
+AM_CONDITIONAL(HAVE_LIBDW_STRTAB,[test "$HAVE_LIBDW_STRTAB" = yes])
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 #=================
Panu Matilainen 5b4d98
 # Process --with/without-external-db
Panu Matilainen 5b4d98
diff --git a/tools/debugedit.c b/tools/debugedit.c
Igor Gnatenko 082d5d
index c0147f086..4798c6370 100644
Panu Matilainen 5b4d98
--- a/tools/debugedit.c
Panu Matilainen 5b4d98
+++ b/tools/debugedit.c
Panu Matilainen 5b4d98
@@ -1,6 +1,7 @@
Panu Matilainen 5b4d98
-/* Copyright (C) 2001-2003, 2005, 2007, 2009-2011, 2016 Red Hat, Inc.
Panu Matilainen 5b4d98
+/* Copyright (C) 2001-2003, 2005, 2007, 2009-2011, 2016, 2017 Red Hat, Inc.
Panu Matilainen 5b4d98
    Written by Alexander Larsson <alexl@redhat.com>, 2002
Panu Matilainen 5b4d98
    Based on code by Jakub Jelinek <jakub@redhat.com>, 2001.
Panu Matilainen 5b4d98
+   String/Line table rewriting by Mark Wielaard <mjw@redhat.com>, 2017.
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
    This program is free software; you can redistribute it and/or modify
Panu Matilainen 5b4d98
    it under the terms of the GNU General Public License as published by
Panu Matilainen 5b4d98
@@ -30,6 +31,7 @@
Panu Matilainen 5b4d98
 #include <string.h>
Panu Matilainen 5b4d98
 #include <stdlib.h>
Panu Matilainen 5b4d98
 #include <stdint.h>
Panu Matilainen 5b4d98
+#include <inttypes.h>
Panu Matilainen 5b4d98
 #include <unistd.h>
Panu Matilainen 5b4d98
 #include <sys/types.h>
Panu Matilainen 5b4d98
 #include <sys/stat.h>
Panu Matilainen 5b4d98
@@ -39,6 +41,35 @@
Panu Matilainen 5b4d98
 #include <gelf.h>
Panu Matilainen 5b4d98
 #include <dwarf.h>
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+/* Unfortunately strtab manipulation functions were only officially added
Panu Matilainen 5b4d98
+   to elfutils libdw in 0.167.  Before that there were internal unsupported
Panu Matilainen 5b4d98
+   ebl variants.  While libebl.h isn't supported we'll try to use it anyway
Panu Matilainen 5b4d98
+   if the elfutils we build against is too old.  */
Panu Matilainen 5b4d98
+#include <elfutils/version.h>
Panu Matilainen 5b4d98
+#if _ELFUTILS_PREREQ (0, 167)
Panu Matilainen 5b4d98
+#include <elfutils/libdwelf.h>
Panu Matilainen 5b4d98
+typedef Dwelf_Strent		Strent;
Panu Matilainen 5b4d98
+typedef Dwelf_Strtab		Strtab;
Panu Matilainen 5b4d98
+#define strtab_init		dwelf_strtab_init
Panu Matilainen 5b4d98
+#define strtab_add(X,Y)		dwelf_strtab_add(X,Y)
Panu Matilainen 5b4d98
+#define strtab_add_len(X,Y,Z)	dwelf_strtab_add_len(X,Y,Z)
Panu Matilainen 5b4d98
+#define strtab_free		dwelf_strtab_free
Panu Matilainen 5b4d98
+#define strtab_finalize		dwelf_strtab_finalize
Panu Matilainen 5b4d98
+#define strent_offset		dwelf_strent_off
Panu Matilainen 5b4d98
+#else
Panu Matilainen 5b4d98
+#include <elfutils/libebl.h>
Panu Matilainen 5b4d98
+typedef struct Ebl_Strent	Strent;
Panu Matilainen 5b4d98
+typedef struct Ebl_Strtab	Strtab;
Panu Matilainen 5b4d98
+#define strtab_init		ebl_strtabinit
Panu Matilainen 5b4d98
+#define strtab_add(X,Y)		ebl_strtabadd(X,Y,0)
Panu Matilainen 5b4d98
+#define strtab_add_len(X,Y,Z)	ebl_strtabadd(X,Y,Z)
Panu Matilainen 5b4d98
+#define strtab_free		ebl_strtabfree
Panu Matilainen 5b4d98
+#define strtab_finalize		ebl_strtabfinalize
Panu Matilainen 5b4d98
+#define strent_offset		ebl_strtaboffset
Panu Matilainen 5b4d98
+#endif
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+#include <search.h>
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
 #include <rpm/rpmio.h>
Panu Matilainen 5b4d98
 #include <rpm/rpmpgp.h>
Panu Matilainen 5b4d98
 #include "tools/hashtab.h"
Panu Matilainen 5b4d98
@@ -56,6 +87,99 @@ int list_file_fd = -1;
Panu Matilainen 5b4d98
 int do_build_id = 0;
Panu Matilainen 5b4d98
 char *build_id_seed = NULL;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+/* We go over the debug sections in two phases. In phase zero we keep
Panu Matilainen 5b4d98
+   track of any needed changes and collect strings, indexes and
Panu Matilainen 5b4d98
+   sizes. In phase one we do the actual replacements updating the
Panu Matilainen 5b4d98
+   strings, indexes and writing out new debug sections. The following
Panu Matilainen 5b4d98
+   keep track of various changes that might be needed. */
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Whether we need to do any literal string (DW_FORM_string) replacements
Panu Matilainen 5b4d98
+   in debug_info. */
Panu Matilainen 5b4d98
+static bool need_string_replacement = false;
Panu Matilainen 5b4d98
+/* Whether we need to do any updates of the string indexes (DW_FORM_strp)
Panu Matilainen 5b4d98
+   in debug_info for string indexes. */
Panu Matilainen 5b4d98
+static bool need_strp_update = false;
Panu Matilainen 5b4d98
+/* If the debug_line changes size we will need to update the
Panu Matilainen 5b4d98
+   DW_AT_stmt_list attributes indexes in the debug_info. */
Panu Matilainen 5b4d98
+static bool need_stmt_update = false;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Storage for dynamically allocated strings to put into string
Panu Matilainen 5b4d98
+   table. Keep together in memory blocks of 16K. */
Panu Matilainen 5b4d98
+#define STRMEMSIZE (16 * 1024)
Panu Matilainen 5b4d98
+struct strmemblock
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  struct strmemblock *next;
Panu Matilainen 5b4d98
+  char memory[0];
Panu Matilainen 5b4d98
+};
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* We keep track of each index in the original string table and the
Panu Matilainen 5b4d98
+   associated entry in the new table so we don't insert identical
Panu Matilainen 5b4d98
+   strings into the new string table. If constructed correctly the
Panu Matilainen 5b4d98
+   original strtab shouldn't contain duplicate strings anyway. Any
Panu Matilainen 5b4d98
+   actual identical strings could be deduplicated, but searching for
Panu Matilainen 5b4d98
+   and comparing the indexes is much faster than comparing strings
Panu Matilainen 5b4d98
+   (and we don't have to construct replacement strings). */
Panu Matilainen 5b4d98
+struct stridxentry
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  uint32_t idx; /* Original index in the string table. */
Panu Matilainen 5b4d98
+  Strent *entry; /* Entry in the new table. */
Panu Matilainen 5b4d98
+};
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Storage for new string table entries. Keep together in memory to
Panu Matilainen 5b4d98
+   quickly search through them with tsearch. */
Panu Matilainen 5b4d98
+#define STRIDXENTRIES ((16 * 1024) / sizeof (struct stridxentry))
Panu Matilainen 5b4d98
+struct strentblock
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  struct strentblock *next;
Panu Matilainen 5b4d98
+  struct stridxentry entry[0];
Panu Matilainen 5b4d98
+};
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* All data to keep track of the existing and new string table. */
Panu Matilainen 5b4d98
+struct strings
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  Strtab *str_tab;			/* The new string table. */
Panu Matilainen 5b4d98
+  char *str_buf;			/* New Elf_Data d_buf. */
Panu Matilainen 5b4d98
+  struct strmemblock *blocks;		/* The first strmemblock. */
Panu Matilainen 5b4d98
+  struct strmemblock *last_block;	/* The currently used strmemblock. */
Panu Matilainen 5b4d98
+  size_t stridx;			/* Next free byte in last block. */
Panu Matilainen 5b4d98
+  struct strentblock *entries;		/* The first string index block. */
Panu Matilainen 5b4d98
+  struct strentblock *last_entries;	/* The currently used strentblock. */
Panu Matilainen 5b4d98
+  size_t entryidx;			/* Next free entry in the last block. */
Panu Matilainen 5b4d98
+  void *strent_root;			/* strent binary search tree root. */
Panu Matilainen 5b4d98
+};
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+struct line_table
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  size_t old_idx;     /* Original offset. */
Panu Matilainen 5b4d98
+  size_t new_idx;     /* Offset in new debug_line section. */
Panu Matilainen 5b4d98
+  ssize_t size_diff;  /* Difference in (header) size. */
Panu Matilainen 5b4d98
+  bool replace_dirs;  /* Whether to replace any dir paths.  */
Panu Matilainen 5b4d98
+  bool replace_files; /* Whether to replace any file paths. */
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* Header fields. */
Panu Matilainen 5b4d98
+  uint32_t unit_length;
Panu Matilainen 5b4d98
+  uint16_t version;
Panu Matilainen 5b4d98
+  uint32_t header_length;
Panu Matilainen 5b4d98
+  uint8_t min_instr_len;
Panu Matilainen 5b4d98
+  uint8_t max_op_per_instr; /* Only if version >= 4 */
Panu Matilainen 5b4d98
+  uint8_t default_is_stmt;
Panu Matilainen 5b4d98
+  int8_t line_base;
Panu Matilainen 5b4d98
+  uint8_t line_range;
Panu Matilainen 5b4d98
+  uint8_t opcode_base;
Panu Matilainen 5b4d98
+};
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+struct debug_lines
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  struct line_table *table; /* Malloc/Realloced. */
Panu Matilainen 5b4d98
+  size_t size;              /* Total number of line_tables.
Panu Matilainen 5b4d98
+			       Updated by get_line_table. */
Panu Matilainen 5b4d98
+  size_t used;              /* Used number of line_tables.
Panu Matilainen 5b4d98
+			       Updated by get_line_table. */
Panu Matilainen 5b4d98
+  size_t debug_lines_len;   /* Total size of new debug_line section.
Panu Matilainen 5b4d98
+			       updated by edit_dwarf2_line. */
Panu Matilainen 5b4d98
+  char *line_buf;           /* New Elf_Data d_buf. */
Panu Matilainen 5b4d98
+};
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
 typedef struct
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
   Elf *elf;
Panu Matilainen 5b4d98
@@ -63,15 +187,42 @@ typedef struct
Panu Matilainen 5b4d98
   Elf_Scn **scn;
Panu Matilainen 5b4d98
   const char *filename;
Panu Matilainen 5b4d98
   int lastscn;
Panu Matilainen 5b4d98
+  size_t phnum;
Panu Matilainen 5b4d98
+  struct strings strings;
Panu Matilainen 5b4d98
+  struct debug_lines lines;
Panu Matilainen 5b4d98
   GElf_Shdr shdr[0];
Panu Matilainen 5b4d98
 } DSO;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+static void
Panu Matilainen 5b4d98
+setup_lines (struct debug_lines *lines)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  lines->table = NULL;
Panu Matilainen 5b4d98
+  lines->size = 0;
Panu Matilainen 5b4d98
+  lines->used = 0;
Panu Matilainen 5b4d98
+  lines->debug_lines_len = 0;
Panu Matilainen 5b4d98
+  lines->line_buf = NULL;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+static void
Panu Matilainen 5b4d98
+destroy_lines (struct debug_lines *lines)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  free (lines->table);
Panu Matilainen 5b4d98
+  free (lines->line_buf);
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
 typedef struct
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
   unsigned char *ptr;
Panu Matilainen 5b4d98
   uint32_t addend;
Panu Matilainen 5b4d98
+  int ndx;
Panu Matilainen 5b4d98
 } REL;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+typedef struct
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  Elf64_Addr r_offset;
Panu Matilainen 5b4d98
+  int ndx;
Panu Matilainen 5b4d98
+} LINE_REL;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
 #define read_uleb128(ptr) ({		\
Panu Matilainen 5b4d98
   unsigned int ret = 0;			\
Panu Matilainen 5b4d98
   unsigned int c;			\
Panu Matilainen 5b4d98
@@ -88,9 +239,23 @@ typedef struct
Panu Matilainen 5b4d98
   ret;					\
Panu Matilainen 5b4d98
 })
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+#define write_uleb128(ptr,val) ({	\
Panu Matilainen 5b4d98
+  uint32_t valv = (val);		\
Panu Matilainen 5b4d98
+  do					\
Panu Matilainen 5b4d98
+    {					\
Panu Matilainen 5b4d98
+      unsigned char c = valv & 0x7f;	\
Panu Matilainen 5b4d98
+      valv >>= 7;			\
Panu Matilainen 5b4d98
+      if (valv)				\
Panu Matilainen 5b4d98
+	c |= 0x80;			\
Panu Matilainen 5b4d98
+      *ptr++ = c;			\
Panu Matilainen 5b4d98
+    }					\
Panu Matilainen 5b4d98
+  while (valv);				\
Panu Matilainen 5b4d98
+})
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
 static uint16_t (*do_read_16) (unsigned char *ptr);
Panu Matilainen 5b4d98
 static uint32_t (*do_read_32) (unsigned char *ptr);
Panu Matilainen 5b4d98
-static void (*write_32) (unsigned char *ptr, GElf_Addr val);
Panu Matilainen 5b4d98
+static void (*do_write_16) (unsigned char *ptr, uint16_t val);
Panu Matilainen 5b4d98
+static void (*do_write_32) (unsigned char *ptr, uint32_t val);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 static int ptr_size;
Panu Matilainen 5b4d98
 static int cu_version;
Panu Matilainen 5b4d98
@@ -129,7 +294,7 @@ strptr (DSO *dso, int sec, off_t offset)
Panu Matilainen 5b4d98
   if (offset >= 0 && (GElf_Addr) offset < dso->shdr[sec].sh_size)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       data = NULL;
Panu Matilainen 5b4d98
-      while ((data = elf_rawdata (scn, data)) != NULL)
Panu Matilainen 5b4d98
+      while ((data = elf_getdata (scn, data)) != NULL)
Panu Matilainen 5b4d98
 	{
Panu Matilainen 5b4d98
 	  if (data->d_buf
Panu Matilainen 5b4d98
 	      && offset >= data->d_off
Panu Matilainen 5b4d98
@@ -142,7 +307,7 @@ strptr (DSO *dso, int sec, off_t offset)
Panu Matilainen 5b4d98
 }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-#define read_1(ptr) *ptr++
Panu Matilainen 5b4d98
+#define read_8(ptr) *ptr++
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 #define read_16(ptr) ({					\
Panu Matilainen 5b4d98
   uint16_t ret = do_read_16 (ptr);			\
Panu Matilainen 5b4d98
@@ -183,28 +348,73 @@ int reltype;
Panu Matilainen 5b4d98
 })
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 static void
Panu Matilainen 5b4d98
-dwarf2_write_le32 (unsigned char *p, GElf_Addr val)
Panu Matilainen 5b4d98
+dwarf2_write_le16 (unsigned char *p, uint16_t v)
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
-  uint32_t v = (uint32_t) val;
Panu Matilainen 5b4d98
+  p[0] = v;
Panu Matilainen 5b4d98
+  p[1] = v >> 8;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+static void
Panu Matilainen 5b4d98
+dwarf2_write_le32 (unsigned char *p, uint32_t v)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
   p[0] = v;
Panu Matilainen 5b4d98
   p[1] = v >> 8;
Panu Matilainen 5b4d98
   p[2] = v >> 16;
Panu Matilainen 5b4d98
   p[3] = v >> 24;
Panu Matilainen 5b4d98
 }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
 static void
Panu Matilainen 5b4d98
-dwarf2_write_be32 (unsigned char *p, GElf_Addr val)
Panu Matilainen 5b4d98
+dwarf2_write_be16 (unsigned char *p, uint16_t v)
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
-  uint32_t v = (uint32_t) val;
Panu Matilainen 5b4d98
+  p[1] = v;
Panu Matilainen 5b4d98
+  p[0] = v >> 8;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+static void
Panu Matilainen 5b4d98
+dwarf2_write_be32 (unsigned char *p, uint32_t v)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
   p[3] = v;
Panu Matilainen 5b4d98
   p[2] = v >> 8;
Panu Matilainen 5b4d98
   p[1] = v >> 16;
Panu Matilainen 5b4d98
   p[0] = v >> 24;
Panu Matilainen 5b4d98
 }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+#define write_8(ptr,val) ({	\
Panu Matilainen 5b4d98
+  *ptr++ = (val);		\
Panu Matilainen 5b4d98
+})
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+#define write_16(ptr,val) ({	\
Panu Matilainen 5b4d98
+  do_write_16 (ptr,val);	\
Panu Matilainen 5b4d98
+  ptr += 2;			\
Panu Matilainen 5b4d98
+})
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+#define write_32(ptr,val) ({	\
Panu Matilainen 5b4d98
+  do_write_32 (ptr,val);	\
Panu Matilainen 5b4d98
+  ptr += 4;			\
Panu Matilainen 5b4d98
+})
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* relocated writes can only be called immediately after
Panu Matilainen 5b4d98
+   do_read_32_relocated.  ptr must be equal to relptr->ptr (or
Panu Matilainen 5b4d98
+   relend). Might just update the addend. So relocations need to be
Panu Matilainen 5b4d98
+   updated at the end.  */
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+#define do_write_32_relocated(ptr,val) ({ \
Panu Matilainen 5b4d98
+  if (relptr && relptr < relend && relptr->ptr == ptr)	\
Panu Matilainen 5b4d98
+    {							\
Panu Matilainen 5b4d98
+      if (reltype == SHT_REL)				\
Panu Matilainen 5b4d98
+	do_write_32 (ptr, val - relptr->addend);	\
Panu Matilainen 5b4d98
+      else						\
Panu Matilainen 5b4d98
+	relptr->addend = val;				\
Panu Matilainen 5b4d98
+    }							\
Panu Matilainen 5b4d98
+  else							\
Panu Matilainen 5b4d98
+    do_write_32 (ptr,val);				\
Panu Matilainen 5b4d98
+})
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+#define write_32_relocated(ptr,val) ({ \
Panu Matilainen 5b4d98
+  do_write_32_relocated (ptr,val);     \
Panu Matilainen 5b4d98
+  ptr += 4;			       \
Panu Matilainen 5b4d98
+})
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
 static struct
Panu Matilainen 5b4d98
   {
Panu Matilainen 5b4d98
     const char *name;
Panu Matilainen 5b4d98
@@ -448,90 +658,638 @@ canonicalize_path (const char *s, char *d)
Panu Matilainen 5b4d98
   return rv;
Panu Matilainen 5b4d98
 }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+/* Returns the rest of PATH if it starts with DIR_PREFIX, skipping any
Panu Matilainen 5b4d98
+   / path separators, or NULL if PATH doesn't start with
Panu Matilainen 5b4d98
+   DIR_PREFIX. Might return the empty string if PATH equals DIR_PREFIX
Panu Matilainen 5b4d98
+   (modulo trailing slashes). Never returns path starting with '/'. */
Panu Matilainen 5b4d98
+static const char *
Panu Matilainen 5b4d98
+skip_dir_prefix (const char *path, const char *dir_prefix)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  size_t prefix_len = strlen (dir_prefix);
Panu Matilainen 5b4d98
+  if (strncmp (path, dir_prefix, prefix_len) == 0)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      path += prefix_len;
Panu Matilainen 5b4d98
+      while (IS_DIR_SEPARATOR (path[0]))
Panu Matilainen 5b4d98
+	path++;
Panu Matilainen 5b4d98
+      return path;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  return 0;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Most strings will be in the existing debug string table. But to
Panu Matilainen 5b4d98
+   replace the base/dest directory prefix we need some new storage.
Panu Matilainen 5b4d98
+   Keep new strings somewhat close together for faster comparison and
Panu Matilainen 5b4d98
+   copying.  SIZE should be at least one (and includes space for the
Panu Matilainen 5b4d98
+   zero terminator). The returned pointer points to uninitialized
Panu Matilainen 5b4d98
+   data.  */
Panu Matilainen 5b4d98
+static char *
Panu Matilainen 5b4d98
+new_string_storage (struct strings *strings, size_t size)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  assert (size > 0);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* If the string is extra long just create a whole block for
Panu Matilainen 5b4d98
+     it. Normally strings are much smaller than STRMEMSIZE. */
Panu Matilainen 5b4d98
+  if (strings->last_block == NULL
Panu Matilainen 5b4d98
+      || size > STRMEMSIZE
Panu Matilainen 5b4d98
+      || strings->stridx > STRMEMSIZE
Panu Matilainen 5b4d98
+      || (STRMEMSIZE - strings->stridx) < size)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      struct strmemblock *newblock = malloc (sizeof (struct strmemblock)
Panu Matilainen 5b4d98
+					     + MAX (STRMEMSIZE, size));
Panu Matilainen 5b4d98
+      if (newblock == NULL)
Panu Matilainen 5b4d98
+	return NULL;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      newblock->next = NULL;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      if (strings->blocks == NULL)
Panu Matilainen 5b4d98
+	strings->blocks = newblock;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      if (strings->last_block != NULL)
Panu Matilainen 5b4d98
+	strings->last_block->next = newblock;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      strings->last_block = newblock;
Panu Matilainen 5b4d98
+      strings->stridx = 0;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  size_t stridx = strings->stridx;
Panu Matilainen 5b4d98
+  strings->stridx += size + 1;
Panu Matilainen 5b4d98
+  return &strings->last_block->memory[stridx];
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Comparison function used for tsearch. */
Panu Matilainen 5b4d98
 static int
Panu Matilainen 5b4d98
-has_prefix (const char  *str,
Panu Matilainen 5b4d98
-	    const char  *prefix)
Panu Matilainen 5b4d98
+strent_compare (const void *a, const void *b)
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
-  size_t str_len;
Panu Matilainen 5b4d98
-  size_t prefix_len;
Panu Matilainen 5b4d98
+  struct stridxentry *entry_a = (struct stridxentry *)a;
Panu Matilainen 5b4d98
+  struct stridxentry *entry_b = (struct stridxentry *)b;
Panu Matilainen 5b4d98
+  size_t idx_a = entry_a->idx;
Panu Matilainen 5b4d98
+  size_t idx_b = entry_b->idx;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  str_len = strlen (str);
Panu Matilainen 5b4d98
-  prefix_len = strlen (prefix);
Panu Matilainen 5b4d98
+  if (idx_a < idx_b)
Panu Matilainen 5b4d98
+    return -1;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  if (str_len < prefix_len)
Panu Matilainen 5b4d98
-    return 0;
Panu Matilainen 5b4d98
+  if (idx_a > idx_b)
Panu Matilainen 5b4d98
+    return 1;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  return strncmp (str, prefix, prefix_len) == 0;
Panu Matilainen 5b4d98
+  return 0;
Panu Matilainen 5b4d98
 }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-static int dirty_elf;
Panu Matilainen 5b4d98
+/* Allocates and inserts a new entry for the old index if not yet
Panu Matilainen 5b4d98
+   seen.  Returns a stridxentry if the given index has not yet been
Panu Matilainen 5b4d98
+   seen and needs to be filled in with the associated string (either
Panu Matilainen 5b4d98
+   the original string or the replacement string). Returns NULL if the
Panu Matilainen 5b4d98
+   idx is already known. Use in phase 0 to add all strings seen. In
Panu Matilainen 5b4d98
+   phase 1 use string_find_entry instead to get existing entries. */
Panu Matilainen 5b4d98
+static struct stridxentry *
Panu Matilainen 5b4d98
+string_find_new_entry (struct strings *strings, size_t old_idx)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  /* Use next entry in the pool for lookup so we can use it directly
Panu Matilainen 5b4d98
+     if this is a new index. */
Panu Matilainen 5b4d98
+  struct stridxentry *entry;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* Keep entries close together to make key comparison fast. */
Panu Matilainen 5b4d98
+  if (strings->last_entries == NULL || strings->entryidx >= STRIDXENTRIES)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      size_t entriessz = (sizeof (struct strentblock)
Panu Matilainen 5b4d98
+			  + (STRIDXENTRIES * sizeof (struct stridxentry)));
Panu Matilainen 5b4d98
+      struct strentblock *newentries = malloc (entriessz);
Panu Matilainen 5b4d98
+      if (newentries == NULL)
Panu Matilainen 5b4d98
+	error (1, errno, "Couldn't allocate new string entries block");
Panu Matilainen 5b4d98
+      else
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  if (strings->entries == NULL)
Panu Matilainen 5b4d98
+	    strings->entries = newentries;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  if (strings->last_entries != NULL)
Panu Matilainen 5b4d98
+	    strings->last_entries->next = newentries;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  strings->last_entries = newentries;
Panu Matilainen 5b4d98
+	  strings->last_entries->next = NULL;
Panu Matilainen 5b4d98
+	  strings->entryidx = 0;
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  entry = &strings->last_entries->entry[strings->entryidx];
Panu Matilainen 5b4d98
+  entry->idx = old_idx;
Panu Matilainen 5b4d98
+  struct stridxentry **tres = tsearch (entry, &strings->strent_root,
Panu Matilainen 5b4d98
+				       strent_compare);
Panu Matilainen 5b4d98
+  if (tres == NULL)
Panu Matilainen 5b4d98
+    error (1, ENOMEM, "Couldn't insert new strtab idx");
Panu Matilainen 5b4d98
+  else if (*tres == entry)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      /* idx not yet seen, must add actual str.  */
Panu Matilainen 5b4d98
+      strings->entryidx++;
Panu Matilainen 5b4d98
+      return entry;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  return NULL; /* We already know about this idx, entry already complete. */
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+static struct stridxentry *
Panu Matilainen 5b4d98
+string_find_entry (struct strings *strings, size_t old_idx)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  struct stridxentry **ret;
Panu Matilainen 5b4d98
+  struct stridxentry key;
Panu Matilainen 5b4d98
+  key.idx = old_idx;
Panu Matilainen 5b4d98
+  ret = tfind (&key, &strings->strent_root, strent_compare);
Panu Matilainen 5b4d98
+  assert (ret != NULL); /* Can only happen for a bad/non-existing old_idx. */
Panu Matilainen 5b4d98
+  return *ret;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Adds a string_idx_entry given an index into the old/existing string
Panu Matilainen 5b4d98
+   table. Should be used in phase 0. Does nothing if the index was
Panu Matilainen 5b4d98
+   already registered. Otherwise it checks the string associated with
Panu Matilainen 5b4d98
+   the index. If the old string doesn't start with base_dir an entry
Panu Matilainen 5b4d98
+   will be recorded for the index with the same string. Otherwise a
Panu Matilainen 5b4d98
+   string will be recorded where the base_dir prefix will be replaced
Panu Matilainen 5b4d98
+   by dest_dir. Returns true if this is a not yet seen index and there
Panu Matilainen 5b4d98
+   a replacement file string has been recorded for it, otherwise
Panu Matilainen 5b4d98
+   returns false.  */
Panu Matilainen 5b4d98
+static bool
Panu Matilainen 5b4d98
+record_file_string_entry_idx (struct strings *strings, size_t old_idx)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  bool ret = false;
Panu Matilainen 5b4d98
+  struct stridxentry *entry = string_find_new_entry (strings, old_idx);
Panu Matilainen 5b4d98
+  if (entry != NULL)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      Strent *strent;
Panu Matilainen 5b4d98
+      const char *old_str = (char *)debug_sections[DEBUG_STR].data + old_idx;
Panu Matilainen 5b4d98
+      const char *file = skip_dir_prefix (old_str, base_dir);
Panu Matilainen 5b4d98
+      if (file == NULL)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  /* Just record the existing string.  */
Panu Matilainen 5b4d98
+	  strent = strtab_add_len (strings->str_tab, old_str,
Panu Matilainen 5b4d98
+				   strlen (old_str) + 1);
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+      else
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  /* Create and record the altered file path. */
Panu Matilainen 5b4d98
+	  size_t dest_len = strlen (dest_dir);
Panu Matilainen 5b4d98
+	  size_t file_len = strlen (file);
Panu Matilainen 5b4d98
+	  size_t nsize = dest_len + 1; /* + '\0' */
Panu Matilainen 5b4d98
+	  if (file_len > 0)
Panu Matilainen 5b4d98
+	    nsize += 1 + file_len;     /* + '/' */
Panu Matilainen 5b4d98
+	  char *nname = new_string_storage (strings, nsize);
Panu Matilainen 5b4d98
+	  if (nname == NULL)
Panu Matilainen 5b4d98
+	    error (1, ENOMEM, "Couldn't allocate new string storage");
Panu Matilainen 5b4d98
+	  memcpy (nname, dest_dir, dest_len);
Panu Matilainen 5b4d98
+	  if (file_len > 0)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      nname[dest_len] = '/';
Panu Matilainen 5b4d98
+	      memcpy (nname + dest_len + 1, file, file_len + 1);
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+	  else
Panu Matilainen 5b4d98
+	    nname[dest_len] = '\0';
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  strent = strtab_add_len (strings->str_tab, nname, nsize);
Panu Matilainen 5b4d98
+	  ret = true;
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+      if (strent == NULL)
Panu Matilainen 5b4d98
+	error (1, ENOMEM, "Could not create new string table entry");
Panu Matilainen 5b4d98
+      else
Panu Matilainen 5b4d98
+	entry->entry = strent;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  return ret;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Same as record_new_string_file_string_entry_idx but doesn't replace
Panu Matilainen 5b4d98
+   base_dir with dest_dir, just records the existing string associated
Panu Matilainen 5b4d98
+   with the index. */
Panu Matilainen 5b4d98
 static void
Panu Matilainen 5b4d98
-dirty_section (unsigned int sec)
Panu Matilainen 5b4d98
+record_existing_string_entry_idx (struct strings *strings, size_t old_idx)
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
-  elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY);
Panu Matilainen 5b4d98
-  dirty_elf = 1;
Panu Matilainen 5b4d98
+  struct stridxentry *entry = string_find_new_entry (strings, old_idx);
Panu Matilainen 5b4d98
+  if (entry != NULL)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      const char *str = (char *)debug_sections[DEBUG_STR].data + old_idx;
Panu Matilainen 5b4d98
+      Strent *strent = strtab_add_len (strings->str_tab,
Panu Matilainen 5b4d98
+				       str, strlen (str) + 1);
Panu Matilainen 5b4d98
+      if (strent == NULL)
Panu Matilainen 5b4d98
+	error (1, ENOMEM, "Could not create new string table entry");
Panu Matilainen 5b4d98
+      else
Panu Matilainen 5b4d98
+	entry->entry = strent;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
 }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-static int
Panu Matilainen 5b4d98
-edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase)
Panu Matilainen 5b4d98
+static void
Panu Matilainen 5b4d98
+setup_strings (struct strings *strings)
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
-  unsigned char *ptr = debug_sections[DEBUG_LINE].data, *dir;
Panu Matilainen 5b4d98
-  unsigned char **dirt;
Panu Matilainen 5b4d98
-  unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size;
Panu Matilainen 5b4d98
-  unsigned char *endcu, *endprol;
Panu Matilainen 5b4d98
-  unsigned char opcode_base;
Panu Matilainen 5b4d98
-  uint32_t value, dirt_cnt;
Panu Matilainen 5b4d98
-  size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir);
Panu Matilainen 5b4d98
-  size_t abs_file_cnt = 0, abs_dir_cnt = 0;
Panu Matilainen 5b4d98
+  strings->str_tab = strtab_init (false);
Panu Matilainen 5b4d98
+  strings->str_buf = NULL;
Panu Matilainen 5b4d98
+  strings->blocks = NULL;
Panu Matilainen 5b4d98
+  strings->last_block = NULL;
Panu Matilainen 5b4d98
+  strings->entries = NULL;
Panu Matilainen 5b4d98
+  strings->last_entries = NULL;
Panu Matilainen 5b4d98
+  strings->strent_root = NULL;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  if (phase != 0)
Panu Matilainen 5b4d98
-    return 0;
Panu Matilainen 5b4d98
+/* Noop for tdestroy. */
Panu Matilainen 5b4d98
+static void free_node (void *p __attribute__((__unused__))) { }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  /* XXX: RhBug:929365, should we error out instead of ignoring? */
Panu Matilainen 5b4d98
+static void
Panu Matilainen 5b4d98
+destroy_strings (struct strings *strings)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  struct strmemblock *smb = strings->blocks;
Panu Matilainen 5b4d98
+  while (smb != NULL)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      void *old = smb;
Panu Matilainen 5b4d98
+      smb = smb->next;
Panu Matilainen 5b4d98
+      free (old);
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  struct strentblock *emb = strings->entries;
Panu Matilainen 5b4d98
+  while (emb != NULL)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      void *old = emb;
Panu Matilainen 5b4d98
+      emb = emb->next;
Panu Matilainen 5b4d98
+      free (old);
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  strtab_free (strings->str_tab);
Panu Matilainen 5b4d98
+  tdestroy (strings->strent_root, &free_node);
Panu Matilainen 5b4d98
+  free (strings->str_buf);
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* The minimum number of line tables we pre-allocate. */
Panu Matilainen 5b4d98
+#define MIN_LINE_TABLES 64
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Gets a line_table at offset. Returns true if not yet know and
Panu Matilainen 5b4d98
+   successfully read, false otherwise.  Sets *table to NULL and
Panu Matilainen 5b4d98
+   outputs a warning if there was a problem reading the table at the
Panu Matilainen 5b4d98
+   given offset.  */
Panu Matilainen 5b4d98
+static bool
Panu Matilainen 5b4d98
+get_line_table (DSO *dso, size_t off, struct line_table **table)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  struct debug_lines *lines = &dso->lines;
Panu Matilainen 5b4d98
+  /* Assume there aren't that many, just do a linear search.  The
Panu Matilainen 5b4d98
+     array is probably already sorted because the stmt_lists are
Panu Matilainen 5b4d98
+     probably inserted in order. But we cannot rely on that (maybe we
Panu Matilainen 5b4d98
+     should check that to make searching quicker if possible?).  Once
Panu Matilainen 5b4d98
+     we have all line tables for phase 1 (rewriting) we do explicitly
Panu Matilainen 5b4d98
+     sort the array.*/
Panu Matilainen 5b4d98
+  for (int i = 0; i < lines->used; i++)
Panu Matilainen 5b4d98
+    if (lines->table[i].old_idx == off)
Panu Matilainen 5b4d98
+      {
Panu Matilainen 5b4d98
+	*table = &lines->table[i];
Panu Matilainen 5b4d98
+	return false;
Panu Matilainen 5b4d98
+      }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  if (lines->size == lines->used)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      struct line_table *new_table = realloc (lines->table,
Panu Matilainen 5b4d98
+					      (sizeof (struct line_table)
Panu Matilainen 5b4d98
+					       * (lines->size
Panu Matilainen 5b4d98
+						  + MIN_LINE_TABLES)));
Panu Matilainen 5b4d98
+      if (new_table == NULL)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  error (0, ENOMEM, "Couldn't add more debug_line tables");
Panu Matilainen 5b4d98
+	  *table = NULL;
Panu Matilainen 5b4d98
+	  return false;
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+      lines->table = new_table;
Panu Matilainen 5b4d98
+      lines->size += MIN_LINE_TABLES;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  struct line_table *t = &lines->table[lines->used];
Panu Matilainen 5b4d98
+  *table = NULL;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  t->old_idx = off;
Panu Matilainen 5b4d98
+  t->size_diff = 0;
Panu Matilainen 5b4d98
+  t->replace_dirs = false;
Panu Matilainen 5b4d98
+  t->replace_files = false;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  unsigned char *ptr = debug_sections[DEBUG_LINE].data;
Panu Matilainen 5b4d98
+  unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size;
Panu Matilainen 5b4d98
   if (ptr == NULL)
Panu Matilainen 5b4d98
-    return 0;
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      error (0, 0, "%s: No .line_table section", dso->filename);
Panu Matilainen 5b4d98
+      return false;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+  if (off > debug_sections[DEBUG_LINE].size)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      error (0, 0, "%s: Invalid .line_table offset 0x%zx",
Panu Matilainen 5b4d98
+	     dso->filename, off);
Panu Matilainen 5b4d98
+      return false;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
   ptr += off;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  endcu = ptr + 4;
Panu Matilainen 5b4d98
-  endcu += read_32 (ptr);
Panu Matilainen 5b4d98
+  /* unit_length */
Panu Matilainen 5b4d98
+  unsigned char *endcu = ptr + 4;
Panu Matilainen 5b4d98
+  t->unit_length = read_32 (ptr);
Panu Matilainen 5b4d98
+  endcu += t->unit_length;
Panu Matilainen 5b4d98
   if (endcu == ptr + 0xffffffff)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
Panu Matilainen 5b4d98
-      return 1;
Panu Matilainen 5b4d98
+      return false;
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
   if (endcu > endsec)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       error (0, 0, "%s: .debug_line CU does not fit into section",
Panu Matilainen 5b4d98
 	     dso->filename);
Panu Matilainen 5b4d98
-      return 1;
Panu Matilainen 5b4d98
+      return false;
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  value = read_16 (ptr);
Panu Matilainen 5b4d98
-  if (value != 2 && value != 3 && value != 4)
Panu Matilainen 5b4d98
+  /* version */
Panu Matilainen 5b4d98
+  t->version = read_16 (ptr);
Panu Matilainen 5b4d98
+  if (t->version != 2 && t->version != 3 && t->version != 4)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
Panu Matilainen 5b4d98
-	     value);
Panu Matilainen 5b4d98
-      return 1;
Panu Matilainen 5b4d98
+	     t->version);
Panu Matilainen 5b4d98
+      return false;
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  endprol = ptr + 4;
Panu Matilainen 5b4d98
-  endprol += read_32 (ptr);
Panu Matilainen 5b4d98
+  /* header_length */
Panu Matilainen 5b4d98
+  unsigned char *endprol = ptr + 4;
Panu Matilainen 5b4d98
+  t->header_length = read_32 (ptr);
Panu Matilainen 5b4d98
+  endprol += t->header_length;
Panu Matilainen 5b4d98
   if (endprol > endcu)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       error (0, 0, "%s: .debug_line CU prologue does not fit into CU",
Panu Matilainen 5b4d98
 	     dso->filename);
Panu Matilainen 5b4d98
-      return 1;
Panu Matilainen 5b4d98
+      return false;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* min instr len */
Panu Matilainen 5b4d98
+  t->min_instr_len = *ptr++;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* max op per instr, if version >= 4 */
Panu Matilainen 5b4d98
+  if (t->version >= 4)
Panu Matilainen 5b4d98
+    t->max_op_per_instr = *ptr++;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* default is stmt */
Panu Matilainen 5b4d98
+  t->default_is_stmt = *ptr++;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* line base */
Panu Matilainen 5b4d98
+  t->line_base = (*(int8_t *)ptr++);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* line range */
Panu Matilainen 5b4d98
+  t->line_range = *ptr++;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* opcode base */
Panu Matilainen 5b4d98
+  t->opcode_base = *ptr++;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  if (ptr + t->opcode_base - 1 >= endcu)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      error (0, 0, "%s: .debug_line opcode table does not fit into CU",
Panu Matilainen 5b4d98
+	     dso->filename);
Panu Matilainen 5b4d98
+      return false;
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
+  lines->used++;
Panu Matilainen 5b4d98
+  *table = t;
Panu Matilainen 5b4d98
+  return true;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  opcode_base = ptr[4 + (value >= 4)];
Panu Matilainen 5b4d98
-  ptr = dir = ptr + 4 + (value >= 4) + opcode_base;
Panu Matilainen 5b4d98
+static int dirty_elf;
Panu Matilainen 5b4d98
+static void
Panu Matilainen 5b4d98
+dirty_section (unsigned int sec)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY);
Panu Matilainen 5b4d98
+  dirty_elf = 1;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+static int
Panu Matilainen 5b4d98
+line_table_cmp (const void *a, const void *b)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  struct line_table *ta = (struct line_table *) a;
Panu Matilainen 5b4d98
+  struct line_table *tb = (struct line_table *) b;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  if (ta->old_idx < tb->old_idx)
Panu Matilainen 5b4d98
+    return -1;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  if (ta->old_idx > tb->old_idx)
Panu Matilainen 5b4d98
+    return 1;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  return 0;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Called after phase zero (which records all adjustments needed for
Panu Matilainen 5b4d98
+   the line tables referenced from debug_info) and before phase one
Panu Matilainen 5b4d98
+   starts (phase one will adjust the .debug_line section stmt
Panu Matilainen 5b4d98
+   references using the updated data structures). */
Panu Matilainen 5b4d98
+static void
Panu Matilainen 5b4d98
+edit_dwarf2_line (DSO *dso)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  Elf_Data *linedata = debug_sections[DEBUG_LINE].elf_data;
Panu Matilainen 5b4d98
+  int linendx = debug_sections[DEBUG_LINE].sec;
Panu Matilainen 5b4d98
+  Elf_Scn *linescn = dso->scn[linendx];
Panu Matilainen 5b4d98
+  unsigned char *old_buf = linedata->d_buf;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* Out with the old. */
Panu Matilainen 5b4d98
+  linedata->d_size = 0;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* In with the new. */
Panu Matilainen 5b4d98
+  linedata = elf_newdata (linescn);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  dso->lines.line_buf = malloc (dso->lines.debug_lines_len);
Panu Matilainen 5b4d98
+  if (dso->lines.line_buf == NULL)
Panu Matilainen 5b4d98
+    error (1, ENOMEM, "No memory for new .debug_line table (0x%zx bytes)",
Panu Matilainen 5b4d98
+	   dso->lines.debug_lines_len);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  linedata->d_size = dso->lines.debug_lines_len;
Panu Matilainen 5b4d98
+  linedata->d_buf = dso->lines.line_buf;
Panu Matilainen 5b4d98
+  debug_sections[DEBUG_LINE].size = linedata->d_size;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* Make sure the line tables are sorted on the old index. */
Panu Matilainen 5b4d98
+  qsort (dso->lines.table, dso->lines.used, sizeof (struct line_table),
Panu Matilainen 5b4d98
+	 line_table_cmp);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  unsigned char *ptr = linedata->d_buf;
Panu Matilainen 5b4d98
+  for (int ldx = 0; ldx < dso->lines.used; ldx++)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      struct line_table *t = &dso->lines.table[ldx];
Panu Matilainen 5b4d98
+      unsigned char *optr = old_buf + t->old_idx;
Panu Matilainen 5b4d98
+      t->new_idx = ptr - (unsigned char *) linedata->d_buf;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* Just copy the whole table if nothing needs replacing. */
Panu Matilainen 5b4d98
+      if (! t->replace_dirs && ! t->replace_files)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  assert (t->size_diff == 0);
Panu Matilainen 5b4d98
+	  memcpy (ptr, optr, t->unit_length + 4);
Panu Matilainen 5b4d98
+	  ptr += t->unit_length + 4;
Panu Matilainen 5b4d98
+	  continue;
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* Header fields. */
Panu Matilainen 5b4d98
+      write_32 (ptr, t->unit_length + t->size_diff);
Panu Matilainen 5b4d98
+      write_16 (ptr, t->version);
Panu Matilainen 5b4d98
+      write_32 (ptr, t->header_length + t->size_diff);
Panu Matilainen 5b4d98
+      write_8 (ptr, t->min_instr_len);
Panu Matilainen 5b4d98
+      if (t->version >= 4)
Panu Matilainen 5b4d98
+	write_8 (ptr, t->max_op_per_instr);
Panu Matilainen 5b4d98
+      write_8 (ptr, t->default_is_stmt);
Panu Matilainen 5b4d98
+      write_8 (ptr, t->line_base);
Panu Matilainen 5b4d98
+      write_8 (ptr, t->line_range);
Panu Matilainen 5b4d98
+      write_8 (ptr, t->opcode_base);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      optr += (4 /* unit len */
Panu Matilainen 5b4d98
+	       + 2 /* version */
Panu Matilainen 5b4d98
+	       + 4 /* header len */
Panu Matilainen 5b4d98
+	       + 1 /* min instr len */
Panu Matilainen 5b4d98
+	       + (t->version >= 4) /* max op per instr, if version >= 4 */
Panu Matilainen 5b4d98
+	       + 1 /* default is stmt */
Panu Matilainen 5b4d98
+	       + 1 /* line base */
Panu Matilainen 5b4d98
+	       + 1 /* line range */
Panu Matilainen 5b4d98
+	       + 1); /* opcode base */
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* opcode len table. */
Panu Matilainen 5b4d98
+      memcpy (ptr, optr, t->opcode_base - 1);
Panu Matilainen 5b4d98
+      optr += t->opcode_base - 1;
Panu Matilainen 5b4d98
+      ptr += t->opcode_base - 1;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* directory table. We need to find the end (start of file
Panu Matilainen 5b4d98
+	 table) anyway, so loop over all dirs, even if replace_dirs is
Panu Matilainen 5b4d98
+	 false. */
Panu Matilainen 5b4d98
+      while (*optr != 0)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  const char *dir = (const char *) optr;
Panu Matilainen 5b4d98
+	  const char *file_path = NULL;
Panu Matilainen 5b4d98
+	  if (t->replace_dirs)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      file_path = skip_dir_prefix (dir, base_dir);
Panu Matilainen 5b4d98
+	      if (file_path != NULL)
Panu Matilainen 5b4d98
+		{
Panu Matilainen 5b4d98
+		  size_t dest_len = strlen (dest_dir);
Panu Matilainen 5b4d98
+		  size_t file_len = strlen (file_path);
Panu Matilainen 5b4d98
+		  memcpy (ptr, dest_dir, dest_len);
Panu Matilainen 5b4d98
+		  ptr += dest_len;
Panu Matilainen 5b4d98
+		  if (file_len > 0)
Panu Matilainen 5b4d98
+		    {
Panu Matilainen 5b4d98
+		      *ptr++ = '/';
Panu Matilainen 5b4d98
+		      memcpy (ptr, file_path, file_len);
Panu Matilainen 5b4d98
+		      ptr += file_len;
Panu Matilainen 5b4d98
+		    }
Panu Matilainen 5b4d98
+		  *ptr++ = '\0';
Panu Matilainen 5b4d98
+		}
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+	  if (file_path == NULL)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      size_t dir_len = strlen (dir);
Panu Matilainen 5b4d98
+	      memcpy (ptr, dir, dir_len + 1);
Panu Matilainen 5b4d98
+	      ptr += dir_len + 1;
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  optr = (unsigned char *) strchr (dir, 0) + 1;
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+      optr++;
Panu Matilainen 5b4d98
+      *ptr++ = '\0';
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* file table */
Panu Matilainen 5b4d98
+      if (t->replace_files)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  while (*optr != 0)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      const char *file = (const char *) optr;
Panu Matilainen 5b4d98
+	      const char *file_path = NULL;
Panu Matilainen 5b4d98
+	      if (t->replace_dirs)
Panu Matilainen 5b4d98
+		{
Panu Matilainen 5b4d98
+		  file_path = skip_dir_prefix (file, base_dir);
Panu Matilainen 5b4d98
+		  if (file_path != NULL)
Panu Matilainen 5b4d98
+		    {
Panu Matilainen 5b4d98
+		      size_t dest_len = strlen (dest_dir);
Panu Matilainen 5b4d98
+		      size_t file_len = strlen (file_path);
Panu Matilainen 5b4d98
+		      memcpy (ptr, dest_dir, dest_len);
Panu Matilainen 5b4d98
+		      ptr += dest_len;
Panu Matilainen 5b4d98
+		      if (file_len > 0)
Panu Matilainen 5b4d98
+			{
Panu Matilainen 5b4d98
+			  *ptr++ = '/';
Panu Matilainen 5b4d98
+			  memcpy (ptr, file_path, file_len);
Panu Matilainen 5b4d98
+			  ptr += file_len;
Panu Matilainen 5b4d98
+			}
Panu Matilainen 5b4d98
+		      *ptr++ = '\0';
Panu Matilainen 5b4d98
+		    }
Panu Matilainen 5b4d98
+		}
Panu Matilainen 5b4d98
+	      if (file_path == NULL)
Panu Matilainen 5b4d98
+		{
Panu Matilainen 5b4d98
+		  size_t file_len = strlen (file);
Panu Matilainen 5b4d98
+		  memcpy (ptr, file, file_len + 1);
Panu Matilainen 5b4d98
+		  ptr += file_len + 1;
Panu Matilainen 5b4d98
+		}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      optr = (unsigned char *) strchr (file, 0) + 1;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      /* dir idx, time, len */
Panu Matilainen 5b4d98
+	      uint32_t dir_idx = read_uleb128 (optr);
Panu Matilainen 5b4d98
+	      write_uleb128 (ptr, dir_idx);
Panu Matilainen 5b4d98
+	      uint32_t time = read_uleb128 (optr);
Panu Matilainen 5b4d98
+	      write_uleb128 (ptr, time);
Panu Matilainen 5b4d98
+	      uint32_t len = read_uleb128 (optr);
Panu Matilainen 5b4d98
+	      write_uleb128 (ptr, len);
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+	  optr++;
Panu Matilainen 5b4d98
+	  *ptr++ = '\0';
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* line number program (and file table if not copied above). */
Panu Matilainen 5b4d98
+      size_t remaining = (t->unit_length + 4
Panu Matilainen 5b4d98
+			  - (optr - (old_buf + t->old_idx)));
Panu Matilainen 5b4d98
+      memcpy (ptr, optr, remaining);
Panu Matilainen 5b4d98
+      ptr += remaining;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+/* Called during phase zero for each debug_line table referenced from
Panu Matilainen 5b4d98
+   .debug_info.  Outputs all source files seen and records any
Panu Matilainen 5b4d98
+   adjustments needed in the debug_list data structures. Returns true
Panu Matilainen 5b4d98
+   if line_table needs to be rewrite either the dir or file paths. */
Panu Matilainen 5b4d98
+static bool
Panu Matilainen 5b4d98
+read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  unsigned char *ptr, *dir;
Panu Matilainen 5b4d98
+  unsigned char **dirt;
Panu Matilainen 5b4d98
+  uint32_t value, dirt_cnt;
Panu Matilainen 5b4d98
+  size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir);
Panu Matilainen 5b4d98
+  struct line_table *table;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  if (get_line_table (dso, off, &table) == false
Panu Matilainen 5b4d98
+      || table == NULL)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      if (table != NULL)
Panu Matilainen 5b4d98
+	error (0, 0, ".debug_line offset 0x%x referenced multiple times",
Panu Matilainen 5b4d98
+	       off);
Panu Matilainen 5b4d98
+      return false;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* Skip to the directory table. The rest of the header has already
Panu Matilainen 5b4d98
+     been read and checked by get_line_table. */
Panu Matilainen 5b4d98
+  ptr = debug_sections[DEBUG_LINE].data + off;
Panu Matilainen 5b4d98
+  ptr += (4 /* unit len */
Panu Matilainen 5b4d98
+	  + 2 /* version */
Panu Matilainen 5b4d98
+	  + 4 /* header len */
Panu Matilainen 5b4d98
+	  + 1 /* min instr len */
Panu Matilainen 5b4d98
+	  + (table->version >= 4) /* max op per instr, if version >= 4 */
Panu Matilainen 5b4d98
+	  + 1 /* default is stmt */
Panu Matilainen 5b4d98
+	  + 1 /* line base */
Panu Matilainen 5b4d98
+	  + 1 /* line range */
Panu Matilainen 5b4d98
+	  + 1 /* opcode base */
Panu Matilainen 5b4d98
+	  + table->opcode_base - 1); /* opcode len table */
Panu Matilainen 5b4d98
+  dir = ptr;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
   /* dir table: */
Panu Matilainen 5b4d98
   value = 1;
Panu Matilainen 5b4d98
   while (*ptr != 0)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
+      if (base_dir && dest_dir)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  /* Do we need to replace any of the dirs? Calculate new size. */
Panu Matilainen 5b4d98
+	  const char *file_path = skip_dir_prefix ((const char *)ptr,
Panu Matilainen 5b4d98
+						   base_dir);
Panu Matilainen 5b4d98
+	  if (file_path != NULL)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      size_t old_size = strlen ((const char *)ptr) + 1;
Panu Matilainen 5b4d98
+	      size_t file_len = strlen (file_path);
Panu Matilainen 5b4d98
+	      size_t new_size = strlen (dest_dir) + 1;
Panu Matilainen 5b4d98
+	      if (file_len > 0)
Panu Matilainen 5b4d98
+		new_size += 1 + file_len;
Panu Matilainen 5b4d98
+	      table->size_diff += (new_size - old_size);
Panu Matilainen 5b4d98
+	      table->replace_dirs = true;
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
       ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
Panu Matilainen 5b4d98
       ++value;
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
@@ -561,21 +1319,34 @@ edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase)
Panu Matilainen 5b4d98
 	{
Panu Matilainen 5b4d98
 	  error (0, 0, "%s: Wrong directory table index %u",
Panu Matilainen 5b4d98
 		 dso->filename, value);
Panu Matilainen 5b4d98
-	  return 1;
Panu Matilainen 5b4d98
+	  return false;
Panu Matilainen 5b4d98
 	}
Panu Matilainen 5b4d98
       file_len = strlen (file);
Panu Matilainen 5b4d98
+      if (base_dir && dest_dir)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  /* Do we need to replace any of the files? Calculate new size. */
Panu Matilainen 5b4d98
+	  const char *file_path = skip_dir_prefix (file, base_dir);
Panu Matilainen 5b4d98
+	  if (file_path != NULL)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      size_t old_size = file_len + 1;
Panu Matilainen 5b4d98
+	      size_t file_len = strlen (file_path);
Panu Matilainen 5b4d98
+	      size_t new_size = strlen (dest_dir) + 1;
Panu Matilainen 5b4d98
+	      if (file_len > 0)
Panu Matilainen 5b4d98
+		new_size += 1 + file_len;
Panu Matilainen 5b4d98
+	      table->size_diff += (new_size - old_size);
Panu Matilainen 5b4d98
+	      table->replace_files = true;
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
       dir_len = strlen ((char *)dirt[value]);
Panu Matilainen 5b4d98
       s = malloc (comp_dir_len + 1 + file_len + 1 + dir_len + 1);
Panu Matilainen 5b4d98
       if (s == NULL)
Panu Matilainen 5b4d98
 	{
Panu Matilainen 5b4d98
 	  error (0, ENOMEM, "%s: Reading file table", dso->filename);
Panu Matilainen 5b4d98
-	  return 1;
Panu Matilainen 5b4d98
+	  return false;
Panu Matilainen 5b4d98
 	}
Panu Matilainen 5b4d98
       if (*file == '/')
Panu Matilainen 5b4d98
 	{
Panu Matilainen 5b4d98
 	  memcpy (s, file, file_len + 1);
Panu Matilainen 5b4d98
-	  if (dest_dir && has_prefix (file, base_dir))
Panu Matilainen 5b4d98
-	    ++abs_file_cnt;
Panu Matilainen 5b4d98
 	}
Panu Matilainen 5b4d98
       else if (*dirt[value] == '/')
Panu Matilainen 5b4d98
 	{
Panu Matilainen 5b4d98
@@ -599,13 +1370,15 @@ edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase)
Panu Matilainen 5b4d98
       canonicalize_path (s, s);
Panu Matilainen 5b4d98
       if (list_file_fd != -1)
Panu Matilainen 5b4d98
 	{
Panu Matilainen 5b4d98
-	  char *p = NULL;
Panu Matilainen 5b4d98
+	  const char *p = NULL;
Panu Matilainen 5b4d98
 	  if (base_dir == NULL)
Panu Matilainen 5b4d98
 	    p = s;
Panu Matilainen 5b4d98
-	  else if (has_prefix (s, base_dir))
Panu Matilainen 5b4d98
-	    p = s + strlen (base_dir);
Panu Matilainen 5b4d98
-	  else if (has_prefix (s, dest_dir))
Panu Matilainen 5b4d98
-	    p = s + strlen (dest_dir);
Panu Matilainen 5b4d98
+	  else
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      p = skip_dir_prefix (s, base_dir);
Panu Matilainen 5b4d98
+	      if (p == NULL && dest_dir != NULL)
Panu Matilainen 5b4d98
+		p = skip_dir_prefix (s, dest_dir);
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 	  if (p)
Panu Matilainen 5b4d98
 	    {
Panu Matilainen 5b4d98
@@ -626,112 +1399,28 @@ edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase)
Panu Matilainen 5b4d98
       read_uleb128 (ptr);
Panu Matilainen 5b4d98
       read_uleb128 (ptr);
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
-  ++ptr;
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-  if (dest_dir)
Panu Matilainen 5b4d98
-    {
Panu Matilainen 5b4d98
-      unsigned char *srcptr, *buf = NULL;
Panu Matilainen 5b4d98
-      size_t base_len = strlen (base_dir);
Panu Matilainen 5b4d98
-      size_t dest_len = strlen (dest_dir);
Panu Matilainen 5b4d98
-      size_t shrank = 0;
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-      if (dest_len == base_len)
Panu Matilainen 5b4d98
-	abs_file_cnt = 0;
Panu Matilainen 5b4d98
-      if (abs_file_cnt)
Panu Matilainen 5b4d98
-	{
Panu Matilainen 5b4d98
-	  srcptr = buf = malloc (ptr - dir);
Panu Matilainen 5b4d98
-	  memcpy (srcptr, dir, ptr - dir);
Panu Matilainen 5b4d98
-	  ptr = dir;
Panu Matilainen 5b4d98
-	}
Panu Matilainen 5b4d98
-      else
Panu Matilainen 5b4d98
-	ptr = srcptr = dir;
Panu Matilainen 5b4d98
-      while (*srcptr != 0)
Panu Matilainen 5b4d98
-	{
Panu Matilainen 5b4d98
-	  size_t len = strlen ((char *)srcptr) + 1;
Panu Matilainen 5b4d98
-	  const unsigned char *readptr = srcptr;
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-	  char *orig = strdup ((const char *) srcptr);
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-	  if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir))
Panu Matilainen 5b4d98
-	    {
Panu Matilainen 5b4d98
-	      if (dest_len < base_len)
Panu Matilainen 5b4d98
-		++abs_dir_cnt;
Panu Matilainen 5b4d98
-	      memcpy (ptr, dest_dir, dest_len);
Panu Matilainen 5b4d98
-	      ptr += dest_len;
Panu Matilainen 5b4d98
-	      readptr += base_len;
Panu Matilainen 5b4d98
-	    }
Panu Matilainen 5b4d98
-	  srcptr += len;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-	  shrank += srcptr - readptr;
Panu Matilainen 5b4d98
-	  canonicalize_path ((char *)readptr, (char *)ptr);
Panu Matilainen 5b4d98
-	  len = strlen ((char *)ptr) + 1;
Panu Matilainen 5b4d98
-	  shrank -= len;
Panu Matilainen 5b4d98
-	  ptr += len;
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-	  if (memcmp (orig, ptr - len, len))
Panu Matilainen 5b4d98
-	    dirty_section (DEBUG_STR);
Panu Matilainen 5b4d98
-	  free (orig);
Panu Matilainen 5b4d98
-	}
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-      if (shrank > 0)
Panu Matilainen 5b4d98
-	{
Panu Matilainen 5b4d98
-	  if (--shrank == 0)
Panu Matilainen 5b4d98
-	    error (EXIT_FAILURE, 0,
Panu Matilainen 5b4d98
-		   "canonicalization unexpectedly shrank by one character");
Panu Matilainen 5b4d98
-	  else
Panu Matilainen 5b4d98
-	    {
Panu Matilainen 5b4d98
-	      memset (ptr, 'X', shrank);
Panu Matilainen 5b4d98
-	      ptr += shrank;
Panu Matilainen 5b4d98
-	      *ptr++ = '\0';
Panu Matilainen 5b4d98
-	    }
Panu Matilainen 5b4d98
-	}
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-      if (abs_dir_cnt + abs_file_cnt != 0)
Panu Matilainen 5b4d98
-	{
Panu Matilainen 5b4d98
-	  size_t len = (abs_dir_cnt + abs_file_cnt) * (base_len - dest_len);
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-	  if (len == 1)
Panu Matilainen 5b4d98
-	    error (EXIT_FAILURE, 0, "-b arg has to be either the same length as -d arg, or more than 1 char longer");
Panu Matilainen 5b4d98
-	  memset (ptr, 'X', len - 1);
Panu Matilainen 5b4d98
-	  ptr += len - 1;
Panu Matilainen 5b4d98
-	  *ptr++ = '\0';
Panu Matilainen 5b4d98
-	}
Panu Matilainen 5b4d98
-      *ptr++ = '\0';
Panu Matilainen 5b4d98
-      ++srcptr;
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-      while (*srcptr != 0)
Panu Matilainen 5b4d98
-	{
Panu Matilainen 5b4d98
-	  size_t len = strlen ((char *)srcptr) + 1;
Panu Matilainen 5b4d98
+  dso->lines.debug_lines_len += 4 + table->unit_length + table->size_diff;
Panu Matilainen 5b4d98
+  return table->replace_dirs || table->replace_files;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-	  if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir))
Panu Matilainen 5b4d98
-	    {
Panu Matilainen 5b4d98
-	      memcpy (ptr, dest_dir, dest_len);
Panu Matilainen 5b4d98
-	      if (dest_len < base_len)
Panu Matilainen 5b4d98
-		{
Panu Matilainen 5b4d98
-		  memmove (ptr + dest_len, srcptr + base_len,
Panu Matilainen 5b4d98
-			   len - base_len);
Panu Matilainen 5b4d98
-		  ptr += dest_len - base_len;
Panu Matilainen 5b4d98
-		}
Panu Matilainen 5b4d98
-	      dirty_section (DEBUG_STR);
Panu Matilainen 5b4d98
-	    }
Panu Matilainen 5b4d98
-	  else if (ptr != srcptr)
Panu Matilainen 5b4d98
-	    memmove (ptr, srcptr, len);
Panu Matilainen 5b4d98
-	  srcptr += len;
Panu Matilainen 5b4d98
-	  ptr += len;
Panu Matilainen 5b4d98
-	  dir = srcptr;
Panu Matilainen 5b4d98
-	  read_uleb128 (srcptr);
Panu Matilainen 5b4d98
-	  read_uleb128 (srcptr);
Panu Matilainen 5b4d98
-	  read_uleb128 (srcptr);
Panu Matilainen 5b4d98
-	  if (ptr != dir)
Panu Matilainen 5b4d98
-	    memmove (ptr, dir, srcptr - dir);
Panu Matilainen 5b4d98
-	  ptr += srcptr - dir;
Panu Matilainen 5b4d98
-	}
Panu Matilainen 5b4d98
-      *ptr = '\0';
Panu Matilainen 5b4d98
-      free (buf);
Panu Matilainen 5b4d98
-    }
Panu Matilainen 5b4d98
-  return 0;
Panu Matilainen 5b4d98
+/* Called during phase one, after the table has been sorted. */
Panu Matilainen 5b4d98
+static size_t
Panu Matilainen 5b4d98
+find_new_list_offs (struct debug_lines *lines, size_t idx)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  struct line_table key;
Panu Matilainen 5b4d98
+  key.old_idx = idx;
Panu Matilainen 5b4d98
+  struct line_table *table = bsearch (&key, lines->table,
Panu Matilainen 5b4d98
+				      lines->used,
Panu Matilainen 5b4d98
+				      sizeof (struct line_table),
Panu Matilainen 5b4d98
+				      line_table_cmp);
Panu Matilainen 5b4d98
+  return table->new_idx;
Panu Matilainen 5b4d98
 }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+/* This scans the attributes of one DIE described by the given abbrev_tag.
Panu Matilainen 5b4d98
+   PTR points to the data in the debug_info. It will be advanced till all
Panu Matilainen 5b4d98
+   abbrev data is consumed. In phase zero data is collected, in phase one
Panu Matilainen 5b4d98
+   data might be replaced/updated.  */
Panu Matilainen 5b4d98
 static unsigned char *
Panu Matilainen 5b4d98
 edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
@@ -747,20 +1436,36 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       uint32_t form = t->attr[i].form;
Panu Matilainen 5b4d98
       size_t len = 0;
Panu Matilainen 5b4d98
-      size_t base_len, dest_len;
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
       while (1)
Panu Matilainen 5b4d98
 	{
Panu Matilainen 5b4d98
+	  /* Whether we already handled a string as file for this
Panu Matilainen 5b4d98
+	     attribute.  If we did then we don't need to handle/record
Panu Matilainen 5b4d98
+	     it again when handling the DW_FORM_strp later. */
Panu Matilainen 5b4d98
+	  bool handled_strp = false;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  /* A stmt_list points into the .debug_line section.  In
Panu Matilainen 5b4d98
+	     phase zero record all offsets. Then in phase one replace
Panu Matilainen 5b4d98
+	     them with the new offsets if we rewrote the line
Panu Matilainen 5b4d98
+	     tables.  */
Panu Matilainen 5b4d98
 	  if (t->attr[i].attr == DW_AT_stmt_list)
Panu Matilainen 5b4d98
 	    {
Panu Matilainen 5b4d98
 	      if (form == DW_FORM_data4
Panu Matilainen 5b4d98
 		  || form == DW_FORM_sec_offset)
Panu Matilainen 5b4d98
 		{
Panu Matilainen 5b4d98
 		  list_offs = do_read_32_relocated (ptr);
Panu Matilainen 5b4d98
-		  found_list_offs = 1;
Panu Matilainen 5b4d98
+		  if (phase == 0)
Panu Matilainen 5b4d98
+		    found_list_offs = 1;
Panu Matilainen 5b4d98
+		  else if (need_stmt_update) /* phase one */
Panu Matilainen 5b4d98
+		    {
Panu Matilainen 5b4d98
+		      size_t idx, new_idx;
Panu Matilainen 5b4d98
+		      idx = do_read_32_relocated (ptr);
Panu Matilainen 5b4d98
+		      new_idx = find_new_list_offs (&dso->lines, idx);
Panu Matilainen 5b4d98
+		      do_write_32_relocated (ptr, new_idx);
Panu Matilainen 5b4d98
+		    }
Panu Matilainen 5b4d98
 		}
Panu Matilainen 5b4d98
 	    }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+	  /* DW_AT_comp_dir is the current working directory. */
Panu Matilainen 5b4d98
 	  if (t->attr[i].attr == DW_AT_comp_dir)
Panu Matilainen 5b4d98
 	    {
Panu Matilainen 5b4d98
 	      if (form == DW_FORM_string)
Panu Matilainen 5b4d98
@@ -768,44 +1473,65 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
Panu Matilainen 5b4d98
 		  free (comp_dir);
Panu Matilainen 5b4d98
 		  comp_dir = strdup ((char *)ptr);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-		  if (phase == 1 && dest_dir && has_prefix ((char *)ptr, base_dir))
Panu Matilainen 5b4d98
+		  if (dest_dir)
Panu Matilainen 5b4d98
 		    {
Panu Matilainen 5b4d98
-		      base_len = strlen (base_dir);
Panu Matilainen 5b4d98
-		      dest_len = strlen (dest_dir);
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-		      memcpy (ptr, dest_dir, dest_len);
Panu Matilainen 5b4d98
-		      if (dest_len < base_len)
Panu Matilainen 5b4d98
+		      /* In phase zero we are just collecting dir/file
Panu Matilainen 5b4d98
+			 names and check whether any need to be
Panu Matilainen 5b4d98
+			 adjusted. If so, in phase one we replace
Panu Matilainen 5b4d98
+			 those dir/files.  */
Panu Matilainen 5b4d98
+		      const char *file = skip_dir_prefix (comp_dir, base_dir);
Panu Matilainen 5b4d98
+		      if (file != NULL && phase == 0)
Panu Matilainen 5b4d98
+			need_string_replacement = true;
Panu Matilainen 5b4d98
+		      else if (file != NULL && phase == 1)
Panu Matilainen 5b4d98
 			{
Panu Matilainen 5b4d98
-			  memset(ptr + dest_len, '/',
Panu Matilainen 5b4d98
-				 base_len - dest_len);
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
+			  size_t orig_len = strlen (comp_dir);
Panu Matilainen 5b4d98
+			  size_t dest_len = strlen (dest_dir);
Panu Matilainen 5b4d98
+			  size_t file_len = strlen (file);
Panu Matilainen 5b4d98
+			  size_t new_len = dest_len;
Panu Matilainen 5b4d98
+			  if (file_len > 0)
Panu Matilainen 5b4d98
+			    new_len += 1 + file_len; /* + '/' */
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+			  /* We don't want to rewrite the whole
Panu Matilainen 5b4d98
+			     debug_info section, so we only replace
Panu Matilainen 5b4d98
+			     the comp_dir with something equal or
Panu Matilainen 5b4d98
+			     smaller, possibly adding some slashes
Panu Matilainen 5b4d98
+			     at the end of the new compdir.  This
Panu Matilainen 5b4d98
+			     normally doesn't happen since most
Panu Matilainen 5b4d98
+			     producers will use DW_FORM_strp which is
Panu Matilainen 5b4d98
+			     more efficient.  */
Panu Matilainen 5b4d98
+			  if (orig_len < new_len)
Panu Matilainen 5b4d98
+			    fprintf (stderr, "Warning, not replacing comp_dir "
Panu Matilainen 5b4d98
+				     "'%s' prefix ('%s' -> '%s') encoded as "
Panu Matilainen 5b4d98
+				     "DW_FORM_string. "
Panu Matilainen 5b4d98
+				     "Replacement too large.\n",
Panu Matilainen 5b4d98
+				     comp_dir, base_dir, dest_dir);
Panu Matilainen 5b4d98
+			  else
Panu Matilainen 5b4d98
+			    {
Panu Matilainen 5b4d98
+			      /* Add one or more slashes in between to
Panu Matilainen 5b4d98
+				 fill up all space (replacement must be
Panu Matilainen 5b4d98
+				 of the same length). */
Panu Matilainen 5b4d98
+			      memcpy (ptr, dest_dir, dest_len);
Panu Matilainen 5b4d98
+			      memset (ptr + dest_len, '/',
Panu Matilainen 5b4d98
+				      orig_len - new_len + 1);
Panu Matilainen 5b4d98
+			    }
Panu Matilainen 5b4d98
 			}
Panu Matilainen 5b4d98
-		      dirty_section (DEBUG_INFO);
Panu Matilainen 5b4d98
 		    }
Panu Matilainen 5b4d98
 		}
Panu Matilainen 5b4d98
 	      else if (form == DW_FORM_strp &&
Panu Matilainen 5b4d98
 		       debug_sections[DEBUG_STR].data)
Panu Matilainen 5b4d98
 		{
Panu Matilainen 5b4d98
-		  char *dir;
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-		  dir = (char *) debug_sections[DEBUG_STR].data
Panu Matilainen 5b4d98
-		    + do_read_32_relocated (ptr);
Panu Matilainen 5b4d98
+		  const char *dir;
Panu Matilainen 5b4d98
+		  size_t idx = do_read_32_relocated (ptr);
Panu Matilainen 5b4d98
+		  dir = (char *) debug_sections[DEBUG_STR].data + idx;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 		  free (comp_dir);
Panu Matilainen 5b4d98
 		  comp_dir = strdup (dir);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-		  if (phase == 1 && dest_dir && has_prefix (dir, base_dir))
Panu Matilainen 5b4d98
+		  if (dest_dir != NULL && phase == 0)
Panu Matilainen 5b4d98
 		    {
Panu Matilainen 5b4d98
-		      base_len = strlen (base_dir);
Panu Matilainen 5b4d98
-		      dest_len = strlen (dest_dir);
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-		      memcpy (dir, dest_dir, dest_len);
Panu Matilainen 5b4d98
-		      if (dest_len < base_len)
Panu Matilainen 5b4d98
-			{
Panu Matilainen 5b4d98
-			  memmove (dir + dest_len, dir + base_len,
Panu Matilainen 5b4d98
-				   strlen (dir + base_len) + 1);
Panu Matilainen 5b4d98
-			}
Panu Matilainen 5b4d98
-		      dirty_section (DEBUG_STR);
Panu Matilainen 5b4d98
+		      if (record_file_string_entry_idx (&dso->strings, idx))
Panu Matilainen 5b4d98
+			need_strp_update = true;
Panu Matilainen 5b4d98
+		      handled_strp = true;
Panu Matilainen 5b4d98
 		    }
Panu Matilainen 5b4d98
 		}
Panu Matilainen 5b4d98
 	    }
Panu Matilainen 5b4d98
@@ -815,10 +1541,13 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
Panu Matilainen 5b4d98
 		   && form == DW_FORM_strp
Panu Matilainen 5b4d98
 		   && debug_sections[DEBUG_STR].data)
Panu Matilainen 5b4d98
 	    {
Panu Matilainen 5b4d98
+	      /* DW_AT_name is the primary file for this compile
Panu Matilainen 5b4d98
+		 unit. If starting with / it is a full path name.
Panu Matilainen 5b4d98
+		 Note that we don't handle DW_FORM_string in this
Panu Matilainen 5b4d98
+		 case.  */
Panu Matilainen 5b4d98
 	      char *name;
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-	      name = (char *) debug_sections[DEBUG_STR].data
Panu Matilainen 5b4d98
-		+ do_read_32_relocated (ptr);
Panu Matilainen 5b4d98
+	      size_t idx = do_read_32_relocated (ptr);
Panu Matilainen 5b4d98
+	      name = (char *) debug_sections[DEBUG_STR].data + idx;
Panu Matilainen 5b4d98
 	      if (*name == '/' && comp_dir == NULL)
Panu Matilainen 5b4d98
 		{
Panu Matilainen 5b4d98
 		  char *enddir = strrchr (name, '/');
Panu Matilainen 5b4d98
@@ -833,18 +1562,14 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
Panu Matilainen 5b4d98
 		    comp_dir = strdup ("/");
Panu Matilainen 5b4d98
 		}
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-	      if (phase == 1 && dest_dir && has_prefix (name, base_dir))
Panu Matilainen 5b4d98
+	      /* First pass (0) records the new name to be
Panu Matilainen 5b4d98
+		 added to the debug string pool, the second
Panu Matilainen 5b4d98
+		 pass (1) stores it (the new index). */
Panu Matilainen 5b4d98
+	      if (dest_dir && phase == 0)
Panu Matilainen 5b4d98
 		{
Panu Matilainen 5b4d98
-		  base_len = strlen (base_dir);
Panu Matilainen 5b4d98
-		  dest_len = strlen (dest_dir);
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
-		  memcpy (name, dest_dir, dest_len);
Panu Matilainen 5b4d98
-		  if (dest_len < base_len)
Panu Matilainen 5b4d98
-		    {
Panu Matilainen 5b4d98
-		      memmove (name + dest_len, name + base_len,
Panu Matilainen 5b4d98
-			       strlen (name + base_len) + 1);
Panu Matilainen 5b4d98
-		    }
Panu Matilainen 5b4d98
-		  dirty_section (DEBUG_STR);
Panu Matilainen 5b4d98
+		  if (record_file_string_entry_idx (&dso->strings, idx))
Panu Matilainen 5b4d98
+		    need_strp_update = true;
Panu Matilainen 5b4d98
+		  handled_strp = true;
Panu Matilainen 5b4d98
 		}
Panu Matilainen 5b4d98
 	    }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
@@ -886,6 +1611,29 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
Panu Matilainen 5b4d98
 	      read_uleb128 (ptr);
Panu Matilainen 5b4d98
 	      break;
Panu Matilainen 5b4d98
 	    case DW_FORM_strp:
Panu Matilainen 5b4d98
+	      /* In the first pass we collect all strings, in the
Panu Matilainen 5b4d98
+		 second we put the new references back (if there are
Panu Matilainen 5b4d98
+		 any changes).  */
Panu Matilainen 5b4d98
+	      if (phase == 0)
Panu Matilainen 5b4d98
+		{
Panu Matilainen 5b4d98
+		  /* handled_strp is set for attributes refering to
Panu Matilainen 5b4d98
+		     files. If it is set the string is already
Panu Matilainen 5b4d98
+		     recorded. */
Panu Matilainen 5b4d98
+		  if (! handled_strp)
Panu Matilainen 5b4d98
+		    {
Panu Matilainen 5b4d98
+		      size_t idx = do_read_32_relocated (ptr);
Panu Matilainen 5b4d98
+		      record_existing_string_entry_idx (&dso->strings, idx);
Panu Matilainen 5b4d98
+		    }
Panu Matilainen 5b4d98
+		}
Panu Matilainen 5b4d98
+	      else if (need_strp_update) /* && phase == 1 */
Panu Matilainen 5b4d98
+		{
Panu Matilainen 5b4d98
+		  struct stridxentry *entry;
Panu Matilainen 5b4d98
+		  size_t idx, new_idx;
Panu Matilainen 5b4d98
+		  idx = do_read_32_relocated (ptr);
Panu Matilainen 5b4d98
+		  entry = string_find_entry (&dso->strings, idx);
Panu Matilainen 5b4d98
+		  new_idx = strent_offset (entry->entry);
Panu Matilainen 5b4d98
+		  do_write_32_relocated (ptr, new_idx);
Panu Matilainen 5b4d98
+		}
Panu Matilainen 5b4d98
 	      ptr += 4;
Panu Matilainen 5b4d98
 	      break;
Panu Matilainen 5b4d98
 	    case DW_FORM_string:
Panu Matilainen 5b4d98
@@ -930,14 +1678,17 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
Panu Matilainen 5b4d98
      CU current dir subdirectories.  */
Panu Matilainen 5b4d98
   if (comp_dir && list_file_fd != -1)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
-      char *p;
Panu Matilainen 5b4d98
+      const char *p = NULL;
Panu Matilainen 5b4d98
       size_t size;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-      if (base_dir && has_prefix (comp_dir, base_dir))
Panu Matilainen 5b4d98
-	p = comp_dir + strlen (base_dir);
Panu Matilainen 5b4d98
-      else if (dest_dir && has_prefix (comp_dir, dest_dir))
Panu Matilainen 5b4d98
-	p = comp_dir + strlen (dest_dir);
Panu Matilainen 5b4d98
-      else
Panu Matilainen 5b4d98
+      if (base_dir)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  p = skip_dir_prefix (comp_dir, base_dir);
Panu Matilainen 5b4d98
+	  if (p == NULL && dest_dir != NULL)
Panu Matilainen 5b4d98
+	    p = skip_dir_prefix (comp_dir, dest_dir);
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      if (p == NULL)
Panu Matilainen 5b4d98
 	p = comp_dir;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
       size = strlen (p) + 1;
Panu Matilainen 5b4d98
@@ -951,8 +1702,13 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
Panu Matilainen 5b4d98
 	}
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  if (found_list_offs)
Panu Matilainen 5b4d98
-    edit_dwarf2_line (dso, list_offs, comp_dir, phase);
Panu Matilainen 5b4d98
+  /* In phase zero we collect all file names (we need the comp_dir for
Panu Matilainen 5b4d98
+     that).  Note that calculating the new size and offsets is done
Panu Matilainen 5b4d98
+     separately (at the end of phase zero after all CUs have been
Panu Matilainen 5b4d98
+     scanned in dwarf2_edit). */
Panu Matilainen 5b4d98
+  if (phase == 0 && found_list_offs
Panu Matilainen 5b4d98
+      && read_dwarf2_line (dso, list_offs, comp_dir))
Panu Matilainen 5b4d98
+    need_stmt_update = true;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
   free (comp_dir);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
@@ -974,6 +1730,20 @@ rel_cmp (const void *a, const void *b)
Panu Matilainen 5b4d98
 }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 static int
Panu Matilainen 5b4d98
+line_rel_cmp (const void *a, const void *b)
Panu Matilainen 5b4d98
+{
Panu Matilainen 5b4d98
+  LINE_REL *rela = (LINE_REL *) a, *relb = (LINE_REL *) b;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  if (rela->r_offset < relb->r_offset)
Panu Matilainen 5b4d98
+    return -1;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  if (rela->r_offset > relb->r_offset)
Panu Matilainen 5b4d98
+    return 1;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  return 0;
Panu Matilainen 5b4d98
+}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+static int
Panu Matilainen 5b4d98
 edit_dwarf2 (DSO *dso)
Panu Matilainen 5b4d98
 {
Panu Matilainen 5b4d98
   Elf_Data *data;
Panu Matilainen 5b4d98
@@ -1009,9 +1779,9 @@ edit_dwarf2 (DSO *dso)
Panu Matilainen 5b4d98
 		    }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 		  scn = dso->scn[i];
Panu Matilainen 5b4d98
-		  data = elf_rawdata (scn, NULL);
Panu Matilainen 5b4d98
+		  data = elf_getdata (scn, NULL);
Panu Matilainen 5b4d98
 		  assert (data != NULL && data->d_buf != NULL);
Panu Matilainen 5b4d98
-		  assert (elf_rawdata (scn, data) == NULL);
Panu Matilainen 5b4d98
+		  assert (elf_getdata (scn, data) == NULL);
Panu Matilainen 5b4d98
 		  assert (data->d_off == 0);
Panu Matilainen 5b4d98
 		  assert (data->d_size == dso->shdr[i].sh_size);
Panu Matilainen 5b4d98
 		  debug_sections[j].data = data->d_buf;
Panu Matilainen 5b4d98
@@ -1050,13 +1820,15 @@ edit_dwarf2 (DSO *dso)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       do_read_16 = buf_read_ule16;
Panu Matilainen 5b4d98
       do_read_32 = buf_read_ule32;
Panu Matilainen 5b4d98
-      write_32 = dwarf2_write_le32;
Panu Matilainen 5b4d98
+      do_write_16 = dwarf2_write_le16;
Panu Matilainen 5b4d98
+      do_write_32 = dwarf2_write_le32;
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
   else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       do_read_16 = buf_read_ube16;
Panu Matilainen 5b4d98
       do_read_32 = buf_read_ube32;
Panu Matilainen 5b4d98
-      write_32 = dwarf2_write_be32;
Panu Matilainen 5b4d98
+      do_write_16 = dwarf2_write_be16;
Panu Matilainen 5b4d98
+      do_write_32 = dwarf2_write_be32;
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
   else
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
@@ -1179,6 +1951,7 @@ edit_dwarf2 (DSO *dso)
Panu Matilainen 5b4d98
 	      relend->ptr = debug_sections[DEBUG_INFO].data
Panu Matilainen 5b4d98
 			    + (rela.r_offset - base);
Panu Matilainen 5b4d98
 	      relend->addend = rela.r_addend;
Panu Matilainen 5b4d98
+	      relend->ndx = ndx;
Panu Matilainen 5b4d98
 	      ++relend;
Panu Matilainen 5b4d98
 	    }
Panu Matilainen 5b4d98
 	  if (relbuf == relend)
Panu Matilainen 5b4d98
@@ -1193,6 +1966,13 @@ edit_dwarf2 (DSO *dso)
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
       for (phase = 0; phase < 2; phase++)
Panu Matilainen 5b4d98
 	{
Panu Matilainen 5b4d98
+	  /* If we don't need to update anyhing, skip phase 1. */
Panu Matilainen 5b4d98
+	  if (phase == 1
Panu Matilainen 5b4d98
+	      && !need_strp_update
Panu Matilainen 5b4d98
+	      && !need_string_replacement
Panu Matilainen 5b4d98
+	      && !need_stmt_update)
Panu Matilainen 5b4d98
+	    break;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
 	  ptr = debug_sections[DEBUG_INFO].data;
Panu Matilainen 5b4d98
 	  relptr = relbuf;
Panu Matilainen 5b4d98
 	  endsec = ptr + debug_sections[DEBUG_INFO].size;
Panu Matilainen 5b4d98
@@ -1240,7 +2020,7 @@ edit_dwarf2 (DSO *dso)
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 	      if (ptr_size == 0)
Panu Matilainen 5b4d98
 		{
Panu Matilainen 5b4d98
-		  ptr_size = read_1 (ptr);
Panu Matilainen 5b4d98
+		  ptr_size = read_8 (ptr);
Panu Matilainen 5b4d98
 		  if (ptr_size != 4 && ptr_size != 8)
Panu Matilainen 5b4d98
 		    {
Panu Matilainen 5b4d98
 		      error (0, 0, "%s: Invalid DWARF pointer size %d",
Panu Matilainen 5b4d98
@@ -1248,7 +2028,7 @@ edit_dwarf2 (DSO *dso)
Panu Matilainen 5b4d98
 		      return 1;
Panu Matilainen 5b4d98
 		    }
Panu Matilainen 5b4d98
 		}
Panu Matilainen 5b4d98
-	      else if (read_1 (ptr) != ptr_size)
Panu Matilainen 5b4d98
+	      else if (read_8 (ptr) != ptr_size)
Panu Matilainen 5b4d98
 		{
Panu Matilainen 5b4d98
 		  error (0, 0, "%s: DWARF pointer size differs between CUs",
Panu Matilainen 5b4d98
 			 dso->filename);
Panu Matilainen 5b4d98
@@ -1281,7 +2061,185 @@ edit_dwarf2 (DSO *dso)
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 	      htab_delete (abbrev);
Panu Matilainen 5b4d98
 	    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  /* We might have to recalculate/rewrite the debug_line
Panu Matilainen 5b4d98
+	     section.  We need to do that before going into phase one
Panu Matilainen 5b4d98
+	     so we have all new offsets.  We do this separately from
Panu Matilainen 5b4d98
+	     scanning the dirs/file names because the DW_AT_stmt_lists
Panu Matilainen 5b4d98
+	     might not be in order or skip some padding we might have
Panu Matilainen 5b4d98
+	     to (re)move. */
Panu Matilainen 5b4d98
+	  if (phase == 0 && need_stmt_update)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      edit_dwarf2_line (dso);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      /* The line table programs will be moved
Panu Matilainen 5b4d98
+		 forward/backwards a bit in the new data. Update the
Panu Matilainen 5b4d98
+		 debug_line relocations to the new offsets. */
Panu Matilainen 5b4d98
+	      int rndx = debug_sections[DEBUG_LINE].relsec;
Panu Matilainen 5b4d98
+	      if (rndx != 0)
Panu Matilainen 5b4d98
+		{
Panu Matilainen 5b4d98
+		  LINE_REL *rbuf;
Panu Matilainen 5b4d98
+		  size_t rels;
Panu Matilainen 5b4d98
+		  Elf_Data *rdata = elf_getdata (dso->scn[rndx], NULL);
Panu Matilainen 5b4d98
+		  int rtype = dso->shdr[rndx].sh_type;
Panu Matilainen 5b4d98
+		  rels = dso->shdr[rndx].sh_size / dso->shdr[rndx].sh_entsize;
Panu Matilainen 5b4d98
+		  rbuf = malloc (rels * sizeof (LINE_REL));
Panu Matilainen 5b4d98
+		  if (rbuf == NULL)
Panu Matilainen 5b4d98
+		    error (1, errno, "%s: Could not allocate line relocations",
Panu Matilainen 5b4d98
+			   dso->filename);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		  /* Sort them by offset into section. */
Panu Matilainen 5b4d98
+		  for (size_t i = 0; i < rels; i++)
Panu Matilainen 5b4d98
+		    {
Panu Matilainen 5b4d98
+		      if (rtype == SHT_RELA)
Panu Matilainen 5b4d98
+			{
Panu Matilainen 5b4d98
+			  GElf_Rela rela;
Panu Matilainen 5b4d98
+			  if (gelf_getrela (rdata, i, &rela) == NULL)
Panu Matilainen 5b4d98
+			    error (1, 0, "Couldn't get relocation: %s",
Panu Matilainen 5b4d98
+				   elf_errmsg (-1));
Panu Matilainen 5b4d98
+			  rbuf[i].r_offset = rela.r_offset;
Panu Matilainen 5b4d98
+			  rbuf[i].ndx = i;
Panu Matilainen 5b4d98
+			}
Panu Matilainen 5b4d98
+		      else
Panu Matilainen 5b4d98
+			{
Panu Matilainen 5b4d98
+			  GElf_Rel rel;
Panu Matilainen 5b4d98
+			  if (gelf_getrel (rdata, i, &rel) == NULL)
Panu Matilainen 5b4d98
+			    error (1, 0, "Couldn't get relocation: %s",
Panu Matilainen 5b4d98
+				   elf_errmsg (-1));
Panu Matilainen 5b4d98
+			  rbuf[i].r_offset = rel.r_offset;
Panu Matilainen 5b4d98
+			  rbuf[i].ndx = i;
Panu Matilainen 5b4d98
+			}
Panu Matilainen 5b4d98
+		    }
Panu Matilainen 5b4d98
+		  qsort (rbuf, rels, sizeof (LINE_REL), line_rel_cmp);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		  size_t lndx = 0;
Panu Matilainen 5b4d98
+		  for (size_t i = 0; i < rels; i++)
Panu Matilainen 5b4d98
+		    {
Panu Matilainen 5b4d98
+		      /* These relocations only happen in ET_REL files
Panu Matilainen 5b4d98
+			 and are section offsets. */
Panu Matilainen 5b4d98
+		      GElf_Addr r_offset;
Panu Matilainen 5b4d98
+		      size_t ndx = rbuf[i].ndx;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		      GElf_Rel rel;
Panu Matilainen 5b4d98
+		      GElf_Rela rela;
Panu Matilainen 5b4d98
+		      if (rtype == SHT_RELA)
Panu Matilainen 5b4d98
+			{
Panu Matilainen 5b4d98
+			  if (gelf_getrela (rdata, ndx, &rela) == NULL)
Panu Matilainen 5b4d98
+			    error (1, 0, "Couldn't get relocation: %s",
Panu Matilainen 5b4d98
+				   elf_errmsg (-1));
Panu Matilainen 5b4d98
+			  r_offset = rela.r_offset;
Panu Matilainen 5b4d98
+			}
Panu Matilainen 5b4d98
+		      else
Panu Matilainen 5b4d98
+			{
Panu Matilainen 5b4d98
+			  if (gelf_getrel (rdata, ndx, &rel) == NULL)
Panu Matilainen 5b4d98
+			    error (1, 0, "Couldn't get relocation: %s",
Panu Matilainen 5b4d98
+				   elf_errmsg (-1));
Panu Matilainen 5b4d98
+			  r_offset = rel.r_offset;
Panu Matilainen 5b4d98
+			}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		      while (r_offset > (dso->lines.table[lndx].old_idx
Panu Matilainen 5b4d98
+					 + 4
Panu Matilainen 5b4d98
+					 + dso->lines.table[lndx].unit_length)
Panu Matilainen 5b4d98
+			     && lndx < dso->lines.used)
Panu Matilainen 5b4d98
+			lndx++;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		      if (lndx >= dso->lines.used)
Panu Matilainen 5b4d98
+			error (1, 0,
Panu Matilainen 5b4d98
+			       ".debug_line relocation offset out of range");
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		      /* Offset (pointing into the line program) moves
Panu Matilainen 5b4d98
+			 from old to new index including the header
Panu Matilainen 5b4d98
+			 size diff. */
Panu Matilainen 5b4d98
+		      r_offset += ((dso->lines.table[lndx].new_idx
Panu Matilainen 5b4d98
+				    - dso->lines.table[lndx].old_idx)
Panu Matilainen 5b4d98
+				   + dso->lines.table[lndx].size_diff);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		      if (rtype == SHT_RELA)
Panu Matilainen 5b4d98
+			{
Panu Matilainen 5b4d98
+			  rela.r_offset = r_offset;
Panu Matilainen 5b4d98
+			  if (gelf_update_rela (rdata, ndx, &rela) == 0)
Panu Matilainen 5b4d98
+			    error (1, 0, "Couldn't update relocation: %s",
Panu Matilainen 5b4d98
+				   elf_errmsg (-1));
Panu Matilainen 5b4d98
+			}
Panu Matilainen 5b4d98
+		      else
Panu Matilainen 5b4d98
+			{
Panu Matilainen 5b4d98
+			  rel.r_offset = r_offset;
Panu Matilainen 5b4d98
+			  if (gelf_update_rel (rdata, ndx, &rel) == 0)
Panu Matilainen 5b4d98
+			    error (1, 0, "Couldn't update relocation: %s",
Panu Matilainen 5b4d98
+				   elf_errmsg (-1));
Panu Matilainen 5b4d98
+			}
Panu Matilainen 5b4d98
+		    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		  elf_flagdata (rdata, ELF_C_SET, ELF_F_DIRTY);
Panu Matilainen 5b4d98
+		  free (rbuf);
Panu Matilainen 5b4d98
+		}
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  /* Same for the debug_str section. Make sure everything is
Panu Matilainen 5b4d98
+	     in place for phase 1 updating of debug_info
Panu Matilainen 5b4d98
+	     references. */
Panu Matilainen 5b4d98
+	  if (phase == 0 && need_strp_update)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      Strtab *strtab = dso->strings.str_tab;
Panu Matilainen 5b4d98
+	      Elf_Data *strdata = debug_sections[DEBUG_STR].elf_data;
Panu Matilainen 5b4d98
+	      int strndx = debug_sections[DEBUG_STR].sec;
Panu Matilainen 5b4d98
+	      Elf_Scn *strscn = dso->scn[strndx];
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      /* Out with the old. */
Panu Matilainen 5b4d98
+	      strdata->d_size = 0;
Panu Matilainen 5b4d98
+	      /* In with the new. */
Panu Matilainen 5b4d98
+	      strdata = elf_newdata (strscn);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      /* We really should check whether we had enough memory,
Panu Matilainen 5b4d98
+		 but the old ebl version will just abort on out of
Panu Matilainen 5b4d98
+		 memory... */
Panu Matilainen 5b4d98
+	      strtab_finalize (strtab, strdata);
Panu Matilainen 5b4d98
+	      debug_sections[DEBUG_STR].size = strdata->d_size;
Panu Matilainen 5b4d98
+	      dso->strings.str_buf = strdata->d_buf;
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* After phase 1 we might have rewritten the debug_info with
Panu Matilainen 5b4d98
+	 new strp, strings and/or linep offsets.  */
Panu Matilainen 5b4d98
+      if (need_strp_update || need_string_replacement || need_stmt_update)
Panu Matilainen 5b4d98
+	dirty_section (DEBUG_INFO);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* Update any debug_info relocations addends we might have touched. */
Panu Matilainen 5b4d98
+      if (relbuf != NULL && reltype == SHT_RELA)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  Elf_Data *symdata;
Panu Matilainen 5b4d98
+          int relsec_ndx = debug_sections[DEBUG_INFO].relsec;
Panu Matilainen 5b4d98
+          data = elf_getdata (dso->scn[relsec_ndx], NULL);
Panu Matilainen 5b4d98
+	  symdata = elf_getdata (dso->scn[dso->shdr[relsec_ndx].sh_link],
Panu Matilainen 5b4d98
+				 NULL);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  relptr = relbuf;
Panu Matilainen 5b4d98
+	  while (relptr < relend)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      GElf_Sym sym;
Panu Matilainen 5b4d98
+	      GElf_Rela rela;
Panu Matilainen 5b4d98
+	      int ndx = relptr->ndx;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      if (gelf_getrela (data, ndx, &rela) == NULL)
Panu Matilainen 5b4d98
+		error (1, 0, "Couldn't get relocation: %s",
Panu Matilainen 5b4d98
+		       elf_errmsg (-1));
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      if (gelf_getsym (symdata, GELF_R_SYM (rela.r_info),
Panu Matilainen 5b4d98
+			       &sym) == NULL)
Panu Matilainen 5b4d98
+		error (1, 0, "Couldn't get symbol: %s", elf_errmsg (-1));
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      rela.r_addend = relptr->addend - sym.st_value;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      if (gelf_update_rela (data, ndx, &rela) == 0)
Panu Matilainen 5b4d98
+		error (1, 0, "Couldn't update relocations: %s",
Panu Matilainen 5b4d98
+		       elf_errmsg (-1));
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      ++relptr;
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+	  elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
Panu Matilainen 5b4d98
 	}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
       free (relbuf);
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
@@ -1310,8 +2268,9 @@ fdopen_dso (int fd, const char *name)
Panu Matilainen 5b4d98
   GElf_Ehdr ehdr;
Panu Matilainen 5b4d98
   int i;
Panu Matilainen 5b4d98
   DSO *dso = NULL;
Panu Matilainen 5b4d98
+  size_t phnum;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  elf = elf_begin (fd, ELF_C_RDWR_MMAP, NULL);
Panu Matilainen 5b4d98
+  elf = elf_begin (fd, ELF_C_RDWR, NULL);
Panu Matilainen 5b4d98
   if (elf == NULL)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1));
Panu Matilainen 5b4d98
@@ -1348,10 +2307,20 @@ fdopen_dso (int fd, const char *name)
Panu Matilainen 5b4d98
       goto error_out;
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT);
Panu Matilainen 5b4d98
+  if (elf_getphdrnum (elf, &phnum) != 0)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      error (0, 0, "Couldn't get number of phdrs: %s", elf_errmsg (-1));
Panu Matilainen 5b4d98
+      goto error_out;
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  /* If there are phdrs we want to maintain the layout of the
Panu Matilainen 5b4d98
+     allocated sections in the file.  */
Panu Matilainen 5b4d98
+  if (phnum != 0)
Panu Matilainen 5b4d98
+    elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
   memset (dso, 0, sizeof(DSO));
Panu Matilainen 5b4d98
   dso->elf = elf;
Panu Matilainen 5b4d98
+  dso->phnum = phnum;
Panu Matilainen 5b4d98
   dso->ehdr = ehdr;
Panu Matilainen 5b4d98
   dso->scn = (Elf_Scn **) &dso->shdr[ehdr.e_shnum + 20];
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
@@ -1362,12 +2331,16 @@ fdopen_dso (int fd, const char *name)
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
   dso->filename = (const char *) strdup (name);
Panu Matilainen 5b4d98
+  setup_strings (&dso->strings);
Panu Matilainen 5b4d98
+  setup_lines (&dso->lines);
Panu Matilainen 5b4d98
   return dso;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 error_out:
Panu Matilainen 5b4d98
   if (dso)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       free ((char *) dso->filename);
Panu Matilainen 5b4d98
+      destroy_strings (&dso->strings);
Panu Matilainen 5b4d98
+      destroy_lines (&dso->lines);
Panu Matilainen 5b4d98
       free (dso);
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
   if (elf)
Panu Matilainen 5b4d98
@@ -1406,13 +2379,6 @@ handle_build_id (DSO *dso, Elf_Data *build_id,
Panu Matilainen 5b4d98
   if (!dirty_elf && build_id_seed == NULL)
Panu Matilainen 5b4d98
     goto print;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  if (elf_update (dso->elf, ELF_C_NULL) < 0)
Panu Matilainen 5b4d98
-    {
Panu Matilainen 5b4d98
-      fprintf (stderr, "Failed to update file: %s\n",
Panu Matilainen 5b4d98
-	       elf_errmsg (elf_errno ()));
Panu Matilainen 5b4d98
-      exit (1);
Panu Matilainen 5b4d98
-    }
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
   /* Clear the old bits so they do not affect the new hash.  */
Panu Matilainen 5b4d98
   memset ((char *) build_id->d_buf + build_id_offset, 0, build_id_size);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
@@ -1475,7 +2441,7 @@ handle_build_id (DSO *dso, Elf_Data *build_id,
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
 	  if (u.shdr.sh_type != SHT_NOBITS)
Panu Matilainen 5b4d98
 	    {
Panu Matilainen 5b4d98
-	      Elf_Data *d = elf_rawdata (dso->scn[i], NULL);
Panu Matilainen 5b4d98
+	      Elf_Data *d = elf_getdata (dso->scn[i], NULL);
Panu Matilainen 5b4d98
 	      if (d == NULL)
Panu Matilainen 5b4d98
 		goto bad;
Panu Matilainen 5b4d98
 	      rpmDigestUpdate(ctx, d->d_buf, d->d_size);
Panu Matilainen 5b4d98
@@ -1509,7 +2475,6 @@ main (int argc, char *argv[])
Panu Matilainen 5b4d98
   int nextopt;
Panu Matilainen 5b4d98
   const char **args;
Panu Matilainen 5b4d98
   struct stat stat_buf;
Panu Matilainen 5b4d98
-  char *p;
Panu Matilainen 5b4d98
   Elf_Data *build_id = NULL;
Panu Matilainen 5b4d98
   size_t build_id_offset = 0, build_id_size = 0;
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
@@ -1541,11 +2506,6 @@ main (int argc, char *argv[])
Panu Matilainen 5b4d98
 	  fprintf (stderr, "You must specify a base dir if you specify a dest dir\n");
Panu Matilainen 5b4d98
 	  exit (1);
Panu Matilainen 5b4d98
 	}
Panu Matilainen 5b4d98
-      if (strlen (dest_dir) > strlen (base_dir))
Panu Matilainen 5b4d98
-	{
Panu Matilainen 5b4d98
-	  fprintf (stderr, "Dest dir longer than base dir is not supported\n");
Panu Matilainen 5b4d98
-	  exit (1);
Panu Matilainen 5b4d98
-	}
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
   if (build_id_seed != NULL && do_build_id == 0)
Panu Matilainen 5b4d98
@@ -1561,30 +2521,13 @@ main (int argc, char *argv[])
Panu Matilainen 5b4d98
       exit (1);
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  /* Ensure clean paths, users can muck with these */
Panu Matilainen 5b4d98
+  /* Ensure clean paths, users can muck with these. Also removes any
Panu Matilainen 5b4d98
+     trailing '/' from the paths. */
Panu Matilainen 5b4d98
   if (base_dir)
Panu Matilainen 5b4d98
     canonicalize_path(base_dir, base_dir);
Panu Matilainen 5b4d98
   if (dest_dir)
Panu Matilainen 5b4d98
     canonicalize_path(dest_dir, dest_dir);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
-  /* Make sure there are trailing slashes in dirs */
Panu Matilainen 5b4d98
-  if (base_dir != NULL && base_dir[strlen (base_dir)-1] != '/')
Panu Matilainen 5b4d98
-    {
Panu Matilainen 5b4d98
-      p = malloc (strlen (base_dir) + 2);
Panu Matilainen 5b4d98
-      strcpy (p, base_dir);
Panu Matilainen 5b4d98
-      strcat (p, "/");
Panu Matilainen 5b4d98
-      free (base_dir);
Panu Matilainen 5b4d98
-      base_dir = p;
Panu Matilainen 5b4d98
-    }
Panu Matilainen 5b4d98
-  if (dest_dir != NULL && dest_dir[strlen (dest_dir)-1] != '/')
Panu Matilainen 5b4d98
-    {
Panu Matilainen 5b4d98
-      p = malloc (strlen (dest_dir) + 2);
Panu Matilainen 5b4d98
-      strcpy (p, dest_dir);
Panu Matilainen 5b4d98
-      strcat (p, "/");
Panu Matilainen 5b4d98
-      free (dest_dir);
Panu Matilainen 5b4d98
-      dest_dir = p;
Panu Matilainen 5b4d98
-    }
Panu Matilainen 5b4d98
-
Panu Matilainen 5b4d98
   if (list_file != NULL)
Panu Matilainen 5b4d98
     {
Panu Matilainen 5b4d98
       list_file_fd = open (list_file, O_WRONLY|O_CREAT|O_APPEND, 0644);
Panu Matilainen 5b4d98
@@ -1641,7 +2584,7 @@ main (int argc, char *argv[])
Panu Matilainen 5b4d98
 	      && build_id == NULL && (dso->shdr[i].sh_flags & SHF_ALLOC))
Panu Matilainen 5b4d98
 	    {
Panu Matilainen 5b4d98
 	      /* Look for a build-ID note here.  */
Panu Matilainen 5b4d98
-	      Elf_Data *data = elf_rawdata (elf_getscn (dso->elf, i), NULL);
Panu Matilainen 5b4d98
+	      Elf_Data *data = elf_getdata (elf_getscn (dso->elf, i), NULL);
Panu Matilainen 5b4d98
 	      Elf32_Nhdr nh;
Panu Matilainen 5b4d98
 	      Elf_Data dst =
Panu Matilainen 5b4d98
 		{
Panu Matilainen 5b4d98
@@ -1679,6 +2622,123 @@ main (int argc, char *argv[])
Panu Matilainen 5b4d98
 	}
Panu Matilainen 5b4d98
     }
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+  /* We might have changed the size of some debug sections. If so make
Panu Matilainen 5b4d98
+     sure the section headers are updated and the data offsets are
Panu Matilainen 5b4d98
+     correct. We set ELF_F_LAYOUT above because we don't want libelf
Panu Matilainen 5b4d98
+     to move any allocated sections around itself if there are any
Panu Matilainen 5b4d98
+     phdrs. Which means we are reponsible for setting the section size
Panu Matilainen 5b4d98
+     and offset fields. Plus the shdr offsets. We don't want to change
Panu Matilainen 5b4d98
+     anything for the phdrs allocated sections. Keep the offset of
Panu Matilainen 5b4d98
+     allocated sections so they are at the same place in the file. Add
Panu Matilainen 5b4d98
+     unallocated ones after the allocated ones. */
Panu Matilainen 5b4d98
+  if (dso->phnum != 0 && (need_strp_update || need_stmt_update))
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      Elf *elf = dso->elf;
Panu Matilainen 5b4d98
+      GElf_Off last_offset;
Panu Matilainen 5b4d98
+      /* We position everything after the phdrs (which normally would
Panu Matilainen 5b4d98
+	 be at the start of the ELF file after the ELF header. */
Panu Matilainen 5b4d98
+      last_offset = (dso->ehdr.e_phoff + gelf_fsize (elf, ELF_T_PHDR,
Panu Matilainen 5b4d98
+						     dso->phnum, EV_CURRENT));
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* First find the last allocated section.  */
Panu Matilainen 5b4d98
+      Elf_Scn *scn = NULL;
Panu Matilainen 5b4d98
+      while ((scn = elf_nextscn (elf, scn)) != NULL)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  GElf_Shdr shdr_mem;
Panu Matilainen 5b4d98
+	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Panu Matilainen 5b4d98
+	  if (shdr == NULL)
Panu Matilainen 5b4d98
+	    error (1, 0, "Couldn't get shdr: %s\n", elf_errmsg (-1));
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  /* Any sections we have changed aren't allocated sections,
Panu Matilainen 5b4d98
+	     so we don't need to lookup any changed section sizes. */
Panu Matilainen 5b4d98
+	  if ((shdr->sh_flags & SHF_ALLOC) != 0)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS
Panu Matilainen 5b4d98
+						? shdr->sh_size : 0);
Panu Matilainen 5b4d98
+	      if (last_offset < off)
Panu Matilainen 5b4d98
+		last_offset = off;
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* Now adjust any sizes and offsets for the unallocated sections. */
Panu Matilainen 5b4d98
+      scn = NULL;
Panu Matilainen 5b4d98
+      while ((scn = elf_nextscn (elf, scn)) != NULL)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  GElf_Shdr shdr_mem;
Panu Matilainen 5b4d98
+	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Panu Matilainen 5b4d98
+	  if (shdr == NULL)
Panu Matilainen 5b4d98
+	    error (1, 0, "Couldn't get shdr: %s\n", elf_errmsg (-1));
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	  /* A bug in elfutils before 0.169 means we have to write out
Panu Matilainen 5b4d98
+	     all section data, even when nothing changed.
Panu Matilainen 5b4d98
+	     https://sourceware.org/bugzilla/show_bug.cgi?id=21199 */
Panu Matilainen 5b4d98
+#if !_ELFUTILS_PREREQ (0, 169)
Panu Matilainen 5b4d98
+	  if (shdr->sh_type != SHT_NOBITS)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      Elf_Data *d = elf_getdata (scn, NULL);
Panu Matilainen 5b4d98
+	      elf_flagdata (d, ELF_C_SET, ELF_F_DIRTY);
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+#endif
Panu Matilainen 5b4d98
+	  if ((shdr->sh_flags & SHF_ALLOC) == 0)
Panu Matilainen 5b4d98
+	    {
Panu Matilainen 5b4d98
+	      GElf_Off sec_offset = shdr->sh_offset;
Panu Matilainen 5b4d98
+	      GElf_Xword sec_size = shdr->sh_size;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      /* We might have changed the size (and content) of the
Panu Matilainen 5b4d98
+		 debug_str or debug_line section. */
Panu Matilainen 5b4d98
+	      size_t secnum = elf_ndxscn (scn);
Panu Matilainen 5b4d98
+	      if (secnum == debug_sections[DEBUG_STR].sec)
Panu Matilainen 5b4d98
+		sec_size = debug_sections[DEBUG_STR].size;
Panu Matilainen 5b4d98
+	      if (secnum == debug_sections[DEBUG_LINE].sec)
Panu Matilainen 5b4d98
+		sec_size = debug_sections[DEBUG_LINE].size;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      /* Zero means one.  No alignment constraints.  */
Panu Matilainen 5b4d98
+	      size_t addralign = shdr->sh_addralign ?: 1;
Panu Matilainen 5b4d98
+	      last_offset = (last_offset + addralign - 1) & ~(addralign - 1);
Panu Matilainen 5b4d98
+	      sec_offset = last_offset;
Panu Matilainen 5b4d98
+	      if (shdr->sh_type != SHT_NOBITS)
Panu Matilainen 5b4d98
+		last_offset += sec_size;
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+	      if (shdr->sh_size != sec_size
Panu Matilainen 5b4d98
+		  || shdr->sh_offset != sec_offset)
Panu Matilainen 5b4d98
+		{
Panu Matilainen 5b4d98
+		  /* Make sure unchanged section data is written out
Panu Matilainen 5b4d98
+		     at the new location. */
Panu Matilainen 5b4d98
+		  if (shdr->sh_offset != sec_offset
Panu Matilainen 5b4d98
+		      && shdr->sh_type != SHT_NOBITS)
Panu Matilainen 5b4d98
+		    {
Panu Matilainen 5b4d98
+		      Elf_Data *d = elf_getdata (scn, NULL);
Panu Matilainen 5b4d98
+		      elf_flagdata (d, ELF_C_SET, ELF_F_DIRTY);
Panu Matilainen 5b4d98
+		    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+		  shdr->sh_size = sec_size;
Panu Matilainen 5b4d98
+		  shdr->sh_offset = sec_offset;
Panu Matilainen 5b4d98
+		  if (gelf_update_shdr (scn, shdr) == 0)
Panu Matilainen 5b4d98
+		    error (1, 0, "Couldn't update shdr: %s\n",
Panu Matilainen 5b4d98
+			   elf_errmsg (-1));
Panu Matilainen 5b4d98
+		}
Panu Matilainen 5b4d98
+	    }
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+      /* Position the shdrs after the last (unallocated) section.  */
Panu Matilainen 5b4d98
+      const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT);
Panu Matilainen 5b4d98
+      GElf_Off new_offset = ((last_offset + offsize - 1)
Panu Matilainen 5b4d98
+			     & ~((GElf_Off) (offsize - 1)));
Panu Matilainen 5b4d98
+      if (dso->ehdr.e_shoff != new_offset)
Panu Matilainen 5b4d98
+	{
Panu Matilainen 5b4d98
+	  dso->ehdr.e_shoff = new_offset;
Panu Matilainen 5b4d98
+	  if (gelf_update_ehdr (elf, &dso->ehdr) == 0)
Panu Matilainen 5b4d98
+	    error (1, 0, "Couldn't update ehdr: %s\n", elf_errmsg (-1));
Panu Matilainen 5b4d98
+	}
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
+  if (elf_update (dso->elf, ELF_C_NULL) < 0)
Panu Matilainen 5b4d98
+    {
Panu Matilainen 5b4d98
+      fprintf (stderr, "Failed to update file: %s\n",
Panu Matilainen 5b4d98
+	       elf_errmsg (elf_errno ()));
Panu Matilainen 5b4d98
+      exit (1);
Panu Matilainen 5b4d98
+    }
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
   if (do_build_id && build_id != NULL)
Panu Matilainen 5b4d98
     handle_build_id (dso, build_id, build_id_offset, build_id_size);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
@@ -1697,6 +2757,11 @@ main (int argc, char *argv[])
Panu Matilainen 5b4d98
   /* Restore old access rights */
Panu Matilainen 5b4d98
   chmod (file, stat_buf.st_mode);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
+  free ((char *) dso->filename);
Panu Matilainen 5b4d98
+  destroy_strings (&dso->strings);
Panu Matilainen 5b4d98
+  destroy_lines (&dso->lines);
Panu Matilainen 5b4d98
+  free (dso);
Panu Matilainen 5b4d98
+
Panu Matilainen 5b4d98
   poptFreeContext (optCon);
Panu Matilainen 5b4d98
 
Panu Matilainen 5b4d98
   return 0;
Panu Matilainen 5b4d98
-- 
Igor Gnatenko 082d5d
2.13.2
Panu Matilainen 5b4d98