[PATCH] libelf: Fix some issues with ELF_C_RDWR_MMAP.

Mark Wielaard mark@klomp.org
Wed Sep 12 13:02:00 GMT 2018


When ELF_C_RDWR_MMAP is used libelf might have to write overlapping memory
when moving the section data or headers. Make sure to use memmove, not
memcpy. Also the size of the underlying file might have to change. That
means we will have to also extend the mmap region with mremap. Since we
are using direct pointers into the mmapped area we cannot move the mmap,
only extend it. This might still fail if there is not enough free memory
available to extend the mmap region.

Two new test programs have been added. elfcopy which copies a whole elf
file (using either ELF_C_WRITE or ELF_C_WRITE_MMAP). And addsections which
adds new sections to an existing ELF file (using either ELF_C_RDWR or
ELF_C_RDWR_MMAP). The newly added test will fail under valgrind without
the fixes.

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 libelf/ChangeLog              |   6 +
 libelf/elf32_updatefile.c     |  10 +-
 libelf/elf_update.c           |  30 +++--
 tests/ChangeLog               |  11 ++
 tests/Makefile.am             |  11 +-
 tests/addsections.c           | 286 ++++++++++++++++++++++++++++++++++++++++++
 tests/elfcopy.c               | 286 ++++++++++++++++++++++++++++++++++++++++++
 tests/run-copyadd-sections.sh |  87 +++++++++++++
 8 files changed, 713 insertions(+), 14 deletions(-)
 create mode 100644 tests/addsections.c
 create mode 100644 tests/elfcopy.c
 create mode 100755 tests/run-copyadd-sections.sh

diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index 7c884b0..3542122 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,9 @@
+2018-09-12  Mark Wielaard  <mark@klomp.org>
+
+	* elf32_updatefile.c (updatemmap): Use memmove, not memcpy.
+	* elf_update.c (write_file): Try to mremap if file needs to be
+	extended.
+
 2018-08-18  Mark Wielaard  <mark@klomp.org>
 
 	* libelf.h (elf_compress_gnu): Add documentation about
diff --git a/libelf/elf32_updatefile.c b/libelf/elf32_updatefile.c
index 7ac9951..545ce08 100644
--- a/libelf/elf32_updatefile.c
+++ b/libelf/elf32_updatefile.c
@@ -203,7 +203,7 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
 		   sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1);
 	}
       else
-	memcpy (elf->map_address + elf->start_offset + ehdr->e_phoff,
+	memmove (elf->map_address + elf->start_offset + ehdr->e_phoff,
 		elf->state.ELFW(elf,LIBELFBITS).phdr,
 		sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
 
@@ -371,9 +371,11 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
 			last_position += dl->data.d.d_size;
 		      }
 		    else if (dl->data.d.d_size != 0)
-		      last_position = mempcpy (last_position,
-					       dl->data.d.d_buf,
-					       dl->data.d.d_size);
+		      {
+			memmove (last_position, dl->data.d.d_buf,
+				 dl->data.d.d_size);
+			last_position += dl->data.d.d_size;
+		      }
 
 		    scn_changed = true;
 		  }
diff --git a/libelf/elf_update.c b/libelf/elf_update.c
index 8ce0782..36997c2 100644
--- a/libelf/elf_update.c
+++ b/libelf/elf_update.c
@@ -93,13 +93,29 @@ write_file (Elf *elf, off_t size, int change_bo, size_t shnum)
 	 ENOSPC. Otherwise we ignore the error and treat it as just hint.  */
       if (elf->parent == NULL
 	  && (elf->maximum_size == ~((size_t) 0)
-	      || (size_t) size > elf->maximum_size)
-	  && unlikely (posix_fallocate (elf->fildes, 0, size) != 0))
-	if (errno == ENOSPC)
-	  {
-	    __libelf_seterrno (ELF_E_WRITE_ERROR);
-	    return -1;
-	  }
+	      || (size_t) size > elf->maximum_size))
+	{
+	  if (unlikely (posix_fallocate (elf->fildes, 0, size) != 0))
+	    if (errno == ENOSPC)
+	      {
+		__libelf_seterrno (ELF_E_WRITE_ERROR);
+		return -1;
+	      }
+
+	  /* Extend the mmap address if needed.  */
+	  if (elf->cmd == ELF_C_RDWR_MMAP
+	      && (size_t) size > elf->maximum_size)
+	    {
+	      if (mremap (elf->map_address, elf->maximum_size,
+			  size, 0) == MAP_FAILED)
+		{
+		  __libelf_seterrno (ELF_E_WRITE_ERROR);
+		  return -1;
+		}
+	      elf->maximum_size = size;
+	    }
+
+	}
 
       /* The file is mmaped.  */
       if ((class == ELFCLASS32
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 8d70891..5709857 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,14 @@
+2018-09-12  Mark Wielaard  <mark@klomp.org>
+
+	* Makefile.am (check_PROGRAMS): Add elfcopy and addsections.
+	(TESTS): Add run-copyadd-sections.sh.
+	(EXTRA_DIST): Likewise.
+	(elfcopy_LDADD): New variable.
+	(addsections_LDADD): Likewise.
+	* addsections.c: New file.
+	* elfcopy.c: Likewise.
+	* run-copyadd-sections.sh: New test.
+
 2018-09-11  Mark Wielaard  <mark@klomp.org>
 
 	* backtrace-dwarf.c (main): Add section attribute.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2946083..e0edef0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -59,7 +59,8 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  elfgetzdata elfputzdata zstrptr emptyfile vendorelf \
 		  fillfile dwarf_default_lower_bound dwarf-die-addr-die \
 		  get-units-invalid get-units-split attr-integrate-skel \
-		  all-dwarf-ranges unit-info next_cfi
+		  all-dwarf-ranges unit-info next_cfi \
+		  elfcopy addsections
 
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
 	    asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -153,7 +154,8 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	run-attr-integrate-skel.sh \
 	run-all-dwarf-ranges.sh run-unit-info.sh \
 	run-reloc-bpf.sh \
-	run-next-cfi.sh run-next-cfi-self.sh
+	run-next-cfi.sh run-next-cfi-self.sh \
+	run-copyadd-sections.sh
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -403,7 +405,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 	     testfile-ranges-hello.dwo.bz2 testfile-ranges-world.dwo.bz2 \
 	     run-unit-info.sh run-next-cfi.sh run-next-cfi-self.sh \
 	     testfile-riscv64.bz2 testfile-riscv64-s.bz2 \
-	     testfile-riscv64-core.bz2
+	     testfile-riscv64-core.bz2 \
+	     run-copyadd-sections.sh
 
 if USE_VALGRIND
 valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1'
@@ -571,6 +574,8 @@ attr_integrate_skel_LDADD = $(libdw)
 all_dwarf_ranges_LDADD = $(libdw)
 unit_info_LDADD = $(libdw)
 next_cfi_LDADD = $(libelf) $(libdw)
+elfcopy_LDADD = $(libelf)
+addsections_LDADD = $(libelf)
 
 # We want to test the libelf header against the system elf.h header.
 # Don't include any -I CPPFLAGS.
diff --git a/tests/addsections.c b/tests/addsections.c
new file mode 100644
index 0000000..391c5b4
--- /dev/null
+++ b/tests/addsections.c
@@ -0,0 +1,286 @@
+/* Test program for adding (more than SHN_LORESERVE) sections.
+   Copyright (C) 2018 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/>.  */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include ELFUTILS_HEADER(elf)
+#include <gelf.h>
+
+
+/* shstrndx is special, might overflow into section zero header sh_link.  */
+static int
+setshstrndx (Elf *elf, size_t ndx)
+{
+  printf ("setshstrndx: %zd\n", ndx);
+
+  GElf_Ehdr ehdr_mem;
+  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+  if (ehdr == NULL)
+    return -1;
+
+  if (ndx < SHN_LORESERVE)
+    ehdr->e_shstrndx = ndx;
+  else
+    {
+      ehdr->e_shstrndx = SHN_XINDEX;
+      Elf_Scn *zscn = elf_getscn (elf, 0);
+      GElf_Shdr zshdr_mem;
+      GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem);
+      if (zshdr == NULL)
+	return -1;
+      zshdr->sh_link = ndx;
+      if (gelf_update_shdr (zscn, zshdr) == 0)
+	return -1;
+    }
+
+  if (gelf_update_ehdr (elf, ehdr) == 0)
+    return -1;
+
+  return 0;
+}
+
+/* Will add nr new '.extra' sections and a new '.new_shstrtab' section
+   at the end.  */
+static void
+add_sections (const char *name, size_t nr, int use_mmap)
+{
+  printf ("add_sections '%s': %zd\n", name, nr);
+
+  int fd = open (name, O_RDWR);
+  if (fd < 0)
+    {
+      fprintf (stderr, "Couldn't open file '%s': %s\n",
+	       name, strerror (errno));
+      exit (1);
+    }
+
+  Elf *elf = elf_begin (fd, use_mmap ? ELF_C_RDWR_MMAP : ELF_C_RDWR, NULL);
+  if (elf == NULL)
+    {
+      fprintf (stderr, "Couldn't open ELF file '%s': %s\n",
+	       name, elf_errmsg (-1));
+      exit (1);
+    }
+
+  /* We will add a new shstrtab section with two new names at the end.
+     Just get the current shstrtab table and add two entries '.extra'
+     and '.new_shstrtab' at the end of the table, so all existing indexes
+     are still valid.  */
+  size_t shstrndx;
+  if (elf_getshdrstrndx (elf, &shstrndx) < 0)
+    {
+      printf ("cannot get shstrndx: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  Elf_Scn *shstrtab_scn = elf_getscn (elf, shstrndx);
+  if (shstrtab_scn == NULL)
+    {
+      printf ("couldn't get shstrtab scn: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+  Elf_Data *shstrtab_data = elf_getdata (shstrtab_scn, NULL);
+  if (shstrtab_data == NULL)
+    {
+      printf ("couldn't get shstrtab data: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+  size_t new_shstrtab_size = (shstrtab_data->d_size
+			      + strlen (".extra") + 1
+			      + strlen (".new_shstrtab") + 1);
+  void *new_shstrtab_buf = malloc (new_shstrtab_size);
+  if (new_shstrtab_buf == NULL)
+    {
+      printf ("couldn't allocate new shstrtab data d_buf\n");
+      exit (1);
+    }
+  memcpy (new_shstrtab_buf, shstrtab_data->d_buf, shstrtab_data->d_size);
+  size_t extra_idx = shstrtab_data->d_size;
+  size_t new_shstrtab_idx = extra_idx + strlen (".extra") + 1;
+  strcpy (new_shstrtab_buf + extra_idx, ".extra");
+  strcpy (new_shstrtab_buf + new_shstrtab_idx, ".new_shstrtab");
+
+  // Add lots of .extra sections...
+  size_t cnt = 0;
+  while (cnt++ < nr)
+    {
+      Elf_Scn *scn = elf_newscn (elf);
+      if (scn == NULL)
+	{
+	  printf ("cannot create .extra section (%zd): %s\n", cnt,
+		  elf_errmsg (-1));
+	  exit (1);
+	}
+
+      Elf_Data *data = elf_newdata (scn);
+      if (data == NULL)
+	{
+	  printf ("couldn't create new section data (%zd): %s\n", cnt,
+		  elf_errmsg (-1));
+	  exit (1);
+	}
+
+      data->d_size = strlen ("extra") + 1;
+      data->d_buf = "extra";
+      data->d_type = ELF_T_BYTE;
+      data->d_align = 1;
+
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+      if (shdr == NULL)
+	{
+	  printf ("cannot get header for new section (%zd): %s\n", cnt,
+		  elf_errmsg (-1));
+	  exit (1);
+	}
+
+      shdr->sh_type = SHT_PROGBITS;
+      shdr->sh_flags = 0;
+      shdr->sh_addr = 0;
+      shdr->sh_link = SHN_UNDEF;
+      shdr->sh_info = SHN_UNDEF;
+      shdr->sh_addralign = 1;
+      shdr->sh_entsize = 0;
+      shdr->sh_size = data->d_size;
+      shdr->sh_name = extra_idx;
+
+      if (gelf_update_shdr (scn, shdr) == 0)
+	{
+	  printf ("cannot update new section header (%zd): %s\n", cnt,
+		  elf_errmsg (-1));
+	  exit (1);
+	}
+    }
+
+  // Create new shstrtab section.
+  Elf_Scn *new_shstrtab_scn = elf_newscn (elf);
+  if (new_shstrtab_scn == NULL)
+    {
+      printf ("cannot create new shstrtab section: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  Elf_Data *new_shstrtab_data = elf_newdata (new_shstrtab_scn);
+  if (new_shstrtab_data == NULL)
+    {
+      printf ("couldn't create new shstrtab section data: %s\n",
+	      elf_errmsg (-1));
+      exit (1);
+    }
+
+  new_shstrtab_data->d_size = new_shstrtab_size;
+  new_shstrtab_data->d_buf = new_shstrtab_buf;
+  new_shstrtab_data->d_type = ELF_T_BYTE;
+  new_shstrtab_data->d_align = 1;
+
+  GElf_Shdr shdr_mem;
+  GElf_Shdr *shdr = gelf_getshdr (new_shstrtab_scn, &shdr_mem);
+  if (shdr == NULL)
+    {
+      printf ("cannot get header for new shstrtab section: %s\n",
+	      elf_errmsg (-1));
+      exit (1);
+    }
+
+  shdr->sh_type = SHT_STRTAB;
+  shdr->sh_flags = 0;
+  shdr->sh_addr = 0;
+  shdr->sh_link = SHN_UNDEF;
+  shdr->sh_info = SHN_UNDEF;
+  shdr->sh_addralign = 1;
+  shdr->sh_entsize = 0;
+  shdr->sh_size = new_shstrtab_size;
+  shdr->sh_name = new_shstrtab_idx;
+
+  // Finished new shstrtab section, update the header.
+  if (gelf_update_shdr (new_shstrtab_scn, shdr) == 0)
+    {
+      printf ("cannot update new shstrtab section header: %s\n",
+	      elf_errmsg (-1));
+      exit (1);
+    }
+
+  // Set it as the new shstrtab section to get the names correct.
+  size_t new_shstrndx = elf_ndxscn (new_shstrtab_scn);
+  if (setshstrndx (elf, new_shstrndx) < 0)
+    {
+      printf ("cannot set shstrndx: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  // Write everything to disk.
+  if (elf_update (elf, ELF_C_WRITE) < 0)
+    {
+      printf ("failure in elf_update: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  if (elf_end (elf) != 0)
+    {
+      printf ("couldn't cleanup elf '%s': %s\n", name, elf_errmsg (-1));
+      exit (1);
+    }
+
+  if (close (fd) != 0)
+    {
+      printf ("couldn't close '%s': %s\n", name, strerror (errno));
+      exit (1);
+    }
+
+  free (new_shstrtab_buf);
+}
+
+int
+main (int argc, char *argv[])
+{
+  elf_version (EV_CURRENT);
+
+  /* Takes the given file, and adds the given number of sections.  */
+  if (argc < 3 || argc > 4)
+    {
+      fprintf (stderr, "addsections [--mmap] nr elf.file\n");
+      exit (1);
+    }
+
+  int argn = 1;
+  bool use_mmap = false;
+  if (strcmp (argv[argn], "--mmap") == 0)
+    {
+      use_mmap = true;
+      argn++;
+    }
+
+  size_t nr = atoi (argv[argn++]);
+  const char *file = argv[argn];
+  add_sections (file, nr, use_mmap);
+
+  return 0;
+}
diff --git a/tests/elfcopy.c b/tests/elfcopy.c
new file mode 100644
index 0000000..9000cc9
--- /dev/null
+++ b/tests/elfcopy.c
@@ -0,0 +1,286 @@
+/* Test program for copying a whole ELF file using libelf.
+   Copyright (C) 2018 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/>.  */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include ELFUTILS_HEADER(elf)
+#include <gelf.h>
+
+
+/* shstrndx is special, might overflow into section zero header sh_link.  */
+static int
+setshstrndx (Elf *elf, size_t ndx)
+{
+  printf ("setshstrndx: %zd\n", ndx);
+
+  GElf_Ehdr ehdr_mem;
+  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+  if (ehdr == NULL)
+    return -1;
+
+  if (ndx < SHN_LORESERVE)
+    ehdr->e_shstrndx = ndx;
+  else
+    {
+      ehdr->e_shstrndx = SHN_XINDEX;
+      Elf_Scn *zscn = elf_getscn (elf, 0);
+      GElf_Shdr zshdr_mem;
+      GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem);
+      if (zshdr == NULL)
+	return -1;
+      zshdr->sh_link = ndx;
+      if (gelf_update_shdr (zscn, zshdr) == 0)
+	return -1;
+    }
+
+  if (gelf_update_ehdr (elf, ehdr) == 0)
+    return -1;
+
+  return 0;
+}
+
+/* Copies all elements of an ELF file either using mmap or read.  */
+static void
+copy_elf (const char *in, const char *out, bool use_mmap)
+{
+  printf ("\ncopy_elf: %s -> %s (%s)\n", in, out, use_mmap ? "mmap" : "read");
+
+  /* Existing ELF file.  */
+  int fda = open (in, O_RDONLY);
+  if (fda < 0)
+    {
+      fprintf (stderr, "Couldn't open file '%s': %s\n",
+	       in, strerror (errno));
+      exit (1);
+    }
+
+  Elf *elfa = elf_begin (fda, use_mmap ? ELF_C_READ_MMAP : ELF_C_READ, NULL);
+  if (elfa == NULL)
+    {
+      fprintf (stderr, "Couldn't open ELF file '%s': %s\n",
+	       in, elf_errmsg (-1));
+      exit (1);
+    }
+
+  /* Open new file.  */
+  int fdb = open (out, O_RDWR | O_CREAT | O_TRUNC, 0644);
+  if (fdb < 0)
+    {
+      fprintf (stderr, "Couldn't create file '%s': %s\n",
+	       out, strerror (errno));
+      exit (1);
+    }
+
+  Elf *elfb = elf_begin (fdb, use_mmap ? ELF_C_WRITE_MMAP : ELF_C_WRITE, NULL);
+  if (elfb == NULL)
+    {
+      fprintf (stderr, "Couldn't create ELF file '%s': %s\n",
+	       out, elf_errmsg (-1));
+      exit (1);
+    }
+
+  // Copy ELF header.
+  GElf_Ehdr ehdr_mema;
+  GElf_Ehdr *ehdra = gelf_getehdr (elfa, &ehdr_mema);
+  if (ehdra == NULL)
+    {
+      printf ("cannot get ELF header: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  int class = gelf_getclass (elfa);
+  // Create an ELF header.
+  if (gelf_newehdr (elfb, class) == 0)
+    {
+      printf ("cannot create ELF header: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  /* New elf header is an exact copy.  */
+  GElf_Ehdr ehdr_memb;
+  GElf_Ehdr *ehdrb = &ehdr_memb;
+  *ehdrb = *ehdra;
+  if (gelf_update_ehdr (elfb, ehdrb) == 0)
+    {
+      printf ("cannot update ELF header: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  /* shstrndx is special.  (Technically phdrnum and shdrnum are also
+     special, but they are handled by libelf.)  */
+  size_t shstrndx;
+  if (elf_getshdrstrndx (elfa, &shstrndx) < 0)
+    {
+      printf ("cannot get shstrndx: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+  if (setshstrndx (elfb, shstrndx) < 0)
+    {
+      printf ("cannot set shstrndx: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  /* If there are phdrs, copy them over.  */
+  size_t phnum;
+  if (elf_getphdrnum (elfa, &phnum) != 0)
+    {
+      printf ("cannot get phdrs: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  if (phnum > 0)
+    {
+      if (gelf_newphdr (elfb, phnum) == 0)
+	{
+	  printf ("cannot create phdrs: %s\n", elf_errmsg (-1));
+	  exit (1);
+	}
+
+      for (size_t cnt = 0; cnt < phnum; ++cnt)
+	{
+	  GElf_Phdr phdr_mem;
+	  GElf_Phdr *phdr = gelf_getphdr (elfa, cnt, &phdr_mem);
+	  if (phdr == NULL)
+	    {
+	      printf ("couldn't get phdr %zd: %s\n", cnt, elf_errmsg (-1));
+	      exit (1);
+	    }
+
+	  if (gelf_update_phdr (elfb, cnt, phdr) == 0)
+	    {
+	      printf ("couldn't update phdr %zd: %s\n", cnt, elf_errmsg (-1));
+	      exit (1);
+	    }
+	}
+    }
+
+  /* Copy all sections, headers and data.  */
+  Elf_Scn *scn = NULL;
+  while ((scn = elf_nextscn (elfa, scn)) != NULL)
+    {
+      /* Get the header.  */
+      GElf_Shdr shdr;
+      if (gelf_getshdr (scn, &shdr) == NULL)
+	{
+	  printf ("couldn't get shdr: %s\n", elf_errmsg (-1));
+	  exit (1);
+	}
+
+      /* Create new section.  */
+      Elf_Scn *new_scn = elf_newscn (elfb);
+      if (new_scn == NULL)
+	{
+	  printf ("couldn't create new section: %s\n", elf_errmsg (-1));
+	  exit (1);
+	}
+
+      if (gelf_update_shdr (new_scn, &shdr) == 0)
+	{
+	  printf ("couldn't update shdr: %s\n", elf_errmsg (-1));
+	  exit (1);
+	}
+
+      /* Copy over section data.  */
+      Elf_Data *data = NULL;
+      while ((data = elf_getdata (scn, data)) != NULL)
+	{
+	  Elf_Data *new_data = elf_newdata (new_scn);
+	  if (new_data == NULL)
+	    {
+	      printf ("couldn't create new section data: %s\n",
+		      elf_errmsg (-1));
+	      exit (1);
+	    }
+	  *new_data = *data;
+	}
+    }
+
+  /* Write everything to disk.  If there are any phdrs then we want
+     the exact same layout.  Do we want ELF_F_PERMISSIVE?  */
+  if (phnum > 0)
+    elf_flagelf (elfb, ELF_C_SET, ELF_F_LAYOUT);
+  if (elf_update (elfb, ELF_C_WRITE) < 0)
+    {
+      printf ("failure in elf_update: %s\n", elf_errmsg (-1));
+      exit (1);
+    }
+
+  if (elf_end (elfa) != 0)
+    {
+      printf ("couldn't cleanup elf '%s': %s\n", in, elf_errmsg (-1));
+      exit (1);
+    }
+
+  if (close (fda) != 0)
+    {
+      printf ("couldn't close '%s': %s\n", in, strerror (errno));
+      exit (1);
+    }
+
+  if (elf_end (elfb) != 0)
+    {
+      printf ("couldn't cleanup elf '%s': %s\n", out, elf_errmsg (-1));
+      exit (1);
+    }
+
+  if (close (fdb) != 0)
+    {
+      printf ("couldn't close '%s': %s\n", out, strerror (errno));
+      exit (1);
+    }
+}
+
+int
+main (int argc, const char *argv[])
+{
+  elf_version (EV_CURRENT);
+
+  /* Takes the given file, and create a new identical one.  */
+  if (argc < 3 || argc > 4)
+    {
+      fprintf (stderr, "elfcopy [--mmap] in.elf out.elf\n");
+      exit (1);
+    }
+
+  int argn = 1;
+  bool use_mmap = false;
+  if (strcmp (argv[argn], "--mmap") == 0)
+    {
+      use_mmap = true;
+      argn++;
+    }
+
+  const char *in = argv[argn++];
+  const char *out = argv[argn];
+  copy_elf (in, out, use_mmap);
+
+  return 0;
+}
diff --git a/tests/run-copyadd-sections.sh b/tests/run-copyadd-sections.sh
new file mode 100755
index 0000000..bc20f6e
--- /dev/null
+++ b/tests/run-copyadd-sections.sh
@@ -0,0 +1,87 @@
+#! /bin/sh
+# Copyright (C) 2018 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
+
+test_copy_and_add ()
+{
+  in_file="$1"
+  out_file="${in_file}.copy"
+  out_file_mmap="${out_file}.mmap"
+
+  testfiles ${in_file}
+  tempfiles ${out_file} ${out_file_mmap} readelf.out
+
+  # Can we copy the file?
+  testrun ${abs_builddir}/elfcopy ${in_file} ${out_file}
+  testrun ${abs_top_builddir}/src/elfcmp ${in_file} ${out_file}
+
+  # Can we add a section (in-place)?
+  testrun ${abs_builddir}/addsections 3 ${out_file}
+  testrun ${abs_top_builddir}/src/readelf -S ${out_file} > readelf.out
+  nr=$(grep '.extra' readelf.out | wc -l)
+  if test ${nr} != 3; then
+    # Show what went wrong
+    testrun ${abs_top_builddir}/src/readelf -S ${out_file}
+    exit 1
+  fi
+
+  # Can we add a section (in-place) using ELF_C_WRITE_MMAP?
+  testrun ${abs_builddir}/elfcopy --mmap ${in_file} ${out_file_mmap}
+  testrun ${abs_top_builddir}/src/elfcmp ${in_file} ${out_file_mmap}
+
+  # Can we add a section (in-place) using ELF_C_RDWR_MMAP?
+  # Note we are only adding one sections, adding more might fail
+  # because mremap cannot extend too much.
+  testrun ${abs_builddir}/addsections --mmap 1 ${out_file_mmap}
+  testrun ${abs_top_builddir}/src/readelf -S ${out_file_mmap} > readelf.out
+  nr=$(grep '.extra' readelf.out | wc -l)
+  if test ${nr} != 1; then
+    # Show what went wrong
+    testrun ${abs_top_builddir}/src/readelf -S ${out_file_mmap}
+    exit 1
+  fi
+}
+
+# A collection of random testfiles to test 32/64bit, little/big endian
+# and non-ET_REL (with phdrs)/ET_REL (without phdrs).
+
+# 32bit, big endian, rel
+test_copy_and_add testfile29
+
+# 64bit, big endian, rel
+test_copy_and_add testfile23
+
+# 32bit, little endian, rel
+test_copy_and_add testfile9
+
+# 64bit, little endian, rel
+test_copy_and_add testfile38
+
+# 32bit, big endian, non-rel
+test_copy_and_add testfile26
+
+# 64bit, big endian, non-rel
+test_copy_and_add testfile27
+
+# 32bit, little endian, non-rel
+test_copy_and_add testfile
+
+# 64bit, little endian, non-rel
+test_copy_and_add testfile10
+
+exit 0
-- 
1.8.3.1



More information about the Elfutils-devel mailing list