diff options
Diffstat (limited to 'trunk/src/arch-alpha.c')
-rw-r--r-- | trunk/src/arch-alpha.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/trunk/src/arch-alpha.c b/trunk/src/arch-alpha.c new file mode 100644 index 0000000..51182ed --- /dev/null +++ b/trunk/src/arch-alpha.c @@ -0,0 +1,497 @@ +/* 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" + +static int +alpha_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + return 0; +} + +static int +alpha_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", dso->filename); + return 1; +} + +static int +alpha_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + if (GELF_R_TYPE (rela->r_info) == R_ALPHA_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_ALPHA_JMP_SLOT) + { + GElf_Addr val = read_ule64 (dso, rela->r_offset); + + if (val >= start) + { + write_le64 (dso, rela->r_offset, val + adjust); + if (val == rela->r_addend) + rela->r_addend += adjust; + } + } + else if (GELF_R_TYPE (rela->r_info) == R_ALPHA_GLOB_DAT) + { + GElf_Addr val = read_ule64 (dso, rela->r_offset) - rela->r_addend; + + if (val && val >= start) + write_le64 (dso, rela->r_offset, val + adjust + rela->r_addend); + } + return 0; +} + +static int +alpha_prelink_rel (struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", info->dso->filename); + return 1; +} + +static void +alpha_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr, + GElf_Addr value) +{ + Elf64_Sxword disp; + Elf64_Addr plt; + + relaaddr -= dso->info[DT_JMPREL]; + relaaddr /= sizeof (Elf64_Rela); + relaaddr *= 12; + plt = dso->info[DT_PLTGOT] + 32 + relaaddr; + disp = ((Elf64_Sxword) (value - plt - 12)) / 4; + if (disp >= -0x100000 && disp < 0x100000) + { + int32_t hi, lo; + + hi = value - plt; + lo = (int16_t) hi; + hi = (hi - lo) >> 16; + + /* ldah $27,hi($27) + lda $27,lo($27) + br $31,value */ + write_le32 (dso, plt, 0x277b0000 | (hi & 0xffff)); + write_le32 (dso, plt + 4, 0x237b0000 | (lo & 0xffff)); + write_le32 (dso, plt + 8, 0xc3e00000 | (disp & 0x1fffff)); + } + else + { + int32_t hi, lo; + + hi = rela->r_offset - plt; + lo = (int16_t) hi; + hi = (hi - lo) >> 16; + + /* ldah $27,hi($27) + ldq $27,lo($27) + jmp $31,($27) */ + write_le32 (dso, plt, 0x277b0000 | (hi & 0xffff)); + write_le32 (dso, plt + 4, 0xa77b0000 | (lo & 0xffff)); + write_le32 (dso, plt + 8, 0x6bfb0000); + } +} + +static int +alpha_is_indirect_plt (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + Elf64_Addr pltaddr; + uint32_t plt[3]; + int32_t hi, lo; + + relaaddr -= dso->info[DT_JMPREL]; + relaaddr /= sizeof (Elf64_Rela); + relaaddr *= 12; + pltaddr = dso->info[DT_PLTGOT] + 32 + relaaddr; + hi = rela->r_offset - pltaddr; + lo = (int16_t) hi; + hi = (hi - lo) >> 16; + plt[0] = read_ule32 (dso, pltaddr); + plt[1] = read_ule32 (dso, pltaddr + 4); + plt[2] = read_ule32 (dso, pltaddr + 8); + if (plt[0] == (0x277b0000 | (hi & 0xffff)) + && plt[1] == (0xa77b0000 | (lo & 0xffff)) + && plt[2] == 0x6bfb0000) + return 1; + return 0; +} + +static int +alpha_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_ALPHA_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_ALPHA_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; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ALPHA_GLOB_DAT: + case R_ALPHA_REFQUAD: + case R_ALPHA_DTPREL64: + write_le64 (dso, rela->r_offset, value); + break; + case R_ALPHA_JMP_SLOT: + write_le64 (dso, rela->r_offset, value); + alpha_fixup_plt (dso, rela, relaaddr, value); + break; + /* DTPMOD64 and TPREL64 is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_ALPHA_DTPMOD64: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_ALPHA_DTPMOD64 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_ALPHA_TPREL64: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_le64 (dso, rela->r_offset, value + info->resolvetls->offset); + break; + default: + error (0, 0, "%s: Unknown alpha relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +alpha_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf) +{ + switch (GELF_R_TYPE (rela->r_info) & 0xff) + { + case R_ALPHA_GLOB_DAT: + case R_ALPHA_REFQUAD: + case R_ALPHA_JMP_SLOT: + buf_write_le64 (buf, rela->r_addend); + break; + default: + abort (); + } + return 0; +} + +static int +alpha_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +alpha_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_ALPHA_NONE: + break; + case R_ALPHA_GLOB_DAT: + case R_ALPHA_REFQUAD: + case R_ALPHA_JMP_SLOT: + buf_write_le64 (buf, value + rela->r_addend); + break; + case R_ALPHA_RELATIVE: + error (0, 0, "%s: R_ALPHA_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +alpha_prelink_conflict_rel (DSO *dso, struct prelink_info *info, + GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", dso->filename); + return 1; +} + +static int +alpha_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_ALPHA_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_ALPHA_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 DTPMOD64 and TPREL64 relocs need conflicts. */ + case R_ALPHA_DTPMOD64: + case R_ALPHA_TPREL64: + break; + default: + return 0; + } + value = 0; + } + else + { + /* DTPREL64 wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rela->r_info) == R_ALPHA_DTPREL64 + && 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_ALPHA_GLOB_DAT: + case R_ALPHA_REFQUAD: + ret->r_addend = value + rela->r_addend; + break; + case R_ALPHA_JMP_SLOT: + ret->r_addend = value + rela->r_addend; + if (alpha_is_indirect_plt (dso, rela, relaaddr)) + ret->r_info = GELF_R_INFO (0, R_ALPHA_GLOB_DAT); + else + { + relaaddr -= dso->info[DT_JMPREL]; + relaaddr /= sizeof (Elf64_Rela); + if (relaaddr > 0xffffff) + { + error (0, 0, "%s: Cannot create R_ALPHA_JMP_SLOT conflict against .rel.plt with more than 16M entries", + dso->filename); + return 1; + } + ret->r_info = GELF_R_INFO (0, (relaaddr << 8) | R_ALPHA_JMP_SLOT); + } + break; + case R_ALPHA_DTPMOD64: + case R_ALPHA_DTPREL64: + case R_ALPHA_TPREL64: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc 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_ALPHA_GLOB_DAT); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ALPHA_DTPMOD64: + ret->r_addend = tls->modid; + break; + case R_ALPHA_DTPREL64: + ret->r_addend = value + rela->r_addend; + break; + case R_ALPHA_TPREL64: + ret->r_addend = value + rela->r_addend + tls->offset; + break; + } + break; + default: + error (0, 0, "%s: Unknown Alpha relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +alpha_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", dso->filename); + return 1; +} + +static int +alpha_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +alpha_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + + /* Correct sh_entsize on .plt sections. */ + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT] + 16); + assert (sec != -1); + if (dso->shdr[sec].sh_type == SHT_PROGBITS + && dso->shdr[sec].sh_entsize == 32) + dso->shdr[sec].sh_entsize = 0; + } + return 0; +} + +static int +alpha_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + int sec; + Elf_Scn *scn; + Elf_Data *data; + GElf_Sym sym; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ALPHA_NONE: + case R_ALPHA_RELATIVE: + break; + case R_ALPHA_JMP_SLOT: + relaaddr -= dso->info[DT_JMPREL]; + relaaddr /= sizeof (Elf64_Rela); + relaaddr *= 12; + relaaddr += dso->info[DT_PLTGOT] + 32; + /* br at,.plt */ + write_le32 (dso, relaaddr, + 0xc39fffff - (relaaddr - dso->info[DT_PLTGOT]) / 4); + write_le64 (dso, relaaddr + 4, 0); + write_le64 (dso, rela->r_offset, relaaddr); + break; + case R_ALPHA_GLOB_DAT: + /* This is ugly. Linker doesn't clear memory at r_offset of GLOB_DAT + reloc, but instead puts in sym.st_value + addend. */ + sec = addr_to_sec (dso, relaaddr); + assert (sec != -1); + sec = dso->shdr[sec].sh_link; + assert (sec > 0 && sec < dso->ehdr.e_shnum); + scn = dso->scn[sec]; + data = elf_getdata (scn, NULL); + assert (data != NULL && elf_getdata (scn, data) == NULL); + assert (GELF_R_SYM (rela->r_info) + <= dso->shdr[sec].sh_size / sizeof (Elf64_Sym)); + gelfx_getsym (dso->elf, data, GELF_R_SYM (rela->r_info), &sym); + write_le64 (dso, rela->r_offset, sym.st_value + rela->r_addend); + break; + case R_ALPHA_REFQUAD: + case R_ALPHA_DTPMOD64: + case R_ALPHA_DTPREL64: + case R_ALPHA_TPREL64: + write_le64 (dso, rela->r_offset, 0); + break; + default: + error (0, 0, "%s: Unknown alpha relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +alpha_reloc_size (int reloc_type) +{ + return 8; +} + +static int +alpha_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_ALPHA_JMP_SLOT: + return RTYPE_CLASS_PLT; + case R_ALPHA_DTPMOD64: + case R_ALPHA_DTPREL64: + case R_ALPHA_TPREL64: + return RTYPE_CLASS_TLS; + default: + return RTYPE_CLASS_VALID; + } +} + +PL_ARCH = { + .name = "Alpha", + .class = ELFCLASS64, + .machine = EM_ALPHA, + .alternate_machine = { EM_FAKE_ALPHA }, + .R_JMP_SLOT = R_ALPHA_JMP_SLOT, + .R_COPY = -1, + .R_RELATIVE = R_ALPHA_RELATIVE, + .dynamic_linker = "/lib/ld-linux.so.2", + .adjust_dyn = alpha_adjust_dyn, + .adjust_rel = alpha_adjust_rel, + .adjust_rela = alpha_adjust_rela, + .prelink_rel = alpha_prelink_rel, + .prelink_rela = alpha_prelink_rela, + .prelink_conflict_rel = alpha_prelink_conflict_rel, + .prelink_conflict_rela = alpha_prelink_conflict_rela, + .apply_conflict_rela = alpha_apply_conflict_rela, + .apply_rel = alpha_apply_rel, + .apply_rela = alpha_apply_rela, + .rel_to_rela = alpha_rel_to_rela, + .need_rel_to_rela = alpha_need_rel_to_rela, + .reloc_size = alpha_reloc_size, + .reloc_class = alpha_reloc_class, + .max_reloc_size = 8, + .arch_prelink = alpha_arch_prelink, + .undo_prelink_rela = alpha_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x0000020000000000, 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 = 0x0000020001000000LL, + .mmap_end = 0x0000020100000000LL, + .max_page_size = 0x10000, + .page_size = 0x02000 +}; |