Merge "Upgrade elfutils to 31c8b3f098b0654db8f573b2a15d5b6d07d4d3b0"
diff --git a/METADATA b/METADATA
index 1f06e6e..6899f9f 100644
--- a/METADATA
+++ b/METADATA
@@ -9,10 +9,10 @@
     type: GIT
     value: "git://sourceware.org/git/elfutils.git"
   }
-  version: "d08c68fec94b26663257d24dbb8d94f2ed8935cd"
+  version: "31c8b3f098b0654db8f573b2a15d5b6d07d4d3b0"
   last_upgrade_date {
     year: 2019
-    month: 6
-    day: 5
+    month: 7
+    day: 8
   }
 }
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index 82e18eb..dde6c81 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,35 @@
+2019-06-18  Mark Wielaard  <mark@klomp.org>
+
+	* common.h (allocate_elf): Use int64_t instead of off_t for offset.
+	* elf32_newphdr.c (newphdr): Document why Elf32/64_Word is correct.
+	* elf32_updatefile.c (fill): Use int64_t instead of off_t for pos.
+	(updatefile): Define last_offset, shdr_offset and scn_start as
+	int64_t instead of off_t.
+	* elf32_updatenull.c: Define Elf32_SizeWord and Elf64_SizeWord.
+	(updatenull_wrlock): Return int64_t instead of off_t. Define size,
+	sh_entsize, sh_align and sh_size as SizeWords. Define offset as
+	int64_t.  Cast data->d_off to SizeWord instead of GElf_Word. Drop
+	size GElf_Word cast. Cast offset to SizeWord instead of GElf_Word
+	when comparing with sh_size.
+	* elf_begin.c (get_shnum): Define offset as int64_t instead of
+	off_t. Document why use GElf_Word is correct.
+	(file_read_elf): Define offset as int64_t instead of off_t.
+	(__libelf_read_mmaped_file): Likewise.
+	(read_unmmaped_file): Likewise.
+	(read_file): Likewise.
+	* elf_getaroff.c (elf_getaroff): Return int64_t.
+	* elf_getbase.c (elf_getbase): Likewise.
+	* elf_getdata_rawchunk.c (elf_getdata_rawchunk): Define offset as
+	int64_t instead of off_t.
+	* elf_update.c (write_file): Return int64_t instead of off_t,
+	define size as int64_t instead of off_t.
+	(elf_update): Likewise.
+	* libelfP.h (struct Elf): Define start_offset, sizestr_offset and
+	offset as int64_t.
+	(__libelf_read_mmaped_file): Define offset as int64_t.
+	(__elf32_updatenull_wrlock): Return int64_t.
+	(__elf64_updatenull_wrlock): Return int64_t.
+
 2019-05-12  Mark Wielaard  <mark@klomp.org>
 
 	* elf32_updatenull.c (updatenull_wrlock): Mark shdr_flags dirty if
diff --git a/libelf/common.h b/libelf/common.h
index 6248690..b0175f6 100644
--- a/libelf/common.h
+++ b/libelf/common.h
@@ -68,7 +68,7 @@
 /* Allocate an Elf descriptor and fill in the generic information.  */
 static inline Elf *
 __attribute__ ((unused))
-allocate_elf (int fildes, void *map_address, off_t offset, size_t maxsize,
+allocate_elf (int fildes, void *map_address, int64_t offset, size_t maxsize,
               Elf_Cmd cmd, Elf *parent, Elf_Kind kind, size_t extra)
 {
   Elf *result = (Elf *) calloc (1, sizeof (Elf) + extra);
diff --git a/libelf/elf32_newphdr.c b/libelf/elf32_newphdr.c
index 4aa7213..7dd78ca 100644
--- a/libelf/elf32_newphdr.c
+++ b/libelf/elf32_newphdr.c
@@ -56,6 +56,9 @@
       return NULL;
     }
 
+  /* This check is correct, it is for sh_info, which is either
+     Elf32_Word or Elf64_Word, both being 32 bits.  But count is size_t
+     so might not fit on 32bit ELF files.  */
   if (unlikely ((ElfW2(LIBELFBITS,Word)) count != count))
     {
       __libelf_seterrno (ELF_E_INVALID_OPERAND);
diff --git a/libelf/elf32_updatefile.c b/libelf/elf32_updatefile.c
index eea51a7..f67e626 100644
--- a/libelf/elf32_updatefile.c
+++ b/libelf/elf32_updatefile.c
@@ -498,7 +498,7 @@
 
 /* Helper function to write out fill bytes.  */
 static int
-fill (int fd, off_t pos, size_t len, char *fillbuf, size_t *filledp)
+fill (int fd, int64_t pos, size_t len, char *fillbuf, size_t *filledp)
 {
   size_t filled = *filledp;
   size_t fill_len = MIN (len, FILLBUFSIZE);
@@ -651,7 +651,7 @@
 
   /* From now on we have to keep track of the last position to eventually
      fill the gaps with the prescribed fill byte.  */
-  off_t last_offset;
+  int64_t last_offset;
   if (elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
     last_offset = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
   else
@@ -664,7 +664,7 @@
 					+ sizeof (ElfW2(LIBELFBITS,Shdr)))))
 	return 1;
 
-      off_t shdr_offset = elf->start_offset + ehdr->e_shoff;
+      int64_t shdr_offset = elf->start_offset + ehdr->e_shoff;
 #undef shdr_fctp
 #define shdr_fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
 
@@ -712,7 +712,7 @@
 	  if (shdr->sh_type == SHT_NOBITS)
 	    goto next;
 
-	  off_t scn_start = elf->start_offset + shdr->sh_offset;
+	  int64_t scn_start = elf->start_offset + shdr->sh_offset;
 	  Elf_Data_List *dl = &scn->data_list;
 	  bool scn_changed = false;
 
diff --git a/libelf/elf32_updatenull.c b/libelf/elf32_updatenull.c
index 303055a..5f3cdbf 100644
--- a/libelf/elf32_updatenull.c
+++ b/libelf/elf32_updatenull.c
@@ -45,6 +45,10 @@
 # define LIBELFBITS 32
 #endif
 
+/* Some fields contain 32/64 sizes.  We cannot use Elf32/64_Word for those,
+   since those are both 32bits.  Elf32/64_Xword is always 64bits.  */
+#define Elf32_SizeWord Elf32_Word
+#define Elf64_SizeWord Elf64_Xword
 
 
 static int
@@ -122,7 +126,7 @@
 }
 
 
-off_t
+int64_t
 internal_function
 __elfw2(LIBELFBITS,updatenull_wrlock) (Elf *elf, int *change_bop, size_t shnum)
 {
@@ -137,7 +141,7 @@
     return -1;
 
   /* At least the ELF header is there.  */
-  off_t size = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
+  ElfW2(LIBELFBITS,SizeWord) size = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
 
   /* Set the program header position.  */
   if (elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
@@ -152,7 +156,7 @@
 	{
 	  /* The user is supposed to fill out e_phoff.  Use it and
 	     e_phnum to determine the maximum extend.  */
-	  size = MAX ((size_t) size,
+	  size = MAX (size,
 		      ehdr->e_phoff
 		      + elf_typesize (LIBELFBITS, ELF_T_PHDR, phnum));
 	}
@@ -205,11 +209,11 @@
 	    {
 	      Elf_Scn *scn = &list->data[cnt];
 	      ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
-	      off_t offset = 0;
+	      int64_t offset = 0;
 
 	      assert (shdr != NULL);
-	      ElfW2(LIBELFBITS,Word) sh_entsize = shdr->sh_entsize;
-	      ElfW2(LIBELFBITS,Word) sh_align = shdr->sh_addralign ?: 1;
+	      ElfW2(LIBELFBITS,SizeWord) sh_entsize = shdr->sh_entsize;
+	      ElfW2(LIBELFBITS,SizeWord) sh_align = shdr->sh_addralign ?: 1;
 	      if (unlikely (! powerof2 (sh_align)))
 		{
 		  __libelf_seterrno (ELF_E_INVALID_ALIGN);
@@ -299,8 +303,8 @@
 			  /* The user specified the offset and the size.
 			     All we have to do is check whether this block
 			     fits in the size specified for the section.  */
-			  if (unlikely ((GElf_Word) (data->d_off
-						     + data->d_size)
+			  if (unlikely ((ElfW2(LIBELFBITS,SizeWord))
+					(data->d_off + data->d_size)
 					> shdr->sh_size))
 			    {
 			      __libelf_seterrno (ELF_E_SECTION_TOO_SMALL);
@@ -329,7 +333,7 @@
 
 	      if (elf->flags & ELF_F_LAYOUT)
 		{
-		  size = MAX ((GElf_Word) size,
+		  size = MAX (size,
 			      (shdr->sh_type != SHT_NOBITS
 			       ? shdr->sh_offset + shdr->sh_size : 0));
 
@@ -353,8 +357,7 @@
 
 		  size = (size + sh_align - 1) & ~(sh_align - 1);
 		  int offset_changed = 0;
-		  update_if_changed (shdr->sh_offset, (GElf_Word) size,
-				     offset_changed);
+		  update_if_changed (shdr->sh_offset, size, offset_changed);
 		  changed |= offset_changed;
 
 		  if (offset_changed && scn->data_list_rear == NULL)
@@ -367,7 +370,8 @@
 
 		  /* See whether the section size is correct.  */
 		  int size_changed = 0;
-		  update_if_changed (shdr->sh_size, (GElf_Word) offset,
+		  update_if_changed (shdr->sh_size,
+				     (ElfW2(LIBELFBITS,SizeWord)) offset,
 				     size_changed);
 		  changed |= size_changed;
 
@@ -384,7 +388,7 @@
 		  && (elf->flags & ELF_F_PERMISSIVE) == 0)
 		{
 		  /* For compressed sections check the uncompressed size.  */
-		  ElfW2(LIBELFBITS,Word) sh_size;
+		  ElfW2(LIBELFBITS,SizeWord) sh_size;
 		  if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
 		    sh_size = shdr->sh_size;
 		  else
@@ -418,7 +422,7 @@
 	  /* The user is supposed to fill out e_shoff.  Use it and
 	     e_shnum (or sh_size of the dummy, first section header)
 	     to determine the maximum extend.  */
-	  size = MAX ((GElf_Word) size,
+	  size = MAX (size,
 		      (ehdr->e_shoff
 		       + (elf_typesize (LIBELFBITS, ELF_T_SHDR, shnum))));
 	}
@@ -432,7 +436,7 @@
 #define SHDR_ALIGN sizeof (ElfW2(LIBELFBITS,Off))
 	  size = (size + SHDR_ALIGN - 1) & ~(SHDR_ALIGN - 1);
 
-	  update_if_changed (ehdr->e_shoff, (GElf_Word) size, elf->flags);
+	  update_if_changed (ehdr->e_shoff, size, elf->flags);
 
 	  /* Account for the section header size.  */
 	  size += elf_typesize (LIBELFBITS, ELF_T_SHDR, shnum);
diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c
index 5d095ff..8107a10 100644
--- a/libelf/elf_begin.c
+++ b/libelf/elf_begin.c
@@ -71,8 +71,8 @@
 
 
 static size_t
-get_shnum (void *map_address, unsigned char *e_ident, int fildes, off_t offset,
-	   size_t maxsize)
+get_shnum (void *map_address, unsigned char *e_ident, int fildes,
+	   int64_t offset, size_t maxsize)
 {
   size_t result;
   union
@@ -243,6 +243,9 @@
 		CONVERT (size);
 	    }
 
+	  /* Although sh_size is an Elf64_Xword and can contain a 64bit
+	     value, we only expect an 32bit value max.  GElf_Word is
+	     32bit unsigned.  */
 	  if (size > ~((GElf_Word) 0))
 	    {
 	      /* Invalid value, it is too large.  */
@@ -266,7 +269,7 @@
 /* Create descriptor for ELF file in memory.  */
 static Elf *
 file_read_elf (int fildes, void *map_address, unsigned char *e_ident,
-	       off_t offset, size_t maxsize, Elf_Cmd cmd, Elf *parent)
+	       int64_t offset, size_t maxsize, Elf_Cmd cmd, Elf *parent)
 {
   /* Verify the binary is of the class we can handle.  */
   if (unlikely ((e_ident[EI_CLASS] != ELFCLASS32
@@ -531,7 +534,7 @@
 
 Elf *
 internal_function
-__libelf_read_mmaped_file (int fildes, void *map_address,  off_t offset,
+__libelf_read_mmaped_file (int fildes, void *map_address,  int64_t offset,
 			   size_t maxsize, Elf_Cmd cmd, Elf *parent)
 {
   /* We have to find out what kind of file this is.  We handle ELF
@@ -564,7 +567,7 @@
 
 
 static Elf *
-read_unmmaped_file (int fildes, off_t offset, size_t maxsize, Elf_Cmd cmd,
+read_unmmaped_file (int fildes, int64_t offset, size_t maxsize, Elf_Cmd cmd,
 		    Elf *parent)
 {
   /* We have to find out what kind of file this is.  We handle ELF
@@ -626,7 +629,7 @@
 
 /* Open a file for reading.  If possible we will try to mmap() the file.  */
 static struct Elf *
-read_file (int fildes, off_t offset, size_t maxsize,
+read_file (int fildes, int64_t offset, size_t maxsize,
 	   Elf_Cmd cmd, Elf *parent)
 {
   void *map_address = NULL;
diff --git a/libelf/elf_getaroff.c b/libelf/elf_getaroff.c
index 5b59203..5c102ad 100644
--- a/libelf/elf_getaroff.c
+++ b/libelf/elf_getaroff.c
@@ -38,7 +38,7 @@
 #include "libelfP.h"
 
 
-off_t
+int64_t
 elf_getaroff (Elf *elf)
 {
   /* Be gratious, the specs demand it.  */
diff --git a/libelf/elf_getbase.c b/libelf/elf_getbase.c
index 8ec5f87..4890d33 100644
--- a/libelf/elf_getbase.c
+++ b/libelf/elf_getbase.c
@@ -37,8 +37,8 @@
 #include "libelfP.h"
 
 
-off_t
+int64_t
 elf_getbase (Elf *elf)
 {
-  return elf == NULL ? (off_t) -1 : elf->start_offset;
+  return elf == NULL ? (int64_t) -1 : elf->start_offset;
 }
diff --git a/libelf/elf_getdata_rawchunk.c b/libelf/elf_getdata_rawchunk.c
index 6a13073..1072f7d 100644
--- a/libelf/elf_getdata_rawchunk.c
+++ b/libelf/elf_getdata_rawchunk.c
@@ -41,7 +41,7 @@
 #include "common.h"
 
 Elf_Data *
-elf_getdata_rawchunk (Elf *elf, off_t offset, size_t size, Elf_Type type)
+elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type)
 {
   if (unlikely (elf == NULL))
     return NULL;
diff --git a/libelf/elf_update.c b/libelf/elf_update.c
index 36997c2..9b8867c 100644
--- a/libelf/elf_update.c
+++ b/libelf/elf_update.c
@@ -40,8 +40,8 @@
 #include "libelfP.h"
 
 
-static off_t
-write_file (Elf *elf, off_t size, int change_bo, size_t shnum)
+static int64_t
+write_file (Elf *elf, int64_t size, int change_bo, size_t shnum)
 {
   int class = elf->class;
 
@@ -164,11 +164,11 @@
 }
 
 
-off_t
+int64_t
 elf_update (Elf *elf, Elf_Cmd cmd)
 {
   size_t shnum;
-  off_t size;
+  int64_t size;
   int change_bo = 0;
 
   if (cmd != ELF_C_NULL
diff --git a/libelf/libelfP.h b/libelf/libelfP.h
index 5134414..b55d5c4 100644
--- a/libelf/libelfP.h
+++ b/libelf/libelfP.h
@@ -296,7 +296,7 @@
   int fildes;
 
   /* Offset in the archive this file starts or zero.  */
-  off_t start_offset;
+  int64_t start_offset;
 
   /* Size of the file in the archive or the entire file size, or ~0
      for an (yet) unknown size.  */
@@ -350,7 +350,7 @@
       int ehdr_flags;		/* Flags (dirty) for ELF header.  */
       int phdr_flags;		/* Flags (dirty|malloc) for program header.  */
       int shdr_malloced;	/* Nonzero if shdr array was allocated.  */
-      off_t sizestr_offset;	/* Offset of the size string in the parent
+      int64_t sizestr_offset;	/* Offset of the size string in the parent
 				   if this is an archive member.  */
       Elf32_Ehdr ehdr_mem;	/* Memory used for ELF header when not
 				   mmaped.  */
@@ -375,7 +375,7 @@
       int ehdr_flags;		/* Flags (dirty) for ELF header.  */
       int phdr_flags;		/* Flags (dirty|malloc) for program header.  */
       int shdr_malloced;	/* Nonzero if shdr array was allocated.  */
-      off_t sizestr_offset;	/* Offset of the size string in the parent
+      int64_t sizestr_offset;	/* Offset of the size string in the parent
 				   if this is an archive member.  */
       Elf64_Ehdr ehdr_mem;	/* Memory used for ELF header when not
 				   mmaped.  */
@@ -392,7 +392,7 @@
       char *long_names;		/* If no index is available but long names
 				   are used this elements points to the data.*/
       size_t long_names_len;	/* Length of the long name table.  */
-      off_t offset;		/* Offset in file we are currently at.
+      int64_t offset;		/* Offset in file we are currently at.
 				   elf_next() advances this to the next
 				   member of the archive.  */
       Elf_Arhdr elf_ar_hdr;	/* Structure returned by 'elf_getarhdr'.  */
@@ -445,7 +445,7 @@
 
 /* Create Elf descriptor from memory image.  */
 extern Elf *__libelf_read_mmaped_file (int fildes, void *map_address,
-				       off_t offset, size_t maxsize,
+				       int64_t offset, size_t maxsize,
 				       Elf_Cmd cmd, Elf *parent)
      internal_function;
 
@@ -467,10 +467,10 @@
 
 
 /* Helper functions for elf_update.  */
-extern off_t __elf32_updatenull_wrlock (Elf *elf, int *change_bop,
-					size_t shnum) internal_function;
-extern off_t __elf64_updatenull_wrlock (Elf *elf, int *change_bop,
-					size_t shnum) internal_function;
+extern int64_t __elf32_updatenull_wrlock (Elf *elf, int *change_bop,
+					  size_t shnum) internal_function;
+extern int64_t __elf64_updatenull_wrlock (Elf *elf, int *change_bop,
+					  size_t shnum) internal_function;
 
 extern int __elf32_updatemmap (Elf *elf, int change_bo, size_t shnum)
      internal_function;
diff --git a/src/ChangeLog b/src/ChangeLog
index 580eea9..c78e6fa 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,12 @@
+2019-06-25  Mark Wielaard  <mark@klomp.org>
+
+	* stack.c (parse_opt): Fix dwfl_core_file_attach error message.
+
+2019-06-18  Mark Wielaard  <mark@klomp.org>
+
+	* strip.c (handle_elf): Use elf_begin ELF_C_WRITE, instead of
+	ELF_C_WRITE_MMAP.
+
 2019-05-10  Mark Wielaard  <mark@klomp.org>
 
 	* readelf.c (struct attrcb_args): Rename die to dies.
diff --git a/src/stack.c b/src/stack.c
index c5f347e..4daabce 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -608,7 +608,7 @@
       if (core != NULL)
 	{
 	  if (dwfl_core_file_attach (dwfl, core) < 0)
-	    error (EXIT_BAD, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1));
+	    error (EXIT_BAD, 0, "dwfl_core_file_attach: %s", dwfl_errmsg (-1));
 	}
 
       /* Makes sure we are properly attached.  */
diff --git a/src/strip.c b/src/strip.c
index 4cd8750..48792a7 100644
--- a/src/strip.c
+++ b/src/strip.c
@@ -1097,7 +1097,7 @@
   if (debug_fname != NULL)
     {
       /* Also create an ELF descriptor for the debug file */
-      debugelf = elf_begin (debug_fd, ELF_C_WRITE_MMAP, NULL);
+      debugelf = elf_begin (debug_fd, ELF_C_WRITE, NULL);
       if (unlikely (gelf_newehdr (debugelf, gelf_getclass (elf)) == 0))
 	{
 	  error (0, 0, gettext ("cannot create new ehdr for file '%s': %s"),
diff --git a/tests/ChangeLog b/tests/ChangeLog
index e038793..400588f 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,17 @@
+2019-07-01  Mark Wielaard  <mark@klomp.org>
+
+	* run-large-elf-file.sh: Add 2GB to mem_needed when running under
+	valgrind.
+
+2019-06-18  Mark Wielaard  <mark@klomp.org>
+
+	* Makefile.am (TESTS): Add run-large-elf-file.sh.
+	(EXTRA_DIST): Likewise.
+	* addsections.c (add_sections): Add sec_size argument, use it
+	as the size of the section data.
+	(main): Handle extra sec_size argument. Pass to add_sections.
+	* run-large-elf-file.sh: New test.
+
 2019-06-03  Mark Wielaard  <mark@klomp.org>
 
 	* elfcopy.c (copy_elf): When swapping the sh_offsets of two sections,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 87428aa..3d95cf6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -83,6 +83,7 @@
 	run-next-files.sh run-next-lines.sh \
 	run-get-pubnames.sh run-get-aranges.sh run-allfcts.sh \
 	run-show-abbrev.sh run-line2addr.sh hash \
+	run-large-elf-file.sh \
 	newscn run-strip-test.sh run-strip-test2.sh \
 	run-strip-test3.sh run-strip-test4.sh run-strip-test5.sh \
 	run-strip-test6.sh run-strip-test7.sh run-strip-test8.sh \
@@ -422,6 +423,7 @@
 	     testfile-riscv64-core.bz2 \
 	     run-reverse-sections.sh run-reverse-sections-self.sh \
 	     run-copyadd-sections.sh run-copymany-sections.sh \
+	     run-large-elf-file.sh \
 	     run-typeiter-many.sh run-strip-test-many.sh \
 	     testfile-debug-rel-ppc64-g.o.bz2 \
 	     testfile-debug-rel-ppc64-z.o.bz2 \
diff --git a/tests/addsections.c b/tests/addsections.c
index cc8d065..c1b0fa8 100644
--- a/tests/addsections.c
+++ b/tests/addsections.c
@@ -70,9 +70,9 @@
 /* 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)
+add_sections (const char *name, size_t nr, int use_mmap, size_t sec_size)
 {
-  printf ("add_sections '%s': %zd\n", name, nr);
+  printf ("add_sections '%s': %zd (sec_size: %zd)\n", name, nr, sec_size);
 
   int fd = open (name, O_RDWR);
   if (fd < 0)
@@ -149,6 +149,25 @@
       exit (1);
     }
 
+  void *buf;
+  size_t bufsz;
+  if (sec_size == 0)
+    {
+      buf = strdup ("extra");
+      bufsz = strlen ("extra") + 1;
+    }
+  else
+    {
+      buf = malloc (sec_size);
+      if (buf == NULL)
+	{
+	  printf ("cannot allocate buffer data of %zd bytes\n", sec_size);
+	  exit (1);
+	}
+      memset (buf, 0xAA, sec_size);
+      bufsz = sec_size;
+    }
+
   // Add lots of .extra sections...
   size_t cnt = 0;
   while (cnt++ < nr)
@@ -169,8 +188,8 @@
 	  exit (1);
 	}
 
-      data->d_size = strlen ("extra") + 1;
-      data->d_buf = "extra";
+      data->d_size = bufsz;
+      data->d_buf = buf;
       data->d_type = ELF_T_BYTE;
       data->d_align = 1;
 
@@ -274,6 +293,7 @@
       exit (1);
     }
 
+  free (buf);
   free (new_shstrtab_buf);
 }
 
@@ -282,10 +302,11 @@
 {
   elf_version (EV_CURRENT);
 
-  /* Takes the given file, and adds the given number of sections.  */
-  if (argc < 3 || argc > 4)
+  /* Takes the given file, and adds the given number of sections.
+     Optionally using mmap and optionally using a given section size.  */
+  if (argc < 3 || argc > 5)
     {
-      fprintf (stderr, "addsections [--mmap] nr elf.file\n");
+      fprintf (stderr, "addsections [--mmap] nr elf.file [sec_size]\n");
       exit (1);
     }
 
@@ -298,8 +319,13 @@
     }
 
   size_t nr = atoi (argv[argn++]);
-  const char *file = argv[argn];
-  add_sections (file, nr, use_mmap);
+  const char *file = argv[argn++];
+
+  size_t sec_size = 0;
+  if (argn < argc)
+    sec_size = atol (argv[argn++]);
+
+  add_sections (file, nr, use_mmap, sec_size);
 
   return 0;
 }
diff --git a/tests/run-large-elf-file.sh b/tests/run-large-elf-file.sh
new file mode 100755
index 0000000..6146cfe
--- /dev/null
+++ b/tests/run-large-elf-file.sh
@@ -0,0 +1,114 @@
+#! /bin/bash
+# Copyright (C) 2019 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
+
+# Only run on 64bit systems, 32bit systems don't support > 4GB
+# ELF files.
+long_bit=$(getconf LONG_BIT)
+echo "long_bit: $long_bit"
+if test $long_bit -ne 64; then
+  echo "Only 64bit systems can create > 4GB ELF files"
+  exit 77
+fi
+
+# These tests need lots of disk space since they test files > 4GB.
+# Skip if there just isn't enough (2.5 * 4 = 10GB).
+space_available=$[$(stat -f --format="%a*%S" .)/(1024 * 1024 * 1024)]
+echo "space_available: $space_available"
+if test $space_available -lt 10; then
+  echo "Not enough disk space, need at least 10GB available"
+  exit 77
+fi
+
+# Make sure the files fit into memory, assume 6GB needed (2.5 * 2 + 1 extra).
+# Running under valgrind might need even more.
+mem_needed=6
+if [ "x$VALGRIND_CMD" != "x" ]; then
+  mem_needed=$[${mem_needed} + 2]
+fi
+echo "mem_needed: $mem_needed"
+mem_available=$(free -g | grep ^Mem: | awk -F ' +' '{print $7}')
+echo "mem_available: $mem_available"
+if test $mem_available -lt $mem_needed; then
+  echo "Need at least ${mem_needed}GB free available memory"
+  exit 77
+fi
+
+# Make sure the disk is reasonably fast, should be able to write 100MB/s
+fast_disk=1
+timeout -s9 10s dd conv=fsync if=/dev/zero of=tempfile bs=1M count=1K \
+  || fast_disk=0; rm tempfile
+if test $fast_disk -eq 0; then
+  echo "File system not fast enough, need at least 100MB/s"
+  exit 77
+fi
+
+# NOTE: test file will be mangled and removed!
+test_file ()
+{
+  in_file="$1"
+  readelf_out="${in_file}.readelf.out"
+  out_file_strip="${in_file}.strip"
+  out_file_debug="${in_file}.debug"
+
+  testfiles ${in_file}
+  tempfiles ${readelf_out} ${out_file_mmap} ${out_file_strip} ${out_file_debug}
+
+  # Add two 2GB sections to the file.
+  echo "addsections 2 ${in_file} 2147483648"
+  testrun ${abs_builddir}/addsections 2 ${in_file} 2147483648
+  testrun ${abs_top_builddir}/src/readelf -S ${in_file} > ${readelf_out}
+  nr=$(grep '.extra' ${readelf_out} | wc -l)
+  if test ${nr} != 2; then
+    # Show what went wrong
+    cat ${readelf_out}
+    exit 1
+  fi
+
+  echo "strip -o ${out_file_strip} -f ${out_file_debug} ${in_file}"
+  testrun ${abs_top_builddir}/src/strip -o ${out_file_strip} \
+                                        -f ${out_file_debug} ${in_file}
+
+  echo "elflint --gnu ${out_file_strip}"
+  testrun ${abs_top_builddir}/src/elflint --gnu ${out_file_strip}
+
+  echo "elflint --gnu -d ${out_file_debug}"
+  testrun ${abs_top_builddir}/src/elflint --gnu -d ${out_file_debug}
+
+  # Now test unstrip recombining those files.
+  echo "unstrip ${out_file_strip} ${out_file_debug}"
+  testrun ${abs_top_builddir}/src/unstrip ${out_file_strip} ${out_file_debug}
+
+  echo "elfcmp ${out_file} ${out_file_strip}"
+  testrun ${abs_top_builddir}/src/elfcmp ${in_file} ${out_file_debug}
+
+  # Remove the temp files immediately, they are big...
+  rm -f ${in_file} ${out_file_strip} ${out_file_debug}
+}
+
+# A collection of random testfiles to test 64bit, little/big endian
+# and non-ET_REL (with phdrs)/ET_REL (without phdrs).
+# Don't test 32bit, they cannot go beyond 4GB.
+
+# 64bit, little endian, rel
+test_file testfile38
+
+# 64bit, big endian, non-rel
+test_file testfile27
+
+exit 0