summaryrefslogtreecommitdiffstats
path: root/trunk/src/arch-i386.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/arch-i386.c')
-rw-r--r--trunk/src/arch-i386.c1087
1 files changed, 1087 insertions, 0 deletions
diff --git a/trunk/src/arch-i386.c b/trunk/src/arch-i386.c
new file mode 100644
index 0000000..d5211b2
--- /dev/null
+++ b/trunk/src/arch-i386.c
@@ -0,0 +1,1087 @@
+/* Copyright (C) 2001, 2002, 2003, 2004 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 <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <locale.h>
+#include <error.h>
+#include <argp.h>
+#include <stdlib.h>
+
+#include "prelink.h"
+#include "layout.h"
+
+static int
+i386_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
+ GElf_Addr adjust)
+{
+ if (dyn->d_tag == DT_PLTGOT)
+ {
+ int sec = addr_to_sec (dso, dyn->d_un.d_ptr);
+ Elf32_Addr data;
+
+ if (sec == -1)
+ return 0;
+
+ data = read_ule32 (dso, dyn->d_un.d_ptr);
+ /* If .got.plt[0] points to _DYNAMIC, it needs to be adjusted. */
+ if (data == dso->shdr[n].sh_addr && data >= start)
+ write_le32 (dso, dyn->d_un.d_ptr, data + adjust);
+
+ data = read_ule32 (dso, dyn->d_un.d_ptr + 4);
+ /* If .got.plt[1] points to .plt + 0x16, it needs to be adjusted. */
+ if (data && data >= start)
+ {
+ int i;
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ if (data == dso->shdr[i].sh_addr + 0x16
+ && dso->shdr[i].sh_type == SHT_PROGBITS
+ && strcmp (strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name), ".plt") == 0)
+ {
+ write_le32 (dso, dyn->d_un.d_ptr + 4, data + adjust);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+i386_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
+ GElf_Addr adjust)
+{
+ Elf32_Addr data;
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ case R_386_RELATIVE:
+ case R_386_JMP_SLOT:
+ data = read_ule32 (dso, rel->r_offset);
+ if (data >= start)
+ write_le32 (dso, rel->r_offset, data + adjust);
+ break;
+ }
+ return 0;
+}
+
+static int
+i386_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
+ GElf_Addr adjust)
+{
+ Elf32_Addr data;
+
+ switch (GELF_R_TYPE (rela->r_info))
+ {
+ case R_386_RELATIVE:
+ if ((Elf32_Addr) rela->r_addend >= start)
+ {
+ rela->r_addend += (Elf32_Sword) adjust;
+ /* Write it to the memory location as well.
+ Not necessary, but we can do it. */
+ write_le32 (dso, rela->r_offset, rela->r_addend);
+ }
+ break;
+ case R_386_JMP_SLOT:
+ data = read_ule32 (dso, rela->r_offset);
+ if (data >= start)
+ write_le32 (dso, rela->r_offset, data + adjust);
+ break;
+ break;
+ }
+ return 0;
+}
+
+static int
+i386_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr)
+{
+ DSO *dso;
+ GElf_Addr value;
+
+ if (GELF_R_TYPE (rel->r_info) == R_386_RELATIVE
+ || GELF_R_TYPE (rel->r_info) == R_386_NONE)
+ /* Fast path: nothing to do. */
+ return 0;
+ dso = info->dso;
+ value = info->resolve (info, GELF_R_SYM (rel->r_info),
+ GELF_R_TYPE (rel->r_info));
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ write_le32 (dso, rel->r_offset, value);
+ break;
+ case R_386_32:
+ {
+ if (read_ule32 (dso, rel->r_offset))
+ {
+ error (0, 0, "%s: R_386_32 relocs with non-zero addend should not be present in prelinked REL sections",
+ dso->filename);
+ return 1;
+ }
+ rel->r_info = GELF_R_INFO (GELF_R_SYM (rel->r_info), R_386_GLOB_DAT);
+ write_le32 (dso, rel->r_offset, value);
+ /* Tell prelink_rel routine *rel has changed. */
+ return 2;
+ }
+ case R_386_PC32:
+ error (0, 0, "%s: R_386_PC32 relocs should not be present in prelinked REL sections",
+ dso->filename);
+ return 1;
+ case R_386_TLS_DTPOFF32:
+ write_le32 (dso, rel->r_offset, value);
+ break;
+ /* DTPMOD32 and TPOFF{32,} is impossible to predict unless prelink
+ sets the rules. Also for TPOFF{32,} there is REL->RELA problem. */
+ case R_386_TLS_DTPMOD32:
+ if (dso->ehdr.e_type == ET_EXEC)
+ {
+ error (0, 0, "%s: R_386_TLS_DTPMOD32 reloc in executable?",
+ dso->filename);
+ return 1;
+ }
+ break;
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ if (dso->ehdr.e_type == ET_EXEC)
+ error (0, 0, "%s: R_386_TLS_TPOFF relocs should not be present in prelinked ET_EXEC REL sections",
+ dso->filename);
+ break;
+ case R_386_COPY:
+ if (dso->ehdr.e_type == ET_EXEC)
+ /* COPY relocs are handled specially in generic code. */
+ return 0;
+ error (0, 0, "%s: R_386_COPY reloc in shared library?", dso->filename);
+ return 1;
+ default:
+ error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename,
+ (int) GELF_R_TYPE (rel->r_info));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+i386_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
+ GElf_Addr relaaddr)
+{
+ DSO *dso;
+ GElf_Addr value;
+
+ if (GELF_R_TYPE (rela->r_info) == R_386_RELATIVE
+ || GELF_R_TYPE (rela->r_info) == R_386_NONE)
+ /* Fast path: nothing to do. */
+ return 0;
+ dso = info->dso;
+ value = info->resolve (info, GELF_R_SYM (rela->r_info),
+ GELF_R_TYPE (rela->r_info));
+ switch (GELF_R_TYPE (rela->r_info))
+ {
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ write_le32 (dso, rela->r_offset, value + rela->r_addend);
+ break;
+ case R_386_32:
+ write_le32 (dso, rela->r_offset, value + rela->r_addend);
+ break;
+ case R_386_PC32:
+ write_le32 (dso, rela->r_offset, value + rela->r_addend - rela->r_offset);
+ break;
+ case R_386_TLS_DTPOFF32:
+ write_le32 (dso, rela->r_offset, value + rela->r_addend);
+ break;
+ /* DTPMOD32 and TPOFF{32,} is impossible to predict unless prelink
+ sets the rules. */
+ case R_386_TLS_DTPMOD32:
+ if (dso->ehdr.e_type == ET_EXEC)
+ {
+ error (0, 0, "%s: R_386_TLS_DTPMOD32 reloc in executable?",
+ dso->filename);
+ return 1;
+ }
+ break;
+ case R_386_TLS_TPOFF32:
+ if (dso->ehdr.e_type == ET_EXEC && info->resolvetls)
+ write_le32 (dso, rela->r_offset,
+ -(value + rela->r_addend - info->resolvetls->offset));
+ break;
+ case R_386_TLS_TPOFF:
+ if (dso->ehdr.e_type == ET_EXEC && info->resolvetls)
+ write_le32 (dso, rela->r_offset,
+ value + rela->r_addend - info->resolvetls->offset);
+ break;
+ case R_386_COPY:
+ if (dso->ehdr.e_type == ET_EXEC)
+ /* COPY relocs are handled specially in generic code. */
+ return 0;
+ error (0, 0, "%s: R_386_COPY reloc in shared library?", dso->filename);
+ return 1;
+ default:
+ error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename,
+ (int) GELF_R_TYPE (rela->r_info));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+i386_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
+ char *buf)
+{
+ switch (GELF_R_TYPE (rela->r_info))
+ {
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ case R_386_32:
+ buf_write_le32 (buf, rela->r_addend);
+ break;
+ default:
+ abort ();
+ }
+ return 0;
+}
+
+static int
+i386_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf)
+{
+ GElf_Addr value;
+
+ value = info->resolve (info, GELF_R_SYM (rel->r_info),
+ GELF_R_TYPE (rel->r_info));
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ case R_386_NONE:
+ break;
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ buf_write_le32 (buf, value);
+ break;
+ case R_386_32:
+ buf_write_le32 (buf, value + read_ule32 (info->dso, rel->r_offset));
+ break;
+ case R_386_PC32:
+ buf_write_le32 (buf, value + read_ule32 (info->dso, rel->r_offset)
+ - rel->r_offset);
+ break;
+ case R_386_COPY:
+ abort ();
+ case R_386_RELATIVE:
+ error (0, 0, "%s: R_386_RELATIVE in ET_EXEC object?", info->dso->filename);
+ return 1;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+i386_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
+{
+ GElf_Addr value;
+
+ value = info->resolve (info, GELF_R_SYM (rela->r_info),
+ GELF_R_TYPE (rela->r_info));
+ switch (GELF_R_TYPE (rela->r_info))
+ {
+ case R_386_NONE:
+ break;
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ case R_386_32:
+ buf_write_le32 (buf, value + rela->r_addend);
+ break;
+ case R_386_PC32:
+ buf_write_le32 (buf, value + rela->r_addend - rela->r_offset);
+ break;
+ case R_386_COPY:
+ abort ();
+ case R_386_RELATIVE:
+ error (0, 0, "%s: R_386_RELATIVE in ET_EXEC object?", info->dso->filename);
+ return 1;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+i386_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
+ GElf_Addr reladdr)
+{
+ GElf_Addr value;
+ struct prelink_conflict *conflict;
+ struct prelink_tls *tls;
+ GElf_Rela *ret;
+
+ if (GELF_R_TYPE (rel->r_info) == R_386_RELATIVE
+ || GELF_R_TYPE (rel->r_info) == R_386_NONE)
+ /* Fast path: nothing to do. */
+ return 0;
+ conflict = prelink_conflict (info, GELF_R_SYM (rel->r_info),
+ GELF_R_TYPE (rel->r_info));
+ if (conflict == NULL)
+ {
+ if (info->curtls == NULL)
+ return 0;
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ /* Even local DTPMOD and TPOFF relocs need conflicts. */
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ break;
+ default:
+ return 0;
+ }
+ value = 0;
+ }
+ else
+ {
+ /* DTPOFF32 wants to see only real conflicts, not lookups
+ with reloc_class RTYPE_CLASS_TLS. */
+ if (GELF_R_TYPE (rel->r_info) == R_386_TLS_DTPOFF32
+ && conflict->lookup.tls == conflict->conflict.tls
+ && conflict->lookupval == conflict->conflictval)
+ return 0;
+
+ value = conflict_lookup_value (conflict);
+ }
+ ret = prelink_conflict_add_rela (info);
+ if (ret == NULL)
+ return 1;
+ ret->r_offset = rel->r_offset;
+ ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rel->r_info));
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ case R_386_GLOB_DAT:
+ ret->r_info = GELF_R_INFO (0, R_386_32);
+ /* FALLTHROUGH */
+ case R_386_JMP_SLOT:
+ ret->r_addend = (Elf32_Sword) value;
+ break;
+ case R_386_32:
+ case R_386_PC32:
+ error (0, 0, "%s: R_386_%s32 relocs should not be present in prelinked REL sections",
+ dso->filename, GELF_R_TYPE (rel->r_info) == R_386_32 ? "" : "PC");
+ return 1;
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ if (conflict != NULL
+ && (conflict->reloc_class != RTYPE_CLASS_TLS
+ || conflict->lookup.tls == NULL))
+ {
+ error (0, 0, "%s: R_386_TLS not resolving to STT_TLS symbol",
+ dso->filename);
+ return 1;
+ }
+ tls = conflict ? conflict->lookup.tls : info->curtls;
+ ret->r_info = GELF_R_INFO (0, R_386_32);
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ case R_386_TLS_DTPMOD32:
+ ret->r_addend = tls->modid;
+ break;
+ case R_386_TLS_DTPOFF32:
+ ret->r_addend = value;
+ break;
+ case R_386_TLS_TPOFF32:
+ ret->r_addend = -(value + read_ule32 (dso, rel->r_offset)
+ - tls->offset);
+ break;
+ case R_386_TLS_TPOFF:
+ ret->r_addend = value + read_ule32 (dso, rel->r_offset)
+ - tls->offset;
+ }
+ break;
+ case R_386_COPY:
+ error (0, 0, "R_386_COPY should not be present in shared libraries");
+ return 1;
+ default:
+ error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename,
+ (int) GELF_R_TYPE (rel->r_info));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+i386_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
+ GElf_Rela *rela, GElf_Addr relaaddr)
+{
+ GElf_Addr value;
+ struct prelink_conflict *conflict;
+ struct prelink_tls *tls;
+ GElf_Rela *ret;
+
+ if (GELF_R_TYPE (rela->r_info) == R_386_RELATIVE
+ || GELF_R_TYPE (rela->r_info) == R_386_NONE)
+ /* Fast path: nothing to do. */
+ return 0;
+ conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
+ GELF_R_TYPE (rela->r_info));
+ if (conflict == NULL)
+ {
+ if (info->curtls == NULL)
+ return 0;
+ switch (GELF_R_TYPE (rela->r_info))
+ {
+ /* Even local DTPMOD and TPOFF relocs need conflicts. */
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ break;
+ default:
+ return 0;
+ }
+ value = 0;
+ }
+ else
+ {
+ /* DTPOFF32 wants to see only real conflicts, not lookups
+ with reloc_class RTYPE_CLASS_TLS. */
+ if (GELF_R_TYPE (rela->r_info) == R_386_TLS_DTPOFF32
+ && conflict->lookup.tls == conflict->conflict.tls
+ && conflict->lookupval == conflict->conflictval)
+ return 0;
+
+ value = conflict_lookup_value (conflict);
+ }
+ ret = prelink_conflict_add_rela (info);
+ if (ret == NULL)
+ return 1;
+ ret->r_offset = rela->r_offset;
+ ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info));
+ switch (GELF_R_TYPE (rela->r_info))
+ {
+ case R_386_GLOB_DAT:
+ ret->r_info = GELF_R_INFO (0, R_386_32);
+ /* FALLTHROUGH */
+ case R_386_JMP_SLOT:
+ ret->r_addend = (Elf32_Sword) (value + rela->r_addend);
+ break;
+ case R_386_32:
+ value += rela->r_addend;
+ ret->r_addend = (Elf32_Sword) value;
+ break;
+ case R_386_PC32:
+ ret->r_addend = (Elf32_Sword) (value + rela->r_addend - rela->r_offset);
+ ret->r_info = GELF_R_INFO (0, R_386_32);
+ break;
+ case R_386_COPY:
+ error (0, 0, "R_386_COPY should not be present in shared libraries");
+ return 1;
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ if (conflict != NULL
+ && (conflict->reloc_class != RTYPE_CLASS_TLS
+ || conflict->lookup.tls == NULL))
+ {
+ error (0, 0, "%s: R_386_TLS not resolving to STT_TLS symbol",
+ dso->filename);
+ return 1;
+ }
+ tls = conflict ? conflict->lookup.tls : info->curtls;
+ ret->r_info = GELF_R_INFO (0, R_386_32);
+ switch (GELF_R_TYPE (rela->r_info))
+ {
+ case R_386_TLS_DTPMOD32:
+ ret->r_addend = tls->modid;
+ break;
+ case R_386_TLS_DTPOFF32:
+ ret->r_addend += value;
+ break;
+ case R_386_TLS_TPOFF32:
+ ret->r_addend = -(value + rela->r_addend - tls->offset);
+ break;
+ case R_386_TLS_TPOFF:
+ ret->r_addend = value + rela->r_addend - tls->offset;
+ break;
+ }
+ break;
+ default:
+ error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename,
+ (int) GELF_R_TYPE (rela->r_info));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+i386_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela)
+{
+ rela->r_offset = rel->r_offset;
+ rela->r_info = rel->r_info;
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ case R_386_JMP_SLOT:
+ /* We should be never converting .rel.plt into .rela.plt. */
+ abort ();
+ case R_386_RELATIVE:
+ case R_386_32:
+ case R_386_PC32:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ rela->r_addend = (Elf32_Sword) read_ule32 (dso, rel->r_offset);
+ break;
+ case R_386_COPY:
+ case R_386_GLOB_DAT:
+ case R_386_TLS_DTPOFF32:
+ case R_386_TLS_DTPMOD32:
+ rela->r_addend = 0;
+ break;
+ }
+ return 0;
+}
+
+static int
+i386_need_rel_to_rela (DSO *dso, int first, int last)
+{
+ Elf_Data *data;
+ Elf_Scn *scn;
+ Elf32_Rel *rel, *relend;
+ unsigned int val;
+
+ while (first <= last)
+ {
+ data = NULL;
+ scn = dso->scn[first++];
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ rel = (Elf32_Rel *) data->d_buf;
+ relend = rel + data->d_size / sizeof (Elf32_Rel);
+ for (; rel < relend; rel++)
+ switch (ELF32_R_TYPE (rel->r_info))
+ {
+ case R_386_32:
+ val = read_ule32 (dso, rel->r_offset);
+ /* R_386_32 with addend 0 can be converted
+ to R_386_GLOB_DAT and we don't have to convert
+ to RELA because of that. */
+ if (val == 0)
+ break;
+ /* FALLTHROUGH */
+ case R_386_PC32:
+ return 1;
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ /* In shared libraries TPOFF is changed always into
+ conflicts, for executables we need to preserve
+ original addend. */
+ if (dso->ehdr.e_type == ET_EXEC)
+ return 1;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+i386_arch_prelink (struct prelink_info *info)
+{
+ DSO *dso;
+ int i;
+
+ dso = info->dso;
+ if (dso->info[DT_PLTGOT])
+ {
+ /* Write address of .plt + 0x16 into got[1].
+ .plt + 0x16 is what got[3] contains unless prelinking. */
+ int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]);
+ Elf32_Addr data;
+
+ if (sec == -1)
+ return 1;
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ if (dso->shdr[i].sh_type == SHT_PROGBITS
+ && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name),
+ ".plt"))
+ break;
+
+ if (i == dso->ehdr.e_shnum)
+ return 0;
+ data = dso->shdr[i].sh_addr + 0x16;
+ write_le32 (dso, dso->info[DT_PLTGOT] + 4, data);
+ }
+
+ return 0;
+}
+
+static int
+i386_arch_undo_prelink (DSO *dso)
+{
+ int i;
+
+ if (dso->info[DT_PLTGOT])
+ {
+ /* Clear got[1] if it contains address of .plt + 0x16. */
+ int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]);
+ Elf32_Addr data;
+
+ if (sec == -1)
+ return 1;
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ if (dso->shdr[i].sh_type == SHT_PROGBITS
+ && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name),
+ ".plt"))
+ break;
+
+ if (i == dso->ehdr.e_shnum)
+ return 0;
+ data = read_ule32 (dso, dso->info[DT_PLTGOT] + 4);
+ if (data == dso->shdr[i].sh_addr + 0x16)
+ write_le32 (dso, dso->info[DT_PLTGOT] + 4, 0);
+ }
+
+ return 0;
+}
+
+static int
+i386_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr)
+{
+ int sec;
+ const char *name;
+
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ case R_386_NONE:
+ case R_386_RELATIVE:
+ break;
+ case R_386_JMP_SLOT:
+ sec = addr_to_sec (dso, rel->r_offset);
+ name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name);
+ if (sec == -1 || (strcmp (name, ".got") && strcmp (name, ".got.plt")))
+ {
+ error (0, 0, "%s: R_386_JMP_SLOT not pointing into .got section",
+ dso->filename);
+ return 1;
+ }
+ else
+ {
+ Elf32_Addr data = read_ule32 (dso, dso->shdr[sec].sh_addr + 4);
+
+ assert (rel->r_offset >= dso->shdr[sec].sh_addr + 12);
+ assert (((rel->r_offset - dso->shdr[sec].sh_addr) & 3) == 0);
+ write_le32 (dso, rel->r_offset,
+ 4 * (rel->r_offset - dso->shdr[sec].sh_addr - 12)
+ + data);
+ }
+ break;
+ case R_386_GLOB_DAT:
+ sec = addr_to_sec (dso, rel->r_offset);
+
+ write_le32 (dso, rel->r_offset, 0);
+ if (sec != -1)
+ {
+ if (strcmp (strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[sec].sh_name),
+ ".got"))
+ {
+ rel->r_info = GELF_R_INFO (GELF_R_SYM (rel->r_info), R_386_32);
+ return 2;
+ }
+ }
+ break;
+ case R_386_32:
+ case R_386_PC32:
+ error (0, 0, "%s: R_386_%s32 relocs should not be present in prelinked REL sections",
+ GELF_R_TYPE (rel->r_info) == R_386_32 ? "" : "PC", dso->filename);
+ return 1;
+ case R_386_COPY:
+ if (dso->ehdr.e_type == ET_EXEC)
+ /* COPY relocs are handled specially in generic code. */
+ return 0;
+ error (0, 0, "%s: R_386_COPY reloc in shared library?", dso->filename);
+ return 1;
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ write_le32 (dso, rel->r_offset, 0);
+ break;
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ break;
+ default:
+ error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename,
+ (int) GELF_R_TYPE (rel->r_info));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+i386_rela_to_rel (DSO *dso, GElf_Rela *rela, GElf_Rel *rel)
+{
+ rel->r_offset = rela->r_offset;
+ rel->r_info = rela->r_info;
+ switch (GELF_R_TYPE (rel->r_info))
+ {
+ case R_386_JMP_SLOT:
+ /* We should be never converting .rel.plt into .rela.plt
+ and thus never .rela.plt back to .rel.plt. */
+ abort ();
+ case R_386_RELATIVE:
+ case R_386_32:
+ case R_386_PC32:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ write_le32 (dso, rela->r_offset, rela->r_addend);
+ break;
+ case R_386_COPY:
+ case R_386_GLOB_DAT:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ write_le32 (dso, rela->r_offset, 0);
+ break;
+ }
+ return 0;
+}
+
+static int
+i386_reloc_size (int reloc_type)
+{
+ assert (reloc_type != R_386_COPY);
+ return 4;
+}
+
+static int
+i386_reloc_class (int reloc_type)
+{
+ switch (reloc_type)
+ {
+ case R_386_COPY: return RTYPE_CLASS_COPY;
+ case R_386_JMP_SLOT: return RTYPE_CLASS_PLT;
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ return RTYPE_CLASS_TLS;
+ default: return RTYPE_CLASS_VALID;
+ }
+}
+
+/* Library memory regions if --exec-shield in order of precedence:
+ 0x00101000 + (rand % 0x00cff000) .. 0x00e00000 bottom to top
+ 0x00101000 .. 0x00101000 + (rand % 0x00cff000) bottom to top
+ 0x02000000 + (rand % 0x06000000) .. 0x08000000 bottom to top
+ 0x02000000 .. 0x02000000 + (rand % 0x06000000) bottom to top
+ 0x41000000 + (rand % 0x0f000000) .. 0x50000000 bottom to top
+ 0x41000000 .. 0x41000000 + (rand % 0x0f000000) bottom to top */
+
+#define REG0S 0x00101000
+#define REG0E 0x00e00000
+#define REG1S 0x02000000
+#define REG1E 0x08000000
+#define REG2S 0x41000000
+#define REG2E 0x50000000
+
+struct i386_layout_data
+{
+ struct prelink_entry e[6];
+ Elf32_Addr addrs[12];
+};
+
+static inline void
+list_append (struct prelink_entry *x, struct prelink_entry *e)
+{
+ x->prev->next = e;
+ e->prev = x->prev;
+ e->next = NULL;
+ x->prev = e;
+}
+
+static inline void
+list_merge (struct prelink_entry *x, struct prelink_entry *e)
+{
+ struct prelink_entry *end = e->prev;
+ x->prev->next = e;
+ e->prev = x->prev;
+ x->prev = end;
+}
+
+static int
+i386_layout_libs_init (struct layout_libs *l)
+{
+ if (exec_shield)
+ {
+ int i;
+ struct prelink_entry *e;
+
+ l->mmap_base = REG0S;
+ l->mmap_end = REG2E;
+ /* Don't allow this to be overridden. */
+ mmap_reg_start = ~(GElf_Addr) 0;
+ mmap_reg_end = ~(GElf_Addr) 0;
+ for (i = 0; i < l->nlibs; ++i)
+ {
+ e = l->libs[i];
+ if (e->done == 0)
+ continue;
+ if (e->base < REG0S
+ || (e->base < REG1S && e->layend > REG0E)
+ || (e->base < REG2S && e->layend > REG1E)
+ || e->layend > REG2E)
+ e->done = 0;
+ }
+ }
+ else
+ {
+ l->mmap_base = REG2S;
+ l->mmap_end = REG2E;
+ }
+ return 0;
+}
+
+static void
+i386_find_free_addr (struct layout_libs *l, Elf32_Addr *ret,
+ Elf32_Addr beg, Elf32_Addr end, Elf32_Addr start)
+{
+ struct prelink_entry *e;
+ Elf32_Addr low, hi;
+
+ ret[0] = beg;
+ ret[3] = end;
+ for (e = l->list; e != NULL; e = e->next)
+ if (e->base >= start)
+ break;
+ if (e == l->list)
+ {
+ ret[1] = ret[2] = start;
+ return;
+ }
+
+ if (e == NULL)
+ e = l->list;
+ low = start;
+ for (e = e->prev; ; e = e->prev)
+ {
+ if (e->base < beg)
+ break;
+ if (e->layend > low)
+ low = e->base;
+ if (e == l->list)
+ break;
+ }
+
+ if (low == start)
+ {
+ ret[1] = ret[2] = start;
+ return;
+ }
+
+ hi = start;
+ for (; e; e = e->next)
+ {
+ if (e->base >= end)
+ break;
+ if (e->base >= hi)
+ break;
+ if (e->layend > hi)
+ hi = e->layend;
+ }
+
+ assert (low >= beg && hi <= end);
+
+ if (hi - start > start - low)
+ start = low;
+ else
+ start = hi;
+
+ ret[1] = ret[2] = start;
+}
+
+static int
+i386_layout_libs_pre (struct layout_libs *l)
+{
+ Elf32_Addr mmap_start, virt;
+ struct prelink_entry *e, *next;
+ struct i386_layout_data *pld;
+ int i;
+
+ if (!exec_shield)
+ {
+ l->mmap_fin = l->mmap_end;
+ l->fake = NULL;
+ l->fakecnt = 0;
+ return 0;
+ }
+
+ pld = calloc (sizeof (*pld), 1);
+ if (pld == NULL)
+ error (EXIT_FAILURE, ENOMEM, "Cannot lay libraries out");
+
+ l->arch_data = pld;
+
+ mmap_start = l->mmap_start - REG0S;
+ /* Unless not randomizing, try not to make the first region
+ too small, because otherwise it is likely libc.so as first
+ big library would often end up at REG0S. */
+ virt = mmap_start % (REG0E - REG0S - 0x200000);
+ i386_find_free_addr (l, pld->addrs + 0, REG0S, REG0E, REG0S + virt);
+ virt = mmap_start % (REG1E - REG1S - 0x200000);
+ i386_find_free_addr (l, pld->addrs + 4, REG1S, REG1E, REG1S + virt);
+ virt = mmap_start % (REG0E - REG0S - 0x200000);
+ i386_find_free_addr (l, pld->addrs + 8, REG2S, REG2E, REG2S + virt);
+ i = 0;
+ virt = pld->addrs[3] - pld->addrs[2];
+ pld->e[0].u.tmp = -1;
+ pld->e[0].base = virt;
+ pld->e[0].end = pld->e[0].base;
+ pld->e[0].layend = pld->e[0].end;
+ pld->e[0].prev = &pld->e[0];
+ next = NULL;
+ for (e = l->list; e != NULL; e = next)
+ {
+ next = e->next;
+ while (i < 5
+ && (e->base >= pld->addrs[2 * i + 1]
+ || pld->addrs[2 * i] == pld->addrs[2 * i + 1]))
+ {
+ ++i;
+ pld->e[i].u.tmp = -1;
+ if (i & 1)
+ virt -= pld->addrs[2 * i + 1] - pld->addrs[2 * i];
+ else
+ {
+ virt += pld->addrs[2 * i - 1] - pld->addrs[2 * i - 4];
+ virt += pld->addrs[2 * i + 3] - pld->addrs[2 * i + 2];
+ }
+ pld->e[i].base = virt;
+ pld->e[i].end = pld->e[i].base;
+ pld->e[i].layend = pld->e[i].end;
+ pld->e[i].prev = &pld->e[i];
+ }
+ e->base += (Elf32_Sword) (virt - pld->addrs[2 * i]);
+ e->end += (Elf32_Sword) (virt - pld->addrs[2 * i]);
+ e->layend += (Elf32_Sword) (virt - pld->addrs[2 * i]);
+ list_append (&pld->e[i], e);
+ }
+ while (i < 5)
+ {
+ ++i;
+ pld->e[i].u.tmp = -1;
+ if (i & 1)
+ virt -= pld->addrs[2 * i + 1] - pld->addrs[2 * i];
+ else
+ {
+ virt += pld->addrs[2 * i - 1] - pld->addrs[2 * i - 4];
+ virt += pld->addrs[2 * i + 3] - pld->addrs[2 * i + 2];
+ }
+ pld->e[i].base = virt;
+ pld->e[i].end = pld->e[i].base;
+ pld->e[i].layend = pld->e[i].end;
+ pld->e[i].prev = &pld->e[i];
+ }
+ l->list = &pld->e[1];
+ list_merge (&pld->e[1], &pld->e[0]);
+ list_merge (&pld->e[1], &pld->e[3]);
+ list_merge (&pld->e[1], &pld->e[2]);
+ list_merge (&pld->e[1], &pld->e[5]);
+ list_merge (&pld->e[1], &pld->e[4]);
+
+ l->mmap_start = 0;
+ l->mmap_base = 0;
+ l->mmap_fin = virt + pld->addrs[2 * i + 1] - pld->addrs[2 * i];
+ l->mmap_end = l->mmap_fin;
+ l->fakecnt = 6;
+ l->fake = pld->e;
+
+ return 0;
+}
+
+static int
+i386_layout_libs_post (struct layout_libs *l)
+{
+ struct prelink_entry *e;
+ struct i386_layout_data *pld = (struct i386_layout_data *) l->arch_data;
+ Elf32_Sword adj = 0;
+ int i;
+
+ if (!exec_shield)
+ return 0;
+
+ for (i = 0, e = l->list; e != NULL; e = e->next)
+ {
+ if (e == &pld->e[i ^ 1])
+ {
+ adj = pld->addrs[2 * (i ^ 1)] - e->base;
+ ++i;
+ }
+ else
+ {
+ e->base += adj;
+ e->end += adj;
+ e->layend += adj;
+ }
+ }
+
+ free (l->arch_data);
+ return 0;
+}
+
+PL_ARCH = {
+ .name = "i386",
+ .class = ELFCLASS32,
+ .machine = EM_386,
+ .alternate_machine = { EM_NONE },
+ .R_JMP_SLOT = R_386_JMP_SLOT,
+ .R_COPY = R_386_COPY,
+ .R_RELATIVE = R_386_RELATIVE,
+ .dynamic_linker = "/lib/ld-linux.so.2",
+ .adjust_dyn = i386_adjust_dyn,
+ .adjust_rel = i386_adjust_rel,
+ .adjust_rela = i386_adjust_rela,
+ .prelink_rel = i386_prelink_rel,
+ .prelink_rela = i386_prelink_rela,
+ .prelink_conflict_rel = i386_prelink_conflict_rel,
+ .prelink_conflict_rela = i386_prelink_conflict_rela,
+ .apply_conflict_rela = i386_apply_conflict_rela,
+ .apply_rel = i386_apply_rel,
+ .apply_rela = i386_apply_rela,
+ .rel_to_rela = i386_rel_to_rela,
+ .rela_to_rel = i386_rela_to_rel,
+ .need_rel_to_rela = i386_need_rel_to_rela,
+ .reloc_size = i386_reloc_size,
+ .reloc_class = i386_reloc_class,
+ .max_reloc_size = 4,
+ .arch_prelink = i386_arch_prelink,
+ .arch_undo_prelink = i386_arch_undo_prelink,
+ .undo_prelink_rel = i386_undo_prelink_rel,
+ .layout_libs_init = i386_layout_libs_init,
+ .layout_libs_pre = i386_layout_libs_pre,
+ .layout_libs_post = i386_layout_libs_post,
+ /* Although TASK_UNMAPPED_BASE is 0x40000000, we leave some
+ area so that mmap of /etc/ld.so.cache and ld.so's malloc
+ does not take some library's VA slot.
+ Also, if this guard area isn't too small, typically
+ even dlopened libraries will get the slots they desire. */
+ .mmap_base = REG2S,
+ .mmap_end = REG2E,
+ .max_page_size = 0x1000,
+ .page_size = 0x1000
+};