summaryrefslogtreecommitdiffstats
path: root/arch/riscv/lib/reloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/riscv/lib/reloc.c')
-rw-r--r--arch/riscv/lib/reloc.c66
1 files changed, 66 insertions, 0 deletions
diff --git a/arch/riscv/lib/reloc.c b/arch/riscv/lib/reloc.c
new file mode 100644
index 0000000000..2fc8818cd6
--- /dev/null
+++ b/arch/riscv/lib/reloc.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-FileCopyrightText: Copyright (c) 2021 Ahmad Fatoum, Pengutronix
+
+#include <common.h>
+#include <linux/linkage.h>
+#include <asm/sections.h>
+#include <asm/barebox-riscv.h>
+#include <debug_ll.h>
+#include <asm-generic/module.h>
+
+#include <elf.h>
+
+#if __riscv_xlen == 64
+#define Elf_Rela Elf64_Rela
+#define R_RISCV_ABSOLUTE R_RISCV_64
+#define DYNSYM_ENTRY(dynsym, rela) dynsym[ELF_R_SYM(rela->r_info) * 3 + 1]
+#elif __riscv_xlen == 32
+#define Elf_Rela Elf32_Rela
+#define R_RISCV_ABSOLUTE R_RISCV_32
+#define DYNSYM_ENTRY(dynsym, rela) dynsym[ELF_R_SYM(rela->r_info) * 4 + 1]
+#else
+#error unknown riscv target
+#endif
+
+#define RISC_R_TYPE(x) ((x) & 0xFF)
+
+void relocate_to_current_adr(void)
+{
+ unsigned long offset;
+ unsigned long *dynsym;
+ void *dstart, *dend;
+ Elf_Rela *rela;
+
+ /* Get offset between linked address and runtime address */
+ offset = get_runtime_offset();
+ if (!offset)
+ return;
+
+ dstart = __rel_dyn_start + offset;
+ dend = __rel_dyn_end + offset;
+ dynsym = (void *)__dynsym_start + offset;
+
+ for (rela = dstart; (void *)rela < dend; rela++) {
+ unsigned long *fixup;
+
+ fixup = (unsigned long *)(rela->r_offset + offset);
+
+ switch (RISC_R_TYPE(rela->r_info)) {
+ case R_RISCV_RELATIVE:
+ *fixup = rela->r_addend + offset;
+ break;
+ case R_RISCV_ABSOLUTE:
+ *fixup = DYNSYM_ENTRY(dynsym, rela) + rela->r_addend + offset;
+ break;
+ default:
+ putc_ll('>');
+ puthex_ll(rela->r_info);
+ putc_ll(' ');
+ puthex_ll(rela->r_offset);
+ putc_ll(' ');
+ puthex_ll(rela->r_addend);
+ putc_ll('\n');
+ panic("");
+ }
+ }
+}