From aebda5e118748d819291f21fc11d5cc8f20de2bb Mon Sep 17 00:00:00 2001 From: sunway_fw Date: Tue, 18 Feb 2025 15:13:24 +0800 Subject: [PATCH 3/5] sw64: Add awareness for SW64 reloations This patch adds awareness of SW64 relocations throughout the grub tools as well as dynamic linkage and elf->PE relocation conversion support. Signed-off-by: sunway_fw --- grub-core/kern/sw64/dl.c | 214 ++++++++++++++++++++++++++++++++++++ include/grub/dl.h | 16 ++- include/grub/elf.h | 39 +++++++ util/grub-mkimagexx.c | 146 +++++++++++++++++++++++- util/grub-module-verifier.c | 18 +++ 5 files changed, 430 insertions(+), 3 deletions(-) create mode 100644 grub-core/kern/sw64/dl.c diff --git a/grub-core/kern/sw64/dl.c b/grub-core/kern/sw64/dl.c new file mode 100644 index 0000000..8e3cac2 --- /dev/null +++ b/grub-core/kern/sw64/dl.c @@ -0,0 +1,214 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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_DATA] != ELFDATA2LSB || e->e_machine != EM_SW_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; + grub_uint64_t *gpptr, *gotptr; + + gotptr = (grub_uint64_t *) mod->got; + gpptr = (grub_uint64_t *) ((grub_uint64_t) mod->got + 0x8000); + + 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_SW64_NONE: + break; + case R_SW64_REFLONG: + *(grub_uint32_t *)addr = value; + break; + case R_SW64_REFQUAD: + { + *(grub_uint32_t *)addr = value; + *((grub_uint32_t *)addr + 1) = value >> 32; + break; + } + case R_SW64_GPREL32: + { + value -= (grub_uint64_t)gpptr; + if ((grub_int32_t)value != value) + return grub_error (GRUB_ERR_BAD_MODULE, + "R_SW64_GPREL32 relocation out of range"); + *(grub_uint32_t *) addr = value; + break; + } + case R_SW64_LITERAL: + { + grub_uint64_t li_hi; + grub_uint64_t li_lo; + + li_hi = (grub_uint64_t) gotptr + (((grub_uint64_t)ELF_R_TYPE(rel->r_info)) >> 8 ); + li_lo = li_hi - ((grub_uint64_t) gpptr); + if ((grub_int16_t)li_lo != li_lo) + return grub_error (GRUB_ERR_BAD_MODULE, + "R_SW64_LITERAL relocation out of range"); + + *(grub_uint16_t *) addr = (li_lo & 0xffff); + *(grub_uint64_t *) (li_hi) = value; + gotptr++; + break; + } + case R_SW64_LITERAL_GOT: + break; + case R_SW64_LITUSE: + break; + case R_SW64_GPDISP: + { + grub_uint64_t hi; + grub_uint64_t lo; + grub_uint64_t gpoffset = (grub_int64_t)gpptr - addr; + lo = (grub_int16_t) gpoffset; + hi = (grub_int32_t) (gpoffset - lo); + if (hi + lo != gpoffset) + return grub_error (GRUB_ERR_BAD_MODULE, + "R_SW64_GPDISP relocation out of range"); + + if (gpoffset & 0x8000) { + hi = ((gpoffset + 0x8000) >> 16) & 0xffff; + lo = gpoffset & 0xffff; + } else { + hi = (gpoffset >> 16) & 0xffff; + lo = gpoffset & 0xffff; + } + + *(grub_uint32_t *) addr = ( *(grub_uint32_t *)addr & 0xffff0000) | (hi & 0xffff); + *(grub_uint32_t *) ((unsigned long)addr + rel->r_addend) = + (((*(grub_uint32_t *)((unsigned long)addr + rel->r_addend)) & 0xffff0000) | (lo & 0xffff)); + break; + } + case R_SW64_BRADDR: + { + grub_uint64_t braddr = (*(grub_uint64_t *)addr) + value - ((grub_uint64_t)addr + 4); + braddr = (grub_int64_t)braddr >> 2; + if (braddr + (1<<21) >= 1<<21) + return grub_error (GRUB_ERR_BAD_MODULE, + "R_SW64_BRADDR relocation out of range"); + + *(grub_uint32_t *) addr = (*(grub_uint32_t *)addr & (~0x1fffff)) | (braddr & 0x1fffff); + break; + } + case R_SW64_HINT: + break; + case R_SW64_SREL32: + { + value -= (grub_uint64_t)addr; + if ((grub_int32_t)value != value) + return grub_error (GRUB_ERR_BAD_MODULE, + "R_SW64_SREL32 relocation out of range"); + + *(grub_uint32_t *) addr = value; + break; + } + case R_SW64_SREL64: + { + grub_uint64_t srel64 = *(grub_uint64_t *)addr + value;; + srel64 -= (grub_uint64_t)addr; + *(grub_uint64_t *) addr = srel64; + break; + } + case R_SW64_GPRELHIGH: + { + grub_uint64_t gprel_hi = (grub_int64_t)(value - (grub_uint64_t)gpptr); + + if (gprel_hi & 0x8000) + gprel_hi = (grub_int64_t)(((grub_int64_t)gprel_hi + 0x8000) >> 16); + else + gprel_hi = (grub_int64_t)gprel_hi >> 16; + + if ((grub_int16_t)gprel_hi != gprel_hi) + return grub_error (GRUB_ERR_BAD_MODULE, + "GPRELHIGH out of range\n"); + + *(grub_uint32_t *) addr = (*(grub_uint32_t *)addr & 0xffff0000) | (gprel_hi & 0xffff); + break; + } + case R_SW64_GPRELLOW: + { + grub_uint64_t gprel_lo = value - (grub_uint64_t)gpptr; + *(grub_uint32_t *) addr = (*(grub_uint32_t *)addr & 0xffff0000) | (gprel_lo & 0xffff); + break; + } + case R_SW64_GPREL16: + { + grub_uint64_t gprel16 = (*(grub_uint64_t *)addr + value); + gprel16 = (grub_uint64_t)(gprel16 - (grub_uint64_t)gpptr); + if ((grub_int16_t)gprel16 != gprel16) + return grub_error (GRUB_ERR_BAD_MODULE, + "R_SW64_GPREL16 relocation out of range"); + *(grub_uint16_t *) addr = gprel16; + break; + } + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%lx is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } + + return GRUB_ERR_NONE; +} diff --git a/include/grub/dl.h b/include/grub/dl.h index c5ab18c..a4cb287 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -292,17 +292,31 @@ grub_err_t grub_arm64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, grub_size_t *got); +#if defined (__sw_64__) +#define GRUB_SW64_DL_TRAMP_ALIGN 16 +#define GRUB_SW64_DL_GOT_ALIGN 16 + +grub_err_t +grub_sw64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got); +#endif + #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 +#elif defined (__sw_64__) +#define GRUB_ARCH_DL_TRAMP_ALIGN GRUB_SW64_DL_TRAMP_ALIGN +#define GRUB_ARCH_DL_GOT_ALIGN GRUB_SW64_DL_GOT_ALIGN +#define grub_arch_dl_get_tramp_got_size grub_sw64_dl_get_tramp_got_size #else grub_err_t grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, grub_size_t *got); #endif +#endif #if defined (__powerpc__) || defined (__mips__) || defined (__arm__) || \ (defined(__riscv) && (__riscv_xlen == 32)) @@ -317,6 +331,4 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, #define GRUB_ARCH_DL_GOT_ALIGN 8 #endif -#endif - #endif /* ! GRUB_DL_H */ diff --git a/include/grub/elf.h b/include/grub/elf.h index bd313a7..2af59fe 100644 --- a/include/grub/elf.h +++ b/include/grub/elf.h @@ -259,6 +259,7 @@ typedef struct chances of collision with official or non-GNU unofficial values. */ #define EM_ALPHA 0x9026 +#define EM_SW_64 0x9916 /* Legal values for e_version (version). */ @@ -2537,6 +2538,44 @@ typedef Elf32_Addr Elf32_Conflict; #define R_RISCV_SET32 56 #define R_RISCV_32_PCREL 57 +/* + * SW-64 ELF relocation types + */ +#define R_SW64_NONE 0 /* No reloc */ +#define R_SW64_REFLONG 1 /* Direct 32 bit */ +#define R_SW64_REFQUAD 2 /* Direct 64 bit */ +#define R_SW64_GPREL32 3 /* GP relative 32 bit */ +#define R_SW64_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_SW64_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_SW64_GPDISP 6 /* Add displacement to GP */ +#define R_SW64_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_SW64_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_SW64_SREL16 9 /* PC relative 16 bit */ +#define R_SW64_SREL32 10 /* PC relative 32 bit */ +#define R_SW64_SREL64 11 /* PC relative 64 bit */ +#define R_SW64_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_SW64_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_SW64_GPREL16 19 /* GP relative 16 bit */ +#define R_SW64_COPY 24 /* Copy symbol at runtime */ +#define R_SW64_GLOB_DAT 25 /* Create GOT entry */ +#define R_SW64_JMP_SLOT 26 /* Create PLT entry */ +#define R_SW64_RELATIVE 27 /* Adjust by program base */ +#define R_SW64_BRSGP 28 +#define R_SW64_TLSGD 29 +#define R_SW64_TLS_LDM 30 +#define R_SW64_DTPMOD64 31 +#define R_SW64_GOTDTPREL 32 +#define R_SW64_DTPREL64 33 +#define R_SW64_DTPRELHI 34 +#define R_SW64_DTPRELLO 35 +#define R_SW64_DTPREL16 36 +#define R_SW64_GOTTPREL 37 +#define R_SW64_TPREL64 38 +#define R_SW64_TPRELHI 39 +#define R_SW64_TPRELLO 40 +#define R_SW64_TPREL16 41 +#define R_SW64_LITERAL_GOT 43 + /* LoongArch relocations */ #define R_LARCH_NONE 0 #define R_LARCH_64 2 diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 9488f05..f327e26 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -819,8 +819,14 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd, Elf_Half i; Elf_Shdr *s; #ifdef MKIMAGE_ELF64 + grub_uint64_t got_offset = 0; + struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off); grub_uint64_t *gpptr = (void *) (pe_target + got_off); + if (image_target->elf_target == EM_SW_64) { + got_offset = got_off; + gpptr = (void *) (pe_target + got_offset + 0x8000); + } unsigned unmatched_adr_got_page = 0; struct grub_loongarch64_stack stack; grub_loongarch64_stack_init (&stack); @@ -1163,6 +1169,119 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd, } break; } + case EM_SW_64: + { + sym_addr += addend; + switch (ELF_R_TYPE (info)) + { + case R_SW64_NONE: + break; + case R_SW64_REFLONG: + *(grub_uint32_t *)target = grub_host_to_target32 (sym_addr); + break; + case R_SW64_REFQUAD: + { + *(grub_uint32_t *)target = grub_host_to_target32(sym_addr); + *((grub_uint32_t *)target + 1) = grub_host_to_target32(sym_addr >> 32); + break; + } + case R_SW64_GPREL32: + { + *(grub_uint32_t *) target = (grub_host_to_target64 (sym_addr) - + ((char *) gpptr - (char *)pe_target + image_target->vaddr_offset )) & 0xffffffff; + break; + } + case R_SW64_LITERAL: + { + grub_uint64_t li_hi; + grub_uint64_t li_lo; + + li_hi = (grub_uint64_t)pe_target + got_offset + (((grub_uint64_t)ELF_R_TYPE(r->r_info)) >> 8 ); + li_lo = li_hi - ((grub_uint64_t) gpptr); + *(grub_uint16_t *) target = (li_lo & 0xffff); + *(grub_uint64_t *) (li_hi) = grub_host_to_target64(grub_host_to_target64 (sym_addr)); + got_offset += 8; + break; + } + case R_SW64_LITERAL_GOT: + break; + case R_SW64_LITUSE: + break; + case R_SW64_GPDISP: + { + grub_uint64_t hi; + grub_uint64_t lo; + grub_int64_t gpoffset = (((char *) gpptr - (char *) pe_target + image_target->vaddr_offset)) + - ((offset + target_section_addr + image_target->vaddr_offset)); + if (gpoffset & 0x8000) { + hi = ((gpoffset + 0x8000) >> 16) & 0xffff; + lo = gpoffset & 0xffff; + } else { + hi = (gpoffset >> 16) & 0xffff; + lo = gpoffset & 0xffff; + } + *(grub_uint32_t *) target = grub_host_to_target32 ((grub_target_to_host32 (*target) & 0xffff0000) | (hi & 0xffff)); + *(grub_uint32_t *) ((unsigned long)target + addend) = + grub_host_to_target32 ((grub_target_to_host32 (*(grub_uint32_t *) + ((unsigned long)target + addend)) & 0xffff0000) | (lo & 0xffff)); + break; + + } + case R_SW64_BRADDR: + { + grub_uint64_t braddr = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr) - ((grub_uint64_t)target + 4); + braddr = braddr >> 2; + *(grub_uint32_t *) target = (*(grub_uint32_t *)target & (~0x1fffff)) | (braddr & 0x1fffff); + break; + } + case R_SW64_HINT: + break; + case R_SW64_SREL32: + { + grub_uint64_t srel32 = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr); + srel32 -= (grub_uint64_t)target; + *(grub_uint32_t *) target = srel32; + break; + } + case R_SW64_SREL64: + { + grub_uint64_t srel64 = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr); + srel64 -= (grub_uint64_t)target; + *(grub_uint64_t *) target = srel64; + break; + } + case R_SW64_GPRELHIGH: + { + grub_uint64_t gprel_hi = ((grub_host_to_target64 (sym_addr) - (((char *) gpptr - (char *)pe_target + image_target->vaddr_offset) + 0x0))); + + if (gprel_hi & 0x8000) + gprel_hi = ((gprel_hi + 0x8000) >> 16) & 0xffff; + else + gprel_hi = (gprel_hi >> 16) & 0xffff; + + *(grub_uint32_t *) target = grub_host_to_target32 ((grub_target_to_host32 (*target) & 0xffff0000) | (gprel_hi & 0xffff)); + break; + } + case R_SW64_GPRELLOW: + { + grub_uint64_t gprel_lo = (grub_host_to_target64 (sym_addr) - ((char *) gpptr - (char *)pe_target + image_target->vaddr_offset)); + *(grub_uint32_t *) target = grub_host_to_target32 ((grub_target_to_host32 (*target) & 0xffff0000) | (gprel_lo & 0xffff)); + break; + } + case R_SW64_GPREL16: + { + grub_uint64_t gprel16 = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr); + gprel16 = (grub_uint64_t)(gprel16 - (grub_uint64_t)gpptr); + *(grub_uint16_t *) target = gprel16; + break; + } + default: + grub_util_error (_("relocation 0x%lx is not implemented yet"), + ELF_R_TYPE (info)); + break; + } + break; + } case EM_LOONGARCH: { grub_int64_t pc; @@ -1700,6 +1819,19 @@ translate_relocation_pe (struct translate_context *ctx, image_target); } break; + case EM_SW_64: + if (ELF_R_TYPE (info) == R_SW64_REFQUAD) + { + grub_util_info ("adding a relocation entry for 0x%llx", + (unsigned long long) addr); + ctx->current_address + = add_fixup_entry (&ctx->lst, + GRUB_PE32_REL_BASED_DIR64, + addr, + 0, ctx->current_address, + image_target); + } + break; case EM_IA_64: switch (ELF_R_TYPE (info)) { @@ -2181,7 +2313,8 @@ 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_SW_64) create_u64_fixups (&ctx, layout->got_off + image_target->vaddr_offset, @@ -2550,6 +2683,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); + } + else if (image_target->elf_target == EM_SW_64) + { + grub_size_t tramp; + + layout->kernel_size = ALIGN_UP (layout->kernel_size, 16); + + grub_sw64_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 91d9e8f..37fd0f6 100644 --- a/util/grub-module-verifier.c +++ b/util/grub-module-verifier.c @@ -209,6 +209,24 @@ struct grub_module_verifier_arch archs[] = { -1 } }, + { "sw64", 8, 0, EM_SW_64, GRUB_MODULE_VERIFY_SUPPORTS_REL | GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){ + R_SW64_NONE, + R_SW64_REFQUAD, + R_SW64_GPREL32, + R_SW64_LITERAL, + R_SW64_LITERAL_GOT, + R_SW64_LITUSE, + R_SW64_GPDISP, + R_SW64_BRSGP, + R_SW64_BRADDR, + R_SW64_HINT, + R_SW64_SREL32, + R_SW64_SREL64, + R_SW64_GPRELHIGH, + R_SW64_GPRELLOW, + R_SW64_GPREL16, + -1 + } }, }; struct platform_whitelist { -- 2.33.0