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