summaryrefslogtreecommitdiffstats
path: root/trunk/src/arch-ia64.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/arch-ia64.c')
-rw-r--r--trunk/src/arch-ia64.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/trunk/src/arch-ia64.c b/trunk/src/arch-ia64.c
new file mode 100644
index 0000000..0be672c
--- /dev/null
+++ b/trunk/src/arch-ia64.c
@@ -0,0 +1,525 @@
+/* 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 "fptr.h"
+
+static int
+ia64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
+ GElf_Addr adjust)
+{
+ if (dyn->d_tag == DT_IA_64_PLT_RESERVE)
+ {
+ int sec = addr_to_sec (dso, dyn->d_un.d_ptr);
+ Elf64_Addr data;
+
+ if (sec != -1)
+ {
+ data = read_ule64 (dso, dyn->d_un.d_ptr + 8);
+
+ /* If .got[1] points to .plt + 0x30, 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 + 0x30
+ && dso->shdr[i].sh_type == SHT_PROGBITS
+ && strcmp (strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name), ".plt") == 0)
+ {
+ write_le64 (dso, dyn->d_un.d_ptr + 8, data + adjust);
+ break;
+ }
+ }
+ }
+
+ if (dyn->d_un.d_ptr >= start)
+ dyn->d_un.d_ptr += adjust;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+ia64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
+ GElf_Addr adjust)
+{
+ error (0, 0, "%s: IA-64 doesn't support REL relocs", dso->filename);
+ return 1;
+}
+
+static int
+ia64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
+ GElf_Addr adjust)
+{
+ if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_REL32MSB
+ && rela->r_addend >= start)
+ {
+ rela->r_addend += adjust;
+ switch (GELF_R_TYPE (rela->r_info) & 3)
+ {
+ case 0: write_be32 (dso, rela->r_offset, rela->r_addend); break;
+ case 1: write_le32 (dso, rela->r_offset, rela->r_addend); break;
+ case 2: write_be64 (dso, rela->r_offset, rela->r_addend); break;
+ case 3: write_le64 (dso, rela->r_offset, rela->r_addend); break;
+ }
+ }
+ else if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB)
+ {
+ GElf_Addr val, gp;
+
+ if (GELF_R_TYPE (rela->r_info) & 1)
+ {
+ val = read_ule64 (dso, rela->r_offset);
+ gp = read_ule64 (dso, rela->r_offset + 8);
+ }
+ else
+ {
+ val = read_ube64 (dso, rela->r_offset);
+ gp = read_ube64 (dso, rela->r_offset + 8);
+ }
+ if (gp == dso->info[DT_PLTGOT])
+ {
+ if (val >= start)
+ val += adjust;
+ if (gp >= start)
+ gp += adjust;
+ }
+ if (GELF_R_TYPE (rela->r_info) & 1)
+ {
+ write_le64 (dso, rela->r_offset, val);
+ write_le64 (dso, rela->r_offset + 8, gp);
+ }
+ else
+ {
+ write_le64 (dso, rela->r_offset, val);
+ write_le64 (dso, rela->r_offset + 8, gp);
+ }
+ }
+ return 0;
+}
+
+static int
+ia64_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr)
+{
+ error (0, 0, "%s: IA-64 doesn't support REL relocs", info->dso->filename);
+ return 1;
+}
+
+static int
+ia64_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
+ GElf_Addr relaaddr)
+{
+ DSO *dso;
+ GElf_Addr value;
+
+ if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_REL32MSB
+ || GELF_R_TYPE (rela->r_info) == R_IA64_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));
+ value += rela->r_addend;
+ if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_DIR32MSB)
+ {
+ /* Nothing to do. */
+ }
+ else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_PCREL32MSB)
+ {
+ value -= rela->r_offset & -16;
+ }
+ else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_FPTR32MSB)
+ {
+ /* FIXME */
+ }
+ else if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB)
+ {
+ GElf_Addr gp = info->resolveent->pltgot;
+
+ if (GELF_R_TYPE (rela->r_info) & 1)
+ {
+ write_le64 (dso, rela->r_offset, value);
+ write_le64 (dso, rela->r_offset + 8, gp);
+ }
+ else
+ {
+ write_be64 (dso, rela->r_offset, value);
+ write_be64 (dso, rela->r_offset + 8, gp);
+ }
+
+ return 0;
+ }
+ else
+ {
+ error (0, 0, "%s: Unknown ia64 relocation type %d", dso->filename,
+ (int) GELF_R_TYPE (rela->r_info));
+ return 1;
+ }
+
+ switch (GELF_R_TYPE (rela->r_info) & 3)
+ {
+ case 0: write_be32 (dso, rela->r_offset, value); break;
+ case 1: write_le32 (dso, rela->r_offset, value); break;
+ case 2: write_be64 (dso, rela->r_offset, value); break;
+ case 3: write_le64 (dso, rela->r_offset, value); break;
+ }
+ return 0;
+}
+
+static int
+ia64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
+ char *buf)
+{
+ if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB)
+ {
+ GElf_Addr gp = 0;
+ int i;
+
+ for (i = 0; i < info->ent->ndepends; ++i)
+ if (info->ent->depends[i]->base <= rela->r_addend
+ && info->ent->depends[i]->end > rela->r_addend)
+ {
+ gp = info->ent->depends[i]->pltgot;
+ break;
+ }
+
+ if (i == info->ent->ndepends)
+ abort ();
+
+ if (GELF_R_TYPE (rela->r_info) & 1)
+ {
+ buf_write_le64 (buf, rela->r_addend);
+ buf_write_le64 (buf + 8, gp);
+ }
+ else
+ {
+ buf_write_be64 (buf, rela->r_addend);
+ buf_write_be64 (buf + 8, gp);
+ }
+ return 0;
+ }
+
+ switch (GELF_R_TYPE (rela->r_info))
+ {
+ case R_IA64_DIR32MSB: buf_write_be32 (buf, rela->r_addend); break;
+ case R_IA64_DIR32LSB: buf_write_le32 (buf, rela->r_addend); break;
+ case R_IA64_DIR64MSB: buf_write_be64 (buf, rela->r_addend); break;
+ case R_IA64_DIR64LSB: buf_write_le64 (buf, rela->r_addend); break;
+ default:
+ abort ();
+ }
+ return 0;
+}
+
+static int
+ia64_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf)
+{
+ error (0, 0, "%s: IA-64 doesn't support REL relocs", info->dso->filename);
+ return 1;
+}
+
+static int
+ia64_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));
+ value += rela->r_addend;
+ if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_DIR32MSB)
+ {
+ /* Nothing to do. */
+ }
+ else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_PCREL32MSB)
+ {
+ value -= rela->r_offset & -16;
+ }
+ else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_FPTR32MSB)
+ {
+ /* FIXME */
+ }
+ else if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB)
+ {
+ GElf_Addr gp = info->resolveent->pltgot;
+
+ if (GELF_R_TYPE (rela->r_info) & 1)
+ {
+ buf_write_le64 (buf, value);
+ buf_write_le64 (buf + 8, gp);
+ }
+ else
+ {
+ buf_write_be64 (buf, value);
+ buf_write_be64 (buf + 8, gp);
+ }
+ return 0;
+ }
+ else
+ return 1;
+
+ switch (GELF_R_TYPE (rela->r_info) & 3)
+ {
+ case 0: buf_write_be32 (buf, value); break;
+ case 1: buf_write_le32 (buf, value); break;
+ case 2: buf_write_be64 (buf, value); break;
+ case 3: buf_write_le64 (buf, value); break;
+ }
+ return 0;
+}
+
+static int
+ia64_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
+ GElf_Addr reladdr)
+{
+ error (0, 0, "%s: IA-64 doesn't support REL relocs", dso->filename);
+ return 1;
+}
+
+static int
+ia64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
+ GElf_Rela *rela, GElf_Addr relaaddr)
+{
+ GElf_Addr value;
+ struct prelink_conflict *conflict;
+ GElf_Rela *ret;
+
+ if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_REL32MSB
+ || GELF_R_TYPE (rela->r_info) == R_IA64_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)
+ 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));
+ if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_DIR32MSB
+ || (GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB)
+ {
+ ret->r_addend = value + rela->r_addend;
+ return 0;
+ }
+ else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_PCREL32MSB)
+ {
+ ret->r_addend = value + rela->r_addend - (rela->r_offset & -16);
+ ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info)
+ + R_IA64_DIR32MSB - R_IA64_PCREL32MSB);
+ return 0;
+ }
+ else if (GELF_R_TYPE (rela->r_info) == R_IA64_COPY)
+ {
+ error (0, 0, "R_IA64_COPY should not be present in shared libraries");
+ return 1;
+ }
+ error (0, 0, "%s: Unknown ia64 relocation type %d", dso->filename,
+ (int) GELF_R_TYPE (rela->r_info));
+ return 1;
+}
+
+static int
+ia64_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela)
+{
+ error (0, 0, "%s: IA-64 doesn't support REL relocs", dso->filename);
+ return 1;
+}
+
+static int
+ia64_need_rel_to_rela (DSO *dso, int first, int last)
+{
+ return 0;
+}
+
+static GElf_Addr
+ia64_create_opd (struct prelink_info *info, int first, int last, int plt)
+{
+ Elf_Data *d;
+ Elf_Scn *scn;
+ Elf64_Rela *rela, *relaend;
+ DSO *dso = info->dso;
+ int sec;
+
+ if (opd_init (info))
+ return -1;
+
+ if (plt)
+ info->ent->opd->plt_start = dso->shdr[dso->shdr[plt].sh_info].sh_addr;
+ else
+ info->ent->opd->plt_start = dso->shdr[dso->dynamic].sh_addr;
+ sec = first;
+ while (sec <= last)
+ {
+ d = NULL;
+ scn = dso->scn[sec++];
+ while ((d = elf_getdata (scn, d)) != NULL)
+ {
+ rela = (Elf64_Rela *) d->d_buf;
+ relaend = rela + d->d_size / sizeof (Elf64_Rela);
+ for (; rela < relaend; rela++)
+ if ((ELF64_R_TYPE (rela->r_info) & ~3) == R_IA64_FPTR32MSB
+ && opd_add (info, ELF64_R_SYM (rela->r_info),
+ R_IA64_FPTR64LSB))
+ return -1;
+ }
+ }
+
+ sec = first;
+ while (sec)
+ {
+ d = NULL;
+ if (sec == plt)
+ break;
+ scn = dso->scn[sec++];
+ if (sec == last + 1)
+ sec = plt;
+ while ((d = elf_getdata (scn, d)) != NULL)
+ {
+ rela = (Elf64_Rela *) d->d_buf;
+ relaend = rela + d->d_size / sizeof (Elf64_Rela);
+ for (; rela < relaend; rela++)
+ if ((ELF64_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB)
+ opd_note_plt (info, ELF64_R_SYM (rela->r_info), R_IA64_IPLTLSB,
+ rela->r_offset);
+ }
+ }
+
+ return opd_size (info, 16);
+}
+
+static int
+ia64_arch_prelink (struct prelink_info *info)
+{
+ DSO *dso;
+ int plt = -1, got = -1, i;
+ const char *name;
+
+ /* Write address of .plt + 0x30 into .got[1].
+ .plt + 0x30 is what .IA_64.pltoff[0] contains unless prelinking. */
+
+ dso = info->dso;
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ if (dso->shdr[i].sh_type == SHT_PROGBITS)
+ {
+ name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name);
+ if (! strcmp (name, ".got"))
+ {
+ if (got != -1)
+ {
+ error (0, 0, "%s: More than one .got section", dso->filename);
+ return 1;
+ }
+ got = i;
+ }
+ else if (! strcmp (name, ".plt"))
+ {
+ if (plt != -1)
+ {
+ error (0, 0, "%s: More than one .plt section", dso->filename);
+ return 1;
+ }
+ plt = i;
+ }
+ }
+
+ if (plt == -1)
+ return 0;
+
+ if (got == -1)
+ {
+ error (0, 0, "%s: Has .plt section but not .got section", dso->filename);
+ return 1;
+ }
+
+ write_le64 (dso, dso->shdr[got].sh_addr + 8, dso->shdr[plt].sh_addr + 0x30);
+ return 0;
+}
+
+static int
+ia64_reloc_size (int reloc_type)
+{
+ if ((reloc_type & ~1) == R_IA64_IPLTMSB)
+ return 16;
+ return (reloc_type & 2) ? 8 : 4;
+}
+
+static int
+ia64_reloc_class (int reloc_type)
+{
+ switch (reloc_type)
+ {
+ case R_IA64_IPLTLSB:
+ case R_IA64_IPLTMSB:
+ return RTYPE_CLASS_PLT;
+ default: return RTYPE_CLASS_VALID;
+ }
+}
+
+PL_ARCH = {
+ .name = "IA-64",
+ .class = ELFCLASS64,
+ .machine = EM_IA_64,
+ .alternate_machine = { EM_NONE },
+ .R_JMP_SLOT = R_IA64_IPLTLSB,
+ .R_COPY = -1,
+ .R_RELATIVE = R_IA64_REL64LSB,
+ .dynamic_linker = "/lib/ld-linux-ia64.so.2",
+ .adjust_dyn = ia64_adjust_dyn,
+ .adjust_rel = ia64_adjust_rel,
+ .adjust_rela = ia64_adjust_rela,
+ .prelink_rel = ia64_prelink_rel,
+ .prelink_rela = ia64_prelink_rela,
+ .prelink_conflict_rel = ia64_prelink_conflict_rel,
+ .prelink_conflict_rela = ia64_prelink_conflict_rela,
+ .apply_conflict_rela = ia64_apply_conflict_rela,
+ .apply_rel = ia64_apply_rel,
+ .apply_rela = ia64_apply_rela,
+ .rel_to_rela = ia64_rel_to_rela,
+ .need_rel_to_rela = ia64_need_rel_to_rela,
+ .create_opd = ia64_create_opd,
+ .reloc_size = ia64_reloc_size,
+ .reloc_class = ia64_reloc_class,
+ .max_reloc_size = 16,
+ .arch_prelink = ia64_arch_prelink,
+ /* Although TASK_UNMAPPED_BASE is 0x2000000000000000, 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 = 0x2000000010000000LL,
+ .mmap_end = 0x4000000000000000LL,
+ .max_page_size = 0x10000,
+ /* The kernel can be configured for 4K, 8K, 16K and 64K,
+ but most kernels have at least 8K. */
+ .page_size = 0x02000
+};