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