This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Handle merged strtab/shstrtab string tables in strip and unstrip.


ELF files can share the section header string table (e_shstrndx) with
the symtab .strtab section. That might in some cases save a bit of space
since symbols and sections might share some (sub)strings. To handle that
eu-strip just needs to not unconditionally remove the .shstrtab section
(it will be properly marked as used/unused as needed). eu-unstrip needs
to make sure the section names are added to the strtab if it decides to
rewrite that section. Also makes sure that eu-strip won't move around
a SHT_NOBITS section that has SHF_ALLOC set. Although it is allowed to
move such sections around, there is no benefit. And some tools might
expect no allocated section to move around, not even a nobits section.
It also makes it harder to do "roundtripping" sanity checks that make
sure splitting a file with eu-strip and then reconstructed with eu-unstrip
produce the same ELF file (as is done in the new run-strip-strmerge.sh).

Introduces a somewhat large test generator elfstrmerge.c that will
hopefully turn into a more generic string table merger program.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
---
 src/ChangeLog               |   7 +
 src/strip.c                 |  15 +-
 src/unstrip.c               |  45 ++-
 tests/ChangeLog             |   8 +
 tests/Makefile.am           |   7 +-
 tests/elfstrmerge.c         | 662 ++++++++++++++++++++++++++++++++++++++++++++
 tests/run-strip-strmerge.sh |  79 ++++++
 7 files changed, 813 insertions(+), 10 deletions(-)
 create mode 100644 tests/elfstrmerge.c
 create mode 100755 tests/run-strip-strmerge.sh

diff --git a/src/ChangeLog b/src/ChangeLog
index 49aa3f7..72bb0ab 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,10 @@
+2015-10-02  Mark Wielaard  <mjw@redhat.com>
+
+	* strip.c (handle_elf): Don't move around allocated NOBITS sections.
+	Don't just mark the section header string table as unused.
+	* unstrip.c (copy_elided_sections): Add header names to strtab if
+	shstrndx equals the symtab strtabndx.
+
 2015-09-22  Mark Wielaard  <mjw@redhat.com>
 
 	* strip.c (cleanup_debug): Remove old-style function definitions.
diff --git a/src/strip.c b/src/strip.c
index 8b08d72..41169ed 100644
--- a/src/strip.c
+++ b/src/strip.c
@@ -644,10 +644,12 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
 	goto illformed;
 
       /* Sections in files other than relocatable object files which
-	 don't contain any file content or are not loaded can be freely
-	 moved by us.  In relocatable object files everything can be moved.  */
+	 not loaded can be freely moved by us.  In theory we can also
+	 freely move around allocated nobits sections.  But we don't
+	 to keep the layout of all allocated sections as similar as
+	 possible to the original file.  In relocatable object files
+	 everything can be moved.  */
       if (ehdr->e_type == ET_REL
-	  || shdr_info[cnt].shdr.sh_type == SHT_NOBITS
 	  || (shdr_info[cnt].shdr.sh_flags & SHF_ALLOC) == 0)
 	shdr_info[cnt].shdr.sh_offset = 0;
 
@@ -1035,9 +1037,10 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
 	}
     }
 
-  /* Mark the section header string table as unused, we will create
-     a new one.  */
-  shdr_info[shstrndx].idx = 0;
+  /* Although we always create a new section header string table we
+     don't explicitly mark the existing one as unused.  It can still
+     be used through a symbol table section we are keeping.  If not it
+     will already be marked as unused.  */
 
   /* We need a string table for the section headers.  */
   shst = ebl_strtabinit (true);
diff --git a/src/unstrip.c b/src/unstrip.c
index 82bcdd8..d40df97 100644
--- a/src/unstrip.c
+++ b/src/unstrip.c
@@ -1,5 +1,5 @@
 /* Combine stripped files with separate symbols and debug information.
-   Copyright (C) 2007-2012, 2014 Red Hat, Inc.
+   Copyright (C) 2007-2012, 2014, 2015 Red Hat, Inc.
    This file is part of elfutils.
    Written by Roland McGrath <roland@redhat.com>, 2007.
 
@@ -1709,9 +1709,52 @@ more sections in stripped file than debug file -- arguments reversed?"));
       symstrdata = elf_getdata (unstripped_strtab, NULL);
       Elf_Data *shndxdata = NULL;	/* XXX */
 
+      /* If symtab and the section header table share the string table
+	 add the section names to the strtab and then (after finalizing)
+	 fixup the section header sh_names.  Also dispose of the old data.  */
+      struct Ebl_Strent *unstripped_strent[unstripped_shnum - 1];
+      if (unstripped_shstrndx == elf_ndxscn (unstripped_strtab))
+	{
+	  for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+	    {
+	      Elf_Scn *sec = elf_getscn (unstripped, i + 1);
+	      GElf_Shdr mem;
+	      GElf_Shdr *hdr = gelf_getshdr (sec, &mem);
+	      const char *name = get_section_name (i + 1, hdr, shstrtab);
+	      unstripped_strent[i + 1] = ebl_strtabadd (symstrtab, name, 0);
+	      ELF_CHECK (unstripped_strent[i + 1] != NULL,
+			 _("cannot add section name to string table: %s"));
+	    }
+
+	  if (strtab != NULL)
+	    {
+	      ebl_strtabfree (strtab);
+	      free (strtab_data->d_buf);
+	      strtab = NULL;
+	    }
+	}
+
       ebl_strtabfinalize (symstrtab, symstrdata);
       elf_flagdata (symstrdata, ELF_C_SET, ELF_F_DIRTY);
 
+      /* And update the section header names if necessary.  */
+      if (unstripped_shstrndx == elf_ndxscn (unstripped_strtab))
+	{
+	  for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+	    {
+	      Elf_Scn *sec = elf_getscn (unstripped, i + 1);
+	      GElf_Shdr mem;
+	      GElf_Shdr *hdr = gelf_getshdr (sec, &mem);
+	      shdr->sh_name = ebl_strtaboffset (unstripped_strent[i + 1]);
+	      update_shdr (sec, hdr);
+	    }
+	}
+
+      /* Now update the symtab shdr.  Reload symtab shdr because sh_name
+	 might have changed above. */
+      shdr = gelf_getshdr (unstripped_symtab, &shdr_mem);
+      ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
       shdr->sh_size = symdata->d_size = (1 + nsym) * shdr->sh_entsize;
       symdata->d_buf = xmalloc (symdata->d_size);
 
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 0f8925e..9d07912 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,11 @@
+2015-10-02  Mark Wielaard  <mjw@redhat.com>
+
+	* elfstrmerge.c: New check program.
+	* run-strip-strmerge.sh: New test.
+	* Makefile.am (check_PROGRAMS): Add elfstrmerge.
+	(EXTRA_DIST): Add run-strip-strmerge.sh
+	(elfstrmerge_LDADD): New variable.
+
 2015-09-29  Mark Wielaard  <mjw@redhat.com>
 
 	* elfshphehdr.c: New test.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index eaa904c..4ff48e6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -52,7 +52,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  backtrace-data backtrace-dwarf debuglink debugaltlink \
 		  buildid deleted deleted-lib.so aggregate_size vdsosyms \
 		  getsrc_die strptr newdata elfstrtab dwfl-proc-attach \
-		  elfshphehdr
+		  elfshphehdr elfstrmerge
 
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
 	    asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -78,7 +78,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
 	run-strip-test3.sh run-strip-test4.sh run-strip-test5.sh \
 	run-strip-test6.sh run-strip-test7.sh run-strip-test8.sh \
 	run-strip-test9.sh run-strip-test10.sh \
-	run-strip-groups.sh run-strip-reloc.sh \
+	run-strip-groups.sh run-strip-reloc.sh run-strip-strmerge.sh \
 	run-unstrip-test.sh run-unstrip-test2.sh \
 	run-unstrip-test3.sh run-unstrip-M.sh \
 	run-ecp-test.sh run-ecp-test2.sh run-alldts.sh \
@@ -161,7 +161,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     run-line2addr.sh run-elflint-test.sh testfile14.bz2 \
 	     run-strip-test4.sh run-strip-test5.sh run-strip-test6.sh \
 	     run-strip-test7.sh run-strip-test8.sh run-strip-groups.sh \
-	     run-strip-test9.sh run-strip-test10.sh \
+	     run-strip-test9.sh run-strip-test10.sh run-strip-strmerge.sh \
 	     run-strip-reloc.sh hello_i386.ko.bz2 hello_x86_64.ko.bz2 \
 	     hello_ppc64.ko.bz2 hello_s390.ko.bz2 hello_aarch64.ko.bz2 \
 	     run-unstrip-test.sh run-unstrip-test2.sh \
@@ -451,6 +451,7 @@ elfstrtab_LDADD = $(libelf)
 dwfl_proc_attach_LDADD = $(libdw)
 dwfl_proc_attach_LDFLAGS = -pthread
 elfshphehdr_LDADD =$(libelf)
+elfstrmerge_LDADD = $(libebl) $(libelf)
 
 if GCOV
 check: check-am coverage
diff --git a/tests/elfstrmerge.c b/tests/elfstrmerge.c
new file mode 100644
index 0000000..5cd93f8
--- /dev/null
+++ b/tests/elfstrmerge.c
@@ -0,0 +1,662 @@
+/* Merge string sections.
+   Copyright (C) 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <error.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <gelf.h>
+#include ELFUTILS_HEADER(ebl)
+
+/* The original ELF file.  */
+static int fd = -1;
+static Elf *elf = NULL;
+static bool replace;
+
+/* The new ELF file.  */
+static char *fnew = NULL;
+static int fdnew = -1;
+static Elf *elfnew = NULL;
+
+/* The merged string table.  */
+static struct Ebl_Strtab *strings = NULL;
+
+/* Section name strents.  */
+static struct Ebl_Strent **scnstrents = NULL;
+
+/* Symbol name strends.  */
+static struct Ebl_Strent **symstrents = NULL;
+
+/* New ELF file buffers.  */
+static Elf_Data newstrtabdata = { .d_buf = NULL };
+static size_t newshnums = 0;
+static void **newscnbufs = NULL;
+
+/* Release all files and resources allocated.  */
+static void
+release (void)
+{
+  /* The new string table.  */
+  if (strings != NULL)
+    ebl_strtabfree (strings);
+
+  free (scnstrents);
+  free (symstrents);
+  free (newstrtabdata.d_buf);
+
+  /* Any new data buffers allocated.  */
+  for (size_t i = 0; i < newshnums; i++)
+    free (newscnbufs[i]);
+  free (newscnbufs);
+
+  /* The new ELF file.  */
+  if (fdnew != -1)
+    {
+      unlink (fnew);
+      elf_end (elfnew);
+      close (fdnew);
+    }
+  // Don't release, we might need it in the error message.
+  // if (replace)
+  //   free (fnew);
+
+  /* The original ELF file.  */
+  elf_end (elf);
+  close (fd);
+}
+
+/* The various ways we can fail... Cleanup and show some message to
+   the user.  The file name may be NULL.  */
+static void __attribute__ ((noreturn))
+fail (const char *msg, const char *fname)
+{
+  release ();
+  if (fname != NULL)
+    error (1, 0, "%s: %s", fname, msg);
+  else
+    error (1, 0, "%s", msg);
+  abort();
+}
+
+static void __attribute__ ((noreturn))
+fail_errno (const char *msg, const char *fname)
+{
+  release ();
+  if (fname != NULL)
+    error (1, errno, "%s: %s", fname, msg);
+  else
+    error (1, errno, "%s", msg);
+  abort();
+}
+
+static void __attribute__ ((noreturn))
+fail_idx (const char *msg, const char *fname, size_t idx)
+{
+  release ();
+  if (fname != NULL)
+    error (1, 0, "%s: %s %zd", fname, msg, idx);
+  else
+    error (1, 0, "%s %zd", msg, idx);
+  abort();
+}
+
+static void __attribute__ ((noreturn))
+fail_elf (const char *msg, const char *fname)
+{
+  release ();
+  if (fname != NULL)
+    error (1, 0, "%s: %s: %s", fname, msg, elf_errmsg (-1));
+  else
+    error (1, 0, "%s: %s", msg, elf_errmsg (-1));
+  abort();
+}
+
+static void __attribute__ ((noreturn))
+fail_elf_idx (const char *msg, const char *fname, size_t idx)
+{
+  release ();
+  if (fname != NULL)
+    error (1, 0, "%s: %s %zd: %s", fname, msg, idx, elf_errmsg (-1));
+  else
+    error (1, 0, "%s %zd: %s", msg, idx, elf_errmsg (-1));
+  abort();
+}
+
+int
+main (int argc, char **argv)
+{
+  elf_version (EV_CURRENT);
+
+  /* Basic command line handling.  Need to replace the input file?  */
+  if ((argc != 2 && argc != 4)
+      || (argc == 4 && strcmp (argv[1], "-o") != 0))
+    fail ("Usage argument: [-o <outputfile>] <inputfile>", NULL);
+  replace = argc == 2;
+
+  /* Get the ELF file.  */
+  const char *fname;
+  if (replace)
+    fname = argv[1];
+  else
+    fname = argv[3];
+  fd = open (fname, O_RDONLY);
+  if (fd < 0)
+    fail_errno ("couldn't open", fname);
+
+  elf = elf_begin (fd, ELF_C_READ, NULL);
+  if (elf == NULL)
+    fail_elf ("couldn't open ELF file for reading", fname);
+
+  GElf_Ehdr ehdr;
+  if (gelf_getehdr (elf, &ehdr) == NULL)
+    fail_elf ("Couldn't get ehdr", fname);
+
+  /* Get the section header string table.  */
+  size_t shdrstrndx;
+  if (elf_getshdrstrndx (elf, &shdrstrndx) != 0)
+    fail_elf ("couldn't get section header string table index", fname);
+
+  Elf_Scn *shdrstrscn = elf_getscn (elf, shdrstrndx);
+  GElf_Shdr shdrstrshdr_mem;
+  GElf_Shdr *shdrstrshdr = gelf_getshdr (shdrstrscn, &shdrstrshdr_mem);
+  if (shdrstrshdr == NULL)
+    fail_elf ("couldn't get section header string table section", fname);
+
+  if ((shdrstrshdr->sh_flags & SHF_ALLOC) != 0)
+    fail ("section header string table is an allocated section", fname);
+
+  /* Get the symtab section.  */
+  size_t symtabndx = 0;
+  Elf_Scn *symtabscn = NULL;
+  GElf_Shdr symtabshdr_mem;
+  GElf_Shdr *symtabshdr;
+  while ((symtabscn = elf_nextscn (elf, symtabscn)) != NULL)
+    {
+      symtabshdr = gelf_getshdr (symtabscn, &symtabshdr_mem);
+      if (symtabshdr == NULL)
+	fail_elf ("couldn't get shdr", fname);
+
+      if (symtabshdr->sh_type == SHT_SYMTAB)
+	{
+	  /* Just pick the first, we don't expect more than one. */
+	  symtabndx = elf_ndxscn (symtabscn);
+	  break;
+	}
+    }
+
+  if (symtabndx == 0)
+    fail ("No symtab found", fname);
+
+  if ((symtabshdr->sh_flags & SHF_ALLOC) != 0)
+    fail ("symtab is an allocated section", fname);
+
+  /* Get the strtab of the symtab.  */
+  size_t strtabndx = symtabshdr->sh_link;
+  Elf_Scn *strtabscn = elf_getscn (elf, strtabndx);
+  GElf_Shdr strtabshdr_mem;
+  GElf_Shdr *strtabshdr = gelf_getshdr (strtabscn, &strtabshdr_mem);
+  if (strtabshdr == NULL)
+    fail_elf ("Couldn't get strtab section", fname);
+
+  if (shdrstrndx == strtabndx)
+    {
+      error (0, 0, "%s: Nothing to do, shstrtab == strtab\n", fname);
+      release ();
+      return 0;
+    }
+
+  if ((strtabshdr->sh_flags & SHF_ALLOC) != 0)
+    fail ("strtab is an allocated section", fname);
+
+  size_t phnum;
+  if (elf_getphdrnum (elf, &phnum) != 0)
+    fail_elf ("Couldn't get number of phdrs", fname);
+
+  /* If there are phdrs we want to maintain the layout of the
+     allocated sections in the file.  */
+  bool layout = phnum != 0;
+
+  /* Create a new merged strings table that starts with the empty string.  */
+  strings = ebl_strtabinit (true);
+  if (strings == NULL)
+    fail ("No memory to create merged string table", NULL);
+
+  /* Add the strings from all the sections.  */
+  size_t shdrnum;
+  if (elf_getshdrnum (elf, &shdrnum) != 0)
+    fail_elf ("Couldn't get number of sections", fname);
+  scnstrents = malloc (shdrnum * sizeof (struct Ebl_Strent *));
+  if (scnstrents == NULL)
+    fail ("couldn't allocate memory for section strings", NULL);
+
+  /* While going through all sections keep track of last allocated
+     offset if needed to keep the layout.  We'll put any unallocated
+     sections behind those (strtab is unallocated and will change
+     size).  */
+  GElf_Off last_offset = 0;
+  if (layout)
+    last_offset = (ehdr.e_phoff
+		   + gelf_fsize (elf, ELF_T_PHDR, phnum, EV_CURRENT));
+  Elf_Scn *scn = NULL;
+  size_t scnnum = 1;
+  while ((scn = elf_nextscn (elf, scn)) != NULL)
+    {
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+      if (shdr == NULL)
+	fail_elf_idx ("couldn't get shdr", fname, scnnum);
+      /* Don't add the .shstrtab section itself, we'll not use it.  */
+      if (shdr->sh_name != 0 && scnnum != shdrstrndx)
+	{
+	  const char *sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+	  if (sname == NULL)
+	    fail_elf_idx ("couldn't get section name", fname, scnnum);
+	  if ((scnstrents[scnnum] = ebl_strtabadd (strings, sname, 0)) == NULL)
+	    fail ("No memory to add to  merged string table", NULL);
+	}
+
+      if (layout)
+	if (shdr->sh_type != SHT_NOBITS && (shdr->sh_flags & SHF_ALLOC) != 0)
+	  if (last_offset < shdr->sh_offset + shdr->sh_size)
+	    last_offset = shdr->sh_offset + shdr->sh_size;
+      scnnum++;
+    }
+
+  /* Add the strings from all the symbols.  */
+  size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
+  Elf_Data *symd = elf_getdata (symtabscn, NULL);
+  if (symd == NULL)
+    fail_elf ("couldn't get symtab data", fname);
+  size_t symsnum = symd->d_size / elsize;
+  symstrents = malloc (symsnum * sizeof (struct Ebl_Strent *));
+  if (symstrents == NULL)
+    fail_errno ("Couldn't allocate memory for symbol strings", NULL);
+  for (size_t i = 0; i < symsnum; i++)
+    {
+      GElf_Sym sym_mem;
+      GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
+      if (sym == NULL)
+	fail_elf_idx ("Couldn't get symbol", fname, i);
+      if (sym->st_name != 0)
+	{
+	  const char *sname = elf_strptr (elf, strtabndx, sym->st_name);
+	  if (sname == NULL)
+	    fail_elf_idx ("Couldn't get symbol name", fname, i);
+	  if ((symstrents[i] = ebl_strtabadd (strings, sname, 0)) == NULL)
+	    fail_idx ("No memory to add to merged string table symbol",
+		      fname, i);
+	}
+    }
+
+  /* We got all strings, build the new string table and store it as
+     new strtab.  */
+  ebl_strtabfinalize (strings, &newstrtabdata);
+
+  /* We share at least the empty string so the result is at least 1
+     byte smaller.  */
+  if (newstrtabdata.d_size >= shdrstrshdr->sh_size + strtabshdr->sh_size)
+    fail ("Impossible, merged string table is larger", fname);
+
+  /* section index mapping and sanity checking.  */
+  size_t newsecndx (size_t secndx, const char *what, size_t widx,
+		    const char *member, size_t midx)
+  {
+    if (unlikely (secndx == 0 || secndx == shdrstrndx || secndx >= shdrnum))
+      {
+	/* Don't use fail... too specialized messages.  Call release
+	   outselves and then error.  Ignores midx if widx is
+	   zero.  */
+	release ();
+	if (widx == 0)
+	  error (0, 1, "%s: bad section index %zd in %s for %s",
+		 fname, secndx, what, member);
+	else if (midx == 0)
+	  error (0, 1, "%s: bad section index %zd in %s %zd for %s",
+		 fname, secndx, what, widx, member);
+	else
+	  error (0, 1, "%s: bad section index %zd in %s %zd for %s %zd",
+		 fname, secndx, what, widx, member, midx);
+      }
+
+    return secndx < shdrstrndx ? secndx : secndx - 1;
+  }
+
+  /* Create a new (temporary) ELF file for the result.  */
+  if (replace)
+    {
+      size_t fname_len = strlen (fname);
+      fnew = malloc (fname_len + sizeof (".XXXXXX"));
+      if (fnew == NULL)
+	fail_errno ("couldn't allocate memory for new file name", NULL);
+      strcpy (mempcpy (fnew, fname, fname_len), ".XXXXXX");
+
+      fdnew = mkstemp (fnew);
+    }
+  else
+    {
+      fnew = argv[2];
+      fdnew = open (fnew, O_WRONLY | O_CREAT);
+    }
+
+  if (fdnew < 0)
+    fail_errno ("couldn't create output file", fnew);
+
+  elfnew = elf_begin (fdnew, ELF_C_WRITE, NULL);
+  if (elfnew == NULL)
+    fail_elf ("couldn't open new ELF for writing", fnew);
+
+  /* Create the new ELF header and copy over all the data.  */
+  if (gelf_newehdr (elfnew, gelf_getclass (elf)) == 0)
+    fail_elf ("Couldn't create new ehdr", fnew);
+  GElf_Ehdr newehdr;
+  if (gelf_getehdr (elfnew, &newehdr) == NULL)
+    fail_elf ("Couldn't get ehdr", fnew);
+
+  newehdr.e_ident[EI_DATA] = ehdr.e_ident[EI_DATA];
+  newehdr.e_type = ehdr.e_type;
+  newehdr.e_machine = ehdr.e_machine;
+  newehdr.e_version = ehdr.e_version;
+  newehdr.e_entry = ehdr.e_entry;
+  newehdr.e_flags = ehdr.e_flags;
+
+  /* The new file uses the new strtab as shstrtab.  */
+  size_t newstrtabndx = newsecndx (strtabndx, "ehdr", 0, "e_shstrndx", 0);
+  if (newstrtabndx < SHN_LORESERVE)
+    newehdr.e_shstrndx = newstrtabndx;
+  else
+    {
+      Elf_Scn *zscn = elf_getscn (elfnew, 0);
+      GElf_Shdr zshdr_mem;
+      GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem);
+      if (zshdr == NULL)
+	fail_elf ("Couldn't get section zero", fnew);
+      zshdr->sh_link = strtabndx;
+      if (gelf_update_shdr (zscn, zshdr) == 0)
+	fail_elf ("Couldn't update section zero", fnew);
+      newehdr.e_shstrndx = SHN_XINDEX;
+    }
+
+  if (gelf_update_ehdr (elfnew, &newehdr) == 0)
+    fail ("Couldn't update ehdr", fnew);
+
+  /* Copy the program headers if any.  */
+  if (phnum != 0)
+    {
+      if (gelf_newphdr (elfnew, phnum) == 0)
+	fail_elf ("Couldn't create phdrs", fnew);
+
+      for (size_t cnt = 0; cnt < phnum; ++cnt)
+	{
+	  GElf_Phdr phdr_mem;
+	  GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
+	  if (phdr == NULL)
+	    fail_elf_idx ("Couldn't get phdr", fname, cnt);
+	  if (gelf_update_phdr (elfnew, cnt, phdr) == 0)
+	    fail_elf_idx ("Couldn't create phdr", fnew, cnt);
+	}
+    }
+
+  newshnums = shdrnum - 1;
+  newscnbufs = calloc (sizeof (void *), newshnums);
+  if (newscnbufs == NULL)
+    fail_errno ("Couldn't allocate memory for new section buffers", NULL);
+
+  /* Copy the sections, except the shstrtab, fill the strtab with the
+     combined strings and adjust section references.  */
+  while ((scn = elf_nextscn (elf, scn)) != NULL)
+    {
+      size_t ndx = elf_ndxscn (scn);
+
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+      if (shdr == NULL)
+	fail_elf_idx ("Couldn't get shdr", fname, ndx);
+
+      /* Section zero is always created.  Skip the shtrtab.  */
+      if (ndx == 0 || ndx == shdrstrndx)
+	continue;
+
+      Elf_Scn *newscn = elf_newscn (elfnew);
+      if (newscn == NULL)
+	fail_elf_idx ("couldn't create new section", fnew, ndx);
+
+      GElf_Shdr newshdr;
+      newshdr.sh_name = ebl_strtaboffset (scnstrents[ndx]);
+      newshdr.sh_type = shdr->sh_type;
+      newshdr.sh_flags = shdr->sh_flags;
+      newshdr.sh_addr = shdr->sh_addr;
+      newshdr.sh_size = shdr->sh_size;
+      if (shdr->sh_link != 0)
+	newshdr.sh_link = newsecndx (shdr->sh_link, "shdr", ndx, "sh_link", 0);
+      else
+	newshdr.sh_link = 0;
+      if (SH_INFO_LINK_P (shdr) && shdr->sh_info != 0)
+	newshdr.sh_info = newsecndx (shdr->sh_info, "shdr", ndx, "sh_info", 0);
+      else
+	newshdr.sh_info = shdr->sh_info;
+      newshdr.sh_entsize = shdr->sh_entsize;
+
+      /* Some sections need a new data buffer because they need to
+	 manipulate the original data.  Allocate and check here, so we
+	 have a list of all data buffers we might need to release when
+	 done.  */
+      void new_data_buf (Elf_Data *d)
+      {
+	size_t s = d->d_size;
+	if (s == 0)
+	  fail_idx ("Expected data in section", fname, ndx);
+	void *b = malloc (d->d_size);
+	if (b == NULL)
+	  fail_idx ("Couldn't allocated buffer for section", NULL, ndx);
+	newscnbufs[ndx] = d->d_buf = b;
+      }
+
+      Elf_Data *newdata = elf_newdata (newscn);
+      if (newdata == NULL)
+	fail_elf_idx ("Couldn't create new data for section", fnew, ndx);
+      if (ndx == strtabndx)
+	*newdata = newstrtabdata;
+      else
+	{
+	  /* The symtab, dynsym, group and symtab_shndx sections
+	     contain section indexes. Symbol tables (symtab and
+	     dynsym) contain indexes to strings. Update both if
+	     necessary.  */
+	  Elf_Data *data = elf_getdata (scn, NULL);
+	  if (data == NULL)
+	    fail_elf_idx ("Couldn't get data from section", fname, ndx);
+	  *newdata = *data;
+	  switch (shdr->sh_type)
+	    {
+	    case SHT_SYMTAB:
+	    case SHT_DYNSYM:
+	      {
+		/* We need to update the section numbers of the
+		   symbols and if this symbol table uses the strtab
+		   section also the name indexes.  */
+		const bool update_name = shdr->sh_link == strtabndx;
+		new_data_buf (newdata);
+		size_t syms = (data->d_size
+			       / gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT));
+		for (size_t i = 0; i < syms; i++)
+		  {
+		    GElf_Sym sym;
+		    if (gelf_getsym (data, i, &sym) == NULL)
+		      fail_elf_idx ("Couldn't get symbol", fname, i);
+
+		    if (sym.st_shndx != SHN_UNDEF
+			&& sym.st_shndx < SHN_LORESERVE)
+		      sym.st_shndx = newsecndx (sym.st_shndx, "section", ndx,
+						"symbol", i);
+		    if (update_name && sym.st_name != 0)
+		      sym.st_name = ebl_strtaboffset (symstrents[i]);
+
+		    /* We explicitly don't update the SHNDX table at
+		       the same time, we do that below.  */
+		    if (gelf_update_sym (newdata, i, &sym) == 0)
+		      fail_elf_idx ("Couldn't update symbol", fnew, i);
+		  }
+	      }
+	      break;
+
+	    case SHT_GROUP:
+	      {
+		new_data_buf (newdata);
+		/* A section group contains Elf32_Words. The first
+		   word is a falg value, the rest of the words are
+		   indexes of the sections belonging to the group.  */
+		Elf32_Word *group = (Elf32_Word *) data->d_buf;
+		Elf32_Word *newgroup = (Elf32_Word *) newdata->d_buf;
+		size_t words = data->d_size / sizeof (Elf32_Word);
+		if (words == 0)
+		  fail_idx ("Not enough data in group section", fname, ndx);
+		newgroup[0] = group[0];
+		for (size_t i = 1; i < words; i++)
+		  newgroup[i] = newsecndx (group[i], "section", ndx,
+					   "group", i);
+	      }
+	      break;
+
+	    case SHT_SYMTAB_SHNDX:
+	      {
+		new_data_buf (newdata);
+		/* A SHNDX just contains an array of section indexes
+		   for the corresponding symbol table.  The entry is
+		   SHN_UNDEF unless the corresponding symbol is
+		   SHN_XINDEX.  */
+		Elf32_Word *shndx = (Elf32_Word *) data->d_buf;
+		Elf32_Word *newshndx = (Elf32_Word *) newdata->d_buf;
+		size_t words = data->d_size / sizeof (Elf32_Word);
+		for (size_t i = 0; i < words; i++)
+		  if (shndx[i] == SHN_UNDEF)
+		    newshndx[i] = SHN_UNDEF;
+		  else
+		    newshndx[i] = newsecndx (shndx[i], "section", ndx,
+					     "shndx", i);
+	      }
+	      break;
+
+	    case SHT_DYNAMIC:
+	      /* Fallthrough.  There are string indexes in here, but
+		 they (should) point to a allocated string table,
+		 which we don't alter.  */
+	    default:
+	      /* Nothing to do.  Section data doesn't contain section
+		 or strtab indexes.  */
+	      break;
+	    }
+	}
+
+      /* When we are responsible for the layout explicitly set
+	 sh_addralign, sh_size and sh_offset.  Otherwise libelf will
+	 calculate those from the Elf_Data.  */
+      if (layout)
+	{
+	  /* Zero means one.  No alignment constraints.  */
+	  newshdr.sh_addralign = shdr->sh_addralign ?: 1;
+
+	  /* We have just one Elf_Data.  */
+	  newshdr.sh_size = newdata->d_size;
+
+	  /* Keep the offset of allocated sections so they are at the
+	     same place in the file. Add unallocated ones after the
+	     allocated ones.  */
+	  if ((shdr->sh_flags & SHF_ALLOC) != 0)
+	    newshdr.sh_offset = shdr->sh_offset;
+	  else
+	    {
+	      newshdr.sh_offset = ((last_offset + newshdr.sh_addralign - 1)
+				   & ~(newshdr.sh_addralign - 1));
+	      if (newshdr.sh_type != SHT_NOBITS)
+		last_offset = newshdr.sh_offset + newshdr.sh_size;
+	    }
+	}
+
+      if (gelf_update_shdr (newscn, &newshdr) == 0)
+	fail_elf_idx ("Couldn't update section header", fnew, ndx);
+    }
+
+  /* If we have phdrs we want elf_update to layout the SHF_ALLOC
+     sections precisely as in the original file.  In that case we are
+     also responsible for setting phoff and shoff */
+  if (layout)
+    {
+      /* Position the shdrs after the last (unallocated) section.  */
+      if (gelf_getehdr (elfnew, &newehdr) == NULL)
+	fail_elf ("Couldn't get ehdr", fnew);
+      const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT);
+      newehdr.e_shoff = ((last_offset + offsize - 1)
+			 & ~((GElf_Off) (offsize - 1)));
+
+      /* The phdrs go in the same place as in the original file.
+	 Normally right after the ELF header.  */
+      newehdr.e_phoff = ehdr.e_phoff;
+
+      if (gelf_update_ehdr (elfnew, &newehdr) == 0)
+	fail_elf ("Couldn't update ehdr", fnew);
+
+      elf_flagelf (elfnew, ELF_C_SET, ELF_F_LAYOUT);
+    }
+
+  if (elf_update (elfnew, ELF_C_WRITE) == -1)
+    fail_elf ("Couldn't write ELF", fnew);
+
+  elf_end (elfnew);
+  elfnew = NULL;
+
+  /* Finally replace the old file with the new merge strings file.  */
+  const char *fout;
+  if (replace)
+    {
+      fout = fname;
+      if (rename (fnew, fout) != 0)
+	fail_errno ("rename", fout);
+    }
+  else
+    fout = fnew;
+
+  struct stat st;
+  if (fstat (fd, &st) != 0)
+    error (0, errno, "Couldn't stat: %s", fname);
+  else
+    {
+      if (fchmod (fdnew, st.st_mode & ALLPERMS) != 0)
+	error (0, errno, "Couldn't fchmod %s", fout);
+      if (fchown (fdnew, st.st_uid, st.st_gid) != 0)
+	error (0, errno, "Couldn't fchown %s", fout);
+    }
+
+  /* We are finally done with the new file, don't unlink it now.  */
+  close (fdnew);
+  if (replace)
+    free (fnew);
+  fnew = NULL;
+  fdnew = -1;
+
+  release ();
+  return 0;
+}
diff --git a/tests/run-strip-strmerge.sh b/tests/run-strip-strmerge.sh
new file mode 100755
index 0000000..aa9c1eb
--- /dev/null
+++ b/tests/run-strip-strmerge.sh
@@ -0,0 +1,79 @@
+#! /bin/sh
+# Copyright (C) 2015 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# Generate a file with merged .shstrtab/.strtab table.
+# strip and unstrip it. Check all files with elflint.
+
+# A random ET_EXEC file
+input=${abs_top_builddir}/tests/elfstrmerge
+merged=merged.elf
+stripped=${merged}.stripped
+debugfile=${merged}.debug
+remerged=remerged.elf
+
+tempfiles $merged $stripped $debugfile $remerged
+
+echo elflint $input
+testrun ${abs_top_builddir}/src/elflint --gnu $input
+echo elfstrmerge
+testrun ${abs_top_builddir}/tests/elfstrmerge -o $merged $input
+echo elflint $merged
+testrun ${abs_top_builddir}/src/elflint --gnu $merged
+echo strip
+testrun ${abs_top_builddir}/src/strip -o $stripped -f $debugfile $merged
+echo elflint $stripped
+testrun ${abs_top_builddir}/src/elflint --gnu $stripped
+echo elflint $debugfile
+testrun ${abs_top_builddir}/src/elflint --gnu -d $debugfile
+echo unstrip
+testrun ${abs_top_builddir}/src/unstrip -o $remerged $stripped $debugfile
+echo elflint $remerged
+testrun ${abs_top_builddir}/src/elflint --gnu $remerged
+echo elfcmp
+testrun ${abs_top_builddir}/src/elfcmp $merged $remerged
+
+# A random ET_REL file
+input=${abs_top_builddir}/tests/elfstrmerge.o
+merged=merged.elf
+stripped=${merged}.stripped
+debugfile=${merged}.debug
+remerged=remerged.elf
+
+tempfiles $merged $stripped $debugfile $remerged
+
+echo elflint $input
+testrun ${abs_top_builddir}/src/elflint --gnu $input
+echo elfstrmerge
+testrun ${abs_top_builddir}/tests/elfstrmerge -o $merged $input
+echo elflint $merged
+testrun ${abs_top_builddir}/src/elflint --gnu $merged
+echo strip
+testrun ${abs_top_builddir}/src/strip -o $stripped -f $debugfile $merged
+echo elflint $stripped
+testrun ${abs_top_builddir}/src/elflint --gnu $stripped
+echo elflint $debugfile
+testrun ${abs_top_builddir}/src/elflint --gnu -d $debugfile
+echo unstrip
+testrun ${abs_top_builddir}/src/unstrip -o $remerged $stripped $debugfile
+echo elflint $remerged
+testrun ${abs_top_builddir}/src/elflint --gnu $remerged
+echo elfcmp
+testrun ${abs_top_builddir}/src/elfcmp $merged $remerged
+
+exit 0
-- 
2.4.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]