aboutsummaryrefslogtreecommitdiffstats
path: root/src/undo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/undo.c')
-rw-r--r--src/undo.c713
1 files changed, 713 insertions, 0 deletions
diff --git a/src/undo.c b/src/undo.c
new file mode 100644
index 0000000..8a55bf2
--- /dev/null
+++ b/src/undo.c
@@ -0,0 +1,713 @@
+/* Copyright (C) 2001, 2002, 2003, 2005, 2010 Red Hat, Inc.
+ Written by Jakub Jelinek <jakub@redhat.com>, 2001.
+
+ This program 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 2, or (at your option)
+ any later version.
+
+ This program 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, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+#include <assert.h>
+#include <endian.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include "prelink.h"
+#include "reloc.h"
+
+static int
+undo_prelink_rel (DSO *dso, int n)
+{
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[n];
+ GElf_Rel rel;
+ int sec;
+
+ if (dso->arch->undo_prelink_rel == NULL)
+ return 0;
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ int ndx, maxndx;
+ GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off;
+
+ maxndx = data->d_size / dso->shdr[n].sh_entsize;
+ for (ndx = 0; ndx < maxndx;
+ ++ndx, addr += dso->shdr[n].sh_entsize)
+ {
+ gelfx_getrel (dso->elf, data, ndx, &rel);
+ sec = addr_to_sec (dso, rel.r_offset);
+ if (sec == -1)
+ continue;
+
+ switch (dso->arch->undo_prelink_rel (dso, &rel, addr))
+ {
+ case 2:
+ gelfx_update_rel (dso->elf, data, ndx, &rel);
+ break;
+ case 0:
+ break;
+ default:
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+undo_prelink_rela (DSO *dso, int n)
+{
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[n];
+ GElf_Rela rela;
+ int sec;
+
+ if (dso->arch->undo_prelink_rela == NULL)
+ return 0;
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ int ndx, maxndx;
+ GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off;
+
+ maxndx = data->d_size / dso->shdr[n].sh_entsize;
+ for (ndx = 0; ndx < maxndx;
+ ++ndx, addr += dso->shdr[n].sh_entsize)
+ {
+ gelfx_getrela (dso->elf, data, ndx, &rela);
+ sec = addr_to_sec (dso, rela.r_offset);
+ if (sec == -1)
+ continue;
+
+ switch (dso->arch->undo_prelink_rela (dso, &rela, addr))
+ {
+ case 2:
+ gelfx_update_rela (dso->elf, data, ndx, &rela);
+ break;
+ case 0:
+ break;
+ default:
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+remove_dynamic_prelink_tags (DSO *dso)
+{
+ Elf_Data *data;
+ Elf_Scn *scn;
+ GElf_Dyn dyn;
+ int ndx;
+
+ assert (dso->shdr[dso->dynamic].sh_type == SHT_DYNAMIC);
+ scn = dso->scn[dso->dynamic];
+ data = elf_getdata (scn, NULL);
+ assert (elf_getdata (scn, data) == NULL);
+ ndx = data->d_size / dso->shdr[dso->dynamic].sh_entsize;
+ while (--ndx >= 0)
+ {
+ gelfx_getdyn (dso->elf, data, ndx, &dyn);
+ switch (dyn.d_tag)
+ {
+ case DT_NULL:
+ continue;
+ case DT_CHECKSUM:
+ case DT_GNU_PRELINKED:
+ case DT_GNU_LIBLIST:
+ case DT_GNU_LIBLISTSZ:
+ case DT_GNU_CONFLICT:
+ case DT_GNU_CONFLICTSZ:
+ dyn.d_tag = DT_NULL;
+ dyn.d_un.d_val = 0;
+ gelfx_update_dyn (dso->elf, data, ndx, &dyn);
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ break;
+ default:
+ ndx = 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+int
+undo_sections (DSO *dso, int undo, struct section_move *move,
+ struct reloc_info *rinfo, GElf_Ehdr *ehdr,
+ GElf_Phdr *phdr, GElf_Shdr *shdr)
+{
+ Elf_Data src, dst, *d;
+ Elf_Scn *scn;
+ int i, j;
+
+ scn = dso->scn[undo];
+ d = elf_getdata (scn, NULL);
+ assert (d != NULL && elf_getdata (scn, d) == NULL);
+
+ src = *d;
+ src.d_type = ELF_T_EHDR;
+ src.d_align = dso->shdr[undo].sh_addralign;
+ src.d_size = gelf_fsize (dso->elf, ELF_T_EHDR, 1, EV_CURRENT);
+ dst = src;
+ if (src.d_size > d->d_size)
+ {
+ error (0, 0, "%s: .gnu.prelink_undo section too small",
+ dso->filename);
+ return 1;
+ }
+ switch (gelf_getclass (dso->elf))
+ {
+ case ELFCLASS32:
+ dst.d_buf = alloca (dst.d_size);
+ break;
+ case ELFCLASS64:
+ dst.d_buf = ehdr;
+ break;
+ default:
+ return 1;
+ }
+ if (gelf_xlatetom (dso->elf, &dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL)
+ {
+ error (0, 0, "%s: Could not read .gnu.prelink_undo section",
+ dso->filename);
+ return 1;
+ }
+ if (gelf_getclass (dso->elf) == ELFCLASS32)
+ {
+ Elf32_Ehdr *ehdr32 = (Elf32_Ehdr *) dst.d_buf;
+
+ memcpy (ehdr->e_ident, ehdr32->e_ident, sizeof (ehdr->e_ident));
+#define COPY(name) ehdr->name = ehdr32->name
+ COPY (e_type);
+ COPY (e_machine);
+ COPY (e_version);
+ COPY (e_entry);
+ COPY (e_phoff);
+ COPY (e_shoff);
+ COPY (e_flags);
+ COPY (e_ehsize);
+ COPY (e_phentsize);
+ COPY (e_phnum);
+ COPY (e_shentsize);
+ COPY (e_shnum);
+ COPY (e_shstrndx);
+#undef COPY
+ }
+
+ if (memcmp (ehdr->e_ident, dso->ehdr.e_ident, sizeof (ehdr->e_ident))
+ || ehdr->e_type != dso->ehdr.e_type
+ || ehdr->e_machine != dso->ehdr.e_machine
+ || ehdr->e_version != dso->ehdr.e_version
+ || ehdr->e_flags != dso->ehdr.e_flags
+ || ehdr->e_ehsize != dso->ehdr.e_ehsize
+ || ehdr->e_phentsize != dso->ehdr.e_phentsize
+ || ehdr->e_shentsize != dso->ehdr.e_shentsize)
+ {
+ error (0, 0, "%s: ELF headers changed since prelinking",
+ dso->filename);
+ return 1;
+ }
+
+ if (ehdr->e_phnum > dso->ehdr.e_phnum)
+ {
+ error (0, 0, "%s: Number of program headers is less than before prelinking",
+ dso->filename);
+ return 1;
+ }
+
+ if (d->d_size != (src.d_size
+ + gelf_fsize (dso->elf, ELF_T_PHDR, ehdr->e_phnum,
+ EV_CURRENT)
+ + gelf_fsize (dso->elf, ELF_T_SHDR, ehdr->e_shnum - 1,
+ EV_CURRENT)))
+ {
+ error (0, 0, "%s: Incorrect size of .gnu.prelink_undo section",
+ dso->filename);
+ return 1;
+ }
+
+ src.d_type = ELF_T_PHDR;
+ src.d_buf += src.d_size;
+ src.d_size = gelf_fsize (dso->elf, ELF_T_PHDR, ehdr->e_phnum, EV_CURRENT);
+ dst = src;
+ switch (gelf_getclass (dso->elf))
+ {
+ case ELFCLASS32:
+ dst.d_buf = alloca (dst.d_size);
+ break;
+ case ELFCLASS64:
+ dst.d_buf = phdr;
+ break;
+ }
+ if (gelf_xlatetom (dso->elf, &dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL)
+ {
+ error (0, 0, "%s: Could not read .gnu.prelink_undo section",
+ dso->filename);
+ return 1;
+ }
+
+ if (gelf_getclass (dso->elf) == ELFCLASS32)
+ {
+ Elf32_Phdr *phdr32 = (Elf32_Phdr *) dst.d_buf;
+
+ for (i = 0; i < ehdr->e_phnum; ++i)
+ {
+#define COPY(name) phdr[i].name = phdr32[i].name
+ COPY(p_type);
+ COPY(p_flags);
+ COPY(p_offset);
+ COPY(p_vaddr);
+ COPY(p_paddr);
+ COPY(p_filesz);
+ COPY(p_memsz);
+ COPY(p_align);
+#undef COPY
+ }
+ }
+
+ memset (shdr, 0, sizeof (GElf_Shdr));
+ src.d_type = ELF_T_SHDR;
+ src.d_buf += src.d_size;
+ src.d_size = gelf_fsize (dso->elf, ELF_T_SHDR, ehdr->e_shnum - 1, EV_CURRENT);
+ dst = src;
+ switch (gelf_getclass (dso->elf))
+ {
+ case ELFCLASS32:
+ dst.d_buf = alloca (dst.d_size);
+ break;
+ case ELFCLASS64:
+ dst.d_buf = shdr + 1;
+ break;
+ default:
+ return 1;
+ }
+ if (gelf_xlatetom (dso->elf, &dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL)
+ {
+ error (0, 0, "%s: Could not read .gnu.prelink_undo section",
+ dso->filename);
+ return 1;
+ }
+
+ if (gelf_getclass (dso->elf) == ELFCLASS32)
+ {
+ Elf32_Shdr *shdr32 = (Elf32_Shdr *) dst.d_buf;
+
+ for (i = 1; i < ehdr->e_shnum; ++i)
+ {
+#define COPY(name) shdr[i].name = shdr32[i - 1].name
+ COPY (sh_name);
+ COPY (sh_type);
+ COPY (sh_flags);
+ COPY (sh_addr);
+ COPY (sh_offset);
+ COPY (sh_size);
+ COPY (sh_link);
+ COPY (sh_info);
+ COPY (sh_addralign);
+ COPY (sh_entsize);
+#undef COPY
+ }
+ }
+
+ move->new_shnum = ehdr->e_shnum;
+ for (i = 1; i < move->old_shnum; ++i)
+ move->old_to_new[i] = -1;
+ for (i = 1; i < move->new_shnum; ++i)
+ move->new_to_old[i] = -1;
+
+ for (i = 1; i < move->old_shnum; ++i)
+ {
+ for (j = 1; j < move->new_shnum; ++j)
+ if (dso->shdr[i].sh_name == shdr[j].sh_name
+ && dso->shdr[i].sh_type == shdr[j].sh_type
+ && dso->shdr[i].sh_flags == shdr[j].sh_flags
+ && dso->shdr[i].sh_addralign == shdr[j].sh_addralign
+ && dso->shdr[i].sh_entsize == shdr[j].sh_entsize
+ && dso->shdr[i].sh_size == shdr[j].sh_size
+ && move->new_to_old[j] == -1)
+ break;
+
+ if (j == move->new_shnum)
+ continue;
+
+ move->old_to_new[i] = j;
+ move->new_to_old[j] = i;
+ }
+
+ for (i = 1; i < move->old_shnum; ++i)
+ if (move->old_to_new[i] == -1)
+ {
+ const char *name = strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name);
+
+ if (! strcmp (name, ".gnu.prelink_undo")
+ || ! strcmp (name, ".gnu.conflict")
+ || ! strcmp (name, ".gnu.liblist")
+ || ! strcmp (name, ".gnu.libstr")
+ || ((! strcmp (name, ".dynbss") || ! strcmp (name, ".sdynbss"))
+ && dso->ehdr.e_type == ET_EXEC))
+ continue;
+
+ if ((! strcmp (name, ".dynstr") && dso->ehdr.e_type == ET_EXEC)
+ || i == dso->ehdr.e_shstrndx)
+ {
+ for (j = 1; j < move->new_shnum; ++j)
+ if (dso->shdr[i].sh_name == shdr[j].sh_name
+ && dso->shdr[i].sh_type == shdr[j].sh_type
+ && dso->shdr[i].sh_flags == shdr[j].sh_flags
+ && dso->shdr[i].sh_addralign == shdr[j].sh_addralign
+ && dso->shdr[i].sh_entsize == shdr[j].sh_entsize
+ && dso->shdr[i].sh_size > shdr[j].sh_size
+ && move->new_to_old[j] == -1)
+ break;
+
+ if (j < move->new_shnum)
+ {
+ move->old_to_new[i] = j;
+ move->new_to_old[j] = i;
+ continue;
+ }
+ }
+
+ if (((i >= rinfo->first && i <= rinfo->last) || i == rinfo->plt)
+ && dso->shdr[i].sh_type == SHT_RELA)
+ {
+ for (j = 1; j < move->new_shnum; ++j)
+ if (dso->shdr[i].sh_name == shdr[j].sh_name
+ && shdr[j].sh_type == SHT_REL
+ && dso->shdr[i].sh_flags == shdr[j].sh_flags
+ && dso->shdr[i].sh_addralign == shdr[j].sh_addralign
+ && 2 * dso->shdr[i].sh_entsize == 3 * shdr[j].sh_entsize
+ && 2 * dso->shdr[i].sh_size == 3 * shdr[j].sh_size
+ && move->new_to_old[j] == -1)
+ break;
+
+ if (j < move->new_shnum)
+ {
+ move->old_to_new[i] = j;
+ move->new_to_old[j] = i;
+ continue;
+ }
+ }
+
+ if (! strcmp (name, ".bss")
+ || ! strcmp (name, ".sbss")
+ || ((! strcmp (name, ".plt") || ! strcmp (name, ".iplt"))
+ && dso->shdr[i].sh_type == SHT_PROGBITS))
+ {
+ int is_plt = ! strcmp (name, ".plt");
+
+ for (j = 1; j < move->new_shnum; ++j)
+ if (dso->shdr[i].sh_name == shdr[j].sh_name
+ && dso->shdr[i].sh_flags == shdr[j].sh_flags
+ && dso->shdr[i].sh_addralign == shdr[j].sh_addralign
+ && (is_plt || dso->shdr[i].sh_entsize == shdr[j].sh_entsize)
+ && move->new_to_old[j] == -1)
+ {
+ if (is_plt)
+ {
+ if (dso->shdr[i].sh_size != shdr[j].sh_size)
+ continue;
+ if (shdr[j].sh_type == SHT_NOBITS
+ && dso->shdr[i].sh_entsize == shdr[j].sh_entsize)
+ break;
+ /* On Alpha prelink fixes bogus sh_entsize of .plt
+ sections. */
+ if (shdr[j].sh_type == SHT_PROGBITS)
+ break;
+ }
+ else
+ {
+ const char *pname;
+
+ if (dso->shdr[i].sh_type != shdr[j].sh_type
+ && (dso->shdr[i].sh_type != SHT_PROGBITS
+ || shdr[j].sh_type != SHT_NOBITS))
+ continue;
+
+ if (dso->shdr[i].sh_size == shdr[j].sh_size)
+ break;
+
+ pname = strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i - 1].sh_name);
+ if (strcmp (pname, ".dynbss")
+ && strcmp (pname, ".sdynbss"))
+ continue;
+
+ if (dso->shdr[i].sh_size + dso->shdr[i - 1].sh_size
+ == shdr[j].sh_size)
+ break;
+ }
+ }
+
+ if (j < move->new_shnum)
+ {
+ move->old_to_new[i] = j;
+ move->new_to_old[j] = i;
+ continue;
+ }
+ }
+
+ error (0, 0, "%s: Section %s created after prelinking",
+ dso->filename, name);
+ return 1;
+ }
+
+ for (i = 1; i < move->new_shnum; ++i)
+ if (move->new_to_old[i] == -1)
+ {
+ const char *name = strptr (dso, dso->ehdr.e_shstrndx, shdr[i].sh_name);
+
+ error (0, 0, "%s: Section %s removed after prelinking", dso->filename,
+ name);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+prelink_undo (DSO *dso)
+{
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr[dso->ehdr.e_shnum + 20], old_shdr[dso->ehdr.e_shnum];
+ GElf_Phdr phdr[dso->ehdr.e_phnum];
+ Elf_Scn *scn;
+ Elf_Data *d;
+ int undo, i;
+ struct section_move *move;
+ struct reloc_info rinfo;
+
+ for (undo = 1; undo < dso->ehdr.e_shnum; ++undo)
+ if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[undo].sh_name),
+ ".gnu.prelink_undo"))
+ break;
+
+ if (undo == dso->ehdr.e_shnum)
+ {
+ if (undo_output)
+ return 0;
+ error (0, 0, "%s does not have .gnu.prelink_undo section", dso->filename);
+ return 1;
+ }
+
+ memcpy (old_shdr, dso->shdr, sizeof (GElf_Shdr) * dso->ehdr.e_shnum);
+ move = init_section_move (dso);
+ if (move == NULL)
+ return 1;
+
+ if (find_reloc_sections (dso, &rinfo))
+ goto error_out;
+
+ if (undo_sections (dso, undo, move, &rinfo, &ehdr, phdr, shdr))
+ goto error_out;
+
+ if (reopen_dso (dso, move, (undo_output && strcmp (undo_output, "-") == 0)
+ ? "/tmp/undo" : undo_output))
+ goto error_out;
+
+ if (find_reloc_sections (dso, &rinfo))
+ goto error_out;
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ {
+ if (! (dso->shdr[i].sh_flags & SHF_ALLOC))
+ continue;
+ if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name),
+ ".gnu.conflict"))
+ continue;
+ switch (dso->shdr[i].sh_type)
+ {
+ case SHT_REL:
+ if (undo_prelink_rel (dso, i))
+ goto error_out;
+ break;
+ case SHT_RELA:
+ if (undo_prelink_rela (dso, i))
+ goto error_out;
+ break;
+ }
+ }
+
+ if (dso->arch->arch_undo_prelink && dso->arch->arch_undo_prelink (dso))
+ goto error_out;
+
+ if (dso->ehdr.e_type == ET_DYN)
+ {
+ GElf_Addr adjust = 0, diff;
+
+ for (i = dso->ehdr.e_shnum - 1; i > 0; --i)
+ if (shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR))
+ {
+ adjust = shdr[i].sh_addr - dso->shdr[i].sh_addr;
+ break;
+ }
+ while (i > 0)
+ {
+ int nsec = 1, j;
+ /* Change here PROGBITS .plt into NOBITS if needed. */
+
+ /* Convert RELA to REL if needed. */
+ if (dso->shdr[i].sh_type == SHT_RELA && shdr[i].sh_type == SHT_REL)
+ {
+ assert (dso->arch->rela_to_rel != NULL);
+ if (i == rinfo.plt)
+ {
+ if (convert_rela_to_rel (dso, i))
+ goto error_out;
+ dso->shdr[i].sh_size = shdr[i].sh_size;
+ }
+ else if (i == rinfo.last)
+ {
+ GElf_Addr start = dso->shdr[rinfo.first].sh_addr;
+
+ for (j = rinfo.first; j <= rinfo.last; ++j)
+ {
+ if (convert_rela_to_rel (dso, j))
+ goto error_out;
+ dso->shdr[j].sh_addr = start;
+ dso->shdr[j].sh_size = shdr[j].sh_size;
+ start += dso->shdr[j].sh_size;
+ }
+ nsec = rinfo.last - rinfo.first + 1;
+ i = rinfo.first;
+ }
+ else
+ {
+ error (0, 0, "%s: Cannot convert RELA to REL", dso->filename);
+ goto error_out;
+ }
+ }
+ diff = shdr[i].sh_addr - dso->shdr[i].sh_addr;
+ if (diff != adjust)
+ {
+ assert (diff >= adjust);
+ if (adjust_dso (dso, dso->shdr[i + nsec].sh_addr, adjust - diff))
+ goto error_out;
+ adjust = diff;
+ }
+ --i;
+ }
+ if (adjust && adjust_dso (dso, 0, adjust))
+ goto error_out;
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ if (shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR))
+ assert (shdr[i].sh_addr == dso->shdr[i].sh_addr);
+ }
+ else
+ {
+ /* Executable. */
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ {
+ const char *name = strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name);
+
+ if (dso->shdr[i].sh_type == SHT_PROGBITS
+ && shdr[i].sh_type == SHT_NOBITS)
+ {
+ assert (strcmp (name, ".bss") == 0
+ || strcmp (name, ".sbss") == 0
+ || strcmp (name, ".plt") == 0
+ || strcmp (name, ".iplt") == 0);
+ scn = dso->scn[i];
+ d = elf_getdata (scn, NULL);
+ assert (d != NULL && elf_getdata (scn, d) == NULL);
+ assert (d->d_size == 0 || d->d_buf != NULL);
+ assert (d->d_size == dso->shdr[i].sh_size);
+ free (d->d_buf);
+ d->d_buf = NULL;
+ dso->shdr[i].sh_type = SHT_NOBITS;
+ }
+ else if (dso->shdr[i].sh_type == SHT_RELA
+ && shdr[i].sh_type == SHT_REL)
+ {
+ if (convert_rela_to_rel (dso, i))
+ goto error_out;
+ dso->shdr[i].sh_size = shdr[i].sh_size;
+ }
+ else
+ assert (dso->shdr[i].sh_type == shdr[i].sh_type);
+ if (dso->shdr[i].sh_size != shdr[i].sh_size)
+ {
+ /* This is handled in code below for both ET_DYN and ET_EXEC. */
+ if (i == dso->ehdr.e_shstrndx)
+ continue;
+ assert (shdr[i].sh_type == SHT_NOBITS
+ || shdr[i].sh_size < dso->shdr[i].sh_size);
+ assert (strcmp (name, ".dynstr") == 0
+ || strcmp (name, ".bss") == 0
+ || strcmp (name, ".sbss") == 0);
+ scn = dso->scn[i];
+ d = elf_getdata (scn, NULL);
+ assert (d != NULL && elf_getdata (scn, d) == NULL);
+ d->d_size = shdr[i].sh_size;
+ }
+ }
+
+ if (update_dynamic_tags (dso, shdr, old_shdr, move))
+ goto error_out;
+
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ if (shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR))
+ dso->shdr[i].sh_addr = shdr[i].sh_addr;
+ }
+
+ /* Clear .dynamic entries added by prelink, update others. */
+ if (remove_dynamic_prelink_tags (dso)
+ || update_dynamic_rel (dso, &rinfo))
+ goto error_out;
+
+ /* Shrink .shstrtab. */
+ i = dso->ehdr.e_shstrndx;
+ if (shdr[i].sh_size < dso->shdr[i].sh_size)
+ {
+ scn = dso->scn[i];
+ d = elf_getdata (scn, NULL);
+ assert (d != NULL && elf_getdata (scn, d) == NULL);
+ assert (d->d_size == dso->shdr[i].sh_size);
+ d->d_size = shdr[i].sh_size;
+ }
+
+ /* Now restore the rest. */
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ dso->shdr[i] = shdr[i];
+ if (dso->ehdr.e_phnum != ehdr.e_phnum)
+ {
+ assert (ehdr.e_phnum < dso->ehdr.e_phnum);
+ if (gelf_newphdr (dso->elf, ehdr.e_phnum) == 0)
+ {
+ error (0, 0, "Could not create new ELF headers");
+ goto error_out;
+ }
+ }
+ for (i = 0; i < ehdr.e_phnum; ++i)
+ dso->phdr[i] = phdr[i];
+ dso->permissive = 1;
+ assert (dso->ehdr.e_entry == ehdr.e_entry);
+ assert (dso->ehdr.e_shnum == ehdr.e_shnum);
+ assert (dso->ehdr.e_shstrndx == ehdr.e_shstrndx);
+ dso->ehdr.e_phoff = ehdr.e_phoff;
+ dso->ehdr.e_shoff = ehdr.e_shoff;
+ dso->ehdr.e_phnum = ehdr.e_phnum;
+ free (move);
+ return 0;
+
+error_out:
+ free (move);
+ return 1;
+}