diff options
Diffstat (limited to '')
-rw-r--r-- | grub-core/kern/ia64/dl.c | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c new file mode 100644 index 0000000..db59300 --- /dev/null +++ b/grub-core/kern/ia64/dl.c @@ -0,0 +1,150 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2005,2007,2009 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/elf.h> +#include <grub/misc.h> +#include <grub/err.h> +#include <grub/mm.h> +#include <grub/i18n.h> +#include <grub/ia64/reloc.h> + +#define MASK19 ((1 << 19) - 1) +#define MASK20 ((1 << 20) - 1) + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS64 + || e->e_ident[EI_DATA] != ELFDATA2LSB + || e->e_machine != EM_IA_64) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rela *rel, *max; + + for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rela *) ((char *) rel + s->sh_entsize)) + { + grub_addr_t addr; + Elf_Sym *sym; + grub_uint64_t value; + + if (seg->size < (rel->r_offset & ~3)) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr = (grub_addr_t) seg->addr + rel->r_offset; + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + /* On the PPC the value does not have an explicit + addend, add it. */ + value = sym->st_value + rel->r_addend; + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_IA64_PCREL21B: + { + grub_int64_t noff; + if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) + { + struct grub_ia64_trampoline *tr = mod->trampptr; + grub_ia64_make_trampoline (tr, value); + noff = ((char *) tr - (char *) (addr & ~3)) >> 4; + mod->trampptr = tr + 1; + } + else + noff = ((char *) value - (char *) (addr & ~3)) >> 4; + + if ((noff & ~MASK19) && ((-noff) & ~MASK19)) + return grub_error (GRUB_ERR_BAD_MODULE, + "jump offset too big (%lx)", noff); + grub_ia64_add_value_to_slot_20b (addr, noff); + } + break; + case R_IA64_SEGREL64LSB: + *(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr; + break; + case R_IA64_FPTR64LSB: + case R_IA64_DIR64LSB: + *(grub_uint64_t *) addr += value; + break; + case R_IA64_PCREL64LSB: + *(grub_uint64_t *) addr += value - addr; + break; + case R_IA64_GPREL64I: + grub_ia64_set_immu64 (addr, value - (grub_addr_t) mod->base); + break; + case R_IA64_GPREL22: + if ((value - (grub_addr_t) mod->base) & ~MASK20) + return grub_error (GRUB_ERR_BAD_MODULE, + "gprel offset too big (%lx)", + value - (grub_addr_t) mod->base); + grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) mod->base); + break; + + case R_IA64_LTOFF22X: + case R_IA64_LTOFF22: + if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) + value = *(grub_uint64_t *) sym->st_value + rel->r_addend; + /* Fallthrough. */ + case R_IA64_LTOFF_FPTR22: + { + grub_uint64_t *gpptr = mod->gotptr; + *gpptr = value; + if (((grub_addr_t) gpptr - (grub_addr_t) mod->base) & ~MASK20) + return grub_error (GRUB_ERR_BAD_MODULE, + "gprel offset too big (%lx)", + (grub_addr_t) gpptr - (grub_addr_t) mod->base); + grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) mod->base); + mod->gotptr = gpptr + 1; + break; + } + /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */ + case R_IA64_LDXMOV: + break; + default: + { + char rel_info[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T, + ELF_R_TYPE (rel->r_info)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%s is not implemented yet"), rel_info); + } + } + } + return GRUB_ERR_NONE; +} |