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