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