1 /*
2  * Copyright (c) 2023 Intel Corporation
3  * Copyright (c) 2024 Schneider Electric
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/llext/elf.h>
9 #include <zephyr/llext/llext.h>
10 #include <zephyr/llext/llext_internal.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_ARM_NONE         0
17 #define R_ARM_PC24         1
18 #define R_ARM_ABS32        2
19 #define R_ARM_REL32        3
20 #define R_ARM_COPY         20
21 #define R_ARM_GLOB_DAT     21
22 #define R_ARM_JUMP_SLOT    22
23 #define R_ARM_RELATIVE     23
24 #define R_ARM_CALL         28
25 #define R_ARM_JUMP24       29
26 #define R_ARM_TARGET1      38
27 #define R_ARM_V4BX         40
28 #define R_ARM_PREL31       42
29 #define R_ARM_MOVW_ABS_NC  43
30 #define R_ARM_MOVT_ABS     44
31 #define R_ARM_MOVW_PREL_NC 45
32 #define R_ARM_MOVT_PREL    46
33 #define R_ARM_ALU_PC_G0_NC 57
34 #define R_ARM_ALU_PC_G1_NC 59
35 #define R_ARM_LDR_PC_G2    63
36 
37 #define R_ARM_THM_CALL         10
38 #define R_ARM_THM_JUMP24       30
39 #define R_ARM_THM_MOVW_ABS_NC  47
40 #define R_ARM_THM_MOVT_ABS     48
41 #define R_ARM_THM_MOVW_PREL_NC 49
42 #define R_ARM_THM_MOVT_PREL    50
43 
44 #define OPCODE2ARMMEM(x) ((uint32_t)(x))
45 #define OPCODE2THM16MEM(x) ((uint16_t)(x))
46 #define MEM2ARMOPCODE(x) OPCODE2ARMMEM(x)
47 #define MEM2THM16OPCODE(x) OPCODE2THM16MEM(x)
48 #define JUMP_UPPER_BOUNDARY ((int32_t)0xfe000000)
49 #define JUMP_LOWER_BOUNDARY ((int32_t)0x2000000)
50 #define PREL31_UPPER_BOUNDARY ((int32_t)0x40000000)
51 #define PREL31_LOWER_BOUNDARY ((int32_t)-0x40000000)
52 #define THM_JUMP_UPPER_BOUNDARY ((int32_t)0xff000000)
53 #define THM_JUMP_LOWER_BOUNDARY ((int32_t)0x01000000)
54 #define MASK_V4BX_RM_COND 0xf000000f
55 #define MASK_V4BX_NOT_RM_COND 0x01a0f000
56 #define MASK_BRANCH_COND GENMASK(31, 28)
57 #define MASK_BRANCH_101 GENMASK(27, 25)
58 #define MASK_BRANCH_L BIT(24)
59 #define MASK_BRANCH_OFFSET GENMASK(23, 0)
60 #define MASK_MOV_COND GENMASK(31, 28)
61 #define MASK_MOV_00 GENMASK(27, 26)
62 #define MASK_MOV_I BIT(25)
63 #define MASK_MOV_OPCODE GENMASK(24, 21)
64 #define MASK_MOV_S BIT(20)
65 #define MASK_MOV_RN GENMASK(19, 16)
66 #define MASK_MOV_RD GENMASK(15, 12)
67 #define MASK_MOV_OPERAND2 GENMASK(11, 0)
68 #define BIT_THM_BW_S 10
69 #define MASK_THM_BW_11110 GENMASK(15, 11)
70 #define MASK_THM_BW_S BIT(10)
71 #define MASK_THM_BW_IMM10 GENMASK(9, 0)
72 #define BIT_THM_BL_J1 13
73 #define BIT_THM_BL_J2 11
74 #define MASK_THM_BL_10 GENMASK(15, 14)
75 #define MASK_THM_BL_J1 BIT(13)
76 #define MASK_THM_BL_1 BIT(12)
77 #define MASK_THM_BL_J2 BIT(11)
78 #define MASK_THM_BL_IMM11 GENMASK(10, 0)
79 #define MASK_THM_MOV_11110 GENMASK(15, 11)
80 #define MASK_THM_MOV_I BIT(10)
81 #define MASK_THM_MOV_100100 GENMASK(9, 4)
82 #define MASK_THM_MOV_IMM4 GENMASK(3, 0)
83 #define MASK_THM_MOV_0 BIT(15)
84 #define MASK_THM_MOV_IMM3 GENMASK(14, 12)
85 #define MASK_THM_MOV_RD GENMASK(11, 8)
86 #define MASK_THM_MOV_IMM8 GENMASK(7, 0)
87 #define SHIFT_PREL31_SIGN 30
88 #define SHIFT_BRANCH_OFFSET 2
89 #define SHIFT_JUMPS_SIGN 25
90 #define SHIFT_MOV_RD 4
91 #define SHIFT_MOV_RN 4
92 #define SHIFT_MOVS_SIGN 15
93 #define SHIFT_THM_JUMPS_SIGN 24
94 #define SHIFT_THM_BW_IMM10 12
95 #define SHIFT_THM_BL_J2 22
96 #define SHIFT_THM_BL_J1 23
97 #define SHIFT_THM_MOVS_SIGN 15
98 #define SHIFT_THM_MOV_I 1
99 #define SHIFT_THM_MOV_IMM3 4
100 #define SHIFT_THM_MOV_IMM4 12
101 
prel31_decode(elf_word reloc_type,uint32_t loc,uint32_t sym_base_addr,const char * sym_name,int32_t * offset)102 static inline int prel31_decode(elf_word reloc_type, uint32_t loc,
103 				uint32_t sym_base_addr, const char *sym_name, int32_t *offset)
104 {
105 	int ret;
106 
107 	*offset = sign_extend(*(int32_t *)loc, SHIFT_PREL31_SIGN);
108 	*offset += sym_base_addr - loc;
109 	if (*offset >= PREL31_UPPER_BOUNDARY || *offset < PREL31_LOWER_BOUNDARY) {
110 		LOG_ERR("sym '%s': relocation out of range (%#x -> %#x)\n",
111 			sym_name, loc, sym_base_addr);
112 		ret = -ENOEXEC;
113 	} else {
114 		ret = 0;
115 	}
116 
117 	return ret;
118 }
119 
prel31_reloc(uint32_t loc,int32_t * offset)120 static inline void prel31_reloc(uint32_t loc, int32_t *offset)
121 {
122 	*(uint32_t *)loc &= BIT(31);
123 	*(uint32_t *)loc |= *offset & GENMASK(30, 0);
124 }
125 
prel31_handler(elf_word reloc_type,uint32_t loc,uint32_t sym_base_addr,const char * sym_name)126 static int prel31_handler(elf_word reloc_type, uint32_t loc,
127 				uint32_t sym_base_addr, const char *sym_name)
128 {
129 	int ret;
130 	int32_t offset;
131 
132 	ret = prel31_decode(reloc_type, loc, sym_base_addr, sym_name, &offset);
133 	if (!ret) {
134 		prel31_reloc(loc, &offset);
135 	}
136 
137 	return ret;
138 }
139 
jumps_decode(elf_word reloc_type,uint32_t loc,uint32_t sym_base_addr,const char * sym_name,int32_t * offset)140 static inline int jumps_decode(elf_word reloc_type, uint32_t loc,
141 				uint32_t sym_base_addr, const char *sym_name, int32_t *offset)
142 {
143 	int ret;
144 
145 	*offset = MEM2ARMOPCODE(*(uint32_t *)loc);
146 	*offset = (*offset & MASK_BRANCH_OFFSET) << SHIFT_BRANCH_OFFSET;
147 	*offset = sign_extend(*offset, SHIFT_JUMPS_SIGN);
148 	*offset += sym_base_addr - loc;
149 	if (*offset >= JUMP_LOWER_BOUNDARY || *offset <= JUMP_UPPER_BOUNDARY) {
150 		LOG_ERR("sym '%s': relocation out of range (%#x -> %#x)\n",
151 			sym_name, loc, sym_base_addr);
152 		ret = -ENOEXEC;
153 	} else {
154 		ret = 0;
155 	}
156 
157 	return ret;
158 }
159 
jumps_reloc(uint32_t loc,int32_t * offset)160 static inline void jumps_reloc(uint32_t loc, int32_t *offset)
161 {
162 	*offset >>= SHIFT_BRANCH_OFFSET;
163 	*offset &= MASK_BRANCH_OFFSET;
164 
165 	*(uint32_t *)loc &= OPCODE2ARMMEM(MASK_BRANCH_COND|MASK_BRANCH_101|MASK_BRANCH_L);
166 	*(uint32_t *)loc |= OPCODE2ARMMEM(*offset);
167 }
168 
jumps_handler(elf_word reloc_type,uint32_t loc,uint32_t sym_base_addr,const char * sym_name)169 static int jumps_handler(elf_word reloc_type, uint32_t loc,
170 				uint32_t sym_base_addr, const char *sym_name)
171 {
172 	int ret;
173 	int32_t offset;
174 
175 	ret = jumps_decode(reloc_type, loc, sym_base_addr, sym_name, &offset);
176 	if (!ret) {
177 		jumps_reloc(loc, &offset);
178 	}
179 
180 	return ret;
181 }
182 
movs_handler(elf_word reloc_type,uint32_t loc,uint32_t sym_base_addr,const char * sym_name)183 static void movs_handler(elf_word reloc_type, uint32_t loc,
184 			uint32_t sym_base_addr, const char *sym_name)
185 {
186 	int32_t offset;
187 	uint32_t tmp;
188 
189 	offset = tmp = MEM2ARMOPCODE(*(uint32_t *)loc);
190 	offset = ((offset & MASK_MOV_RN) >> SHIFT_MOV_RN) | (offset & MASK_MOV_OPERAND2);
191 	offset = sign_extend(offset, SHIFT_MOVS_SIGN);
192 
193 	offset += sym_base_addr;
194 	if (reloc_type == R_ARM_MOVT_PREL || reloc_type == R_ARM_MOVW_PREL_NC) {
195 		offset -= loc;
196 	}
197 	if (reloc_type == R_ARM_MOVT_ABS || reloc_type == R_ARM_MOVT_PREL) {
198 		offset >>= 16;
199 	}
200 
201 	tmp &= (MASK_MOV_COND | MASK_MOV_00 | MASK_MOV_I | MASK_MOV_OPCODE | MASK_MOV_RD);
202 	tmp |= ((offset & MASK_MOV_RD) << SHIFT_MOV_RD) | (offset & MASK_MOV_OPERAND2);
203 
204 	*(uint32_t *)loc = OPCODE2ARMMEM(tmp);
205 }
206 
thm_jumps_decode(elf_word reloc_type,uint32_t loc,uint32_t sym_base_addr,const char * sym_name,int32_t * offset,uint32_t * upper,uint32_t * lower)207 static inline int thm_jumps_decode(elf_word reloc_type, uint32_t loc,
208 			uint32_t sym_base_addr, const char *sym_name, int32_t *offset,
209 			uint32_t *upper, uint32_t *lower)
210 {
211 	int ret;
212 	uint32_t j_one, j_two, sign;
213 
214 	*upper = MEM2THM16OPCODE(*(uint16_t *)loc);
215 	*lower = MEM2THM16OPCODE(*(uint16_t *)(loc + 2));
216 
217 	/* sign is bit10 */
218 	sign = (*upper >> BIT_THM_BW_S) & 1;
219 	j_one = (*lower >> BIT_THM_BL_J1) & 1;
220 	j_two = (*lower >> BIT_THM_BL_J2) & 1;
221 	*offset = (sign << SHIFT_THM_JUMPS_SIGN) |
222 				((~(j_one ^ sign) & 1) << SHIFT_THM_BL_J1) |
223 				((~(j_two ^ sign) & 1) << SHIFT_THM_BL_J2) |
224 				((*upper & MASK_THM_BW_IMM10) << SHIFT_THM_BW_IMM10) |
225 				((*lower & MASK_THM_BL_IMM11) << 1);
226 	*offset = sign_extend(*offset, SHIFT_THM_JUMPS_SIGN);
227 	*offset += sym_base_addr - loc;
228 
229 	if (*offset >= THM_JUMP_LOWER_BOUNDARY || *offset <= THM_JUMP_UPPER_BOUNDARY) {
230 		LOG_ERR("sym '%s': relocation out of range (%#x -> %#x)\n",
231 			sym_name, loc, sym_base_addr);
232 		ret = -ENOEXEC;
233 	} else {
234 		ret = 0;
235 	}
236 
237 	return ret;
238 }
239 
thm_jumps_reloc(uint32_t loc,int32_t * offset,uint32_t * upper,uint32_t * lower)240 static inline void thm_jumps_reloc(uint32_t loc, int32_t *offset,
241 			uint32_t *upper, uint32_t *lower)
242 {
243 	uint32_t j_one, j_two, sign;
244 
245 	sign = (*offset >> SHIFT_THM_JUMPS_SIGN) & 1;
246 	j_one = sign ^ (~(*offset >> SHIFT_THM_BL_J1) & 1);
247 	j_two = sign ^ (~(*offset >> SHIFT_THM_BL_J2) & 1);
248 	*upper = (uint16_t)((*upper & MASK_THM_BW_11110) | (sign << BIT_THM_BW_S) |
249 				((*offset >> SHIFT_THM_BW_IMM10) & MASK_THM_BW_IMM10));
250 	*lower = (uint16_t)((*lower & (MASK_THM_BL_10|MASK_THM_BL_1)) |
251 				(j_one << BIT_THM_BL_J1) | (j_two << BIT_THM_BL_J2) |
252 				((*offset >> 1) & MASK_THM_BL_IMM11));
253 
254 	*(uint16_t *)loc = OPCODE2THM16MEM(*upper);
255 	*(uint16_t *)(loc + 2) = OPCODE2THM16MEM(*lower);
256 }
257 
thm_jumps_handler(elf_word reloc_type,uint32_t loc,uint32_t sym_base_addr,const char * sym_name)258 static int thm_jumps_handler(elf_word reloc_type, uint32_t loc,
259 			uint32_t sym_base_addr, const char *sym_name)
260 {
261 	int ret;
262 	int32_t offset;
263 	uint32_t upper, lower;
264 
265 	ret = thm_jumps_decode(reloc_type, loc, sym_base_addr, sym_name, &offset, &upper, &lower);
266 	if (!ret) {
267 		thm_jumps_reloc(loc, &offset, &upper, &lower);
268 	}
269 
270 	return ret;
271 }
272 
thm_movs_handler(elf_word reloc_type,uint32_t loc,uint32_t sym_base_addr,const char * sym_name)273 static void thm_movs_handler(elf_word reloc_type, uint32_t loc,
274 			uint32_t sym_base_addr, const char *sym_name)
275 {
276 	int32_t offset;
277 	uint32_t upper, lower;
278 
279 	upper = MEM2THM16OPCODE(*(uint16_t *)loc);
280 	lower = MEM2THM16OPCODE(*(uint16_t *)(loc + 2));
281 
282 	/* MOVT/MOVW instructions encoding in Thumb-2 */
283 	offset = ((upper & MASK_THM_MOV_IMM4) << SHIFT_THM_MOV_IMM4) |
284 		((upper & MASK_THM_MOV_I) << SHIFT_THM_MOV_I) |
285 		((lower & MASK_THM_MOV_IMM3) >> SHIFT_THM_MOV_IMM3) | (lower & MASK_THM_MOV_IMM8);
286 	offset = sign_extend(offset, SHIFT_THM_MOVS_SIGN);
287 	offset += sym_base_addr;
288 
289 	if (reloc_type == R_ARM_THM_MOVT_PREL || reloc_type == R_ARM_THM_MOVW_PREL_NC) {
290 		offset -= loc;
291 	}
292 	if (reloc_type == R_ARM_THM_MOVT_ABS || reloc_type == R_ARM_THM_MOVT_PREL) {
293 		offset >>= 16;
294 	}
295 
296 	upper = (uint16_t)((upper & (MASK_THM_MOV_11110|MASK_THM_MOV_100100)) |
297 		((offset & (MASK_THM_MOV_IMM4<<SHIFT_THM_MOV_IMM4)) >> SHIFT_THM_MOV_IMM4) |
298 		((offset & (MASK_THM_MOV_I<<SHIFT_THM_MOV_I)) >> SHIFT_THM_MOV_I));
299 	lower = (uint16_t)((lower & (MASK_THM_MOV_0|MASK_THM_MOV_RD)) |
300 		((offset & (MASK_THM_MOV_IMM3>>SHIFT_THM_MOV_IMM3)) << SHIFT_THM_MOV_IMM3) |
301 		(offset & MASK_THM_MOV_IMM8));
302 	*(uint16_t *)loc = OPCODE2THM16MEM(upper);
303 	*(uint16_t *)(loc + 2) = OPCODE2THM16MEM(lower);
304 }
305 
306 /**
307  * @brief Architecture specific function for relocating partially linked (static) elf
308  *
309  * Elf files contain a series of relocations described in a section. These relocation
310  * instructions are architecture specific and each architecture supporting extensions
311  * must implement this.
312  *
313  * The relocation codes for arm are well documented
314  * https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst#relocation
315  *
316  * Handler functions prefixed by '_thm_' means that they are Thumb instructions specific.
317  * Do NOT mix them with not 'Thumb instructions' in the below switch/case: they are not
318  * intended to work together.
319  */
arch_elf_relocate(struct llext_loader * ldr,struct llext * ext,elf_rela_t * rel,const elf_shdr_t * shdr)320 int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel,
321 		      const elf_shdr_t *shdr)
322 {
323 	int ret = 0;
324 	elf_word reloc_type = ELF32_R_TYPE(rel->r_info);
325 	const uintptr_t load_bias = (uintptr_t)ext->mem[LLEXT_MEM_TEXT];
326 	const uintptr_t loc = llext_get_reloc_instruction_location(ldr, ext, shdr->sh_info, rel);
327 	elf_sym_t sym;
328 	uintptr_t sym_base_addr;
329 	const char *sym_name;
330 
331 	ret = llext_read_symbol(ldr, ext, rel, &sym);
332 
333 	if (ret != 0) {
334 		LOG_ERR("Could not read symbol from binary!");
335 		return ret;
336 	}
337 
338 	sym_name = llext_symbol_name(ldr, ext, &sym);
339 
340 	ret = llext_lookup_symbol(ldr, ext, &sym_base_addr, rel, &sym, sym_name, shdr);
341 
342 	if (ret != 0) {
343 		LOG_ERR("Could not find symbol %s!", sym_name);
344 		return ret;
345 	}
346 
347 	LOG_DBG("%d %lx %lx %s", reloc_type, loc, sym_base_addr, sym_name);
348 
349 	switch (reloc_type) {
350 	case R_ARM_NONE:
351 		break;
352 
353 	case R_ARM_ABS32:
354 	case R_ARM_TARGET1:
355 		*(uint32_t *)loc += sym_base_addr;
356 		break;
357 
358 	case R_ARM_PC24:
359 	case R_ARM_CALL:
360 	case R_ARM_JUMP24:
361 		ret = jumps_handler(reloc_type, loc, sym_base_addr, sym_name);
362 		break;
363 
364 	case R_ARM_V4BX:
365 		/* keep Rm and condition bits */
366 		*(uint32_t *)loc &= OPCODE2ARMMEM(MASK_V4BX_RM_COND);
367 		/* remove the rest */
368 		*(uint32_t *)loc |= OPCODE2ARMMEM(MASK_V4BX_NOT_RM_COND);
369 		break;
370 
371 	case R_ARM_PREL31:
372 		ret = prel31_handler(reloc_type, loc, sym_base_addr, sym_name);
373 		break;
374 
375 	case R_ARM_REL32:
376 		*(uint32_t *)loc += sym_base_addr - loc;
377 		break;
378 
379 	case R_ARM_MOVW_ABS_NC:
380 	case R_ARM_MOVT_ABS:
381 	case R_ARM_MOVW_PREL_NC:
382 	case R_ARM_MOVT_PREL:
383 		movs_handler(reloc_type, loc, sym_base_addr, sym_name);
384 		break;
385 
386 	case R_ARM_THM_CALL:
387 	case R_ARM_THM_JUMP24:
388 		ret = thm_jumps_handler(reloc_type, loc, sym_base_addr, sym_name);
389 		break;
390 
391 	case R_ARM_THM_MOVW_ABS_NC:
392 	case R_ARM_THM_MOVT_ABS:
393 	case R_ARM_THM_MOVW_PREL_NC:
394 	case R_ARM_THM_MOVT_PREL:
395 		thm_movs_handler(reloc_type, loc, sym_base_addr, sym_name);
396 		break;
397 
398 	case R_ARM_RELATIVE:
399 		*(uint32_t *)loc += load_bias;
400 		break;
401 
402 	case R_ARM_GLOB_DAT:
403 	case R_ARM_JUMP_SLOT:
404 		*(uint32_t *)loc = sym_base_addr;
405 		break;
406 
407 	default:
408 		LOG_ERR("unknown relocation: %u\n", reloc_type);
409 		ret = -ENOEXEC;
410 	}
411 
412 	return ret;
413 }
414