summaryrefslogtreecommitdiffstats
path: root/src/libelf/elf32_updatefile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libelf/elf32_updatefile.c')
-rw-r--r--src/libelf/elf32_updatefile.c820
1 files changed, 820 insertions, 0 deletions
diff --git a/src/libelf/elf32_updatefile.c b/src/libelf/elf32_updatefile.c
new file mode 100644
index 00000000..898cf1ab
--- /dev/null
+++ b/src/libelf/elf32_updatefile.c
@@ -0,0 +1,820 @@
+/* Write changed data structures.
+ Copyright (C) 2000-2010 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2000.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#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 <sys/param.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);
+}
+
+
+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
+ 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)
+ {
+ Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
+ Elf_Scn **scns = (Elf_Scn **) alloca (shnum * sizeof (Elf_Scn *));
+ 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 = alloca (sizeof (ElfW2(LIBELFBITS,Shdr)));
+ 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 (p == NULL)
+ {
+ __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;
+
+ void fill_mmap (size_t offset)
+ {
+ 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);
+ }
+ }
+
+ 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 + 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. */
+
+ last_position = scn_start + dl->data.d.d_off;
+
+ 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
+ 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);
+
+ /* 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];
+
+ scn->shdr_flags &= ~ELF_F_DIRTY;
+ }
+ }
+ }
+
+ /* 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 (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)
+ {
+ 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;
+ if (change_bo || elf->state.ELFW(elf,LIBELFBITS).shdr == NULL)
+ shdr_data = (ElfW2(LIBELFBITS,Shdr) *)
+ alloca (shnum * sizeof (ElfW2(LIBELFBITS,Shdr)));
+ 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 **) alloca (shnum * sizeof (Elf_Scn *));
+ 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))
+ return 1;
+ }
+
+ 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. */
+
+ last_offset = scn_start + dl->data.d.d_off;
+
+ 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 (buf == NULL)
+ {
+ __libelf_seterrno (ELF_E_NOMEM);
+ return 1;
+ }
+ }
+
+ /* 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);
+ return 1;
+ }
+
+ 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))
+ return 1;
+ }
+
+ 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)
+ 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))
+ return 1;
+
+ /* 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);
+ return 1;
+ }
+ }
+
+ /* That was the last part. Clear the overall flag. */
+ elf->flags &= ~ELF_F_DIRTY;
+
+ return 0;
+}