Squashed 'third_party/elfutils/' content from commit 555e15e

Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libelf/elf32_updatefile.c b/libelf/elf32_updatefile.c
new file mode 100644
index 0000000..7ac9951
--- /dev/null
+++ b/libelf/elf32_updatefile.c
@@ -0,0 +1,850 @@
+/* Write changed data structures.
+   Copyright (C) 2000-2010, 2014, 2015, 2016 Red Hat, Inc.
+   This file is part of elfutils.
+   Written by Ulrich Drepper <drepper@redhat.com>, 2000.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   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 copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <libelf.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <system.h>
+#include "libelfP.h"
+
+
+#ifndef LIBELFBITS
+# define LIBELFBITS 32
+#endif
+
+
+static int
+compare_sections (const void *a, const void *b)
+{
+  const Elf_Scn **scna = (const Elf_Scn **) a;
+  const Elf_Scn **scnb = (const Elf_Scn **) b;
+
+  if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset
+      < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset)
+    return -1;
+
+  if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset
+      > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset)
+    return 1;
+
+  if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size
+      < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size)
+    return -1;
+
+  if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size
+      > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size)
+    return 1;
+
+  if ((*scna)->index < (*scnb)->index)
+    return -1;
+
+  if ((*scna)->index > (*scnb)->index)
+    return 1;
+
+  return 0;
+}
+
+
+/* Insert the sections in the list into the provided array and sort
+   them according to their start offsets.  For sections with equal
+   start offsets, the size is used; for sections with equal start
+   offsets and sizes, the section index is used.  Sorting by size
+   ensures that zero-length sections are processed first, which
+   is what we want since they do not advance our file writing position.  */
+static void
+sort_sections (Elf_Scn **scns, Elf_ScnList *list)
+{
+  Elf_Scn **scnp = scns;
+  do
+    for (size_t cnt = 0; cnt < list->cnt; ++cnt)
+      *scnp++ = &list->data[cnt];
+  while ((list = list->next) != NULL);
+
+  qsort (scns, scnp - scns, sizeof (*scns), compare_sections);
+}
+
+
+static inline void
+fill_mmap (size_t offset, char *last_position, char *scn_start,
+           char *const shdr_start, char *const shdr_end)
+{
+  size_t written = 0;
+
+  if (last_position < shdr_start)
+    {
+      written = MIN (scn_start + offset - last_position,
+                     shdr_start - last_position);
+
+      memset (last_position, __libelf_fill_byte, written);
+    }
+
+  if (last_position + written != scn_start + offset
+      && shdr_end < scn_start + offset)
+    {
+      char *fill_start = MAX (shdr_end, scn_start);
+      memset (fill_start, __libelf_fill_byte,
+              scn_start + offset - fill_start);
+    }
+}
+
+int
+internal_function
+__elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
+{
+  bool previous_scn_changed = false;
+
+  /* We need the ELF header several times.  */
+  ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
+
+  /* Write out the ELF header.  */
+  if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
+    {
+      /* If the type sizes should be different at some time we have to
+	 rewrite this code.  */
+      assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
+	      == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
+
+      if (unlikely (change_bo))
+	{
+	  /* Today there is only one version of the ELF header.  */
+#if EV_NUM != 2
+	  xfct_t fctp;
+	  fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR];
+#else
+# undef fctp
+# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]
+#endif
+
+	  /* Do the real work.  */
+	  (*fctp) ((char *) elf->map_address + elf->start_offset, ehdr,
+		   sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
+	}
+      else if (elf->map_address + elf->start_offset != ehdr)
+	memcpy (elf->map_address + elf->start_offset, ehdr,
+		sizeof (ElfW2(LIBELFBITS,Ehdr)));
+
+      elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
+
+      /* We start writing sections after the ELF header only if there is
+	 no program header.  */
+      previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL;
+    }
+
+  size_t phnum;
+  if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0))
+    return -1;
+
+  /* Write out the program header table.  */
+  if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
+      && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
+	  & ELF_F_DIRTY))
+    {
+      /* If the type sizes should be different at some time we have to
+	 rewrite this code.  */
+      assert (sizeof (ElfW2(LIBELFBITS,Phdr))
+	      == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
+
+      /* Maybe the user wants a gap between the ELF header and the program
+	 header.  */
+      if (ehdr->e_phoff > ehdr->e_ehsize)
+	memset (elf->map_address + elf->start_offset + ehdr->e_ehsize,
+		__libelf_fill_byte, ehdr->e_phoff - ehdr->e_ehsize);
+
+      if (unlikely (change_bo))
+	{
+	  /* Today there is only one version of the ELF header.  */
+#if EV_NUM != 2
+	  xfct_t fctp;
+	  fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR];
+#else
+# undef fctp
+# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
+#endif
+
+	  /* Do the real work.  */
+	  (*fctp) (elf->map_address + elf->start_offset + ehdr->e_phoff,
+		   elf->state.ELFW(elf,LIBELFBITS).phdr,
+		   sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1);
+	}
+      else
+	memcpy (elf->map_address + elf->start_offset + ehdr->e_phoff,
+		elf->state.ELFW(elf,LIBELFBITS).phdr,
+		sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
+
+      elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
+
+      /* We modified the program header.  Maybe this created a gap so
+	 we have to write fill bytes, if necessary.  */
+      previous_scn_changed = true;
+    }
+
+  /* From now on we have to keep track of the last position to eventually
+     fill the gaps with the prescribed fill byte.  */
+  char *last_position = ((char *) elf->map_address + elf->start_offset
+			 + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
+				ehdr->e_phoff)
+			 + elf_typesize (LIBELFBITS, ELF_T_PHDR, phnum));
+
+  /* Write all the sections.  Well, only those which are modified.  */
+  if (shnum > 0)
+    {
+      if (unlikely (shnum > SIZE_MAX / sizeof (Elf_Scn *)))
+	return 1;
+
+      Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
+      Elf_Scn **scns = (Elf_Scn **) malloc (shnum * sizeof (Elf_Scn *));
+      if (unlikely (scns == NULL))
+	{
+	  __libelf_seterrno (ELF_E_NOMEM);
+	  return -1;
+	}
+      char *const shdr_start = ((char *) elf->map_address + elf->start_offset
+				+ ehdr->e_shoff);
+      char *const shdr_end = shdr_start + ehdr->e_shnum * ehdr->e_shentsize;
+
+#if EV_NUM != 2
+      xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR];
+#else
+# undef shdr_fctp
+# define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
+#endif
+#define shdr_dest ((ElfW2(LIBELFBITS,Shdr) *) shdr_start)
+
+      /* Get all sections into the array and sort them.  */
+      sort_sections (scns, list);
+
+      /* We possibly have to copy the section header data because moving
+	 the sections might overwrite the data.  */
+      for (size_t cnt = 0; cnt < shnum; ++cnt)
+	{
+	  Elf_Scn *scn = scns[cnt];
+
+	  if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
+	      && (scn->shdr_flags & ELF_F_MALLOCED) == 0
+	      && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
+	    {
+	      assert ((char *) elf->map_address + elf->start_offset
+		      < (char *) scn->shdr.ELFW(e,LIBELFBITS));
+	      assert ((char *) scn->shdr.ELFW(e,LIBELFBITS)
+		      < ((char *) elf->map_address + elf->start_offset
+			 + elf->maximum_size));
+
+	      void *p = malloc (sizeof (ElfW2(LIBELFBITS,Shdr)));
+	      if (unlikely (p == NULL))
+		{
+		  free (scns);
+		  __libelf_seterrno (ELF_E_NOMEM);
+		  return -1;
+		}
+	      scn->shdr.ELFW(e,LIBELFBITS)
+		= memcpy (p, scn->shdr.ELFW(e,LIBELFBITS),
+			  sizeof (ElfW2(LIBELFBITS,Shdr)));
+	    }
+
+	  /* If the file is mmaped and the original position of the
+	     section in the file is lower than the new position we
+	     need to save the section content since otherwise it is
+	     overwritten before it can be copied.  If there are
+	     multiple data segments in the list only the first can be
+	     from the file.  */
+	  if (((char *) elf->map_address + elf->start_offset
+	       <= (char  *) scn->data_list.data.d.d_buf)
+	      && ((char *) scn->data_list.data.d.d_buf
+		  < ((char *) elf->map_address + elf->start_offset
+		     + elf->maximum_size))
+	      && (((char *) elf->map_address + elf->start_offset
+		   + scn->shdr.ELFW(e,LIBELFBITS)->sh_offset)
+		  > (char *) scn->data_list.data.d.d_buf))
+	    {
+	      void *p = malloc (scn->data_list.data.d.d_size);
+	      if (unlikely (p == NULL))
+		{
+		  free (scns);
+		  __libelf_seterrno (ELF_E_NOMEM);
+		  return -1;
+		}
+	      scn->data_list.data.d.d_buf = scn->data_base
+		= memcpy (p, scn->data_list.data.d.d_buf,
+			  scn->data_list.data.d.d_size);
+	    }
+	}
+
+      /* Iterate over all the section in the order in which they
+	 appear in the output file.  */
+      for (size_t cnt = 0; cnt < shnum; ++cnt)
+	{
+	  Elf_Scn *scn = scns[cnt];
+	  if (scn->index == 0)
+	    {
+	      /* The dummy section header entry.  It should not be
+		 possible to mark this "section" as dirty.  */
+	      assert ((scn->flags & ELF_F_DIRTY) == 0);
+	      continue;
+	    }
+
+	  ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
+	  if (shdr->sh_type == SHT_NOBITS)
+	    goto next;
+
+	  char *scn_start = ((char *) elf->map_address
+			     + elf->start_offset + shdr->sh_offset);
+	  Elf_Data_List *dl = &scn->data_list;
+	  bool scn_changed = false;
+
+	  if (scn->data_list_rear != NULL)
+	    do
+	      {
+		assert (dl->data.d.d_off >= 0);
+		assert ((GElf_Off) dl->data.d.d_off <= shdr->sh_size);
+		assert (dl->data.d.d_size <= (shdr->sh_size
+					      - (GElf_Off) dl->data.d.d_off));
+
+		/* If there is a gap, fill it.  */
+		if (scn_start + dl->data.d.d_off > last_position
+		    && (dl->data.d.d_off == 0
+			|| ((scn->flags | dl->flags | elf->flags)
+			    & ELF_F_DIRTY) != 0))
+		  {
+		    fill_mmap (dl->data.d.d_off, last_position, scn_start,
+		               shdr_start, shdr_end);
+		  }
+
+		last_position = scn_start + dl->data.d.d_off;
+
+		if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
+		  {
+		    /* Let it go backward if the sections use a bogus
+		       layout with overlaps.  We'll overwrite the stupid
+		       user's section data with the latest one, rather than
+		       crashing.  */
+
+		    if (unlikely (change_bo))
+		      {
+#if EV_NUM != 2
+			xfct_t fctp;
+			fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
+#else
+# undef fctp
+# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
+#endif
+
+			/* Do the real work.  */
+			(*fctp) (last_position, dl->data.d.d_buf,
+				 dl->data.d.d_size, 1);
+
+			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);
+
+		    scn_changed = true;
+		  }
+		else
+		  last_position += dl->data.d.d_size;
+
+		assert (scn_start + dl->data.d.d_off + dl->data.d.d_size
+			== last_position);
+
+		dl->flags &= ~ELF_F_DIRTY;
+
+		dl = dl->next;
+	      }
+	    while (dl != NULL);
+	  else
+	    {
+	      /* If the previous section (or the ELF/program
+		 header) changed we might have to fill the gap.  */
+	      if (scn_start > last_position && previous_scn_changed)
+		fill_mmap (0, last_position, scn_start,
+		           shdr_start, shdr_end);
+
+	      /* We have to trust the existing section header information.  */
+	      last_position = scn_start + shdr->sh_size;
+	    }
+
+
+	  previous_scn_changed = scn_changed;
+	next:
+	  scn->flags &= ~ELF_F_DIRTY;
+	}
+
+      /* Fill the gap between last section and section header table if
+	 necessary.  */
+      if ((elf->flags & ELF_F_DIRTY)
+	  && last_position < ((char *) elf->map_address + elf->start_offset
+			      + ehdr->e_shoff))
+	memset (last_position, __libelf_fill_byte,
+		(char *) elf->map_address + elf->start_offset + ehdr->e_shoff
+		- last_position);
+
+      /* Write the section header table entry if necessary.  */
+      for (size_t cnt = 0; cnt < shnum; ++cnt)
+	{
+	  Elf_Scn *scn = scns[cnt];
+
+	  if ((scn->shdr_flags | elf->flags) & ELF_F_DIRTY)
+	    {
+	      if (unlikely (change_bo))
+		(*shdr_fctp) (&shdr_dest[scn->index],
+			      scn->shdr.ELFW(e,LIBELFBITS),
+			      sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
+	      else
+		memcpy (&shdr_dest[scn->index],
+			scn->shdr.ELFW(e,LIBELFBITS),
+			sizeof (ElfW2(LIBELFBITS,Shdr)));
+
+	      /* If we previously made a copy of the section header
+		 entry we now have to adjust the pointer again so
+		 point to new place in the mapping.  */
+	      if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
+		  && (scn->shdr_flags & ELF_F_MALLOCED) == 0
+		  && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
+		{
+		  free (scn->shdr.ELFW(e,LIBELFBITS));
+		  scn->shdr.ELFW(e,LIBELFBITS) = &shdr_dest[scn->index];
+		}
+
+	      scn->shdr_flags &= ~ELF_F_DIRTY;
+	    }
+	}
+      free (scns);
+    }
+
+  /* That was the last part.  Clear the overall flag.  */
+  elf->flags &= ~ELF_F_DIRTY;
+
+  /* Make sure the content hits the disk.  */
+  char *msync_start = ((char *) elf->map_address
+		       + (elf->start_offset & ~(sysconf (_SC_PAGESIZE) - 1)));
+  char *msync_end = ((char *) elf->map_address
+		     + elf->start_offset + ehdr->e_shoff
+		     + ehdr->e_shentsize * shnum);
+  (void) msync (msync_start, msync_end - msync_start, MS_SYNC);
+
+  return 0;
+}
+
+
+/* Size of the buffer we use to generate the blocks of fill bytes.  */
+#define FILLBUFSIZE	4096
+
+/* If we have to convert the section buffer contents we have to use
+   temporary buffer.  Only buffers up to MAX_TMPBUF bytes are allocated
+   on the stack.  */
+#define MAX_TMPBUF	32768
+
+
+/* Helper function to write out fill bytes.  */
+static int
+fill (int fd, off_t pos, size_t len, char *fillbuf, size_t *filledp)
+{
+  size_t filled = *filledp;
+  size_t fill_len = MIN (len, FILLBUFSIZE);
+
+  if (unlikely (fill_len > filled) && filled < FILLBUFSIZE)
+    {
+      /* Initialize a few more bytes.  */
+      memset (fillbuf + filled, __libelf_fill_byte, fill_len - filled);
+      *filledp = filled = fill_len;
+    }
+
+  do
+    {
+      /* This many bytes we want to write in this round.  */
+      size_t n = MIN (filled, len);
+
+      if (unlikely ((size_t) pwrite_retry (fd, fillbuf, n, pos) != n))
+	{
+	  __libelf_seterrno (ELF_E_WRITE_ERROR);
+	  return 1;
+	}
+
+      pos += n;
+      len -= n;
+    }
+  while (len > 0);
+
+  return 0;
+}
+
+
+int
+internal_function
+__elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
+{
+  char fillbuf[FILLBUFSIZE];
+  size_t filled = 0;
+  bool previous_scn_changed = false;
+
+  /* We need the ELF header several times.  */
+  ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
+
+  /* Write out the ELF header.  */
+  if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
+    {
+      ElfW2(LIBELFBITS,Ehdr) tmp_ehdr;
+      ElfW2(LIBELFBITS,Ehdr) *out_ehdr = ehdr;
+
+      /* If the type sizes should be different at some time we have to
+	 rewrite this code.  */
+      assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
+	      == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
+
+      if (unlikely (change_bo))
+	{
+	  /* Today there is only one version of the ELF header.  */
+#if EV_NUM != 2
+	  xfct_t fctp;
+	  fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR];
+#else
+# undef fctp
+# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]
+#endif
+
+	  /* Write the converted ELF header in a temporary buffer.  */
+	  (*fctp) (&tmp_ehdr, ehdr, sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
+
+	  /* This is the buffer we want to write.  */
+	  out_ehdr = &tmp_ehdr;
+	}
+
+      /* Write out the ELF header.  */
+      if (unlikely (pwrite_retry (elf->fildes, out_ehdr,
+				  sizeof (ElfW2(LIBELFBITS,Ehdr)), 0)
+		    != sizeof (ElfW2(LIBELFBITS,Ehdr))))
+	{
+	  __libelf_seterrno (ELF_E_WRITE_ERROR);
+	  return 1;
+	}
+
+      elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
+
+      /* We start writing sections after the ELF header only if there is
+	 no program header.  */
+      previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL;
+    }
+
+  /* If the type sizes should be different at some time we have to
+     rewrite this code.  */
+  assert (sizeof (ElfW2(LIBELFBITS,Phdr))
+	  == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
+
+  size_t phnum;
+  if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0))
+    return -1;
+
+  /* Write out the program header table.  */
+  if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
+      && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
+	  & ELF_F_DIRTY))
+    {
+      ElfW2(LIBELFBITS,Phdr) *tmp_phdr = NULL;
+      ElfW2(LIBELFBITS,Phdr) *out_phdr = elf->state.ELFW(elf,LIBELFBITS).phdr;
+
+      /* Maybe the user wants a gap between the ELF header and the program
+	 header.  */
+      if (ehdr->e_phoff > ehdr->e_ehsize
+	  && unlikely (fill (elf->fildes, ehdr->e_ehsize,
+			     ehdr->e_phoff - ehdr->e_ehsize, fillbuf, &filled)
+		       != 0))
+	return 1;
+
+      if (unlikely (change_bo))
+	{
+	  /* Today there is only one version of the ELF header.  */
+#if EV_NUM != 2
+	  xfct_t fctp;
+	  fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR];
+#else
+# undef fctp
+# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
+#endif
+
+	  /* Allocate sufficient memory.  */
+	  tmp_phdr = (ElfW2(LIBELFBITS,Phdr) *)
+	    malloc (sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
+	  if (unlikely (tmp_phdr == NULL))
+	    {
+	      __libelf_seterrno (ELF_E_NOMEM);
+	      return 1;
+	    }
+
+	  /* Write the converted ELF header in a temporary buffer.  */
+	  (*fctp) (tmp_phdr, elf->state.ELFW(elf,LIBELFBITS).phdr,
+		   sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1);
+
+	  /* This is the buffer we want to write.  */
+	  out_phdr = tmp_phdr;
+	}
+
+      /* Write out the ELF header.  */
+      size_t phdr_size = sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum;
+      if (unlikely ((size_t) pwrite_retry (elf->fildes, out_phdr,
+					   phdr_size, ehdr->e_phoff)
+		    != phdr_size))
+	{
+	  __libelf_seterrno (ELF_E_WRITE_ERROR);
+	  return 1;
+	}
+
+      /* This is a no-op we we have not allocated any memory.  */
+      free (tmp_phdr);
+
+      elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
+
+      /* We modified the program header.  Maybe this created a gap so
+	 we have to write fill bytes, if necessary.  */
+      previous_scn_changed = true;
+    }
+
+  /* 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;
+  if (elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
+    last_offset = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
+  else
+    last_offset = (ehdr->e_phoff + sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
+
+  /* Write all the sections.  Well, only those which are modified.  */
+  if (shnum > 0)
+    {
+      if (unlikely (shnum > SIZE_MAX / (sizeof (Elf_Scn *)
+					+ sizeof (ElfW2(LIBELFBITS,Shdr)))))
+	return 1;
+
+      off_t shdr_offset = elf->start_offset + ehdr->e_shoff;
+#if EV_NUM != 2
+      xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR];
+#else
+# undef shdr_fctp
+# define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
+#endif
+
+      ElfW2(LIBELFBITS,Shdr) *shdr_data;
+      ElfW2(LIBELFBITS,Shdr) *shdr_data_mem = NULL;
+      if (change_bo || elf->state.ELFW(elf,LIBELFBITS).shdr == NULL
+	  || (elf->flags & ELF_F_DIRTY))
+	{
+	  shdr_data_mem = (ElfW2(LIBELFBITS,Shdr) *)
+	    malloc (shnum * sizeof (ElfW2(LIBELFBITS,Shdr)));
+	  if (unlikely (shdr_data_mem == NULL))
+	    {
+	      __libelf_seterrno (ELF_E_NOMEM);
+	      return -1;
+	    }
+	  shdr_data = shdr_data_mem;
+	}
+      else
+	shdr_data = elf->state.ELFW(elf,LIBELFBITS).shdr;
+      int shdr_flags = elf->flags;
+
+      /* Get all sections into the array and sort them.  */
+      Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
+      Elf_Scn **scns = (Elf_Scn **) malloc (shnum * sizeof (Elf_Scn *));
+      if (unlikely (scns == NULL))
+	{
+	  free (shdr_data_mem);
+	  __libelf_seterrno (ELF_E_NOMEM);
+	  return -1;
+	}
+      sort_sections (scns, list);
+
+      for (size_t cnt = 0; cnt < shnum; ++cnt)
+	{
+	  Elf_Scn *scn = scns[cnt];
+	  if (scn->index == 0)
+	    {
+	      /* The dummy section header entry.  It should not be
+		 possible to mark this "section" as dirty.  */
+	      assert ((scn->flags & ELF_F_DIRTY) == 0);
+	      goto next;
+	    }
+
+	  ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
+	  if (shdr->sh_type == SHT_NOBITS)
+	    goto next;
+
+	  off_t scn_start = elf->start_offset + shdr->sh_offset;
+	  Elf_Data_List *dl = &scn->data_list;
+	  bool scn_changed = false;
+
+	  if (scn->data_list_rear != NULL)
+	    do
+	      {
+		/* If there is a gap, fill it.  */
+		if (scn_start + dl->data.d.d_off > last_offset
+		    && ((previous_scn_changed && dl->data.d.d_off == 0)
+			|| ((scn->flags | dl->flags | elf->flags)
+			    & ELF_F_DIRTY) != 0))
+		  {
+		    if (unlikely (fill (elf->fildes, last_offset,
+					(scn_start + dl->data.d.d_off)
+					- last_offset, fillbuf,
+					&filled) != 0))
+		      {
+		      fail_free:
+			free (shdr_data_mem);
+			free (scns);
+			return 1;
+		      }
+		  }
+
+		last_offset = scn_start + dl->data.d.d_off;
+
+		if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
+		  {
+		    char tmpbuf[MAX_TMPBUF];
+		    void *buf = dl->data.d.d_buf;
+
+		    /* Let it go backward if the sections use a bogus
+		       layout with overlaps.  We'll overwrite the stupid
+		       user's section data with the latest one, rather than
+		       crashing.  */
+
+		    if (unlikely (change_bo))
+		      {
+#if EV_NUM != 2
+			xfct_t fctp;
+			fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
+#else
+# undef fctp
+# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
+#endif
+
+			buf = tmpbuf;
+			if (dl->data.d.d_size > MAX_TMPBUF)
+			  {
+			    buf = malloc (dl->data.d.d_size);
+			    if (unlikely (buf == NULL))
+			      {
+				__libelf_seterrno (ELF_E_NOMEM);
+				goto fail_free;
+			      }
+			  }
+
+			/* Do the real work.  */
+			(*fctp) (buf, dl->data.d.d_buf, dl->data.d.d_size, 1);
+		      }
+
+		    ssize_t n = pwrite_retry (elf->fildes, buf,
+					      dl->data.d.d_size,
+					      last_offset);
+		    if (unlikely ((size_t) n != dl->data.d.d_size))
+		      {
+			if (buf != dl->data.d.d_buf && buf != tmpbuf)
+			  free (buf);
+
+			__libelf_seterrno (ELF_E_WRITE_ERROR);
+			goto fail_free;
+		      }
+
+		    if (buf != dl->data.d.d_buf && buf != tmpbuf)
+		      free (buf);
+
+		    scn_changed = true;
+		  }
+
+		last_offset += dl->data.d.d_size;
+
+		dl->flags &= ~ELF_F_DIRTY;
+
+		dl = dl->next;
+	      }
+	    while (dl != NULL);
+	  else
+	    {
+	      /* If the previous section (or the ELF/program
+		 header) changed we might have to fill the gap.  */
+	      if (scn_start > last_offset && previous_scn_changed)
+		{
+		  if (unlikely (fill (elf->fildes, last_offset,
+				      scn_start - last_offset, fillbuf,
+				      &filled) != 0))
+		    goto fail_free;
+		}
+
+	      last_offset = scn_start + shdr->sh_size;
+	    }
+
+	  previous_scn_changed = scn_changed;
+	next:
+	  /* Collect the section header table information.  */
+	  if (unlikely (change_bo))
+	    (*shdr_fctp) (&shdr_data[scn->index],
+			  scn->shdr.ELFW(e,LIBELFBITS),
+			  sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
+	  else if (elf->state.ELFW(elf,LIBELFBITS).shdr == NULL
+		   || (elf->flags & ELF_F_DIRTY))
+	    memcpy (&shdr_data[scn->index], scn->shdr.ELFW(e,LIBELFBITS),
+		    sizeof (ElfW2(LIBELFBITS,Shdr)));
+
+	  shdr_flags |= scn->shdr_flags;
+	  scn->shdr_flags &= ~ELF_F_DIRTY;
+	}
+
+      /* Fill the gap between last section and section header table if
+	 necessary.  */
+      if ((elf->flags & ELF_F_DIRTY) && last_offset < shdr_offset
+	  && unlikely (fill (elf->fildes, last_offset,
+			     shdr_offset - last_offset,
+			     fillbuf, &filled) != 0))
+	goto fail_free;
+
+      /* Write out the section header table.  */
+      if (shdr_flags & ELF_F_DIRTY
+	  && unlikely ((size_t) pwrite_retry (elf->fildes, shdr_data,
+					      sizeof (ElfW2(LIBELFBITS,Shdr))
+					      * shnum, shdr_offset)
+		       != sizeof (ElfW2(LIBELFBITS,Shdr)) * shnum))
+	{
+	  __libelf_seterrno (ELF_E_WRITE_ERROR);
+	  goto fail_free;
+	}
+
+      free (shdr_data_mem);
+      free (scns);
+    }
+
+  /* That was the last part.  Clear the overall flag.  */
+  elf->flags &= ~ELF_F_DIRTY;
+
+  return 0;
+}