loongarch: Add support for ELF psABI v1.00 relocations

This patch adds support of the stack-based LoongArch relocations
throughout GRUB, including tools, dynamic linkage, and support for
conversion of ELF relocations into PE ones. A stack machine is required
to handle these per the spec [1] (see the R_LARCH_SOP types), of which
a simple implementation is included.

These relocations are produced by binutils 2.38 and 2.39, while the newer
v2.00 relocs require more recent toolchain (binutils 2.40+ & gcc 13+, or
LLVM 16+). GCC 13 has not been officially released as of early 2023, so
support for v1.00 relocs are expected to stay relevant for a while.

[1] https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_relocations

Signed-off-by: Zhou Yang <zhouyang@loongson.cn>
Signed-off-by: Xiaotian Wu <wuxiaotian@loongson.cn>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
This commit is contained in:
Wentao Guan 2024-08-25 15:59:19 +08:00
parent 19c95139b9
commit a12fcf90ed
7 changed files with 522 additions and 3 deletions

View File

@ -225,7 +225,8 @@ 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)
#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
!defined (__loongarch__)
grub_size_t tramp;
grub_size_t got;
grub_err_t err;
@ -241,7 +242,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
talign = s->sh_addralign;
}
#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
!defined (__loongarch__)
err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
if (err)
return err;
@ -304,7 +306,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
mod->segment = seg;
}
}
#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
!defined (__loongarch__)
ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
mod->tramp = ptr;
mod->trampptr = ptr;

View File

@ -0,0 +1,102 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2023 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/types.h>
#include <grub/mm.h>
#include <grub/i18n.h>
#include <grub/cpu/reloc.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_CLASS] != ELFCLASS64
|| e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_LOONGARCH)
return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
return GRUB_ERR_NONE;
}
#pragma GCC diagnostic ignored "-Wcast-align"
/*
* Unified function for both REL and RELA.
*/
grub_err_t
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
Elf_Shdr *s, grub_dl_segment_t seg)
{
Elf_Rel *rel, *max;
struct grub_loongarch64_stack stack;
grub_loongarch64_stack_init (&stack);
for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
max = (Elf_Rel *) ((char *) rel + s->sh_size);
rel < max;
rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
{
Elf_Sym *sym;
grub_uint64_t *place;
grub_uint64_t sym_addr;
if (rel->r_offset >= seg->size)
return grub_error (GRUB_ERR_BAD_MODULE,
"reloc offset is outside the segment");
sym = (Elf_Sym *) ((char*) mod->symtab
+ mod->symsize * ELF_R_SYM (rel->r_info));
sym_addr = sym->st_value;
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);
switch (ELF_R_TYPE (rel->r_info))
{
case R_LARCH_64:
*place = sym_addr;
break;
case R_LARCH_MARK_LA:
break;
case R_LARCH_SOP_PUSH_PCREL:
case R_LARCH_SOP_PUSH_PLT_PCREL:
grub_loongarch64_sop_push (&stack, sym_addr - (grub_uint64_t)place);
break;
GRUB_LOONGARCH64_RELOCATION (&stack, place, sym_addr)
default:
{
char rel_info[17]; /* log16(2^64) = 16, plus NUL. */
grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T,
(grub_uint64_t) ELF_R_TYPE (rel->r_info));
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
N_("relocation 0x%s is not implemented yet"), rel_info);
}
break;
}
}
return GRUB_ERR_NONE;
}

View File

@ -0,0 +1,201 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2023 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/loongarch64/reloc.h>
static void grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x);
static grub_uint64_t grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack);
void
grub_loongarch64_stack_init (grub_loongarch64_stack_t stack)
{
stack->top = -1;
stack->count = LOONGARCH64_STACK_MAX;
}
static void
grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x)
{
if (stack->top == stack->count)
return;
stack->data[++stack->top] = x;
}
static grub_uint64_t
grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack)
{
if (stack->top == -1)
return -1;
return stack->data[stack->top--];
}
void
grub_loongarch64_sop_push (grub_loongarch64_stack_t stack, grub_int64_t offset)
{
grub_loongarch64_stack_push (stack, offset);
}
/* opr2 = pop (), opr1 = pop (), push (opr1 - opr2) */
void
grub_loongarch64_sop_sub (grub_loongarch64_stack_t stack)
{
grub_uint64_t a, b;
b = grub_loongarch64_stack_pop (stack);
a = grub_loongarch64_stack_pop (stack);
grub_loongarch64_stack_push (stack, a - b);
}
/* opr2 = pop (), opr1 = pop (), push (opr1 << opr2) */
void
grub_loongarch64_sop_sl (grub_loongarch64_stack_t stack)
{
grub_uint64_t a, b;
b = grub_loongarch64_stack_pop (stack);
a = grub_loongarch64_stack_pop (stack);
grub_loongarch64_stack_push (stack, a << b);
}
/* opr2 = pop (), opr1 = pop (), push (opr1 >> opr2) */
void
grub_loongarch64_sop_sr (grub_loongarch64_stack_t stack)
{
grub_uint64_t a, b;
b = grub_loongarch64_stack_pop (stack);
a = grub_loongarch64_stack_pop (stack);
grub_loongarch64_stack_push (stack, a >> b);
}
/* opr2 = pop (), opr1 = pop (), push (opr1 + opr2) */
void
grub_loongarch64_sop_add (grub_loongarch64_stack_t stack)
{
grub_uint64_t a, b;
b = grub_loongarch64_stack_pop (stack);
a = grub_loongarch64_stack_pop (stack);
grub_loongarch64_stack_push (stack, a + b);
}
/* opr2 = pop (), opr1 = pop (), push (opr1 & opr2) */
void
grub_loongarch64_sop_and (grub_loongarch64_stack_t stack)
{
grub_uint64_t a, b;
b = grub_loongarch64_stack_pop (stack);
a = grub_loongarch64_stack_pop (stack);
grub_loongarch64_stack_push (stack, a & b);
}
/* opr3 = pop (), opr2 = pop (), opr1 = pop (), push (opr1 ? opr2 : opr3) */
void
grub_loongarch64_sop_if_else (grub_loongarch64_stack_t stack)
{
grub_uint64_t a, b, c;
c = grub_loongarch64_stack_pop (stack);
b = grub_loongarch64_stack_pop (stack);
a = grub_loongarch64_stack_pop (stack);
if (a) {
grub_loongarch64_stack_push (stack, b);
} else {
grub_loongarch64_stack_push (stack, c);
}
}
/* opr1 = pop (), (*(uint32_t *) PC) [14 ... 10] = opr1 [4 ... 0] */
void
grub_loongarch64_sop_32_s_10_5 (grub_loongarch64_stack_t stack,
grub_uint64_t *place)
{
grub_uint64_t a = grub_loongarch64_stack_pop (stack);
*place |= ((a & 0x1f) << 10);
}
/* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */
void
grub_loongarch64_sop_32_u_10_12 (grub_loongarch64_stack_t stack,
grub_uint64_t *place)
{
grub_uint64_t a = grub_loongarch64_stack_pop (stack);
*place = *place | ((a & 0xfff) << 10);
}
/* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */
void
grub_loongarch64_sop_32_s_10_12 (grub_loongarch64_stack_t stack,
grub_uint64_t *place)
{
grub_uint64_t a = grub_loongarch64_stack_pop (stack);
*place = (*place) | ((a & 0xfff) << 10);
}
/* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [15 ... 0] */
void
grub_loongarch64_sop_32_s_10_16 (grub_loongarch64_stack_t stack,
grub_uint64_t *place)
{
grub_uint64_t a = grub_loongarch64_stack_pop (stack);
*place = (*place) | ((a & 0xffff) << 10);
}
/* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2] */
void
grub_loongarch64_sop_32_s_10_16_s2 (grub_loongarch64_stack_t stack,
grub_uint64_t *place)
{
grub_uint64_t a = grub_loongarch64_stack_pop (stack);
*place = (*place) | (((a >> 2) & 0xffff) << 10);
}
/* opr1 = pop (), (*(uint32_t *) PC) [24 ... 5] = opr1 [19 ... 0] */
void
grub_loongarch64_sop_32_s_5_20 (grub_loongarch64_stack_t stack, grub_uint64_t *place)
{
grub_uint64_t a = grub_loongarch64_stack_pop (stack);
*place = (*place) | ((a & 0xfffff)<<5);
}
/* opr1 = pop (), (*(uint32_t *) PC) [4 ... 0] = opr1 [22 ... 18] */
void
grub_loongarch64_sop_32_s_0_5_10_16_s2 (grub_loongarch64_stack_t stack,
grub_uint64_t *place)
{
grub_uint64_t a = grub_loongarch64_stack_pop (stack);
*place =(*place) | (((a >> 2) & 0xffff) << 10);
*place =(*place) | ((a >> 18) & 0x1f);
}
/*
* opr1 = pop ()
* (*(uint32_t *) PC) [9 ... 0] = opr1 [27 ... 18],
* (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2]
*/
void
grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack,
grub_uint64_t *place)
{
grub_uint64_t a = grub_loongarch64_stack_pop (stack);
*place =(*place) | (((a >> 2) & 0xffff) << 10);
*place =(*place) | ((a >> 18) & 0x3ff);
}

View File

@ -299,6 +299,7 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
#endif
#if defined (__aarch64__) || defined (__sparc__) || (defined (__mips__) && (_MIPS_SIM == _ABI64)) || \
defined (__loongarch_lp64) || \
(defined(__riscv) && (__riscv_xlen == 64))
#define GRUB_ARCH_DL_TRAMP_ALIGN 8
#define GRUB_ARCH_DL_GOT_ALIGN 8

View File

@ -0,0 +1,107 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2023 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/>.
*/
#ifndef GRUB_LOONGARCH64_RELOC_H
#define GRUB_LOONGARCH64_RELOC_H 1
#include <grub/types.h>
#define LOONGARCH64_STACK_MAX 16
struct grub_loongarch64_stack
{
grub_uint64_t data[LOONGARCH64_STACK_MAX];
int count;
int top;
};
typedef struct grub_loongarch64_stack* grub_loongarch64_stack_t;
void grub_loongarch64_stack_init (grub_loongarch64_stack_t stack);
void grub_loongarch64_sop_push (grub_loongarch64_stack_t stack,
grub_int64_t offset);
void grub_loongarch64_sop_sub (grub_loongarch64_stack_t stack);
void grub_loongarch64_sop_sl (grub_loongarch64_stack_t stack);
void grub_loongarch64_sop_sr (grub_loongarch64_stack_t stack);
void grub_loongarch64_sop_add (grub_loongarch64_stack_t stack);
void grub_loongarch64_sop_and (grub_loongarch64_stack_t stack);
void grub_loongarch64_sop_if_else (grub_loongarch64_stack_t stack);
void grub_loongarch64_sop_32_s_10_5 (grub_loongarch64_stack_t stack,
grub_uint64_t *place);
void grub_loongarch64_sop_32_u_10_12 (grub_loongarch64_stack_t stack,
grub_uint64_t *place);
void grub_loongarch64_sop_32_s_10_12 (grub_loongarch64_stack_t stack,
grub_uint64_t *place);
void grub_loongarch64_sop_32_s_10_16 (grub_loongarch64_stack_t stack,
grub_uint64_t *place);
void grub_loongarch64_sop_32_s_10_16_s2 (grub_loongarch64_stack_t stack,
grub_uint64_t *place);
void grub_loongarch64_sop_32_s_5_20 (grub_loongarch64_stack_t stack,
grub_uint64_t *place);
void grub_loongarch64_sop_32_s_0_5_10_16_s2 (grub_loongarch64_stack_t stack,
grub_uint64_t *place);
void grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack,
grub_uint64_t *place);
#define GRUB_LOONGARCH64_RELOCATION(STACK, PLACE, OFFSET) \
case R_LARCH_SOP_PUSH_ABSOLUTE: \
grub_loongarch64_sop_push (STACK, OFFSET); \
break; \
case R_LARCH_SOP_SUB: \
grub_loongarch64_sop_sub (STACK); \
break; \
case R_LARCH_SOP_SL: \
grub_loongarch64_sop_sl (STACK); \
break; \
case R_LARCH_SOP_SR: \
grub_loongarch64_sop_sr (STACK); \
break; \
case R_LARCH_SOP_ADD: \
grub_loongarch64_sop_add (STACK); \
break; \
case R_LARCH_SOP_AND: \
grub_loongarch64_sop_and (STACK); \
break; \
case R_LARCH_SOP_IF_ELSE: \
grub_loongarch64_sop_if_else (STACK); \
break; \
case R_LARCH_SOP_POP_32_S_10_5: \
grub_loongarch64_sop_32_s_10_5 (STACK, PLACE); \
break; \
case R_LARCH_SOP_POP_32_U_10_12: \
grub_loongarch64_sop_32_u_10_12 (STACK, PLACE); \
break; \
case R_LARCH_SOP_POP_32_S_10_12: \
grub_loongarch64_sop_32_s_10_12 (STACK, PLACE); \
break; \
case R_LARCH_SOP_POP_32_S_10_16: \
grub_loongarch64_sop_32_s_10_16 (STACK, PLACE); \
break; \
case R_LARCH_SOP_POP_32_S_10_16_S2: \
grub_loongarch64_sop_32_s_10_16_s2 (STACK, PLACE); \
break; \
case R_LARCH_SOP_POP_32_S_5_20: \
grub_loongarch64_sop_32_s_5_20 (STACK, PLACE); \
break; \
case R_LARCH_SOP_POP_32_S_0_5_10_16_S2: \
grub_loongarch64_sop_32_s_0_5_10_16_s2 (STACK, PLACE); \
break; \
case R_LARCH_SOP_POP_32_S_0_10_10_16_S2: \
grub_loongarch64_sop_32_s_0_10_10_16_s2 (STACK, PLACE); \
break;
#endif /* GRUB_LOONGARCH64_RELOC_H */

View File

@ -44,6 +44,7 @@
#include <grub/arm/reloc.h>
#include <grub/arm64/reloc.h>
#include <grub/ia64/reloc.h>
#include <grub/loongarch64/reloc.h>
#include <grub/osdep/hostfile.h>
#include <grub/util/install.h>
#include <grub/util/mkimage.h>
@ -784,6 +785,8 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd,
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;
struct grub_loongarch64_stack stack;
grub_loongarch64_stack_init (&stack);
#define MASK19 ((1 << 19) - 1)
#else
grub_uint32_t *tr = (void *) (pe_target + tramp_off);
@ -1187,6 +1190,31 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd,
}
break;
}
case EM_LOONGARCH:
{
sym_addr += addend;
switch (ELF_R_TYPE (info))
{
case R_LARCH_64:
*target = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr);
break;
case R_LARCH_MARK_LA:
break;
case R_LARCH_SOP_PUSH_PCREL:
case R_LARCH_SOP_PUSH_PLT_PCREL:
grub_loongarch64_sop_push (&stack, sym_addr
-(target_section_addr
+offset
+image_target->vaddr_offset));
break;
GRUB_LOONGARCH64_RELOCATION (&stack, target, sym_addr)
default:
grub_util_error (_("relocation 0x%x is not implemented yet"),
(unsigned int) ELF_R_TYPE (info));
break;
}
break;
}
#endif
#if defined(MKIMAGE_ELF32)
case EM_ARM:
@ -1734,6 +1762,57 @@ translate_relocation_pe (struct translate_context *ctx,
break;
}
break;
case EM_LOONGARCH:
#if defined(MKIMAGE_ELF64)
switch (ELF_R_TYPE (info))
{
case R_LARCH_64:
{
ctx->current_address = add_fixup_entry (&ctx->lst,
GRUB_PE32_REL_BASED_DIR64,
addr, 0, ctx->current_address,
image_target);
}
break;
case R_LARCH_MARK_LA:
{
ctx->current_address = add_fixup_entry (&ctx->lst,
GRUB_PE32_REL_BASED_LOONGARCH64_MARK_LA,
addr, 0, ctx->current_address,
image_target);
}
break;
/* Relative relocations do not require fixup entries. */
case R_LARCH_NONE:
case R_LARCH_SOP_PUSH_PCREL:
case R_LARCH_SOP_PUSH_ABSOLUTE:
case R_LARCH_SOP_PUSH_PLT_PCREL:
case R_LARCH_SOP_SUB:
case R_LARCH_SOP_SL:
case R_LARCH_SOP_SR:
case R_LARCH_SOP_ADD:
case R_LARCH_SOP_AND:
case R_LARCH_SOP_IF_ELSE:
case R_LARCH_SOP_POP_32_S_10_5:
case R_LARCH_SOP_POP_32_U_10_12:
case R_LARCH_SOP_POP_32_S_10_12:
case R_LARCH_SOP_POP_32_S_10_16:
case R_LARCH_SOP_POP_32_S_10_16_S2:
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:
grub_util_info (" %s: not adding fixup: 0x%08x : 0x%08x",
__FUNCTION__,
(unsigned int) addr,
(unsigned int) ctx->current_address);
break;
default:
grub_util_error (_("relocation 0x%x is not implemented yet"),
(unsigned int) ELF_R_TYPE (info));
break;
}
#endif /* defined(MKIMAGE_ELF64) */
break;
#if defined(MKIMAGE_ELF64)
case EM_MIPS:
switch (ELF_R_TYPE (info))

View File

@ -119,6 +119,32 @@ struct grub_module_verifier_arch archs[] = {
R_AARCH64_PREL32,
-1
} },
{ "loongarch64", 8, 0, EM_LOONGARCH, GRUB_MODULE_VERIFY_SUPPORTS_REL | GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
R_LARCH_NONE,
R_LARCH_64,
R_LARCH_MARK_LA,
R_LARCH_SOP_PUSH_PCREL,
R_LARCH_SOP_PUSH_ABSOLUTE,
R_LARCH_SOP_PUSH_PLT_PCREL,
R_LARCH_SOP_SUB,
R_LARCH_SOP_SL,
R_LARCH_SOP_SR,
R_LARCH_SOP_ADD,
R_LARCH_SOP_AND,
R_LARCH_SOP_IF_ELSE,
R_LARCH_SOP_POP_32_S_10_5,
R_LARCH_SOP_POP_32_U_10_12,
R_LARCH_SOP_POP_32_S_10_12,
R_LARCH_SOP_POP_32_S_10_16,
R_LARCH_SOP_POP_32_S_10_16_S2,
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,
-1
}, (int[]){
-1
}
},
{ "mips64el", 8, 0, EM_MIPS, GRUB_MODULE_VERIFY_SUPPORTS_REL | GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
R_MIPS_64,
R_MIPS_32,