summaryrefslogtreecommitdiffstats
path: root/trunk/src/dso-readonly.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/dso-readonly.c')
-rw-r--r--trunk/src/dso-readonly.c1823
1 files changed, 1823 insertions, 0 deletions
diff --git a/trunk/src/dso-readonly.c b/trunk/src/dso-readonly.c
new file mode 100644
index 0000000..8ad5865
--- /dev/null
+++ b/trunk/src/dso-readonly.c
@@ -0,0 +1,1823 @@
+/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 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 <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+#include "prelink.h"
+
+#if defined HAVE_LIBSELINUX && defined HAVE_SELINUX_SELINUX_H
+#include <selinux/selinux.h>
+#define USE_SELINUX
+#endif
+
+#define RELOCATE_SCN(shf) \
+ ((shf) & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR))
+
+#ifndef ELF_F_PERMISSIVE
+# define ELF_F_PERMISSIVE 0
+#endif
+
+void
+read_dynamic (DSO *dso)
+{
+ int i;
+
+ memset (dso->info, 0, sizeof(dso->info));
+ dso->info_set_mask = 0;
+ for (i = 0; i < dso->ehdr.e_shnum; i++)
+ if (dso->shdr[i].sh_type == SHT_DYNAMIC)
+ {
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[i];
+ GElf_Dyn dyn;
+
+ dso->dynamic = i;
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ int ndx, maxndx;
+
+ maxndx = data->d_size / dso->shdr[i].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ++ndx)
+ {
+ gelfx_getdyn (dso->elf, data, ndx, &dyn);
+ if (dyn.d_tag == DT_NULL)
+ break;
+ else if ((GElf_Xword) dyn.d_tag < DT_NUM)
+ {
+ dso->info[dyn.d_tag] = dyn.d_un.d_val;
+ if (dyn.d_tag < 50)
+ dso->info_set_mask |= (1ULL << dyn.d_tag);
+ }
+ else if (dyn.d_tag == DT_CHECKSUM)
+ {
+ dso->info_DT_CHECKSUM = dyn.d_un.d_val;
+ dso->info_set_mask |= (1ULL << DT_CHECKSUM_BIT);
+ }
+ else if (dyn.d_tag == DT_GNU_PRELINKED)
+ {
+ dso->info_DT_GNU_PRELINKED = dyn.d_un.d_val;
+ dso->info_set_mask |= (1ULL << DT_GNU_PRELINKED_BIT);
+ }
+ else if (dyn.d_tag == DT_VERDEF)
+ {
+ dso->info_DT_VERDEF = dyn.d_un.d_val;
+ dso->info_set_mask |= (1ULL << DT_VERDEF_BIT);
+ }
+ else if (dyn.d_tag == DT_VERNEED)
+ {
+ dso->info_DT_VERNEED = dyn.d_un.d_val;
+ dso->info_set_mask |= (1ULL << DT_VERNEED_BIT);
+ }
+ else if (dyn.d_tag == DT_VERSYM)
+ {
+ dso->info_DT_VERSYM = dyn.d_un.d_val;
+ dso->info_set_mask |= (1ULL << DT_VERSYM_BIT);
+ }
+ else if (dyn.d_tag == DT_FILTER)
+ dso->info_set_mask |= (1ULL << DT_FILTER_BIT);
+ else if (dyn.d_tag == DT_AUXILIARY)
+ dso->info_set_mask |= (1ULL << DT_AUXILIARY_BIT);
+ else if (dyn.d_tag == DT_LOPROC)
+ dso->info_set_mask |= (1ULL << DT_LOPROC_BIT);
+ else if (dyn.d_tag == DT_GNU_HASH)
+ {
+ dso->info_DT_GNU_HASH = dyn.d_un.d_val;
+ dso->info_set_mask |= (1ULL << DT_GNU_HASH_BIT);
+ }
+ if (dso->ehdr.e_machine == EM_MIPS)
+ {
+ if (dyn.d_tag == DT_MIPS_LOCAL_GOTNO)
+ dso->info_DT_MIPS_LOCAL_GOTNO = dyn.d_un.d_val;
+ else if (dyn.d_tag == DT_MIPS_GOTSYM)
+ dso->info_DT_MIPS_GOTSYM = dyn.d_un.d_val;
+ else if (dyn.d_tag == DT_MIPS_SYMTABNO)
+ dso->info_DT_MIPS_SYMTABNO = dyn.d_un.d_val;
+ }
+ }
+ if (ndx < maxndx)
+ break;
+ }
+ }
+}
+
+int
+set_dynamic (DSO *dso, GElf_Word tag, GElf_Addr value, int fatal)
+{
+ Elf_Data *data;
+ Elf_Scn *scn;
+ GElf_Dyn dyn;
+ int ndx, maxndx;
+ uint64_t mask = dso->info_set_mask;
+
+ 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);
+
+ switch (tag)
+ {
+ case DT_CHECKSUM:
+ mask |= (1ULL << DT_CHECKSUM_BIT); break;
+ case DT_GNU_PRELINKED:
+ mask |= (1ULL << DT_GNU_PRELINKED_BIT); break;
+ case DT_VERDEF:
+ mask |= (1ULL << DT_VERDEF_BIT); break;
+ case DT_VERNEED:
+ mask |= (1ULL << DT_VERNEED_BIT); break;
+ case DT_VERSYM:
+ mask |= (1ULL << DT_VERSYM_BIT); break;
+ default:
+ if (tag < DT_NUM && tag < 50)
+ mask |= (1ULL << tag);
+ break;
+ }
+
+ maxndx = data->d_size / dso->shdr[dso->dynamic].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ndx++)
+ {
+ gelfx_getdyn (dso->elf, data, ndx, &dyn);
+ if (dyn.d_tag == DT_NULL)
+ break;
+ else if (dyn.d_tag == tag)
+ {
+ if (dyn.d_un.d_ptr != value)
+ {
+ dyn.d_un.d_ptr = value;
+ gelfx_update_dyn (dso->elf, data, ndx, &dyn);
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ }
+
+ return 0;
+ }
+ }
+ assert (ndx < maxndx);
+
+ if (ndx + 1 < maxndx)
+ {
+ /* DT_NULL is not the last dynamic entry. */
+ gelfx_update_dyn (dso->elf, data, ndx + 1, &dyn);
+ dyn.d_tag = tag;
+ dyn.d_un.d_ptr = value;
+ gelfx_update_dyn (dso->elf, data, ndx, &dyn);
+ dso->info_set_mask = mask;
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ return 0;
+ }
+
+ if (fatal)
+ error (0, 0, "%s: Not enough room to add .dynamic entry",
+ dso->filename);
+ return 1;
+}
+
+int
+check_dso (DSO *dso)
+{
+ int i, last = 1;
+
+ /* FIXME: Several routines in prelink and in libelf-0.7.0 too
+ rely on sh_offset's monotonically increasing. */
+ for (i = 2; i < dso->ehdr.e_shnum; ++i)
+ {
+ if (dso->shdr[last].sh_offset
+ + (dso->shdr[last].sh_type == SHT_NOBITS
+ ? 0 : dso->shdr[last].sh_size) > dso->shdr[i].sh_offset)
+ {
+ if (!dso->permissive
+ || RELOCATE_SCN (dso->shdr[last].sh_flags)
+ || RELOCATE_SCN (dso->shdr[i].sh_flags))
+ {
+ error (0, 0, "%s: section file offsets not monotonically increasing",
+ dso->filename);
+ return 1;
+ }
+ }
+ if (!dso->permissive
+ || (dso->shdr[i].sh_type != SHT_NOBITS && dso->shdr[i].sh_size != 0))
+ last = i;
+ }
+ return 0;
+}
+
+DSO *
+open_dso (const char *name)
+{
+ int fd;
+
+ fd = wrap_open (name, O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, "cannot open \"%s\"", name);
+ return NULL;
+ }
+ return fdopen_dso (fd, name);
+}
+
+/* WARNING: If prelink is ever multi-threaded, this will not work
+ Other alternatives are:
+ 1) make section_cmp nested function - trampolines
+ vs. non-exec stack needs to be resolved for it though
+ 2) make the variable __thread
+ 3) use locking around the qsort
+ */
+static DSO *section_cmp_dso;
+
+static int
+section_cmp (const void *A, const void *B)
+{
+ int *a = (int *) A;
+ int *b = (int *) B;
+ DSO *dso = section_cmp_dso;
+
+ if (dso->shdr[*a].sh_offset < dso->shdr[*b].sh_offset)
+ return -1;
+ if (dso->shdr[*a].sh_offset > dso->shdr[*b].sh_offset)
+ return 1;
+ if (*a < *b)
+ return -1;
+ return *a > *b;
+}
+
+DSO *
+fdopen_dso (int fd, const char *name)
+{
+ Elf *elf = NULL;
+ GElf_Ehdr ehdr;
+ GElf_Addr last_off;
+ int i, j, k, last, *sections, *invsections;
+ DSO *dso = NULL;
+#if 0
+ struct PLArch *plarch;
+ extern struct PLArch __start_pl_arch[], __stop_pl_arch[];
+#endif
+
+ elf = elf_begin (fd, ELF_C_READ, NULL);
+ if (elf == NULL)
+ {
+ error (0, 0, "cannot open ELF file: %s (dso_readonly)", elf_errmsg (-1));
+ goto error_out;
+ }
+
+ if (elf_kind (elf) != ELF_K_ELF)
+ {
+ error (0, 0, "\"%s\" is not an ELF file", name);
+ goto error_out;
+ }
+
+ if (gelf_getehdr (elf, &ehdr) == NULL)
+ {
+ error (0, 0, "cannot get the ELF header: %s",
+ elf_errmsg (-1));
+ goto error_out;
+ }
+
+ if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC)
+ {
+ error (0, 0, "\"%s\" is not a shared library", name);
+ goto error_out;
+ }
+
+ if (ehdr.e_shnum == 0)
+ {
+ GElf_Phdr phdr;
+
+ /* Check for UPX compressed executables. */
+ if (ehdr.e_type == ET_EXEC
+ && ehdr.e_phnum > 0
+ && (gelf_getphdr (elf, 0, &phdr), phdr.p_type == PT_LOAD)
+ && phdr.p_filesz >= 256
+ && phdr.p_filesz <= 4096
+ && phdr.p_offset == 0
+ && ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize < phdr.p_filesz)
+ {
+ char *buf = alloca (phdr.p_filesz);
+ size_t start = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
+
+ if (pread (fd, buf, phdr.p_filesz, 0) == phdr.p_filesz
+ && memmem (buf + start, phdr.p_filesz - start,
+ "UPX!", 4) != NULL)
+ {
+ error (0, 0, "\"%s\" is UPX compressed executable", name);
+ goto error_out;
+ }
+ }
+ error (0, 0, "\"%s\" has no section headers", name);
+ goto error_out;
+ }
+
+ /* Allocate DSO structure. Leave place for additional 20 new section
+ headers. */
+ dso = (DSO *)
+ malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr)
+ + (ehdr.e_phnum + 1) * sizeof(GElf_Phdr)
+ + (ehdr.e_shnum + 20) * sizeof(Elf_Scn *));
+ if (!dso)
+ {
+ error (0, ENOMEM, "Could not open DSO");
+ goto error_out;
+ }
+
+ elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE);
+
+ memset (dso, 0, sizeof(DSO));
+ dso->elf = elf;
+ dso->ehdr = ehdr;
+ dso->phdr = (GElf_Phdr *) &dso->shdr[ehdr.e_shnum + 20];
+ dso->scn = (Elf_Scn **) &dso->phdr[ehdr.e_phnum + 1];
+ switch (ehdr.e_ident[EI_CLASS])
+ {
+ case ELFCLASS32:
+ dso->mask = 0xffffffff; break;
+ case ELFCLASS64:
+ dso->mask = 0xffffffffffffffffULL; break;
+ }
+ for (i = 0; i < ehdr.e_phnum; ++i)
+ gelf_getphdr (elf, i, dso->phdr + i);
+ dso->fd = fd;
+
+ for (i = 0, j = 0; i < ehdr.e_shnum; ++i)
+ {
+ dso->scn[i] = elf_getscn (elf, i);
+ gelfx_getshdr (elf, dso->scn[i], dso->shdr + i);
+ if ((dso->shdr[i].sh_flags & SHF_ALLOC) && dso->shdr[i].sh_type != SHT_NOBITS)
+ j = 1;
+ }
+ if (j == 0)
+ {
+ /* If all ALLOC sections are SHT_NOBITS, then this is a
+ stripped-to-file debuginfo. Skip it silently. */
+ goto error_out;
+ }
+
+ sections = (int *) alloca (dso->ehdr.e_shnum * sizeof (int) * 2);
+ sections[0] = 0;
+ for (i = 1, j = 1, k = dso->ehdr.e_shnum, last = -1;
+ i < dso->ehdr.e_shnum; ++i)
+ if (RELOCATE_SCN (dso->shdr[i].sh_flags))
+ {
+ last = i;
+ sections[j++] = i;
+ }
+ else
+ sections[--k] = i;
+ assert (j == k);
+
+ section_cmp_dso = dso;
+ qsort (sections + k, dso->ehdr.e_shnum - k, sizeof (*sections), section_cmp);
+ invsections = sections + dso->ehdr.e_shnum;
+ invsections[0] = 0;
+ for (i = 1, j = 0; i < ehdr.e_shnum; ++i)
+ {
+ if (i != sections[i])
+ {
+ j = 1;
+ dso->scn[i] = elf_getscn (elf, sections[i]);
+ gelfx_getshdr (elf, dso->scn[i], dso->shdr + i);
+ }
+ invsections[sections[i]] = i;
+ }
+
+#if 0
+ if (j)
+ {
+ dso->move = init_section_move (dso);
+ if (dso->move == NULL)
+ goto error_out;
+ memcpy (dso->move->old_to_new, invsections, dso->ehdr.e_shnum * sizeof (int));
+ memcpy (dso->move->new_to_old, sections, dso->ehdr.e_shnum * sizeof (int));
+ }
+#endif
+
+ last_off = 0;
+ for (i = 1; i < ehdr.e_shnum; ++i)
+ {
+ if (dso->shdr[i].sh_link >= ehdr.e_shnum)
+ {
+ error (0, 0, "%s: bogus sh_link value %d", name,
+ dso->shdr[i].sh_link);
+ goto error_out;
+ }
+ dso->shdr[i].sh_link = invsections[dso->shdr[i].sh_link];
+ if (dso->shdr[i].sh_type == SHT_REL
+ || dso->shdr[i].sh_type == SHT_RELA
+ || (dso->shdr[i].sh_flags & SHF_INFO_LINK))
+ {
+ if (dso->shdr[i].sh_info >= ehdr.e_shnum)
+ {
+ error (0, 0, "%s: bogus sh_info value %d", name,
+ dso->shdr[i].sh_info);
+ goto error_out;
+ }
+ dso->shdr[i].sh_info = invsections[dso->shdr[i].sh_info];
+ }
+
+ /* Some linkers mess up sh_offset fields for empty or nobits
+ sections. */
+ if (RELOCATE_SCN (dso->shdr[i].sh_flags)
+ && (dso->shdr[i].sh_size == 0
+ || dso->shdr[i].sh_type == SHT_NOBITS))
+ {
+ for (j = i + 1; j < ehdr.e_shnum; ++j)
+ if (! RELOCATE_SCN (dso->shdr[j].sh_flags))
+ break;
+ else if (dso->shdr[j].sh_size != 0
+ && dso->shdr[j].sh_type != SHT_NOBITS)
+ break;
+ dso->shdr[i].sh_offset = (last_off + dso->shdr[i].sh_addralign - 1)
+ & ~(dso->shdr[i].sh_addralign - 1);
+ if (j < ehdr.e_shnum
+ && dso->shdr[i].sh_offset > dso->shdr[j].sh_offset)
+ {
+ GElf_Addr k;
+
+ for (k = dso->shdr[i].sh_addralign - 1; k; )
+ {
+ k >>= 1;
+ dso->shdr[i].sh_offset = (last_off + k) & ~k;
+ if (dso->shdr[i].sh_offset <= dso->shdr[j].sh_offset)
+ break;
+ }
+ }
+ last_off = dso->shdr[i].sh_offset;
+ }
+ else
+ last_off = dso->shdr[i].sh_offset + dso->shdr[i].sh_size;
+ }
+ dso->ehdr.e_shstrndx = invsections[dso->ehdr.e_shstrndx];
+
+#if 0
+ for (plarch = __start_pl_arch; plarch < __stop_pl_arch; plarch++)
+ if (plarch->class == ehdr.e_ident[EI_CLASS]
+ && (plarch->machine == ehdr.e_machine
+ || plarch->alternate_machine[0] == ehdr.e_machine
+ || plarch->alternate_machine[1] == ehdr.e_machine
+ || plarch->alternate_machine[2] == ehdr.e_machine))
+ break;
+
+ if (plarch == __stop_pl_arch || ehdr.e_machine == EM_NONE)
+ {
+ error (0, 0, "\"%s\"'s architecture is not supported", name);
+ goto error_out;
+ }
+
+ dso->arch = plarch;
+#else
+ dso->arch = NULL;
+#endif
+
+ dso->base = ~(GElf_Addr) 0;
+ dso->align = 0;
+ dso->end = 0;
+ for (i = 0; i < dso->ehdr.e_phnum; i++)
+ if (dso->phdr[i].p_type == PT_LOAD)
+ {
+ GElf_Addr base, end;
+
+ if (dso->phdr[i].p_align > dso->align)
+ dso->align = dso->phdr[i].p_align;
+ base = dso->phdr[i].p_vaddr & ~(dso->phdr[i].p_align - 1);
+ end = dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz;
+ if (base < dso->base)
+ dso->base = base;
+ if (end > dso->end)
+ dso->end = end;
+ }
+
+ if (dso->base == ~(GElf_Addr) 0)
+ {
+ error (0, 0, "%s: cannot find loadable segment", name);
+ goto error_out;
+ }
+
+ read_dynamic (dso);
+
+ dso->filename = (const char *) strdup (name);
+ dso->soname = dso->filename;
+ if (dso->info[DT_STRTAB] && dso->info[DT_SONAME])
+ {
+ const char *soname;
+
+ soname = get_data (dso, dso->info[DT_STRTAB] + dso->info[DT_SONAME],
+ NULL, NULL);
+ if (soname && soname[0] != '\0')
+ dso->soname = (const char *) strdup (soname);
+ }
+
+#if 0
+ if (dso->arch->machine == EM_ALPHA
+ || dso->arch->machine == EM_MIPS)
+ for (i = 1; i < ehdr.e_shnum; ++i)
+ {
+ if ((dso->shdr[i].sh_type == SHT_ALPHA_DEBUG
+ && dso->arch->machine == EM_ALPHA)
+ || (dso->shdr[i].sh_type == SHT_MIPS_DEBUG
+ && dso->arch->machine == EM_MIPS))
+ {
+ const char *name
+ = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name);
+ if (! strcmp (name, ".mdebug"))
+ dso->mdebug_orig_offset = dso->shdr[i].sh_offset;
+ break;
+ }
+ }
+#endif
+
+ return dso;
+
+error_out:
+ if (dso)
+ {
+ free (dso->move);
+ if (dso->soname != dso->filename)
+ free ((char *) dso->soname);
+ free ((char *) dso->filename);
+ free (dso);
+ }
+ if (elf)
+ elf_end (elf);
+ if (fd != -1)
+ close (fd);
+ return NULL;
+}
+
+#if 0
+
+static int
+adjust_symtab_section_indices (DSO *dso, int n, int old_shnum, int *old_to_new)
+{
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[n];
+ GElf_Sym sym;
+ int changed = 0, ndx, maxndx;
+
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ maxndx = data->d_size / dso->shdr[n].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ++ndx)
+ {
+ gelfx_getsym (dso->elf, data, ndx, &sym);
+ if (sym.st_shndx > SHN_UNDEF && sym.st_shndx < SHN_LORESERVE)
+ {
+ if (sym.st_shndx >= old_shnum
+ || old_to_new[sym.st_shndx] == -1)
+ {
+ if (! sym.st_size &&
+ sym.st_info == ELF32_ST_INFO (STB_LOCAL, STT_SECTION))
+ {
+ sym.st_value = 0;
+ sym.st_shndx = SHN_UNDEF;
+ gelfx_update_sym (dso->elf, data, ndx, &sym);
+ changed = 1;
+ continue;
+ }
+ else
+ {
+ if (sym.st_shndx >= old_shnum)
+ {
+ error (0, 0, "%s: Symbol section index outside of section numbers",
+ dso->filename);
+ return 1;
+ }
+ error (0, 0, "%s: Section symbol points into has been removed",
+ dso->filename);
+ return 1;
+ }
+ }
+ if (old_to_new[sym.st_shndx] != sym.st_shndx)
+ {
+ changed = 1;
+ sym.st_shndx = old_to_new[sym.st_shndx];
+ gelfx_update_sym (dso->elf, data, ndx, &sym);
+ }
+ }
+ }
+ }
+
+ if (changed)
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+
+ return 0;
+}
+
+static int
+set_stt_section_values (DSO *dso, int n)
+{
+ Elf_Data *data;
+ Elf_Scn *scn = dso->scn[n];
+ GElf_Sym sym;
+ int ndx, maxndx, sec;
+ char seen[dso->ehdr.e_shnum];
+
+ memset (seen, 0, dso->ehdr.e_shnum);
+ data = elf_getdata (scn, NULL);
+ assert (data != NULL);
+ assert (elf_getdata (scn, data) == NULL);
+ assert (data->d_off == 0);
+
+ maxndx = data->d_size / dso->shdr[n].sh_entsize;
+ gelfx_getsym (dso->elf, data, 0, &sym);
+ if (sym.st_info != ELF32_ST_INFO (STB_LOCAL, STT_NOTYPE)
+ || sym.st_size != 0 || sym.st_other != 0
+ || sym.st_value != 0 || sym.st_shndx != SHN_UNDEF
+ || sym.st_name != 0)
+ return 0;
+
+ for (ndx = 1; ndx < maxndx; ++ndx)
+ {
+ gelfx_getsym (dso->elf, data, ndx, &sym);
+ if (sym.st_info == ELF32_ST_INFO (STB_LOCAL, STT_SECTION)
+ && sym.st_size == 0 && sym.st_other == 0
+ && sym.st_name == 0)
+ {
+ if (sym.st_shndx > SHN_UNDEF && sym.st_shndx < SHN_LORESERVE)
+ {
+ seen[sym.st_shndx] = 1;
+ sym.st_value = dso->shdr[sym.st_shndx].sh_addr;
+ gelfx_update_sym (dso->elf, data, ndx, &sym);
+ }
+ }
+ else
+ break;
+ }
+
+ for (ndx = 1, sec = 1; ndx < maxndx; ++ndx)
+ {
+ gelfx_getsym (dso->elf, data, ndx, &sym);
+ if (sym.st_info == ELF32_ST_INFO (STB_LOCAL, STT_SECTION)
+ && sym.st_size == 0 && sym.st_other == 0
+ && sym.st_name == 0)
+ {
+ if (sym.st_shndx == SHN_UNDEF)
+ {
+ while (sec < dso->ehdr.e_shnum && seen[sec])
+ ++sec;
+
+ if (sec >= dso->ehdr.e_shnum)
+ sym.st_value = 0;
+ else
+ sym.st_value = dso->shdr[sec].sh_addr;
+ sym.st_shndx = sec++;
+ gelfx_update_sym (dso->elf, data, ndx, &sym);
+ }
+ }
+ else
+ break;
+ }
+
+ return 0;
+}
+
+struct section_move *
+init_section_move (DSO *dso)
+{
+ struct section_move *move;
+ int i;
+
+ move = malloc (sizeof (struct section_move)
+ + (dso->ehdr.e_shnum * 2 + 20) * sizeof (int));
+ if (move == NULL)
+ {
+ error (0, ENOMEM, "%s: Could not move sections", dso->filename);
+ return move;
+ }
+ move->old_shnum = dso->ehdr.e_shnum;
+ move->new_shnum = dso->ehdr.e_shnum;
+ move->old_to_new = (int *)(move + 1);
+ move->new_to_old = move->old_to_new + move->new_shnum;
+ for (i = 0; i < move->new_shnum; i++)
+ {
+ move->old_to_new[i] = i;
+ move->new_to_old[i] = i;
+ }
+ return move;
+}
+
+void
+add_section (struct section_move *move, int sec)
+{
+ int i;
+
+ assert (move->new_shnum < move->old_shnum + 20);
+ assert (sec <= move->new_shnum);
+
+ memmove (move->new_to_old + sec + 1, move->new_to_old + sec,
+ (move->new_shnum - sec) * sizeof (int));
+ ++move->new_shnum;
+ move->new_to_old[sec] = -1;
+ for (i = 1; i < move->old_shnum; i++)
+ if (move->old_to_new[i] >= sec)
+ ++move->old_to_new[i];
+}
+
+void
+remove_section (struct section_move *move, int sec)
+{
+ int i;
+
+ assert (sec < move->new_shnum);
+
+ memmove (move->new_to_old + sec, move->new_to_old + sec + 1,
+ (move->new_shnum - sec - 1) * sizeof (int));
+ --move->new_shnum;
+ for (i = 1; i < move->old_shnum; i++)
+ if (move->old_to_new[i] == sec)
+ move->old_to_new[i] = -1;
+ else if (move->old_to_new[i] > sec)
+ --move->old_to_new[i];
+}
+
+int
+reopen_dso (DSO *dso, struct section_move *move, const char *temp_base)
+{
+ char filename[strlen (temp_base ? temp_base : dso->filename)
+ + sizeof ("/dev/shm/.#prelink#.XXXXXX")];
+ int adddel = 0;
+ int free_move = 0;
+ Elf *elf = NULL;
+ GElf_Ehdr ehdr;
+ char *e_ident;
+ int fd, i, j;
+
+ if (move == NULL)
+ {
+ move = init_section_move (dso);
+ if (move == NULL)
+ return 1;
+ free_move = 1;
+ }
+ else
+ assert (dso->ehdr.e_shnum == move->old_shnum);
+
+ if (temp_base == NULL)
+ temp_base = dso->filename;
+ sprintf (filename, "%s.#prelink#.XXXXXX", temp_base);
+
+ fd = wrap_mkstemp (filename);
+ if (fd == -1)
+ {
+ strcpy (filename, "/tmp/#prelink#.XXXXXX");
+ fd = wrap_mkstemp (filename);
+ if (fd == -1)
+ {
+ strcpy (filename, "/dev/shm/#prelink#.XXXXXX");
+ fd = wrap_mkstemp (filename);
+ }
+ if (fd == -1)
+ {
+ error (0, errno, "Could not create temporary file %s", filename);
+ goto error_out;
+ }
+ }
+
+ elf = elf_begin (fd, ELF_C_WRITE, NULL);
+ if (elf == NULL)
+ {
+ error (0, 0, "cannot open ELF file: %s (elf_begin failed, dso_readonly)", elf_errmsg (-1));
+ goto error_out;
+
+ }
+
+ /* Some gelf_newehdr implementations don't return the resulting
+ ElfNN_Ehdr, so we have to do it the hard way instead of:
+ e_ident = (char *) gelf_newehdr (elf, gelf_getclass (dso->elf)); */
+ switch (gelf_getclass (dso->elf))
+ {
+ case ELFCLASS32:
+ e_ident = (char *) elf32_newehdr (elf);
+ break;
+ case ELFCLASS64:
+ e_ident = (char *) elf64_newehdr (elf);
+ break;
+ default:
+ e_ident = NULL;
+ break;
+ }
+
+ if (e_ident == NULL
+ /* This is here just for the gelfx wrapper, so that gelf_update_ehdr
+ already has the correct ELF class. */
+ || memcpy (e_ident, dso->ehdr.e_ident, EI_NIDENT) == NULL
+ || gelf_update_ehdr (elf, &dso->ehdr) == 0
+ || gelf_newphdr (elf, dso->ehdr.e_phnum) == 0)
+ {
+ error (0, 0, "Could not create new ELF headers");
+ goto error_out;
+ }
+ ehdr = dso->ehdr;
+ elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE);
+ for (i = 0; i < ehdr.e_phnum; ++i)
+ gelf_update_phdr (elf, i, dso->phdr + i);
+
+ for (i = 1; i < move->new_shnum; ++i)
+ {
+ Elf_Scn *scn;
+ Elf_Data data, *data1, *data2;
+
+ if (move->new_to_old[i] == -1)
+ {
+ scn = elf_newscn (elf);
+ elf_newdata (scn);
+ }
+ else
+ {
+ j = move->new_to_old[i];
+ scn = elf_newscn (elf);
+ gelfx_update_shdr (elf, scn, &dso->shdr[j]);
+ if (dso->shdr[j].sh_type == SHT_NOBITS)
+ {
+ data1 = elf_getdata (dso->scn[j], NULL);
+ data2 = elf_newdata (scn);
+ memcpy (data2, data1, sizeof (*data1));
+ }
+ else
+ {
+ data.d_type = ELF_T_NUM;
+ data1 = NULL;
+ while ((data1 = elf_getdata (dso->scn[j], data1))
+ != NULL)
+ {
+ if (data.d_type == ELF_T_NUM)
+ data = *data1;
+ else if (data.d_type != data1->d_type
+ || data.d_version != data1->d_version)
+ abort ();
+ else
+ {
+ if (data1->d_off < data.d_off)
+ {
+ data.d_size += data.d_off - data1->d_off;
+ data.d_off = data1->d_off;
+ }
+ if (data1->d_off + data1->d_size
+ > data.d_off + data.d_size)
+ data.d_size = data1->d_off + data1->d_size
+ - data.d_off;
+ if (data1->d_align > data.d_align)
+ data.d_align = data1->d_align;
+ }
+ }
+ if (data.d_type == ELF_T_NUM)
+ {
+ assert (dso->shdr[j].sh_size == 0);
+ continue;
+ }
+ if (data.d_size != 0)
+ {
+ data.d_buf = calloc (1, data.d_size);
+ if (data.d_buf == NULL)
+ {
+ error (0, ENOMEM, "%s: Could not copy section",
+ dso->filename);
+ goto error_out;
+ }
+ }
+ else
+ data.d_buf = NULL;
+ data1 = NULL;
+ while ((data1 = elf_getdata (dso->scn[j], data1))
+ != NULL)
+ memcpy (data.d_buf + data1->d_off - data.d_off, data1->d_buf,
+ data1->d_size);
+ data2 = elf_newdata (scn);
+ memcpy (data2, &data, sizeof (data));
+ }
+ }
+ }
+
+ ehdr.e_shnum = move->new_shnum;
+ dso->temp_filename = strdup (filename);
+ if (dso->temp_filename == NULL)
+ {
+ error (0, ENOMEM, "%s: Could not save temporary filename", dso->filename);
+ goto error_out;
+ }
+ dso->elfro = dso->elf;
+ dso->elf = elf;
+ dso->fdro = dso->fd;
+ dso->fd = fd;
+ dso->ehdr = ehdr;
+ dso->lastscn = 0;
+ elf = NULL;
+ fd = -1;
+ for (i = 1; i < move->old_shnum; i++)
+ if (move->old_to_new[i] != i)
+ {
+ adddel = 1;
+ break;
+ }
+ if (! adddel)
+ for (i = 1; i < move->new_shnum; i++)
+ if (move->new_to_old[i] != i)
+ {
+ adddel = 1;
+ break;
+ }
+
+ for (i = 1; i < move->new_shnum; i++)
+ {
+ dso->scn[i] = elf_getscn (dso->elf, i);
+ gelfx_getshdr (dso->elf, dso->scn[i], dso->shdr + i);
+ if (move->new_to_old[i] == -1)
+ continue;
+ if (dso->move
+ && (dso->shdr[i].sh_type == SHT_SYMTAB
+ || dso->shdr[i].sh_type == SHT_DYNSYM))
+ {
+ if (adjust_symtab_section_indices (dso, i, dso->move->old_shnum,
+ dso->move->old_to_new))
+ goto error_out;
+ }
+ if (adddel)
+ {
+ if (dso->shdr[i].sh_link)
+ {
+ if (dso->shdr[i].sh_link >= move->old_shnum)
+ {
+ error (0, 0, "%s: bogus sh_link value %d", dso->filename,
+ dso->shdr[i].sh_link);
+ goto error_out;
+ }
+ if (move->old_to_new[dso->shdr[i].sh_link] == -1)
+ {
+ error (0, 0, "Section sh_link points to has been removed");
+ goto error_out;
+ }
+ dso->shdr[i].sh_link = move->old_to_new[dso->shdr[i].sh_link];
+ }
+ /* Only some section types use sh_info for section index. */
+ if (dso->shdr[i].sh_info
+ && (dso->shdr[i].sh_type == SHT_REL
+ || dso->shdr[i].sh_type == SHT_RELA
+ || (dso->shdr[i].sh_flags & SHF_INFO_LINK)))
+ {
+ if (dso->shdr[i].sh_info >= move->old_shnum)
+ {
+ error (0, 0, "%s: bogus sh_info value %d", dso->filename,
+ dso->shdr[i].sh_info);
+ goto error_out;
+ }
+ if (move->old_to_new[dso->shdr[i].sh_info] == -1)
+ {
+ error (0, 0, "Section sh_info points to has been removed");
+ goto error_out;
+ }
+ dso->shdr[i].sh_info = move->old_to_new[dso->shdr[i].sh_info];
+ }
+ if (dso->shdr[i].sh_type == SHT_SYMTAB
+ || dso->shdr[i].sh_type == SHT_DYNSYM)
+ {
+ if (adjust_symtab_section_indices (dso, i, move->old_shnum,
+ move->old_to_new))
+ goto error_out;
+ }
+ }
+ }
+
+ free (dso->move);
+ dso->move = NULL;
+
+ dso->ehdr.e_shstrndx = move->old_to_new[dso->ehdr.e_shstrndx];
+ gelf_update_ehdr (dso->elf, &dso->ehdr);
+
+ read_dynamic (dso);
+
+ /* If shoff does not point after last section, we need to adjust the sections
+ after it if we added or removed some sections. */
+ if (move->old_shnum != move->new_shnum
+ && adjust_dso_nonalloc (dso, 0, dso->ehdr.e_shoff + 1,
+ ((long) move->new_shnum - (long) move->old_shnum)
+ * gelf_fsize (dso->elf, ELF_T_SHDR, 1,
+ EV_CURRENT)))
+ goto error_out;
+
+ if (free_move)
+ free (move);
+ return 0;
+
+error_out:
+ if (free_move)
+ free (move);
+ if (elf)
+ elf_end (elf);
+ if (fd != -1)
+ {
+ wrap_unlink (filename);
+ close (fd);
+ }
+ return 1;
+}
+
+/* Return true if the value of symbol SYM, which belongs to DSO,
+ should be treated as an address within the DSO, and should
+ therefore track DSO's relocations. */
+
+int
+adjust_symbol_p (DSO *dso, GElf_Sym *sym)
+{
+ if (sym->st_shndx == SHN_ABS
+ && sym->st_value != 0
+ && (GELF_ST_TYPE (sym->st_info) <= STT_FUNC
+ || (dso->ehdr.e_machine == EM_ARM
+ && GELF_ST_TYPE (sym->st_info) == STT_ARM_TFUNC)))
+ /* This is problematic. How do we find out if
+ we should relocate this? Assume we should. */
+ return 1;
+
+ /* If a MIPS object does not define a symbol, but has a lazy binding
+ stub for it, st_value will point to that stub. Note that unlike
+ other targets, these stub addresses never participate in symbol
+ lookup; the stubs can only be called by the object that defines them.
+ st_values are only used in this way so that the associated GOT entry
+ can store a Quickstart value without losing the original stub
+ address. */
+ if (dso->ehdr.e_machine == EM_MIPS
+ && sym->st_shndx == SHN_UNDEF
+ && sym->st_value != 0)
+ return 1;
+
+ return (sym->st_shndx > SHN_UNDEF
+ && sym->st_shndx < dso->ehdr.e_shnum
+ && ELF32_ST_TYPE (sym->st_info) != STT_TLS
+ && RELOCATE_SCN (dso->shdr[sym->st_shndx].sh_flags));
+}
+
+static int
+adjust_symtab (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust)
+{
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[n];
+ GElf_Sym sym;
+ int ndx, maxndx;
+
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ maxndx = data->d_size / dso->shdr[n].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ++ndx)
+ {
+ gelfx_getsym (dso->elf, data, ndx, &sym);
+ if (adjust_symbol_p (dso, &sym) && sym.st_value >= start)
+ {
+ sym.st_value += adjust;
+ gelfx_update_sym (dso->elf, data, ndx, &sym);
+ }
+ }
+ }
+
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ return 0;
+}
+
+#endif
+
+int
+dso_is_rdwr (DSO *dso)
+{
+ return dso->elfro != NULL;
+}
+
+#if 0
+
+GElf_Addr
+adjust_old_to_new (DSO *dso, GElf_Addr addr)
+{
+ int i;
+
+ if (dso->adjust == NULL)
+ return addr; /* Fast path. */
+
+ for (i = 0; i < dso->nadjust; i++)
+ if (addr >= dso->adjust[i].start)
+ {
+ addr += dso->adjust[i].adjust;
+ assert (dso->ehdr.e_ident[EI_CLASS] != ELFCLASS32
+ || addr == (Elf32_Addr) addr);
+ return addr;
+ }
+
+ return addr;
+}
+
+GElf_Addr
+adjust_new_to_old (DSO *dso, GElf_Addr addr)
+{
+ int i;
+
+ if (dso->adjust == NULL)
+ return addr; /* Fast path. */
+
+ for (i = 0; i < dso->nadjust; i++)
+ if (addr >= dso->adjust[i].start + dso->adjust[i].adjust)
+ {
+ addr -= dso->adjust[i].adjust;
+ assert (dso->ehdr.e_ident[EI_CLASS] != ELFCLASS32
+ || addr == (Elf32_Addr) addr);
+ return addr;
+ }
+
+ return addr;
+}
+
+static int
+adjust_dynamic (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust)
+{
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[n];
+ GElf_Dyn dyn;
+ int ndx, maxndx;
+
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ maxndx = data->d_size / dso->shdr[n].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ++ndx)
+ {
+ gelfx_getdyn (dso->elf, data, ndx, &dyn);
+ if (dso->arch->adjust_dyn (dso, n, &dyn, start, adjust) == 0)
+ switch (dyn.d_tag)
+ {
+ case DT_REL:
+ case DT_RELA:
+ /* On some arches DT_REL* may be 0 indicating no relocations
+ (if DT_REL*SZ is also 0). Don't adjust it in that case. */
+ if (dyn.d_un.d_ptr && dyn.d_un.d_ptr >= start)
+ {
+ dyn.d_un.d_ptr += adjust;
+ gelfx_update_dyn (dso->elf, data, ndx, &dyn);
+ }
+ break;
+ default:
+ if (dyn.d_tag < DT_ADDRRNGLO || dyn.d_tag > DT_ADDRRNGHI)
+ break;
+ /* FALLTHROUGH */
+ case DT_INIT:
+ case DT_FINI:
+ case DT_HASH:
+ case DT_STRTAB:
+ case DT_SYMTAB:
+ case DT_JMPREL:
+ case DT_INIT_ARRAY:
+ case DT_FINI_ARRAY:
+ case DT_PREINIT_ARRAY:
+ case DT_VERDEF:
+ case DT_VERNEED:
+ case DT_VERSYM:
+ case DT_PLTGOT:
+ if (dyn.d_un.d_ptr >= start)
+ {
+ dyn.d_un.d_ptr += adjust;
+ gelfx_update_dyn (dso->elf, data, ndx, &dyn);
+ }
+ break;
+ }
+ else
+ gelfx_update_dyn (dso->elf, data, ndx, &dyn);
+ }
+ }
+
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+
+ /* Update the cached dynamic info as well. */
+ read_dynamic (dso);
+ return 0;
+}
+
+#endif
+
+int
+addr_to_sec (DSO *dso, GElf_Addr addr)
+{
+ GElf_Shdr *shdr;
+ int i;
+
+ shdr = &dso->shdr[dso->lastscn];
+ for (i = -1; i < dso->ehdr.e_shnum; shdr = &dso->shdr[++i])
+ if (RELOCATE_SCN (shdr->sh_flags)
+ && shdr->sh_addr <= addr && shdr->sh_addr + shdr->sh_size > addr
+ && (shdr->sh_type != SHT_NOBITS || (shdr->sh_flags & SHF_TLS) == 0))
+ {
+ if (i != -1)
+ dso->lastscn = i;
+ return dso->lastscn;
+ }
+
+ return -1;
+}
+
+#if 0
+
+static int
+adjust_rel (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust)
+{
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[n];
+ GElf_Rel rel;
+ int sec, ndx, maxndx;
+
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ maxndx = data->d_size / dso->shdr[n].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ++ndx)
+ {
+ gelfx_getrel (dso->elf, data, ndx, &rel);
+ sec = addr_to_sec (dso, rel.r_offset);
+ if (sec == -1)
+ continue;
+
+ dso->arch->adjust_rel (dso, &rel, start, adjust);
+ addr_adjust (rel.r_offset, start, adjust);
+ gelfx_update_rel (dso->elf, data, ndx, &rel);
+ }
+ }
+
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ return 0;
+}
+
+static int
+adjust_rela (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust)
+{
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[n];
+ GElf_Rela rela;
+ int sec, ndx, maxndx;
+
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ maxndx = data->d_size / dso->shdr[n].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ++ndx)
+ {
+ gelfx_getrela (dso->elf, data, ndx, &rela);
+ sec = addr_to_sec (dso, rela.r_offset);
+ if (sec == -1)
+ continue;
+
+ dso->arch->adjust_rela (dso, &rela, start, adjust);
+ addr_adjust (rela.r_offset, start, adjust);
+ gelfx_update_rela (dso->elf, data, ndx, &rela);
+ }
+ }
+
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ return 0;
+}
+
+int
+adjust_nonalloc (DSO *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int first,
+ GElf_Addr start, GElf_Addr adjust)
+{
+ int i;
+
+ for (i = 1; i < ehdr->e_shnum; i++)
+ {
+ if (RELOCATE_SCN (shdr[i].sh_flags) || shdr[i].sh_type == SHT_NULL)
+ continue;
+
+ if ((shdr[i].sh_offset > start
+ || (shdr[i].sh_offset == start && i >= first))
+ && (adjust & (shdr[i].sh_addralign - 1)))
+ adjust = (adjust + shdr[i].sh_addralign - 1)
+ & ~(shdr[i].sh_addralign - 1);
+ }
+
+ if (ehdr->e_shoff >= start)
+ {
+ GElf_Addr shdralign = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT);
+
+ if (adjust & (shdralign - 1))
+ adjust = (adjust + shdralign - 1) & ~(shdralign - 1);
+ ehdr->e_shoff += adjust;
+ }
+
+ for (i = 1; i < ehdr->e_shnum; i++)
+ {
+ if (RELOCATE_SCN (shdr[i].sh_flags) || shdr[i].sh_type == SHT_NULL)
+ continue;
+
+ if (shdr[i].sh_offset > start
+ || (shdr[i].sh_offset == start && i >= first))
+ shdr[i].sh_offset += adjust;
+ }
+ return 0;
+}
+
+int
+adjust_dso_nonalloc (DSO *dso, int first, GElf_Addr start, GElf_Addr adjust)
+{
+ return adjust_nonalloc (dso, &dso->ehdr, dso->shdr, first, start, adjust);
+}
+
+/* Add ADJUST to all addresses above START. */
+int
+adjust_dso (DSO *dso, GElf_Addr start, GElf_Addr adjust)
+{
+ int i;
+
+ if (dso->arch->arch_adjust
+ && dso->arch->arch_adjust (dso, start, adjust))
+ return 1;
+
+ if (dso->ehdr.e_entry >= start)
+ {
+ dso->ehdr.e_entry += adjust;
+ gelf_update_ehdr (dso->elf, &dso->ehdr);
+ elf_flagehdr (dso->elf, ELF_C_SET, ELF_F_DIRTY);
+ }
+
+ for (i = 0; i < dso->ehdr.e_phnum; i++)
+ {
+ /* Leave STACK segment alone, it has
+ p_vaddr == p_paddr == p_offset == p_filesz == p_memsz == 0. */
+ if (dso->phdr[i].p_type == PT_GNU_STACK)
+ continue;
+ if (! start)
+ {
+ dso->phdr[i].p_vaddr += adjust;
+ dso->phdr[i].p_paddr += adjust;
+ }
+ else if (start <= dso->phdr[i].p_vaddr)
+ {
+ dso->phdr[i].p_vaddr += adjust;
+ dso->phdr[i].p_paddr += adjust;
+ dso->phdr[i].p_offset += adjust;
+ }
+ else if (start < dso->phdr[i].p_vaddr + dso->phdr[i].p_filesz)
+ {
+ dso->phdr[i].p_filesz += adjust;
+ dso->phdr[i].p_memsz += adjust;
+ }
+ else if (start < dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz)
+ dso->phdr[i].p_memsz += adjust;
+ else
+ continue;
+ if (dso->phdr[i].p_type == PT_LOAD
+ && (dso->phdr[i].p_vaddr - dso->phdr[i].p_offset)
+ % dso->phdr[i].p_align)
+ {
+ error (0, 0, "%s: PT_LOAD %08llx %08llx 0x%x would be not properly aligned",
+ dso->filename, (long long) dso->phdr[i].p_offset,
+ (long long) dso->phdr[i].p_vaddr, (int) dso->phdr[i].p_align);
+ return 1;
+ }
+ gelf_update_phdr (dso->elf, i, dso->phdr + i);
+ }
+ elf_flagphdr (dso->elf, ELF_C_SET, ELF_F_DIRTY);
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ {
+ const char *name;
+
+ if (dso->arch->adjust_section)
+ {
+ int ret = dso->arch->adjust_section (dso, i, start, adjust);
+
+ if (ret == 1)
+ return 1;
+ else if (ret)
+ continue;
+ }
+ switch (dso->shdr[i].sh_type)
+ {
+ case SHT_PROGBITS:
+ name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name);
+ if (strcmp (name, ".stab") == 0
+ && adjust_stabs (dso, i, start, adjust))
+ return 1;
+ if (strcmp (name, ".debug_info") == 0
+ && adjust_dwarf2 (dso, i, start, adjust))
+ return 1;
+ break;
+ case SHT_HASH:
+ case SHT_GNU_HASH:
+ case SHT_NOBITS:
+ case SHT_STRTAB:
+ break;
+ case SHT_SYMTAB:
+ case SHT_DYNSYM:
+ if (adjust_symtab (dso, i, start, adjust))
+ return 1;
+ break;
+ case SHT_DYNAMIC:
+ if (adjust_dynamic (dso, i, start, adjust))
+ return 1;
+ break;
+ case SHT_REL:
+ /* Don't adjust reloc sections for debug info. */
+ if (dso->shdr[i].sh_flags & SHF_ALLOC)
+ if (adjust_rel (dso, i, start, adjust))
+ return 1;
+ break;
+ case SHT_RELA:
+ if (dso->shdr[i].sh_flags & SHF_ALLOC)
+ if (adjust_rela (dso, i, start, adjust))
+ return 1;
+ break;
+ }
+ if ((dso->arch->machine == EM_ALPHA
+ && dso->shdr[i].sh_type == SHT_ALPHA_DEBUG)
+ || (dso->arch->machine == EM_MIPS
+ && dso->shdr[i].sh_type == SHT_MIPS_DEBUG))
+ if (adjust_mdebug (dso, i, start, adjust))
+ return 1;
+ }
+
+ for (i = 0; i < dso->ehdr.e_shnum; i++)
+ {
+ if (RELOCATE_SCN (dso->shdr[i].sh_flags))
+ {
+ if (dso->shdr[i].sh_addr >= start)
+ {
+ Elf_Scn *scn = dso->scn[i];
+
+ dso->shdr[i].sh_addr += adjust;
+ if (start)
+ dso->shdr[i].sh_offset += adjust;
+ gelfx_update_shdr (dso->elf, scn, dso->shdr + i);
+ elf_flagshdr (scn, ELF_C_SET, ELF_F_DIRTY);
+ }
+ }
+ }
+
+ addr_adjust (dso->base, start, adjust);
+ addr_adjust (dso->end, start, adjust);
+
+ if (start)
+ {
+ start = adjust_new_to_old (dso, start);
+ for (i = 0; i < dso->nadjust; i++)
+ if (start < dso->adjust[i].start)
+ dso->adjust[i].adjust += adjust;
+ else
+ break;
+ if (i < dso->nadjust && start == dso->adjust[i].start)
+ dso->adjust[i].adjust += adjust;
+ else
+ {
+ dso->adjust =
+ realloc (dso->adjust, (dso->nadjust + 1) * sizeof (*dso->adjust));
+ if (dso->adjust == NULL)
+ {
+ error (0, ENOMEM, "Cannot record the list of adjustements being made");
+ return 1;
+ }
+ memmove (dso->adjust + i + 1, dso->adjust + i, dso->nadjust - i);
+ dso->adjust[i].start = start;
+ dso->adjust[i].adjust = adjust;
+ ++dso->nadjust;
+ }
+ }
+
+ return start ? adjust_dso_nonalloc (dso, 0, 0, adjust) : 0;
+}
+
+int
+recompute_nonalloc_offsets (DSO *dso)
+{
+ int i, first_nonalloc, sec_before_shoff = 0;
+ GElf_Addr last_offset = 0;
+ GElf_Addr shdralign = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT);
+ GElf_Addr shdrsize = gelf_fsize (dso->elf, ELF_T_SHDR, 1, EV_CURRENT)
+ * dso->ehdr.e_shnum;
+
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ if (RELOCATE_SCN (dso->shdr[i].sh_flags))
+ {
+ if (dso->shdr[i].sh_type == SHT_NOBITS)
+ last_offset = dso->shdr[i].sh_offset;
+ else
+ last_offset = dso->shdr[i].sh_offset + dso->shdr[i].sh_size;
+ }
+ else
+ break;
+
+ first_nonalloc = i;
+ if (dso->ehdr.e_shoff < dso->shdr[i].sh_offset)
+ {
+ dso->ehdr.e_shoff = (last_offset + shdralign - 1) & ~(shdralign - 1);
+ last_offset = dso->ehdr.e_shoff + shdrsize;
+ }
+ else
+ for (; i < dso->ehdr.e_shnum; ++i)
+ if (dso->shdr[i].sh_offset < dso->ehdr.e_shoff
+ && (i == dso->ehdr.e_shnum - 1
+ || dso->shdr[i + 1].sh_offset > dso->ehdr.e_shoff))
+ {
+ sec_before_shoff = i;
+ break;
+ }
+
+ for (i = first_nonalloc; i < dso->ehdr.e_shnum; ++i)
+ {
+ assert (!RELOCATE_SCN (dso->shdr[i].sh_flags));
+ assert (dso->shdr[i].sh_type != SHT_NOBITS);
+ dso->shdr[i].sh_offset = (last_offset + dso->shdr[i].sh_addralign - 1)
+ & ~(dso->shdr[i].sh_addralign - 1);
+ last_offset = dso->shdr[i].sh_offset + dso->shdr[i].sh_size;
+ if (i == sec_before_shoff)
+ {
+ dso->ehdr.e_shoff = (last_offset + shdralign - 1) & ~(shdralign - 1);
+ last_offset = dso->ehdr.e_shoff + shdrsize;
+ }
+ }
+
+ return 0;
+}
+
+int
+strtabfind (DSO *dso, int strndx, const char *name)
+{
+ Elf_Scn *scn;
+ Elf_Data *data;
+ const char *p, *q, *r;
+ size_t len = strlen (name);
+
+ if (dso->shdr[strndx].sh_type != SHT_STRTAB)
+ return 0;
+
+ scn = dso->scn[strndx];
+ data = elf_getdata (scn, NULL);
+ assert (elf_getdata (scn, data) == NULL);
+ assert (data->d_off == 0);
+ assert (data->d_size == dso->shdr[strndx].sh_size);
+ q = data->d_buf + data->d_size;
+ for (p = data->d_buf; p < q; p = r + 1)
+ {
+ r = strchr (p, '\0');
+ if (r - p >= len && memcmp (r - len, name, len) == 0)
+ return (r - (const char *) data->d_buf) - len;
+ }
+
+ return 0;
+}
+
+int
+shstrtabadd (DSO *dso, const char *name)
+{
+ Elf_Scn *scn;
+ Elf_Data *data;
+ GElf_Addr adjust;
+ const char *p, *q, *r;
+ size_t len = strlen (name), align;
+ int ret;
+
+ scn = dso->scn[dso->ehdr.e_shstrndx];
+ data = elf_getdata (scn, NULL);
+ assert (elf_getdata (scn, data) == NULL);
+ assert (data->d_off == 0);
+ assert (data->d_size == dso->shdr[dso->ehdr.e_shstrndx].sh_size);
+ q = data->d_buf + data->d_size;
+ for (p = data->d_buf; p < q; p = r + 1)
+ {
+ r = strchr (p, '\0');
+ if (r - p >= len && memcmp (r - len, name, len) == 0)
+ return (r - (const char *) data->d_buf) - len;
+ }
+
+ data->d_buf = realloc (data->d_buf, data->d_size + len + 1);
+ if (data->d_buf == NULL)
+ {
+ error (0, ENOMEM, "Cannot add new section name %s", name);
+ return 0;
+ }
+
+ memcpy (data->d_buf + data->d_size, name, len + 1);
+ ret = data->d_size;
+ data->d_size += len + 1;
+ align = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT);
+ adjust = (len + 1 + align - 1) & ~(align - 1);
+ if (adjust_dso_nonalloc (dso, 0,
+ dso->shdr[dso->ehdr.e_shstrndx].sh_offset
+ + dso->shdr[dso->ehdr.e_shstrndx].sh_size,
+ adjust))
+ return 0;
+ dso->shdr[dso->ehdr.e_shstrndx].sh_size += len + 1;
+ return ret;
+}
+
+int
+relocate_dso (DSO *dso, GElf_Addr base)
+{
+ /* Check if it is already relocated. */
+ if (dso->base == base)
+ return 0;
+
+ if (! dso_is_rdwr (dso))
+ {
+ if (reopen_dso (dso, NULL, NULL))
+ return 1;
+ }
+
+ return adjust_dso (dso, 0, base - dso->base);
+}
+
+#endif
+
+static int
+close_dso_1 (DSO *dso)
+{
+ if (dso_is_rdwr (dso))
+ {
+ int i;
+
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ {
+ Elf_Scn *scn = dso->scn[i];
+ Elf_Data *data = NULL;
+
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ free (data->d_buf);
+ data->d_buf = NULL;
+ }
+ }
+ }
+
+ elf_end (dso->elf);
+ close (dso->fd);
+ if (dso->elfro)
+ {
+ elf_end (dso->elfro);
+ close (dso->fdro);
+ }
+ if (dso->filename != dso->soname)
+ free ((char *) dso->soname);
+ free ((char *) dso->filename);
+ free ((char *) dso->temp_filename);
+ free (dso->move);
+ free (dso->adjust);
+ free (dso->undo.d_buf);
+ free (dso);
+ return 0;
+}
+
+int
+close_dso (DSO *dso)
+{
+ int rdwr = dso_is_rdwr (dso);
+
+ if (rdwr && dso->temp_filename != NULL)
+ wrap_unlink (dso->temp_filename);
+ close_dso_1 (dso);
+ return 0;
+}
+
+#if 0
+
+int
+prepare_write_dso (DSO *dso)
+{
+ int i;
+
+ if (check_dso (dso)
+ || (dso->mdebug_orig_offset && finalize_mdebug (dso)))
+ return 1;
+
+ gelf_update_ehdr (dso->elf, &dso->ehdr);
+ for (i = 0; i < dso->ehdr.e_phnum; ++i)
+ gelf_update_phdr (dso->elf, i, dso->phdr + i);
+ for (i = 0; i < dso->ehdr.e_shnum; ++i)
+ {
+ gelfx_update_shdr (dso->elf, dso->scn[i], dso->shdr + i);
+ if (dso->shdr[i].sh_type == SHT_SYMTAB
+ || dso->shdr[i].sh_type == SHT_DYNSYM)
+ set_stt_section_values (dso, i);
+ }
+ return 0;
+}
+
+int
+write_dso (DSO *dso)
+{
+ if (prepare_write_dso (dso))
+ return 1;
+
+ if (! dso->permissive && ELF_F_PERMISSIVE)
+ elf_flagelf (dso->elf, ELF_C_CLR, ELF_F_PERMISSIVE);
+
+ if (elf_update (dso->elf, ELF_C_WRITE) == -1)
+ return 2;
+ return 0;
+}
+
+int
+set_security_context (DSO *dso, const char *temp_name, const char *name)
+{
+#ifdef USE_SELINUX
+ static int selinux_enabled = -1;
+ if (selinux_enabled == -1)
+ selinux_enabled = is_selinux_enabled ();
+ if (selinux_enabled > 0)
+ {
+ security_context_t scontext;
+ if (getfilecon (name, &scontext) < 0)
+ {
+ /* If the filesystem doesn't support extended attributes,
+ the original had no special security context and the
+ target cannot have one either. */
+ if (errno == EOPNOTSUPP)
+ return 0;
+
+ error (0, errno, "Could not get security context for %s",
+ name);
+ return 1;
+ }
+ if (setfilecon (temp_name, scontext) < 0)
+ {
+ error (0, errno, "Could not set security context for %s",
+ name);
+ freecon (scontext);
+ return 1;
+ }
+ freecon (scontext);
+ }
+#endif
+ return 0;
+}
+
+int
+update_dso (DSO *dso, const char *orig_name)
+{
+ int rdwr = dso_is_rdwr (dso);
+
+ if (rdwr)
+ {
+ char *name1, *name2;
+ struct utimbuf u;
+ struct stat64 st;
+
+ switch (write_dso (dso))
+ {
+ case 2:
+ error (0, 0, "Could not write %s: %s", dso->filename,
+ elf_errmsg (-1));
+ /* FALLTHROUGH */
+ case 1:
+ close_dso (dso);
+ return 1;
+ case 0:
+ break;
+ }
+
+ name1 = strdupa (dso->filename);
+ name2 = strdupa (dso->temp_filename);
+ if (fstat64 (dso->fdro, &st) < 0)
+ {
+ error (0, errno, "Could not stat %s", dso->filename);
+ close_dso (dso);
+ return 1;
+ }
+ if (fchown (dso->fd, st.st_uid, st.st_gid) < 0
+ || fchmod (dso->fd, st.st_mode & 07777) < 0)
+ {
+ error (0, errno, "Could not set %s owner or mode", dso->filename);
+ close_dso (dso);
+ return 1;
+ }
+ close_dso_1 (dso);
+ u.actime = time (NULL);
+ u.modtime = st.st_mtime;
+ wrap_utime (name2, &u);
+
+ if (set_security_context (dso, name2, orig_name ? orig_name : name1))
+ {
+ wrap_unlink (name2);
+ return 1;
+ }
+
+ if (wrap_rename (name2, name1))
+ {
+ wrap_unlink (name2);
+ error (0, errno, "Could not rename temporary to %s", name1);
+ return 1;
+ }
+ }
+ else
+ close_dso_1 (dso);
+
+ return 0;
+}
+
+#endif