Signed-off-by: Xiaotian Wu <wuxiaot...@loongson.cn> --- conf/Makefile.common | 3 + configure.ac | 14 +++-- grub-core/kern/dl.c | 9 +-- grub-core/kern/loongarch64/dl.c | 71 ++++++++++++++++++++- grub-core/kern/loongarch64/dl_helper.c | 68 ++++++++++++++++++++ include/grub/elf.h | 6 ++ include/grub/loongarch64/reloc.h | 4 ++ util/grub-mkimagexx.c | 87 +++++++++++++++++++++++++- util/grub-module-verifier.c | 5 ++ 9 files changed, 250 insertions(+), 17 deletions(-)
diff --git a/conf/Makefile.common b/conf/Makefile.common index 2d8f1bf2e..77f20e441 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -17,6 +17,9 @@ endif if COND_arm64 CFLAGS_PLATFORM += -mcmodel=large endif +if COND_loongarch64 + CFLAGS_PLATFORM += -mcmodel=large +endif if COND_powerpc_ieee1275 CFLAGS_PLATFORM += -mcpu=powerpc endif diff --git a/configure.ac b/configure.ac index 71f65a70d..c8daf5dfb 100644 --- a/configure.ac +++ b/configure.ac @@ -859,14 +859,16 @@ if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ); then fi if test "x$target_cpu" = xloongarch64; then - AC_CACHE_CHECK([whether -Wa,-mla-global-with-abs works], [grub_cv_cc_mla_global_with_abs], [ - CFLAGS="$TARGET_CFLAGS -Wa,-mla-global-with-abs -Werror" + AC_CACHE_CHECK([whether _mno_explicit_relocs works], [grub_cv_cc_mno_explicit_relocs], [ + CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs -Werror" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], - [grub_cv_cc_mla_global_with_abs=yes], - [grub_cv_cc_mla_global_with_abs=no]) + [grub_cv_cc_mno_explicit_relocs=yes], + [grub_cv_cc_mno_explicit_relocs=no]) ]) - - if test "x$grub_cv_cc_mla_global_with_abs" = xyes; then + if test "x$grub_cv_cc_mno_explicit_relocs" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS -mno-explicit-relocs" + else TARGET_CFLAGS="$TARGET_CFLAGS -Wa,-mla-global-with-abs" TARGET_CCASFLAGS="$TARGET_CCASFLAGS -Wa,-mla-global-with-abs" fi diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 0bf40caa6..e447fd0fa 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -225,8 +225,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) unsigned i; const Elf_Shdr *s; grub_size_t tsize = 0, talign = 1; -#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ - !defined (__loongarch__) +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) grub_size_t tramp; grub_size_t got; grub_err_t err; @@ -242,8 +241,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) talign = s->sh_addralign; } -#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ - !defined (__loongarch__) +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); if (err) return err; @@ -306,8 +304,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) mod->segment = seg; } } -#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ - !defined (__loongarch__) +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN); mod->tramp = ptr; mod->trampptr = ptr; diff --git a/grub-core/kern/loongarch64/dl.c b/grub-core/kern/loongarch64/dl.c index 3a6aa91cd..20be4f5d5 100644 --- a/grub-core/kern/loongarch64/dl.c +++ b/grub-core/kern/loongarch64/dl.c @@ -49,6 +49,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_got_pc_page = 0; struct grub_loongarch64_stack stack; grub_loongarch64_stack_init (&stack); @@ -58,7 +59,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) { Elf_Sym *sym; - grub_uint64_t *place; + void *place; grub_uint64_t sym_addr; if (rel->r_offset >= seg->size) @@ -72,12 +73,19 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, if (s->sh_type == SHT_RELA) sym_addr += ((Elf_Rela *) rel)->r_addend; - place = (grub_uint64_t *) ((grub_addr_t)seg->addr + rel->r_offset); + place = (void *) ((grub_addr_t)seg->addr + rel->r_offset); switch (ELF_R_TYPE (rel->r_info)) { case R_LARCH_64: - *place = sym_addr; + { + grub_uint64_t *abs_place = place; + + grub_dprintf ("dl", "reloc_abs64 %p => 0x%016llx, %p\n", + place, (unsigned long long) sym_addr, abs_place); + + *abs_place += (grub_uint64_t) sym_addr; + } break; case R_LARCH_MARK_LA: break; @@ -85,6 +93,63 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, case R_LARCH_SOP_PUSH_PLT_PCREL: grub_loongarch64_sop_push (&stack, sym_addr - (grub_uint64_t)place); break; + case R_LARCH_B26: + { + grub_uint32_t *abs_place = place; + grub_ssize_t off = sym_addr - (grub_addr_t) place; + + grub_loongarch64_b26 (abs_place, off); + } + break; + case R_LARCH_PCALA_HI20: + { + grub_uint32_t *abs_place = place; + grub_int32_t off = (((sym_addr + 0x800) & ~0xfffULL) - ((grub_addr_t)place & ~0xfffULL)); + + grub_loongarch64_xxx_hi20 (abs_place, off); + } + break; + case R_LARCH_PCALA_LO12: + { + grub_uint32_t *abs_place = place; + grub_loongarch64_xxx_lo12 (abs_place, sym_addr); + } + break; + case R_LARCH_GOT_PC_HI20: + { + grub_uint64_t *gp = mod->gotptr; + grub_int64_t gpoffset; + Elf_Rela *rel2; + + gpoffset = (((grub_uint64_t) gp + 0x800) & ~0xfff) - (((grub_uint64_t) place) & ~0xfff); + + *gp = (grub_uint64_t) sym_addr; + mod->gotptr = gp + 1; + unmatched_got_pc_page++; + grub_loongarch64_xxx_hi20 (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_LARCH_GOT_PC_LO12) + { + grub_uint32_t *place2; + place2 = (grub_uint32_t *) ((grub_addr_t) seg->addr + rel2->r_offset); + grub_loongarch64_xxx_lo12 (place2, (grub_uint64_t) gp); + break; + } + if (rel2 >= (Elf_Rela *) max) + return grub_error (GRUB_ERR_BAD_MODULE, + "GOT_PC_HI20 without matching GOT_PC_LO12"); + } + break; + case R_LARCH_GOT_PC_LO12: + if (unmatched_got_pc_page == 0) + return grub_error (GRUB_ERR_BAD_MODULE, + "GOT_PC_LO12 without matching GOT_PC_HI2"); + unmatched_got_pc_page--; + break; GRUB_LOONGARCH64_RELOCATION (&stack, place, sym_addr) default: { diff --git a/grub-core/kern/loongarch64/dl_helper.c b/grub-core/kern/loongarch64/dl_helper.c index 627d1fff2..a5b5b8ee9 100644 --- a/grub-core/kern/loongarch64/dl_helper.c +++ b/grub-core/kern/loongarch64/dl_helper.c @@ -200,3 +200,71 @@ grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack, *place =(*place) | (((a >> 2) & 0xffff) << 10); *place =(*place) | ((a >> 18) & 0x3ff); } + +void grub_loongarch64_b26 (grub_uint32_t *place, grub_int64_t offset) +{ + grub_uint32_t val; + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000); + + grub_dprintf ("dl", " reloc_xxxx64 %p %c= 0x%llx\n", + place, offset > 0 ? '+' : '-', + offset < 0 ? (long long) -(unsigned long long) offset : offset); + + val = ((offset >> 18) & 0x3ff) | (((offset >> 2) & 0xffff) << 10); + + *place &= insmask; + *place |= grub_cpu_to_le32 (val) & ~insmask; +} + +void grub_loongarch64_xxx_hi20 (grub_uint32_t *place, grub_int64_t offset) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfe00001f); + grub_uint32_t val; + + offset >>= 12; + val = ((offset & 0xfffff) << 5); + + *place &= insmask; + *place |= grub_cpu_to_le32 (val) & ~insmask; +} + +void grub_loongarch64_xxx_lo12 (grub_uint32_t *place, grub_int64_t offset) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff); + + *place &= insmask; + *place |= grub_cpu_to_le32 (offset << 10) & ~insmask; +} + +grub_err_t +grub_arch_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_RELA)) + { + const Elf64_Rela *rel, *max; + + for (rel = (Elf64_Rela *) ((char *) e + grub_le_to_cpu64 (s->sh_offset)), + max = (const Elf64_Rela *) ((char *) rel + grub_le_to_cpu64 (s->sh_size)); + rel < max; rel = (const Elf64_Rela *) ((char *) rel + grub_le_to_cpu64 (s->sh_entsize))) + switch (ELF64_R_TYPE (grub_le_to_cpu64 (rel->r_info))) + { + case R_LARCH_GOT_PC_HI20: + case R_LARCH_GOT_PC_LO12: + *got += 8; + break; + } + } + + return GRUB_ERR_NONE; +} diff --git a/include/grub/elf.h b/include/grub/elf.h index 1c8d4f5d5..5c17058f3 100644 --- a/include/grub/elf.h +++ b/include/grub/elf.h @@ -2554,6 +2554,12 @@ typedef Elf32_Addr Elf32_Conflict; #define R_LARCH_SOP_POP_32_S_0_5_10_16_S2 44 #define R_LARCH_SOP_POP_32_S_0_10_10_16_S2 45 +#define R_LARCH_B26 66 +#define R_LARCH_PCALA_HI20 71 +#define R_LARCH_PCALA_LO12 72 +#define R_LARCH_GOT_PC_HI20 75 +#define R_LARCH_GOT_PC_LO12 76 + #ifdef GRUB_TARGET_WORDSIZE #if GRUB_TARGET_WORDSIZE == 32 diff --git a/include/grub/loongarch64/reloc.h b/include/grub/loongarch64/reloc.h index 0b7d66ddc..ece565491 100644 --- a/include/grub/loongarch64/reloc.h +++ b/include/grub/loongarch64/reloc.h @@ -57,6 +57,10 @@ void grub_loongarch64_sop_32_s_0_5_10_16_s2 (grub_loongarch64_stack_t stack, void grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack, grub_uint64_t *place); +void grub_loongarch64_b26 (grub_uint32_t *place, grub_int64_t offset); +void grub_loongarch64_xxx_hi20 (grub_uint32_t *place, grub_int64_t offset); +void grub_loongarch64_xxx_lo12 (grub_uint32_t *place, grub_int64_t offset); + #define GRUB_LOONGARCH64_RELOCATION(STACK, PLACE, OFFSET) \ case R_LARCH_SOP_PUSH_ABSOLUTE: \ grub_loongarch64_sop_push (STACK, OFFSET); \ diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 358b9ab47..291015011 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -1128,11 +1128,20 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd, } case EM_LOONGARCH: { + grub_int64_t pc; + + grub_uint32_t *t32 = (grub_uint32_t *) target; sym_addr += addend; + + pc = offset + target_section_addr + image_target->vaddr_offset; + switch (ELF_R_TYPE (info)) { case R_LARCH_64: - *target = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr); + { + grub_uint64_t *t64 = (grub_uint64_t *) target; + *t64 = grub_host_to_target64 (grub_target_to_host64 (*t64) + sym_addr); + } break; case R_LARCH_MARK_LA: break; @@ -1143,6 +1152,62 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd, +offset +image_target->vaddr_offset)); break; + case R_LARCH_B26: + { + grub_int64_t off; + + off = sym_addr - target_section_addr - offset - image_target->vaddr_offset; + + grub_loongarch64_b26 (t32, off); + } + break; + case R_LARCH_PCALA_HI20: + { + grub_int32_t hi20; + + hi20 = (((sym_addr + 0x800) & ~0xfffULL) - (pc & ~0xfffULL)); + + grub_loongarch64_xxx_hi20 (t32, hi20); + } + break; + case R_LARCH_PCALA_LO12: + grub_loongarch64_xxx_lo12 (t32, sym_addr); + break; + case R_LARCH_GOT_PC_HI20: + { + Elf64_Rela *rel2; + grub_int32_t hi20; + unsigned k; + + grub_int64_t gpoffset = (char *) gpptr - (char *) pe_target + image_target->vaddr_offset; + + hi20 = ((gpoffset + 0x800) & ~0xfffULL) - (pc & ~0xfffULL); + + *gpptr = grub_host_to_target64 (sym_addr); + unmatched_adr_got_page++; + grub_loongarch64_xxx_hi20 ((grub_uint32_t *)target, hi20); + 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_LARCH_GOT_PC_LO12) + { + t32 = (grub_uint32_t *) SUFFIX (get_target_address) (e, target_section, + grub_target_to_host (rel2->r_offset), image_target), + grub_loongarch64_xxx_lo12 (t32, gpoffset); + break; + } + if (k >= num_rs) + grub_util_error ("GOT_PC_HI20 without matching GOT_PC_LO12"); + gpptr++; + } + break; + case R_LARCH_GOT_PC_LO12: + if (unmatched_adr_got_page == 0) + grub_util_error ("GOT_PC_LO12 without matching GOT_PC_HI2"); + unmatched_adr_got_page--; + break; GRUB_LOONGARCH64_RELOCATION (&stack, target, sym_addr) default: grub_util_error (_("relocation 0x%x is not implemented yet"), @@ -1736,6 +1801,11 @@ translate_relocation_pe (struct translate_context *ctx, case R_LARCH_SOP_POP_32_S_5_20: case R_LARCH_SOP_POP_32_S_0_5_10_16_S2: case R_LARCH_SOP_POP_32_S_0_10_10_16_S2: + case R_LARCH_B26: + case R_LARCH_PCALA_HI20: + case R_LARCH_PCALA_LO12: + case R_LARCH_GOT_PC_HI20: + case R_LARCH_GOT_PC_LO12: grub_util_info (" %s: not adding fixup: 0x%08x : 0x%08x", __FUNCTION__, (unsigned int) addr, @@ -2095,7 +2165,7 @@ make_reloc_section (Elf_Ehdr *e, struct grub_mkimage_layout *layout, + image_target->vaddr_offset, 2 * layout->ia64jmpnum, image_target); - if (image_target->elf_target == EM_IA_64 || image_target->elf_target == EM_AARCH64) + if (image_target->elf_target == EM_IA_64 || image_target->elf_target == EM_AARCH64 || image_target->elf_target == EM_LOONGARCH) create_u64_fixups (&ctx, layout->got_off + image_target->vaddr_offset, @@ -2246,6 +2316,8 @@ SUFFIX (locate_sections) (Elf_Ehdr *e, const char *kernel_path, /* Page-aligning simplifies relocation handling. */ if (image_target->elf_target == EM_AARCH64) layout->align = 4096; + if (image_target->elf_target == EM_LOONGARCH) + layout->align = 4096; layout->kernel_size = 0; @@ -2464,6 +2536,17 @@ SUFFIX (grub_mkimage_load_image) (const char *kernel_path, 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); + } + if (image_target->elf_target == EM_LOONGARCH) + { + grub_size_t tramp; + + layout->kernel_size = ALIGN_UP (layout->kernel_size, 16); + + grub_arch_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); } diff --git a/util/grub-module-verifier.c b/util/grub-module-verifier.c index b510461fa..88351e7eb 100644 --- a/util/grub-module-verifier.c +++ b/util/grub-module-verifier.c @@ -140,6 +140,11 @@ struct grub_module_verifier_arch archs[] = { R_LARCH_SOP_POP_32_S_5_20, R_LARCH_SOP_POP_32_S_0_5_10_16_S2, R_LARCH_SOP_POP_32_S_0_10_10_16_S2, + R_LARCH_B26, + R_LARCH_PCALA_HI20, + R_LARCH_PCALA_LO12, + R_LARCH_GOT_PC_HI20, + R_LARCH_GOT_PC_LO12, -1 }, (int[]){ -1 -- 2.35.1 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel