1 /*
2  * Copyright (c) 2024 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/llext/elf.h>
8 #include <zephyr/llext/llext.h>
9 #include <zephyr/llext/loader.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/sys/util.h>
12 
13 LOG_MODULE_REGISTER(elf, CONFIG_LLEXT_LOG_LEVEL);
14 
15 #define R_ARC_32            4
16 #define R_ARC_B26           5 /* AKA R_ARC_64 */
17 #define R_ARC_S25W_PCREL    17
18 #define R_ARC_32_ME         27
19 
20 /* ARCompact insns packed in memory have Middle Endian encoding */
21 #define ME(x) (((x & 0xffff0000) >> 16) | ((x & 0xffff) << 16))
22 
23 /**
24  * @brief Architecture specific function for relocating shared elf
25  *
26  * Elf files contain a series of relocations described in multiple sections.
27  * These relocation instructions are architecture specific and each architecture
28  * supporting modules must implement this.
29  *
30  * The relocation codes are well documented:
31  * https://github.com/foss-for-synopsys-dwc-arc-processors/arc-ABI-manual/blob/master/ARCv2_ABI.pdf
32  * https://github.com/zephyrproject-rtos/binutils-gdb
33  */
arch_elf_relocate(elf_rela_t * rel,uintptr_t loc,uintptr_t sym_base_addr,const char * sym_name,uintptr_t load_bias)34 int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr, const char *sym_name,
35 		      uintptr_t load_bias)
36 {
37 	int ret = 0;
38 	uint32_t insn = UNALIGNED_GET((uint32_t *)loc);
39 	uint32_t value;
40 
41 	sym_base_addr += rel->r_addend;
42 
43 	int reloc_type = ELF32_R_TYPE(rel->r_info);
44 
45 	switch (reloc_type) {
46 	case R_ARC_32:
47 	case R_ARC_B26:
48 		UNALIGNED_PUT(sym_base_addr, (uint32_t *)loc);
49 		break;
50 	case R_ARC_S25W_PCREL:
51 		/* ((S + A) - P) >> 2
52 		 * S = symbol address
53 		 * A = addend
54 		 * P = relative offset to PCL
55 		 */
56 		value = (sym_base_addr + rel->r_addend - (loc & ~0x3)) >> 2;
57 
58 		insn = ME(insn);
59 
60 		/* disp25w */
61 		insn = insn & ~0x7fcffcf;
62 		insn |= ((value >> 0) & 0x01ff) << 18;
63 		insn |= ((value >> 9) & 0x03ff) << 6;
64 		insn |= ((value >> 19) & 0x000f) << 0;
65 
66 		insn = ME(insn);
67 
68 		UNALIGNED_PUT(insn, (uint32_t *)loc);
69 		break;
70 	case R_ARC_32_ME:
71 		UNALIGNED_PUT(ME(sym_base_addr), (uint32_t *)loc);
72 		break;
73 	default:
74 		LOG_ERR("unknown relocation: %u\n", reloc_type);
75 		ret = -ENOEXEC;
76 		break;
77 	}
78 
79 	return ret;
80 }
81