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/llext_internal.h>
10 #include <zephyr/llext/loader.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/sys/util.h>
13 
14 LOG_MODULE_REGISTER(elf, CONFIG_LLEXT_LOG_LEVEL);
15 
16 #define R_ARC_32            4
17 #define R_ARC_B26           5 /* AKA R_ARC_64 */
18 #define R_ARC_S25H_PCREL    16
19 #define R_ARC_S25W_PCREL    17
20 #define R_ARC_32_ME         27
21 
22 /* ARCompact insns packed in memory have Middle Endian encoding */
23 #define ME(x) (((x & 0xffff0000) >> 16) | ((x & 0xffff) << 16))
24 
25 /**
26  * @brief Architecture specific function for relocating shared elf
27  *
28  * Elf files contain a series of relocations described in multiple sections.
29  * These relocation instructions are architecture specific and each architecture
30  * supporting modules must implement this.
31  *
32  * The relocation codes are well documented:
33  * https://github.com/foss-for-synopsys-dwc-arc-processors/arc-ABI-manual/blob/master/ARCv2_ABI.pdf
34  * https://github.com/zephyrproject-rtos/binutils-gdb
35  */
arch_elf_relocate(struct llext_loader * ldr,struct llext * ext,elf_rela_t * rel,const elf_shdr_t * shdr)36 int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel,
37 		      const elf_shdr_t *shdr)
38 {
39 	int ret = 0;
40 	uint32_t value;
41 	const uintptr_t loc = llext_get_reloc_instruction_location(ldr, ext, shdr->sh_info, rel);
42 	uint32_t insn = UNALIGNED_GET((uint32_t *)loc);
43 	elf_sym_t sym;
44 	uintptr_t sym_base_addr;
45 	const char *sym_name;
46 
47 	ret = llext_read_symbol(ldr, ext, rel, &sym);
48 
49 	if (ret != 0) {
50 		LOG_ERR("Could not read symbol from binary!");
51 		return ret;
52 	}
53 
54 	sym_name = llext_symbol_name(ldr, ext, &sym);
55 
56 	ret = llext_lookup_symbol(ldr, ext, &sym_base_addr, rel, &sym, sym_name, shdr);
57 
58 	if (ret != 0) {
59 		LOG_ERR("Could not find symbol %s!", sym_name);
60 		return ret;
61 	}
62 
63 	sym_base_addr += rel->r_addend;
64 
65 	int reloc_type = ELF32_R_TYPE(rel->r_info);
66 
67 	switch (reloc_type) {
68 	case R_ARC_32:
69 	case R_ARC_B26:
70 		UNALIGNED_PUT(sym_base_addr, (uint32_t *)loc);
71 		break;
72 	case R_ARC_S25H_PCREL:
73 		/* ((S + A) - P) >> 1
74 		 * S = symbol address
75 		 * A = addend
76 		 * P = relative offset to PCL
77 		 */
78 		value = (sym_base_addr + rel->r_addend - (loc & ~0x3)) >> 1;
79 
80 		insn = ME(insn);
81 
82 		/* disp25h */
83 		insn = insn & ~0x7feffcf;
84 		insn |= ((value >> 0) & 0x03ff) << 17;
85 		insn |= ((value >> 10) & 0x03ff) << 6;
86 		insn |= ((value >> 20) & 0x000f) << 0;
87 
88 		insn = ME(insn);
89 
90 		UNALIGNED_PUT(insn, (uint32_t *)loc);
91 		break;
92 	case R_ARC_S25W_PCREL:
93 		/* ((S + A) - P) >> 2 */
94 		value = (sym_base_addr + rel->r_addend - (loc & ~0x3)) >> 2;
95 
96 		insn = ME(insn);
97 
98 		/* disp25w */
99 		insn = insn & ~0x7fcffcf;
100 		insn |= ((value >> 0) & 0x01ff) << 18;
101 		insn |= ((value >> 9) & 0x03ff) << 6;
102 		insn |= ((value >> 19) & 0x000f) << 0;
103 
104 		insn = ME(insn);
105 
106 		UNALIGNED_PUT(insn, (uint32_t *)loc);
107 		break;
108 	case R_ARC_32_ME:
109 		UNALIGNED_PUT(ME(sym_base_addr), (uint32_t *)loc);
110 		break;
111 	default:
112 		LOG_ERR("unknown relocation: %u\n", reloc_type);
113 		ret = -ENOEXEC;
114 		break;
115 	}
116 
117 	return ret;
118 }
119