#include <grub/i18n.h>
#include <grub/cpu/reloc.h>
-struct trampoline
-{
#define LDR 0x58000050
#define BR 0xd61f0200
- grub_uint32_t ldr; /* ldr x16, 8 */
- grub_uint32_t br; /* br x16 */
- grub_uint64_t addr;
-};
+
/*
* Check if EHDR is a valid ELF header.
#pragma GCC diagnostic ignored "-Wcast-align"
-grub_err_t
-grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
- grub_size_t *got)
-{
- const Elf_Ehdr *e = ehdr;
- const Elf_Shdr *s;
- unsigned i;
-
- *tramp = 0;
- *got = 0;
-
- for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
- i < e->e_shnum;
- i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
- if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA)
- {
- const Elf_Rel *rel, *max;
-
- for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset),
- max = rel + s->sh_size / s->sh_entsize;
- rel < max;
- rel = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_entsize))
- switch (ELF_R_TYPE (rel->r_info))
- {
- case R_AARCH64_CALL26:
- case R_AARCH64_JUMP26:
- {
- *tramp += sizeof (struct trampoline);
- break;
- }
- }
- }
-
- return GRUB_ERR_NONE;
-}
-
/*
* Unified function for both REL and RELA
*/
Elf_Shdr *s, grub_dl_segment_t seg)
{
Elf_Rel *rel, *max;
+ unsigned unmatched_adr_got_page = 0;
for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
max = (Elf_Rel *) ((char *) rel + s->sh_size);
if (!grub_arm_64_check_xxxx26_offset (offset))
{
- struct trampoline *tp = mod->trampptr;
+ struct grub_arm64_trampoline *tp = mod->trampptr;
mod->trampptr = tp + 1;
tp->ldr = LDR;
tp->br = BR;
grub_arm64_set_xxxx26_offset (place, offset);
}
break;
+ case R_AARCH64_PREL32:
+ {
+ grub_int64_t value;
+ Elf64_Word *addr32 = place;
+ value = ((grub_int32_t) *addr32) + sym_addr -
+ (Elf64_Xword) (grub_addr_t) seg->addr - rel->r_offset;
+ if (value != (grub_int32_t) value)
+ return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range");
+ grub_dprintf("dl", " reloc_prel32 %p => 0x%016llx\n",
+ place, (unsigned long long) sym_addr);
+ *addr32 = value;
+ }
+ break;
+ case R_AARCH64_ADR_GOT_PAGE:
+ {
+ grub_uint64_t *gp = mod->gotptr;
+ Elf_Rela *rel2;
+ grub_int64_t gpoffset = ((grub_uint64_t) gp & ~0xfffULL) - (((grub_uint64_t) place) & ~0xfffULL);
+ *gp = (grub_uint64_t) sym_addr;
+ mod->gotptr = gp + 1;
+ unmatched_adr_got_page++;
+ grub_dprintf("dl", " reloc_got %p => 0x%016llx (0x%016llx)\n",
+ place, (unsigned long long) sym_addr, (unsigned long long) gp);
+ if (!grub_arm64_check_hi21_signed (gpoffset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "HI21 out of range");
+ grub_arm64_set_hi21(place, gpoffset);
+ for (rel2 = (Elf_Rela *) ((char *) rel + s->sh_entsize);
+ rel2 < (Elf_Rela *) max;
+ rel2 = (Elf_Rela *) ((char *) rel2 + s->sh_entsize))
+ if (ELF_R_SYM (rel2->r_info)
+ == ELF_R_SYM (rel->r_info)
+ && ((Elf_Rela *) rel)->r_addend == rel2->r_addend
+ && ELF_R_TYPE (rel2->r_info) == R_AARCH64_LD64_GOT_LO12_NC)
+ {
+ grub_arm64_set_abs_lo12_ldst64 ((void *) ((grub_addr_t) seg->addr + rel2->r_offset),
+ (grub_uint64_t)gp);
+ break;
+ }
+ if (rel2 >= (Elf_Rela *) max)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "ADR_GOT_PAGE without matching LD64_GOT_LO12_NC");
+ }
+ break;
+ case R_AARCH64_LD64_GOT_LO12_NC:
+ if (unmatched_adr_got_page == 0)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "LD64_GOT_LO12_NC without matching ADR_GOT_PAGE");
+ unmatched_adr_got_page--;
+ break;
case R_AARCH64_ADR_PREL_PG_HI21:
{
grub_int64_t offset = (sym_addr & ~0xfffULL) - (((grub_uint64_t) place) & ~0xfffULL);
*place &= insmask;
*place |= grub_cpu_to_le32 (target << 7) & ~insmask;
}
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+grub_err_t
+grub_arm64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
+ grub_size_t *got)
+{
+ const Elf64_Ehdr *e = ehdr;
+ const Elf64_Shdr *s;
+ unsigned i;
+
+ *tramp = 0;
+ *got = 0;
+
+ for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu64 (e->e_shoff));
+ i < grub_le_to_cpu16 (e->e_shnum);
+ i++, s = (Elf64_Shdr *) ((char *) s + grub_le_to_cpu16 (e->e_shentsize)))
+ if (s->sh_type == grub_cpu_to_le32_compile_time (SHT_REL)
+ || s->sh_type == grub_cpu_to_le32_compile_time (SHT_RELA))
+ {
+ const Elf64_Rela *rel, *max;
+
+ for (rel = (Elf64_Rela *) ((char *) e + grub_le_to_cpu64 (s->sh_offset)),
+ max = (const Elf64_Rela *) ((grub_addr_t) rel + grub_le_to_cpu64 (s->sh_size));
+ rel < max; rel = (const Elf64_Rela *) ((grub_addr_t) rel + grub_le_to_cpu64 (s->sh_entsize)))
+ switch (ELF64_R_TYPE (rel->r_info))
+ {
+ case R_AARCH64_CALL26:
+ case R_AARCH64_JUMP26:
+ *tramp += sizeof (struct grub_arm64_trampoline);
+ break;
+ case R_AARCH64_ADR_GOT_PAGE:
+ *got += 8;
+ break;
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
#ifndef GRUB_ARM64_RELOC_H
#define GRUB_ARM64_RELOC_H 1
+struct grub_arm64_trampoline
+{
+ grub_uint32_t ldr; /* ldr x16, 8 */
+ grub_uint32_t br; /* br x16 */
+ grub_uint64_t addr;
+};
+
int grub_arm_64_check_xxxx26_offset (grub_int64_t offset);
void
grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset);
grub_err_t
grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
grub_size_t *got);
+grub_err_t
+grub_arm64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
+ grub_size_t *got);
#if defined (__ia64__)
#define GRUB_ARCH_DL_TRAMP_ALIGN GRUB_IA64_DL_TRAMP_ALIGN
#define GRUB_ARCH_DL_GOT_ALIGN GRUB_IA64_DL_GOT_ALIGN
#define grub_arch_dl_get_tramp_got_size grub_ia64_dl_get_tramp_got_size
+#elif defined (__aarch64__)
+#define grub_arch_dl_get_tramp_got_size grub_arm64_dl_get_tramp_got_size
#else
grub_err_t
grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
#define R_AARCH64_NONE 0 /* No relocation. */
#define R_AARCH64_ABS64 257 /* Direct 64 bit. */
#define R_AARCH64_ABS32 258 /* Direct 32 bit. */
+#define R_AARCH64_PREL32 261
#define R_AARCH64_ADR_PREL_PG_HI21 275
#define R_AARCH64_ADD_ABS_LO12_NC 277
#define R_AARCH64_LDST64_ABS_LO12_NC 286
#define R_AARCH64_JUMP26 282 /* 26-bit relative. */
#define R_AARCH64_CALL26 283 /* 26-bit relative. */
+#define R_AARCH64_ADR_GOT_PAGE 311
+#define R_AARCH64_LD64_GOT_LO12_NC 312
#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */
#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */
#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */
size_t align;
grub_size_t ia64jmp_off;
grub_size_t tramp_off;
- grub_size_t ia64_got_off;
+ grub_size_t got_off;
grub_size_t got_size;
unsigned ia64jmpnum;
grub_uint32_t bss_start;
#ifdef MKIMAGE_ELF64
struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off);
grub_uint64_t *gpptr = (void *) (pe_target + got_off);
+ unsigned unmatched_adr_got_page = 0;
#define MASK19 ((1 << 19) - 1)
#else
grub_uint32_t *tr = (void *) (pe_target + tramp_off);
*target = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr);
}
break;
+ case R_AARCH64_PREL32:
+ {
+ grub_uint32_t *t32 = (grub_uint32_t *) target;
+ *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32)
+ + sym_addr
+ - target_section_addr - offset
+ - image_target->vaddr_offset);
+ grub_util_info ("relocating an R_AARCH64_PREL32 entry to 0x%x at the offset 0x%"
+ GRUB_HOST_PRIxLONG_LONG,
+ *t32, (unsigned long long) offset);
+ break;
+ }
case R_AARCH64_ADD_ABS_LO12_NC:
grub_arm64_set_abs_lo12 ((grub_uint32_t *) target,
sym_addr);
sym_addr);
}
break;
+ case R_AARCH64_ADR_GOT_PAGE:
+ {
+ Elf64_Rela *rel2;
+ grub_int64_t gpoffset = (((char *) gpptr - (char *) pe_target + image_target->vaddr_offset) & ~0xfffULL)
+ - ((offset + target_section_addr + image_target->vaddr_offset) & ~0xfffULL);
+ unsigned k;
+ *gpptr = grub_host_to_target64 (sym_addr);
+ unmatched_adr_got_page++;
+ if (!grub_arm64_check_hi21_signed (gpoffset))
+ grub_util_error ("HI21 out of range");
+ grub_arm64_set_hi21((grub_uint32_t *)target,
+ gpoffset);
+ for (k = 0, rel2 = (Elf_Rela *) ((char *) r + r_size);
+ k < num_rs;
+ k++, rel2 = (Elf_Rela *) ((char *) rel2 + r_size))
+ if (ELF_R_SYM (rel2->r_info)
+ == ELF_R_SYM (r->r_info)
+ && r->r_addend == rel2->r_addend
+ && ELF_R_TYPE (rel2->r_info) == R_AARCH64_LD64_GOT_LO12_NC)
+ {
+ grub_arm64_set_abs_lo12_ldst64 ((grub_uint32_t *) SUFFIX (get_target_address) (e, target_section,
+ grub_target_to_host (rel2->r_offset), image_target),
+ ((char *) gpptr - (char *) pe_target + image_target->vaddr_offset));
+ break;
+ }
+ if (k >= num_rs)
+ grub_util_error ("ADR_GOT_PAGE without matching LD64_GOT_LO12_NC");
+ gpptr++;
+ }
+ break;
+ case R_AARCH64_LD64_GOT_LO12_NC:
+ if (unmatched_adr_got_page == 0)
+ grub_util_error ("LD64_GOT_LO12_NC without matching ADR_GOT_PAGE");
+ unmatched_adr_got_page--;
+ break;
case R_AARCH64_ADR_PREL_PG_HI21:
{
sym_addr &= ~0xfffULL;
/* Relative relocations do not require fixup entries. */
case R_AARCH64_CALL26:
case R_AARCH64_JUMP26:
+ case R_AARCH64_PREL32:
break;
/* Page-relative relocations do not require fixup entries. */
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_LDST64_ABS_LO12_NC:
break;
+ /* GOT is relocated separately. */
+ case R_AARCH64_ADR_GOT_PAGE:
+ case R_AARCH64_LD64_GOT_LO12_NC:
+ break;
+
default:
grub_util_error (_("relocation 0x%x is not implemented yet"),
(unsigned int) ELF_R_TYPE (info));
static void
-translate_reloc_jumpers (struct translate_context *ctx,
- Elf_Addr jumpers, grub_size_t njumpers,
- const struct grub_install_image_target_desc *image_target)
+create_u64_fixups (struct translate_context *ctx,
+ Elf_Addr jumpers, grub_size_t njumpers,
+ const struct grub_install_image_target_desc *image_target)
{
unsigned i;
assert (image_target->id == IMAGE_EFI);
}
if (image_target->elf_target == EM_IA_64)
- translate_reloc_jumpers (&ctx,
- layout->ia64jmp_off
- + image_target->vaddr_offset,
- 2 * layout->ia64jmpnum + (layout->got_size / 8),
- image_target);
+ create_u64_fixups (&ctx,
+ layout->ia64jmp_off
+ + image_target->vaddr_offset,
+ 2 * layout->ia64jmpnum,
+ image_target);
+ if (image_target->elf_target == EM_IA_64 || image_target->elf_target == EM_AARCH64)
+ create_u64_fixups (&ctx,
+ layout->got_off
+ + image_target->vaddr_offset,
+ (layout->got_size / 8),
+ image_target);
finish_reloc_translation (&ctx, layout, image_target);
}
image_target);
layout->kernel_size += 16 * layout->ia64jmpnum;
- layout->ia64_got_off = layout->kernel_size;
+ layout->got_off = layout->kernel_size;
+ layout->kernel_size += ALIGN_UP (layout->got_size, 16);
+ }
+ if (image_target->elf_target == EM_AARCH64)
+ {
+ grub_size_t tramp;
+
+ layout->kernel_size = ALIGN_UP (layout->kernel_size, 16);
+
+ grub_arm64_dl_get_tramp_got_size (e, &tramp, &layout->got_size);
+
+ layout->got_off = layout->kernel_size;
layout->kernel_size += ALIGN_UP (layout->got_size, 16);
}
#endif
section_entsize,
num_sections, strtab,
out_img, layout->tramp_off,
- layout->ia64_got_off,
+ layout->got_off,
image_target);
make_reloc_section (e, layout,
R_AARCH64_ABS64,
R_AARCH64_CALL26,
R_AARCH64_JUMP26,
+ R_AARCH64_ADR_GOT_PAGE,
+ R_AARCH64_LD64_GOT_LO12_NC,
-1
}, (int[]){
R_AARCH64_ADR_PREL_PG_HI21,
R_AARCH64_ADD_ABS_LO12_NC,
R_AARCH64_LDST64_ABS_LO12_NC,
+ R_AARCH64_PREL32,
-1
}
},
continue;
grub_util_error ("relocation 0x%x is not module-local", type);
}
+#if defined(MODULEVERIFIER_ELF64)
+ if (arch->machine == EM_AARCH64)
+ {
+ unsigned unmatched_adr_got_page = 0;
+ Elf_Rela *rel2;
+ for (rel = (Elf_Rel *) ((char *) ehdr + grub_target_to_host (s->sh_offset)),
+ max = (Elf_Rel *) ((char *) rel + grub_target_to_host (s->sh_size));
+ rel < max;
+ rel = (Elf_Rel *) ((char *) rel + grub_target_to_host (s->sh_entsize)))
+ {
+ switch (ELF_R_TYPE (grub_target_to_host (rel->r_info)))
+ {
+ case R_AARCH64_ADR_GOT_PAGE:
+ unmatched_adr_got_page++;
+ for (rel2 = (Elf_Rela *) ((char *) rel + grub_target_to_host (s->sh_entsize));
+ rel2 < (Elf_Rela *) max;
+ rel2 = (Elf_Rela *) ((char *) rel2 + grub_target_to_host (s->sh_entsize)))
+ if (ELF_R_SYM (rel2->r_info)
+ == ELF_R_SYM (rel->r_info)
+ && ((Elf_Rela *) rel)->r_addend == rel2->r_addend
+ && ELF_R_TYPE (rel2->r_info) == R_AARCH64_LD64_GOT_LO12_NC)
+ break;
+ if (rel2 >= (Elf_Rela *) max)
+ grub_util_error ("ADR_GOT_PAGE without matching LD64_GOT_LO12_NC");
+ break;
+ case R_AARCH64_LD64_GOT_LO12_NC:
+ if (unmatched_adr_got_page == 0)
+ grub_util_error ("LD64_GOT_LO12_NC without matching ADR_GOT_PAGE");
+ unmatched_adr_got_page--;
+ break;
+ }
+ }
+ }
+#endif
}
static void