summaryrefslogtreecommitdiffstats
path: root/arch/riscv/lib/reloc.c
blob: 0c1ec8b4880227d16ab5e7b580244f1db2e967ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// 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 <asm/cache.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 sync_caches_for_execution(void)
{
	local_flush_icache_all();
}

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;

	/*
	 * We have yet to relocate, so using runtime_address
	 * to compute the relocated address
	 */
	dstart = runtime_address(__rel_dyn_start);
	dend = runtime_address(__rel_dyn_end);
	dynsym = runtime_address(__dynsym_start);

	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');
			__hang();
		}
	}

	sync_caches_for_execution();
}