diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c index cf50d7250..fb0337319 100644 --- a/grub-core/kern/arm64/dl.c +++ b/grub-core/kern/arm64/dl.c @@ -25,14 +25,9 @@ #include #include -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. @@ -53,42 +48,6 @@ grub_arch_dl_check_header (void *ehdr) #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 */ @@ -97,6 +56,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, 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); @@ -145,7 +105,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, 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; @@ -160,6 +120,56 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, 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); diff --git a/grub-core/kern/arm64/dl_helper.c b/grub-core/kern/arm64/dl_helper.c index f031b1ae9..4af3c4a10 100644 --- a/grub-core/kern/arm64/dl_helper.c +++ b/grub-core/kern/arm64/dl_helper.c @@ -93,3 +93,42 @@ grub_arm64_set_abs_lo12_ldst64 (grub_uint32_t *place, grub_int64_t target) *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; +} diff --git a/include/grub/arm64/reloc.h b/include/grub/arm64/reloc.h index 452c14822..c8765de97 100644 --- a/include/grub/arm64/reloc.h +++ b/include/grub/arm64/reloc.h @@ -19,6 +19,13 @@ #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); diff --git a/include/grub/dl.h b/include/grub/dl.h index 9562fa663..2bca56ce0 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -263,11 +263,16 @@ void grub_arch_dl_init_linker (void); 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, diff --git a/include/grub/elf.h b/include/grub/elf.h index db15acecf..c8492f9dc 100644 --- a/include/grub/elf.h +++ b/include/grub/elf.h @@ -2068,11 +2068,14 @@ typedef Elf32_Addr Elf32_Conflict; #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. */ diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h index 2a4894213..1a18708a8 100644 --- a/include/grub/util/mkimage.h +++ b/include/grub/util/mkimage.h @@ -30,7 +30,7 @@ struct grub_mkimage_layout 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; diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 59b6bd232..32be4bfbc 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -717,6 +717,7 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, #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); @@ -965,6 +966,18 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, *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); @@ -985,6 +998,41 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, 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; @@ -1329,6 +1377,7 @@ translate_relocation_pe (struct translate_context *ctx, /* 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: @@ -1339,6 +1388,11 @@ translate_relocation_pe (struct translate_context *ctx, 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)); @@ -1547,9 +1601,9 @@ finish_reloc_translation (struct translate_context *ctx, struct grub_mkimage_lay 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); @@ -1614,11 +1668,17 @@ make_reloc_section (Elf_Ehdr *e, struct grub_mkimage_layout *layout, } 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); } @@ -1944,7 +2004,18 @@ SUFFIX (grub_mkimage_load_image) (const char *kernel_path, 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 @@ -1977,7 +2048,7 @@ SUFFIX (grub_mkimage_load_image) (const char *kernel_path, section_entsize, num_sections, strtab, out_img, layout->tramp_off, - layout->ia64_got_off, + layout->got_off, image_target); make_reloc_section (e, layout, diff --git a/util/grub-module-verifier.c b/util/grub-module-verifier.c index dd72d7833..d0cf8176f 100644 --- a/util/grub-module-verifier.c +++ b/util/grub-module-verifier.c @@ -107,11 +107,14 @@ struct grub_module_verifier_arch archs[] = { 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 } }, diff --git a/util/grub-module-verifierXX.c b/util/grub-module-verifierXX.c index 2c0c690fa..1feaafc9b 100644 --- a/util/grub-module-verifierXX.c +++ b/util/grub-module-verifierXX.c @@ -319,6 +319,40 @@ section_check_relocations (const struct grub_module_verifier_arch *arch, void *e 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