grub2/sw64-Add-awareness-for-SW64-reloations.patch
sunway_fw e100bf4cab add SW64 arch support
Signed-off-by: sunway_fw <sunway_fw@wxiat.com>
(cherry picked from commit c77b80a2ab9a7dc8f49fe33d54ea3a47d39c7d36)
2025-03-11 20:36:59 +08:00

558 lines
21 KiB
Diff

From aebda5e118748d819291f21fc11d5cc8f20de2bb Mon Sep 17 00:00:00 2001
From: sunway_fw <sunway_fw@wxiat.com>
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 <sunway_fw@wxiat.com>
---
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 <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/elf.h>
+
+/*
+ * 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