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(elf_rela_t * rel,uintptr_t loc,uintptr_t sym_base_addr,const char * sym_name,uintptr_t load_bias)29 __weak int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc,
30 			     uintptr_t sym_base_addr, const char *sym_name, uintptr_t load_bias)
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,size_t offset)51 static size_t llext_file_offset(struct llext_loader *ldr, size_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 offset;
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 
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)145 static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr_t *shdr,
146 			   const struct llext_load_param *ldr_parm, elf_shdr_t *tgt)
147 {
148 	unsigned int sh_cnt = shdr->sh_size / shdr->sh_entsize;
149 	/*
150 	 * CPU address where the .text section is stored, we use .text just as a
151 	 * reference point
152 	 */
153 	uint8_t *text = ext->mem[LLEXT_MEM_TEXT];
154 
155 	LOG_DBG("Found %p in PLT %u size %zu cnt %u text %p",
156 		(void *)llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr->sh_name),
157 		shdr->sh_type, (size_t)shdr->sh_entsize, sh_cnt, (void *)text);
158 
159 	const elf_shdr_t *sym_shdr = ldr->sects + LLEXT_MEM_SYMTAB;
160 	unsigned int sym_cnt = sym_shdr->sh_size / sym_shdr->sh_entsize;
161 
162 	for (unsigned int i = 0; i < sh_cnt; i++) {
163 		elf_rela_t rela;
164 
165 		int ret = llext_seek(ldr, shdr->sh_offset + i * shdr->sh_entsize);
166 
167 		if (!ret) {
168 			ret = llext_read(ldr, &rela, sizeof(rela));
169 		}
170 
171 		if (ret != 0) {
172 			LOG_ERR("PLT: failed to read RELA #%u, trying to continue", i);
173 			continue;
174 		}
175 
176 		/* Index in the symbol table */
177 		unsigned int j = ELF_R_SYM(rela.r_info);
178 
179 		if (j >= sym_cnt) {
180 			LOG_WRN("PLT: idx %u >= %u", j, sym_cnt);
181 			continue;
182 		}
183 
184 		elf_sym_t sym;
185 
186 		ret = llext_seek(ldr, sym_shdr->sh_offset + j * sizeof(elf_sym_t));
187 		if (!ret) {
188 			ret = llext_read(ldr, &sym, sizeof(sym));
189 		}
190 
191 		if (ret < 0) {
192 			LOG_ERR("PLT: failed to read symbol table #%u RELA #%u, trying to continue",
193 				j, i);
194 			continue;
195 		}
196 
197 		uint32_t stt = ELF_ST_TYPE(sym.st_info);
198 
199 		if (stt != STT_FUNC &&
200 		    stt != STT_SECTION &&
201 		    stt != STT_OBJECT &&
202 		    (stt != STT_NOTYPE || sym.st_shndx != SHN_UNDEF)) {
203 			continue;
204 		}
205 
206 		const char *name = llext_string(ldr, ext, LLEXT_MEM_STRTAB, sym.st_name);
207 
208 		/*
209 		 * Both r_offset and sh_addr are addresses for which the extension
210 		 * has been built.
211 		 *
212 		 * NOTE: The calculations below assumes offsets from the
213 		 * beginning of the .text section in the ELF file can be
214 		 * applied to the memory location of mem[LLEXT_MEM_TEXT].
215 		 *
216 		 * This is valid only when CONFIG_LLEXT_STORAGE_WRITABLE=y
217 		 * and peek() is usable on the source ELF file.
218 		 */
219 		uint8_t *rel_addr = (uint8_t *)ext->mem[LLEXT_MEM_TEXT] -
220 			ldr->sects[LLEXT_MEM_TEXT].sh_offset;
221 
222 		if (tgt) {
223 			/* Relocatable / partially linked ELF. */
224 			rel_addr += rela.r_offset + tgt->sh_offset;
225 		} else {
226 			/* Shared / dynamically linked ELF */
227 			rel_addr += llext_file_offset(ldr, rela.r_offset);
228 		}
229 
230 		uint32_t stb = ELF_ST_BIND(sym.st_info);
231 		const void *link_addr;
232 
233 		switch (stb) {
234 		case STB_GLOBAL:
235 			/* First try the global symbol table */
236 			link_addr = llext_find_sym(NULL,
237 				SYM_NAME_OR_SLID(name, sym.st_value));
238 
239 			if (!link_addr) {
240 				/* Next try internal tables */
241 				link_addr = llext_find_sym(&ext->sym_tab, name);
242 			}
243 
244 			if (!link_addr) {
245 				/* Finally try any loaded tables */
246 				struct llext *dep;
247 
248 				link_addr = llext_find_extension_sym(name, &dep);
249 				if (link_addr) {
250 					llext_dependency_add(ext, dep);
251 				}
252 			}
253 
254 			if (!link_addr) {
255 				LOG_WRN("PLT: cannot find idx %u name %s", j, name);
256 				continue;
257 			}
258 
259 			/* Resolve the symbol */
260 			arch_elf_relocate_global(ldr, ext, &rela, &sym, rel_addr, link_addr);
261 			break;
262 		case STB_LOCAL:
263 			arch_elf_relocate_local(ldr, ext, &rela, &sym, rel_addr, ldr_parm);
264 		}
265 
266 		LOG_DBG("symbol %s relocation @%p r-offset %#zx .text offset %#zx stb %u",
267 			name, (void *)rel_addr,
268 			(size_t)rela.r_offset, (size_t)ldr->sects[LLEXT_MEM_TEXT].sh_offset, stb);
269 	}
270 }
271 
llext_link(struct llext_loader * ldr,struct llext * ext,const struct llext_load_param * ldr_parm)272 int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_load_param *ldr_parm)
273 {
274 	uintptr_t sect_base = 0;
275 	elf_rela_t rel;
276 	elf_sym_t sym;
277 	elf_word rel_cnt = 0;
278 	const char *name;
279 	int i, ret;
280 
281 	for (i = 0; i < ext->sect_cnt; ++i) {
282 		elf_shdr_t *shdr = ext->sect_hdrs + i;
283 
284 		/* find proper relocation sections */
285 		switch (shdr->sh_type) {
286 		case SHT_REL:
287 			if (shdr->sh_entsize != sizeof(elf_rel_t)) {
288 				LOG_ERR("Invalid entry size %zd for SHT_REL section %d",
289 					(size_t)shdr->sh_entsize, i);
290 				return -ENOEXEC;
291 			}
292 			break;
293 		case SHT_RELA:
294 			if (IS_ENABLED(CONFIG_ARM)) {
295 				LOG_ERR("Found unsupported SHT_RELA section %d", i);
296 				return -ENOTSUP;
297 			}
298 			if (shdr->sh_entsize != sizeof(elf_rela_t)) {
299 				LOG_ERR("Invalid entry size %zd for SHT_RELA section %d",
300 					(size_t)shdr->sh_entsize, i);
301 				return -ENOEXEC;
302 			}
303 			break;
304 		default:
305 			/* ignore this section */
306 			continue;
307 		}
308 
309 		if (shdr->sh_info >= ext->sect_cnt ||
310 		    shdr->sh_size % shdr->sh_entsize != 0) {
311 			LOG_ERR("Sanity checks failed for section %d "
312 				"(info %zd, size %zd, entsize %zd)", i,
313 				(size_t)shdr->sh_info,
314 				(size_t)shdr->sh_size,
315 				(size_t)shdr->sh_entsize);
316 			return -ENOEXEC;
317 		}
318 
319 		rel_cnt = shdr->sh_size / shdr->sh_entsize;
320 
321 		name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr->sh_name);
322 
323 		/*
324 		 * FIXME: The Xtensa port is currently using a different way of
325 		 * handling relocations that ultimately results in separate
326 		 * arch-specific code paths. This code should be merged with
327 		 * the logic below once the differences are resolved.
328 		 */
329 		if (IS_ENABLED(CONFIG_XTENSA)) {
330 			elf_shdr_t *tgt;
331 
332 			if (strcmp(name, ".rela.plt") == 0 ||
333 			    strcmp(name, ".rela.dyn") == 0) {
334 				tgt = NULL;
335 			} else {
336 				/*
337 				 * Entries in .rel.X and .rela.X sections describe references in
338 				 * section .X to local or global symbols. They point to entries
339 				 * in the symbol table, describing respective symbols
340 				 */
341 				tgt = ext->sect_hdrs + shdr->sh_info;
342 			}
343 
344 			llext_link_plt(ldr, ext, shdr, ldr_parm, tgt);
345 			continue;
346 		}
347 
348 		LOG_DBG("relocation section %s (%d) acting on section %d has %zd relocations",
349 			name, i, shdr->sh_info, (size_t)rel_cnt);
350 
351 		enum llext_mem mem_idx = ldr->sect_map[shdr->sh_info].mem_idx;
352 
353 		if (mem_idx == LLEXT_MEM_COUNT) {
354 			LOG_ERR("Section %d not loaded in any memory region", shdr->sh_info);
355 			return -ENOEXEC;
356 		}
357 
358 		sect_base = (uintptr_t)llext_loaded_sect_ptr(ldr, ext, shdr->sh_info);
359 
360 		for (int j = 0; j < rel_cnt; j++) {
361 			/* get each relocation entry */
362 			ret = llext_seek(ldr, shdr->sh_offset + j * shdr->sh_entsize);
363 			if (ret != 0) {
364 				return ret;
365 			}
366 
367 			ret = llext_read(ldr, &rel, shdr->sh_entsize);
368 			if (ret != 0) {
369 				return ret;
370 			}
371 
372 			/* get corresponding symbol */
373 			ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset
374 				    + ELF_R_SYM(rel.r_info) * sizeof(elf_sym_t));
375 			if (ret != 0) {
376 				return ret;
377 			}
378 
379 			ret = llext_read(ldr, &sym, sizeof(elf_sym_t));
380 			if (ret != 0) {
381 				return ret;
382 			}
383 
384 			name = llext_string(ldr, ext, LLEXT_MEM_STRTAB, sym.st_name);
385 
386 			LOG_DBG("relocation %d:%d info 0x%zx (type %zd, sym %zd) offset %zd "
387 				"sym_name %s sym_type %d sym_bind %d sym_ndx %d",
388 				i, j, (size_t)rel.r_info, (size_t)ELF_R_TYPE(rel.r_info),
389 				(size_t)ELF_R_SYM(rel.r_info), (size_t)rel.r_offset,
390 				name, ELF_ST_TYPE(sym.st_info),
391 				ELF_ST_BIND(sym.st_info), sym.st_shndx);
392 
393 			uintptr_t link_addr, op_loc;
394 
395 			op_loc = sect_base + rel.r_offset;
396 
397 			if (ELF_R_SYM(rel.r_info) == 0) {
398 				/* no symbol ex: R_ARM_V4BX relocation, R_ARM_RELATIVE  */
399 				link_addr = 0;
400 			} else if (sym.st_shndx == SHN_UNDEF) {
401 				/* If symbol is undefined, then we need to look it up */
402 				link_addr = (uintptr_t)llext_find_sym(NULL,
403 					SYM_NAME_OR_SLID(name, sym.st_value));
404 
405 				if (link_addr == 0) {
406 					/* Try loaded tables */
407 					struct llext *dep;
408 
409 					link_addr = (uintptr_t)llext_find_extension_sym(name, &dep);
410 					if (link_addr) {
411 						llext_dependency_add(ext, dep);
412 					}
413 				}
414 
415 				if (link_addr == 0) {
416 					LOG_ERR("Undefined symbol with no entry in "
417 						"symbol table %s, offset %zd, link section %d",
418 						name, (size_t)rel.r_offset, shdr->sh_link);
419 					return -ENODATA;
420 				}
421 
422 				LOG_INF("found symbol %s at 0x%lx", name, link_addr);
423 			} else if (sym.st_shndx == SHN_ABS) {
424 				/* Absolute symbol */
425 				link_addr = sym.st_value;
426 			} else if ((sym.st_shndx < ldr->hdr.e_shnum) &&
427 				!IN_RANGE(sym.st_shndx, SHN_LORESERVE, SHN_HIRESERVE)) {
428 				/* This check rejects all relocations whose target symbol
429 				 * has a section index higher than the maximum possible
430 				 * in this ELF file, or belongs in the reserved range:
431 				 * they will be caught by the `else` below and cause an
432 				 * error to be returned. This aborts the LLEXT's loading
433 				 * and prevents execution of improperly relocated code,
434 				 * which is dangerous.
435 				 *
436 				 * Note that the unsupported SHN_COMMON section is rejected
437 				 * as part of this check. Also note that SHN_ABS would be
438 				 * rejected as well, but we want to handle it properly:
439 				 * for this reason, this check must come AFTER handling
440 				 * the case where the symbol's section index is SHN_ABS!
441 				 *
442 				 *
443 				 * For regular symbols, the link address is obtained by
444 				 * adding st_value to the start address of the section
445 				 * in which the target symbol resides.
446 				 */
447 				link_addr = (uintptr_t)llext_loaded_sect_ptr(ldr, ext,
448 									     sym.st_shndx)
449 					    + sym.st_value;
450 			} else {
451 				LOG_ERR("rela section %d, entry %d: cannot apply relocation: "
452 					"target symbol has unexpected section index %d (0x%X)",
453 					i, j, sym.st_shndx, sym.st_shndx);
454 				return -ENOEXEC;
455 			}
456 
457 			LOG_INF("writing relocation symbol %s type %zd sym %zd at addr 0x%lx "
458 				"addr 0x%lx",
459 				name, (size_t)ELF_R_TYPE(rel.r_info), (size_t)ELF_R_SYM(rel.r_info),
460 				op_loc, link_addr);
461 
462 			/* relocation */
463 			ret = arch_elf_relocate(&rel, op_loc, link_addr, name,
464 					     (uintptr_t)ext->mem[LLEXT_MEM_TEXT]);
465 			if (ret != 0) {
466 				return ret;
467 			}
468 		}
469 	}
470 
471 #ifdef CONFIG_CACHE_MANAGEMENT
472 	/* Make sure changes to memory regions are flushed to RAM */
473 	for (i = 0; i < LLEXT_MEM_COUNT; ++i) {
474 		if (ext->mem[i]) {
475 			sys_cache_data_flush_range(ext->mem[i], ext->mem_size[i]);
476 			sys_cache_instr_invd_range(ext->mem[i], ext->mem_size[i]);
477 		}
478 	}
479 
480 	/* Detached section caches should be synchronized in place */
481 	if (ldr_parm->section_detached) {
482 		for (i = 0; i < ext->sect_cnt; ++i) {
483 			elf_shdr_t *shdr = ext->sect_hdrs + i;
484 
485 			if (ldr_parm->section_detached(shdr)) {
486 				void *base = llext_peek(ldr, shdr->sh_offset);
487 
488 				sys_cache_data_flush_range(base, shdr->sh_size);
489 				sys_cache_instr_invd_range(base, shdr->sh_size);
490 			}
491 		}
492 	}
493 #endif
494 
495 	return 0;
496 }
497