1 /*
2  * Copyright (c) 2023 Intel Corporation
3  * Copyright (c) 2024 Arduino SA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/sys/util.h>
9 #include <zephyr/llext/elf.h>
10 #include <zephyr/llext/loader.h>
11 #include <zephyr/llext/llext.h>
12 #include <zephyr/llext/llext_internal.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/cache.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL);
18 
19 #include <string.h>
20 
21 #include "llext_priv.h"
22 
23 #ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
24 #define SYM_NAME_OR_SLID(name, slid) ((const char *) slid)
25 #else
26 #define SYM_NAME_OR_SLID(name, slid) name
27 #endif
28 
arch_elf_relocate(struct llext_loader * ldr,struct llext * ext,elf_rela_t * rel,const elf_shdr_t * shdr)29 __weak int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel,
30 			     const elf_shdr_t *shdr)
31 {
32 	return -ENOTSUP;
33 }
34 
arch_elf_relocate_local(struct llext_loader * ldr,struct llext * ext,const elf_rela_t * rel,const elf_sym_t * sym,uint8_t * rel_addr,const struct llext_load_param * ldr_parm)35 __weak void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext,
36 				    const elf_rela_t *rel, const elf_sym_t *sym, uint8_t *rel_addr,
37 				    const struct llext_load_param *ldr_parm)
38 {
39 }
40 
arch_elf_relocate_global(struct llext_loader * ldr,struct llext * ext,const elf_rela_t * rel,const elf_sym_t * sym,uint8_t * rel_addr,const void * link_addr)41 __weak void arch_elf_relocate_global(struct llext_loader *ldr, struct llext *ext,
42 				     const elf_rela_t *rel, const elf_sym_t *sym, uint8_t *rel_addr,
43 				     const void *link_addr)
44 {
45 }
46 
47 /*
48  * Find the memory region containing the supplied offset and return the
49  * corresponding file offset
50  */
llext_file_offset(struct llext_loader * ldr,uintptr_t offset)51 ssize_t llext_file_offset(struct llext_loader *ldr, uintptr_t offset)
52 {
53 	unsigned int i;
54 
55 	for (i = 0; i < LLEXT_MEM_COUNT; i++) {
56 		if (ldr->sects[i].sh_addr <= offset &&
57 		    ldr->sects[i].sh_addr + ldr->sects[i].sh_size > offset) {
58 			return offset - ldr->sects[i].sh_addr + ldr->sects[i].sh_offset;
59 		}
60 	}
61 
62 	return -ENOEXEC;
63 }
64 
65 /*
66  * We increment use-count every time a new dependent is added, and have to
67  * decrement it again, when one is removed. Ideally we should be able to add
68  * arbitrary numbers of dependencies, but using lists for this doesn't work,
69  * because multiple extensions can have common dependencies. Dynamically
70  * allocating dependency entries would be too wasteful. In this initial
71  * implementation we use an array of dependencies, if at some point we run out
72  * of array entries, we'll implement re-allocation.
73  * We add dependencies incrementally as we discover them, but we only ever
74  * expect them to be removed all at once, when their user is removed. So the
75  * dependency array is always "dense" - it cannot have NULL entries between
76  * valid ones.
77  */
llext_dependency_add(struct llext * ext,struct llext * dependency)78 static int llext_dependency_add(struct llext *ext, struct llext *dependency)
79 {
80 	unsigned int i;
81 
82 	for (i = 0; i < ARRAY_SIZE(ext->dependency); i++) {
83 		if (ext->dependency[i] == dependency) {
84 			return 0;
85 		}
86 
87 		if (!ext->dependency[i]) {
88 			ext->dependency[i] = dependency;
89 			dependency->use_count++;
90 
91 			return 0;
92 		}
93 	}
94 
95 	return -ENOENT;
96 }
97 
llext_dependency_remove_all(struct llext * ext)98 void llext_dependency_remove_all(struct llext *ext)
99 {
100 	unsigned int i;
101 
102 	for (i = 0; i < ARRAY_SIZE(ext->dependency) && ext->dependency[i]; i++) {
103 		/*
104 		 * The use-count of dependencies is tightly bound to dependent's
105 		 * life cycle, so it shouldn't underrun.
106 		 */
107 		ext->dependency[i]->use_count--;
108 		__ASSERT(ext->dependency[i]->use_count, "LLEXT dependency use-count underrun!");
109 		/* No need to NULL-ify the pointer - ext is freed after this */
110 	}
111 }
112 
113 struct llext_extension_sym {
114 	struct llext *ext;
115 	const char *sym;
116 	const void *addr;
117 };
118 
llext_find_extension_sym_iterate(struct llext * ext,void * arg)119 static int llext_find_extension_sym_iterate(struct llext *ext, void *arg)
120 {
121 	struct llext_extension_sym *se = arg;
122 	const void *addr = llext_find_sym(&ext->exp_tab, se->sym);
123 
124 	if (addr) {
125 		se->addr = addr;
126 		se->ext = ext;
127 		return 1;
128 	}
129 
130 	return 0;
131 }
132 
llext_find_extension_sym(const char * sym_name,struct llext ** ext)133 static const void *llext_find_extension_sym(const char *sym_name, struct llext **ext)
134 {
135 	struct llext_extension_sym se = {.sym = sym_name};
136 
137 	llext_iterate(llext_find_extension_sym_iterate, &se);
138 	if (ext) {
139 		*ext = se.ext;
140 	}
141 
142 	return se.addr;
143 }
144 
145 /*
146  * Read the symbol entry corresponding to a relocation from the binary.
147  */
llext_read_symbol(struct llext_loader * ldr,struct llext * ext,const elf_rela_t * rel,elf_sym_t * sym)148 int llext_read_symbol(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel,
149 		      elf_sym_t *sym)
150 {
151 	int ret;
152 
153 	ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset
154 		+ ELF_R_SYM(rel->r_info) * sizeof(elf_sym_t));
155 	if (ret != 0) {
156 		return ret;
157 	}
158 
159 	ret = llext_read(ldr, sym, sizeof(elf_sym_t));
160 
161 	return ret;
162 }
163 
164 /*
165  * Determine address of a symbol.
166  */
llext_lookup_symbol(struct llext_loader * ldr,struct llext * ext,uintptr_t * link_addr,const elf_rela_t * rel,const elf_sym_t * sym,const char * name,const elf_shdr_t * shdr)167 int llext_lookup_symbol(struct llext_loader *ldr, struct llext *ext, uintptr_t *link_addr,
168 			const elf_rela_t *rel, const elf_sym_t *sym, const char *name,
169 			const elf_shdr_t *shdr)
170 {
171 	if (ELF_R_SYM(rel->r_info) == 0) {
172 		/*
173 		 * no symbol
174 		 * example:  R_ARM_V4BX relocation, R_ARM_RELATIVE
175 		 */
176 		*link_addr = 0;
177 	} else if (sym->st_shndx == SHN_UNDEF) {
178 		/* If symbol is undefined, then we need to look it up */
179 		*link_addr = (uintptr_t)llext_find_sym(NULL, SYM_NAME_OR_SLID(name, sym->st_value));
180 
181 		if (*link_addr == 0) {
182 			/* Try loaded tables */
183 			struct llext *dep;
184 
185 			*link_addr = (uintptr_t)llext_find_extension_sym(name, &dep);
186 			if (*link_addr) {
187 				llext_dependency_add(ext, dep);
188 			}
189 		}
190 
191 		if (*link_addr == 0) {
192 			LOG_ERR("Undefined symbol with no entry in "
193 				"symbol table %s, offset %zd, link section %d",
194 				name, (size_t)rel->r_offset, shdr->sh_link);
195 			return -ENODATA;
196 		}
197 
198 		LOG_INF("found symbol %s at %#lx", name, *link_addr);
199 	} else if (sym->st_shndx == SHN_ABS) {
200 		/* Absolute symbol */
201 		*link_addr = sym->st_value;
202 	} else if ((sym->st_shndx < ldr->hdr.e_shnum) &&
203 		   !IN_RANGE(sym->st_shndx, SHN_LORESERVE, SHN_HIRESERVE)) {
204 		/* This check rejects all relocations whose target symbol has a section index higher
205 		 * than the maximum possible in this ELF file, or belongs in the reserved range:
206 		 * they will be caught by the `else` below and cause an error to be returned. This
207 		 * aborts the LLEXT's loading and prevents execution of improperly relocated code,
208 		 * which is dangerous.
209 		 *
210 		 * Note that the unsupported SHN_COMMON section is rejected as part of this check.
211 		 * Also note that SHN_ABS would be rejected as well, but we want to handle it
212 		 * properly: for this reason, this check must come AFTER handling the case where the
213 		 * symbol's section index is SHN_ABS!
214 		 *
215 		 *
216 		 * For regular symbols, the link address is obtained by adding st_value to the start
217 		 * address of the section in which the target symbol resides.
218 		 */
219 		*link_addr =
220 			(uintptr_t)llext_loaded_sect_ptr(ldr, ext, sym->st_shndx) + sym->st_value;
221 	} else {
222 		LOG_ERR("cannot apply relocation: "
223 			"target symbol has unexpected section index %d (%#x)",
224 			sym->st_shndx, sym->st_shndx);
225 		return -ENOEXEC;
226 	}
227 
228 	return 0;
229 }
230 
llext_link_plt(struct llext_loader * ldr,struct llext * ext,elf_shdr_t * shdr,const struct llext_load_param * ldr_parm,elf_shdr_t * tgt)231 static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr_t *shdr,
232 			   const struct llext_load_param *ldr_parm, elf_shdr_t *tgt)
233 {
234 	unsigned int sh_cnt = shdr->sh_size / shdr->sh_entsize;
235 	/*
236 	 * CPU address where the .text section is stored, we use .text just as a
237 	 * reference point
238 	 */
239 	uint8_t *text = ext->mem[LLEXT_MEM_TEXT];
240 
241 	LOG_DBG("Found %p in PLT %u size %zu cnt %u text %p",
242 		(void *)llext_section_name(ldr, ext, shdr),
243 		shdr->sh_type, (size_t)shdr->sh_entsize, sh_cnt, (void *)text);
244 
245 	const elf_shdr_t *sym_shdr = ldr->sects + LLEXT_MEM_SYMTAB;
246 	unsigned int sym_cnt = sym_shdr->sh_size / sym_shdr->sh_entsize;
247 
248 	for (unsigned int i = 0; i < sh_cnt; i++) {
249 		elf_rela_t rela;
250 
251 		int ret = llext_seek(ldr, shdr->sh_offset + i * shdr->sh_entsize);
252 
253 		if (!ret) {
254 			ret = llext_read(ldr, &rela, sizeof(rela));
255 		}
256 
257 		if (ret != 0) {
258 			LOG_ERR("PLT: failed to read RELA #%u, trying to continue", i);
259 			continue;
260 		}
261 
262 		/* Index in the symbol table */
263 		unsigned int j = ELF_R_SYM(rela.r_info);
264 
265 		if (j >= sym_cnt) {
266 			LOG_WRN("PLT: idx %u >= %u", j, sym_cnt);
267 			continue;
268 		}
269 
270 		elf_sym_t sym;
271 
272 		ret = llext_seek(ldr, sym_shdr->sh_offset + j * sizeof(elf_sym_t));
273 		if (!ret) {
274 			ret = llext_read(ldr, &sym, sizeof(sym));
275 		}
276 
277 		if (ret != 0) {
278 			LOG_ERR("PLT: failed to read symbol table #%u RELA #%u, trying to continue",
279 				j, i);
280 			continue;
281 		}
282 
283 		uint32_t stt = ELF_ST_TYPE(sym.st_info);
284 
285 		if (stt != STT_FUNC &&
286 		    stt != STT_SECTION &&
287 		    stt != STT_OBJECT &&
288 		    (stt != STT_NOTYPE || sym.st_shndx != SHN_UNDEF)) {
289 			continue;
290 		}
291 
292 		const char *name = llext_symbol_name(ldr, ext, &sym);
293 
294 		/*
295 		 * Both r_offset and sh_addr are addresses for which the extension
296 		 * has been built.
297 		 *
298 		 * NOTE: The calculations below assumes offsets from the
299 		 * beginning of the .text section in the ELF file can be
300 		 * applied to the memory location of mem[LLEXT_MEM_TEXT].
301 		 *
302 		 * This is valid only when CONFIG_LLEXT_STORAGE_WRITABLE=y
303 		 * and peek() is usable on the source ELF file.
304 		 */
305 		uint8_t *rel_addr = (uint8_t *)ext->mem[LLEXT_MEM_TEXT] -
306 			ldr->sects[LLEXT_MEM_TEXT].sh_offset;
307 
308 		if (tgt) {
309 			/* Relocatable / partially linked ELF. */
310 			rel_addr += rela.r_offset + tgt->sh_offset;
311 		} else {
312 			/* Shared / dynamically linked ELF */
313 			ssize_t offset = llext_file_offset(ldr, rela.r_offset);
314 
315 			if (offset < 0) {
316 				LOG_ERR("Offset %#zx not found in ELF, trying to continue",
317 					(size_t)rela.r_offset);
318 				continue;
319 			}
320 
321 			rel_addr += offset;
322 		}
323 
324 		uint32_t stb = ELF_ST_BIND(sym.st_info);
325 		const void *link_addr;
326 
327 		switch (stb) {
328 		case STB_GLOBAL:
329 			/* First try the global symbol table */
330 			link_addr = llext_find_sym(NULL,
331 				SYM_NAME_OR_SLID(name, sym.st_value));
332 
333 			if (!link_addr) {
334 				/* Next try internal tables */
335 				link_addr = llext_find_sym(&ext->sym_tab, name);
336 			}
337 
338 			if (!link_addr) {
339 				/* Finally try any loaded tables */
340 				struct llext *dep;
341 
342 				link_addr = llext_find_extension_sym(name, &dep);
343 				if (link_addr) {
344 					llext_dependency_add(ext, dep);
345 				}
346 			}
347 
348 			if (!link_addr) {
349 				LOG_WRN("PLT: cannot find idx %u name %s", j, name);
350 				continue;
351 			}
352 
353 			/* Resolve the symbol */
354 			arch_elf_relocate_global(ldr, ext, &rela, &sym, rel_addr, link_addr);
355 			break;
356 		case STB_LOCAL:
357 			arch_elf_relocate_local(ldr, ext, &rela, &sym, rel_addr, ldr_parm);
358 		}
359 
360 		LOG_DBG("symbol %s relocation @%p r-offset %#zx .text offset %#zx stb %u",
361 			name, (void *)rel_addr,
362 			(size_t)rela.r_offset, (size_t)ldr->sects[LLEXT_MEM_TEXT].sh_offset, stb);
363 	}
364 }
365 
llext_link(struct llext_loader * ldr,struct llext * ext,const struct llext_load_param * ldr_parm)366 int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_load_param *ldr_parm)
367 {
368 	uintptr_t sect_base = 0;
369 	elf_rela_t rel;
370 	elf_word rel_cnt = 0;
371 	const char *name;
372 	int i, ret;
373 
374 	for (i = 0; i < ext->sect_cnt; ++i) {
375 		elf_shdr_t *shdr = ext->sect_hdrs + i;
376 
377 		/* find proper relocation sections */
378 		switch (shdr->sh_type) {
379 		case SHT_REL:
380 			if (shdr->sh_entsize != sizeof(elf_rel_t)) {
381 				LOG_ERR("Invalid entry size %zd for SHT_REL section %d",
382 					(size_t)shdr->sh_entsize, i);
383 				return -ENOEXEC;
384 			}
385 			break;
386 		case SHT_RELA:
387 			if (IS_ENABLED(CONFIG_ARM)) {
388 				LOG_ERR("Found unsupported SHT_RELA section %d", i);
389 				return -ENOTSUP;
390 			}
391 			if (shdr->sh_entsize != sizeof(elf_rela_t)) {
392 				LOG_ERR("Invalid entry size %zd for SHT_RELA section %d",
393 					(size_t)shdr->sh_entsize, i);
394 				return -ENOEXEC;
395 			}
396 			break;
397 		default:
398 			/* ignore this section */
399 			continue;
400 		}
401 
402 		if (shdr->sh_info >= ext->sect_cnt ||
403 		    shdr->sh_size % shdr->sh_entsize != 0) {
404 			LOG_ERR("Sanity checks failed for section %d "
405 				"(info %zd, size %zd, entsize %zd)", i,
406 				(size_t)shdr->sh_info,
407 				(size_t)shdr->sh_size,
408 				(size_t)shdr->sh_entsize);
409 			return -ENOEXEC;
410 		}
411 
412 		rel_cnt = shdr->sh_size / shdr->sh_entsize;
413 
414 		name = llext_section_name(ldr, ext, shdr);
415 
416 		/*
417 		 * FIXME: The Xtensa port is currently using a different way of
418 		 * handling relocations that ultimately results in separate
419 		 * arch-specific code paths. This code should be merged with
420 		 * the logic below once the differences are resolved.
421 		 */
422 		if (IS_ENABLED(CONFIG_XTENSA)) {
423 			elf_shdr_t *tgt;
424 
425 			if (strcmp(name, ".rela.plt") == 0 ||
426 			    strcmp(name, ".rela.dyn") == 0) {
427 				tgt = NULL;
428 			} else {
429 				/*
430 				 * Entries in .rel.X and .rela.X sections describe references in
431 				 * section .X to local or global symbols. They point to entries
432 				 * in the symbol table, describing respective symbols
433 				 */
434 				tgt = ext->sect_hdrs + shdr->sh_info;
435 			}
436 
437 			llext_link_plt(ldr, ext, shdr, ldr_parm, tgt);
438 			continue;
439 		}
440 
441 		if (!(ext->sect_hdrs[shdr->sh_info].sh_flags & SHF_ALLOC)) {
442 			/* ignore relocations acting on volatile (debug) sections */
443 			continue;
444 		}
445 
446 		LOG_DBG("relocation section %s (%d) acting on section %d has %zd relocations",
447 			name, i, shdr->sh_info, (size_t)rel_cnt);
448 
449 		enum llext_mem mem_idx = ldr->sect_map[shdr->sh_info].mem_idx;
450 
451 		if (mem_idx == LLEXT_MEM_COUNT) {
452 			LOG_ERR("Section %d not loaded in any memory region", shdr->sh_info);
453 			return -ENOEXEC;
454 		}
455 
456 		sect_base = (uintptr_t) llext_loaded_sect_ptr(ldr, ext, shdr->sh_info);
457 
458 		for (int j = 0; j < rel_cnt; j++) {
459 			/* get each relocation entry */
460 			ret = llext_seek(ldr, shdr->sh_offset + j * shdr->sh_entsize);
461 			if (ret != 0) {
462 				return ret;
463 			}
464 
465 			ret = llext_read(ldr, &rel, shdr->sh_entsize);
466 			if (ret != 0) {
467 				return ret;
468 			}
469 
470 #ifdef CONFIG_LLEXT_LOG_LEVEL
471 			if (CONFIG_LLEXT_LOG_LEVEL >= LOG_LEVEL_INF) {
472 				uintptr_t link_addr;
473 				uintptr_t op_loc =
474 					llext_get_reloc_instruction_location(ldr, ext,
475 									     shdr->sh_info,
476 									     &rel);
477 				elf_sym_t sym;
478 
479 				ret = llext_read_symbol(ldr, ext, &rel, &sym);
480 
481 				if (ret != 0) {
482 					return ret;
483 				}
484 
485 				name = llext_symbol_name(ldr, ext, &sym);
486 
487 				ret = llext_lookup_symbol(ldr, ext, &link_addr, &rel, &sym, name,
488 							  shdr);
489 
490 				if (ret != 0) {
491 					LOG_ERR("Could not find symbol %s!", name);
492 					return ret;
493 				}
494 
495 				LOG_DBG("relocation %d:%d info %#zx (type %zd, sym %zd) offset %zd"
496 					" sym_name %s sym_type %d sym_bind %d sym_ndx %d",
497 					i, j, (size_t)rel.r_info, (size_t)ELF_R_TYPE(rel.r_info),
498 					(size_t)ELF_R_SYM(rel.r_info), (size_t)rel.r_offset,
499 					name, ELF_ST_TYPE(sym.st_info),
500 					ELF_ST_BIND(sym.st_info), sym.st_shndx);
501 
502 				LOG_INF("writing relocation type %d at %#lx with symbol %s (%#lx)",
503 					(int)ELF_R_TYPE(rel.r_info), op_loc, name, link_addr);
504 			}
505 #endif /* CONFIG_LLEXT_LOG_LEVEL */
506 
507 
508 			/* relocation */
509 			ret = arch_elf_relocate(ldr, ext, &rel, shdr);
510 			if (ret != 0) {
511 				return ret;
512 			}
513 		}
514 	}
515 
516 #ifdef CONFIG_CACHE_MANAGEMENT
517 	/* Make sure changes to memory regions are flushed to RAM */
518 	for (i = 0; i < LLEXT_MEM_COUNT; ++i) {
519 		if (ext->mem[i]) {
520 			sys_cache_data_flush_range(ext->mem[i], ext->mem_size[i]);
521 			sys_cache_instr_invd_range(ext->mem[i], ext->mem_size[i]);
522 		}
523 	}
524 
525 	/* Detached section caches should be synchronized in place */
526 	if (ldr_parm->section_detached) {
527 		for (i = 0; i < ext->sect_cnt; ++i) {
528 			elf_shdr_t *shdr = ext->sect_hdrs + i;
529 
530 			if (ldr_parm->section_detached(shdr)) {
531 				void *base = llext_peek(ldr, shdr->sh_offset);
532 
533 				sys_cache_data_flush_range(base, shdr->sh_size);
534 				sys_cache_instr_invd_range(base, shdr->sh_size);
535 			}
536 		}
537 	}
538 #endif
539 
540 	return 0;
541 }
542