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/kernel.h>
13 #include <zephyr/cache.h>
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL);
17 
18 #include <string.h>
19 
20 #include "llext_priv.h"
21 
22 #ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
23 #define SYM_NAME_OR_SLID(name, slid) ((const char *) slid)
24 #else
25 #define SYM_NAME_OR_SLID(name, slid) name
26 #endif
27 
arch_elf_relocate(elf_rela_t * rel,uintptr_t loc,uintptr_t sym_base_addr,const char * sym_name,uintptr_t load_bias)28 __weak int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc,
29 			     uintptr_t sym_base_addr, const char *sym_name, uintptr_t load_bias)
30 {
31 	return -ENOTSUP;
32 }
33 
arch_elf_relocate_local(struct llext_loader * ldr,struct llext * ext,const elf_rela_t * rel,const elf_sym_t * sym,size_t got_offset)34 __weak void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext,
35 				    const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset)
36 {
37 }
38 
39 /*
40  * Find the memory region containing the supplied offset and return the
41  * corresponding file offset
42  */
llext_file_offset(struct llext_loader * ldr,size_t offset)43 static size_t llext_file_offset(struct llext_loader *ldr, size_t offset)
44 {
45 	unsigned int i;
46 
47 	for (i = 0; i < LLEXT_MEM_COUNT; i++)
48 		if (ldr->sects[i].sh_addr <= offset &&
49 		    ldr->sects[i].sh_addr + ldr->sects[i].sh_size > offset)
50 			return offset - ldr->sects[i].sh_addr + ldr->sects[i].sh_offset;
51 
52 	return offset;
53 }
54 
llext_link_plt(struct llext_loader * ldr,struct llext * ext,elf_shdr_t * shdr,bool do_local,elf_shdr_t * tgt)55 static void llext_link_plt(struct llext_loader *ldr, struct llext *ext,
56 			   elf_shdr_t *shdr, bool do_local, elf_shdr_t *tgt)
57 {
58 	unsigned int sh_cnt = shdr->sh_size / shdr->sh_entsize;
59 	/*
60 	 * CPU address where the .text section is stored, we use .text just as a
61 	 * reference point
62 	 */
63 	uint8_t *text = ext->mem[LLEXT_MEM_TEXT];
64 
65 	LOG_DBG("Found %p in PLT %u size %zu cnt %u text %p",
66 		(void *)llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr->sh_name),
67 		shdr->sh_type, (size_t)shdr->sh_entsize, sh_cnt, (void *)text);
68 
69 	const elf_shdr_t *sym_shdr = ldr->sects + LLEXT_MEM_SYMTAB;
70 	unsigned int sym_cnt = sym_shdr->sh_size / sym_shdr->sh_entsize;
71 
72 	for (unsigned int i = 0; i < sh_cnt; i++) {
73 		elf_rela_t rela;
74 
75 		int ret = llext_seek(ldr, shdr->sh_offset + i * shdr->sh_entsize);
76 
77 		if (!ret) {
78 			ret = llext_read(ldr, &rela, sizeof(rela));
79 		}
80 
81 		if (ret < 0) {
82 			LOG_ERR("PLT: failed to read RELA #%u, trying to continue", i);
83 			continue;
84 		}
85 
86 		/* Index in the symbol table */
87 		unsigned int j = ELF32_R_SYM(rela.r_info);
88 
89 		if (j >= sym_cnt) {
90 			LOG_WRN("PLT: idx %u >= %u", j, sym_cnt);
91 			continue;
92 		}
93 
94 		elf_sym_t sym_tbl;
95 
96 		ret = llext_seek(ldr, sym_shdr->sh_offset + j * sizeof(elf_sym_t));
97 		if (!ret) {
98 			ret = llext_read(ldr, &sym_tbl, sizeof(sym_tbl));
99 		}
100 
101 		if (ret < 0) {
102 			LOG_ERR("PLT: failed to read symbol table #%u RELA #%u, trying to continue",
103 				j, i);
104 			continue;
105 		}
106 
107 		uint32_t stt = ELF_ST_TYPE(sym_tbl.st_info);
108 
109 		if (stt != STT_FUNC &&
110 		    stt != STT_SECTION &&
111 		    stt != STT_OBJECT &&
112 		    (stt != STT_NOTYPE || sym_tbl.st_shndx != SHN_UNDEF)) {
113 			continue;
114 		}
115 
116 		const char *name = llext_string(ldr, ext, LLEXT_MEM_STRTAB, sym_tbl.st_name);
117 
118 		/*
119 		 * Both r_offset and sh_addr are addresses for which the extension
120 		 * has been built.
121 		 *
122 		 * NOTE: The calculations below assumes offsets from the
123 		 * beginning of the .text section in the ELF file can be
124 		 * applied to the memory location of mem[LLEXT_MEM_TEXT].
125 		 *
126 		 * This is valid only when CONFIG_LLEXT_STORAGE_WRITABLE=y
127 		 * and peek() is usable on the source ELF file.
128 		 */
129 		size_t got_offset;
130 
131 		if (tgt) {
132 			got_offset = rela.r_offset + tgt->sh_offset -
133 				ldr->sects[LLEXT_MEM_TEXT].sh_offset;
134 		} else {
135 			got_offset = llext_file_offset(ldr, rela.r_offset) -
136 				ldr->sects[LLEXT_MEM_TEXT].sh_offset;
137 		}
138 
139 		uint32_t stb = ELF_ST_BIND(sym_tbl.st_info);
140 		const void *link_addr;
141 
142 		switch (stb) {
143 		case STB_GLOBAL:
144 			link_addr = llext_find_sym(NULL,
145 				SYM_NAME_OR_SLID(name, sym_tbl.st_value));
146 
147 			if (!link_addr)
148 				link_addr = llext_find_sym(&ext->sym_tab, name);
149 
150 			if (!link_addr) {
151 				LOG_WRN("PLT: cannot find idx %u name %s", j, name);
152 				continue;
153 			}
154 
155 			/* Resolve the symbol */
156 			*(const void **)(text + got_offset) = link_addr;
157 			break;
158 		case STB_LOCAL:
159 			if (do_local) {
160 				arch_elf_relocate_local(ldr, ext, &rela, &sym_tbl, got_offset);
161 			}
162 		}
163 
164 		LOG_DBG("symbol %s offset %#zx r-offset %#zx .text offset %#zx stb %u",
165 			name, got_offset,
166 			(size_t)rela.r_offset, (size_t)ldr->sects[LLEXT_MEM_TEXT].sh_offset, stb);
167 	}
168 }
169 
llext_link(struct llext_loader * ldr,struct llext * ext,bool do_local)170 int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local)
171 {
172 	uintptr_t sect_base = 0;
173 	elf_rela_t rel;
174 	elf_sym_t sym;
175 	elf_word rel_cnt = 0;
176 	const char *name;
177 	int i, ret;
178 
179 	for (i = 0; i < ldr->sect_cnt; ++i) {
180 		elf_shdr_t *shdr = ldr->sect_hdrs + i;
181 
182 		/* find proper relocation sections */
183 		switch (shdr->sh_type) {
184 		case SHT_REL:
185 			if (shdr->sh_entsize != sizeof(elf_rel_t)) {
186 				LOG_ERR("Invalid entry size %zd for SHT_REL section %d",
187 					(size_t)shdr->sh_entsize, i);
188 				return -ENOEXEC;
189 			}
190 			break;
191 		case SHT_RELA:
192 			/* FIXME: currently implemented only on the Xtensa code path */
193 			if (!IS_ENABLED(CONFIG_XTENSA)) {
194 				LOG_ERR("Found unsupported SHT_RELA section %d", i);
195 				return -ENOTSUP;
196 			}
197 			if (shdr->sh_entsize != sizeof(elf_rela_t)) {
198 				LOG_ERR("Invalid entry size %zd for SHT_RELA section %d",
199 					(size_t)shdr->sh_entsize, i);
200 				return -ENOEXEC;
201 			}
202 			break;
203 		default:
204 			/* ignore this section */
205 			continue;
206 		}
207 
208 		if (shdr->sh_info >= ldr->sect_cnt ||
209 		    shdr->sh_size % shdr->sh_entsize != 0) {
210 			LOG_ERR("Sanity checks failed for section %d "
211 				"(info %zd, size %zd, entsize %zd)", i,
212 				(size_t)shdr->sh_info,
213 				(size_t)shdr->sh_size,
214 				(size_t)shdr->sh_entsize);
215 			return -ENOEXEC;
216 		}
217 
218 		rel_cnt = shdr->sh_size / shdr->sh_entsize;
219 
220 		name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr->sh_name);
221 
222 		/*
223 		 * FIXME: The Xtensa port is currently using a different way of
224 		 * handling relocations that ultimately results in separate
225 		 * arch-specific code paths. This code should be merged with
226 		 * the logic below once the differences are resolved.
227 		 */
228 		if (IS_ENABLED(CONFIG_XTENSA)) {
229 			elf_shdr_t *tgt;
230 
231 			if (strcmp(name, ".rela.plt") == 0 ||
232 			    strcmp(name, ".rela.dyn") == 0) {
233 				tgt = NULL;
234 			} else {
235 				tgt = ldr->sect_hdrs + shdr->sh_info;
236 			}
237 
238 			llext_link_plt(ldr, ext, shdr, do_local, tgt);
239 			continue;
240 		}
241 
242 		LOG_DBG("relocation section %s (%d) acting on section %d has %zd relocations",
243 			name, i, shdr->sh_info, (size_t)rel_cnt);
244 
245 		enum llext_mem mem_idx = ldr->sect_map[shdr->sh_info].mem_idx;
246 
247 		if (mem_idx == LLEXT_MEM_COUNT) {
248 			LOG_ERR("Section %d not loaded in any memory region", shdr->sh_info);
249 			return -ENOEXEC;
250 		}
251 
252 		sect_base = (uintptr_t)llext_loaded_sect_ptr(ldr, ext, shdr->sh_info);
253 
254 		for (int j = 0; j < rel_cnt; j++) {
255 			/* get each relocation entry */
256 			ret = llext_seek(ldr, shdr->sh_offset + j * shdr->sh_entsize);
257 			if (ret != 0) {
258 				return ret;
259 			}
260 
261 			ret = llext_read(ldr, &rel, shdr->sh_entsize);
262 			if (ret != 0) {
263 				return ret;
264 			}
265 
266 			/* get corresponding symbol */
267 			ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset
268 				    + ELF_R_SYM(rel.r_info) * sizeof(elf_sym_t));
269 			if (ret != 0) {
270 				return ret;
271 			}
272 
273 			ret = llext_read(ldr, &sym, sizeof(elf_sym_t));
274 			if (ret != 0) {
275 				return ret;
276 			}
277 
278 			name = llext_string(ldr, ext, LLEXT_MEM_STRTAB, sym.st_name);
279 
280 			LOG_DBG("relocation %d:%d info 0x%zx (type %zd, sym %zd) offset %zd "
281 				"sym_name %s sym_type %d sym_bind %d sym_ndx %d",
282 				i, j, (size_t)rel.r_info, (size_t)ELF_R_TYPE(rel.r_info),
283 				(size_t)ELF_R_SYM(rel.r_info), (size_t)rel.r_offset,
284 				name, ELF_ST_TYPE(sym.st_info),
285 				ELF_ST_BIND(sym.st_info), sym.st_shndx);
286 
287 			uintptr_t link_addr, op_loc;
288 
289 			op_loc = sect_base + rel.r_offset;
290 
291 			if (ELF_R_SYM(rel.r_info) == 0) {
292 				/* no symbol ex: R_ARM_V4BX relocation, R_ARM_RELATIVE  */
293 				link_addr = 0;
294 			} else if (sym.st_shndx == SHN_UNDEF) {
295 				/* If symbol is undefined, then we need to look it up */
296 				link_addr = (uintptr_t)llext_find_sym(NULL,
297 					SYM_NAME_OR_SLID(name, sym.st_value));
298 
299 				if (link_addr == 0) {
300 					LOG_ERR("Undefined symbol with no entry in "
301 						"symbol table %s, offset %zd, link section %d",
302 						name, (size_t)rel.r_offset, shdr->sh_link);
303 					return -ENODATA;
304 				}
305 
306 				LOG_INF("found symbol %s at 0x%lx", name, link_addr);
307 			} else if (sym.st_shndx == SHN_ABS) {
308 				/* Absolute symbol */
309 				link_addr = sym.st_value;
310 			} else if ((sym.st_shndx < ldr->hdr.e_shnum) &&
311 				!IN_RANGE(sym.st_shndx, SHN_LORESERVE, SHN_HIRESERVE)) {
312 				/* This check rejects all relocations whose target symbol
313 				 * has a section index higher than the maximum possible
314 				 * in this ELF file, or belongs in the reserved range:
315 				 * they will be caught by the `else` below and cause an
316 				 * error to be returned. This aborts the LLEXT's loading
317 				 * and prevents execution of improperly relocated code,
318 				 * which is dangerous.
319 				 *
320 				 * Note that the unsupported SHN_COMMON section is rejected
321 				 * as part of this check. Also note that SHN_ABS would be
322 				 * rejected as well, but we want to handle it properly:
323 				 * for this reason, this check must come AFTER handling
324 				 * the case where the symbol's section index is SHN_ABS!
325 				 *
326 				 *
327 				 * For regular symbols, the link address is obtained by
328 				 * adding st_value to the start address of the section
329 				 * in which the target symbol resides.
330 				 */
331 				link_addr = (uintptr_t)llext_loaded_sect_ptr(ldr, ext,
332 									     sym.st_shndx)
333 					    + sym.st_value;
334 			} else {
335 				LOG_ERR("rela section %d, entry %d: cannot apply relocation: "
336 					"target symbol has unexpected section index %d (0x%X)",
337 					i, j, sym.st_shndx, sym.st_shndx);
338 				return -ENOEXEC;
339 			}
340 
341 			LOG_INF("writing relocation symbol %s type %zd sym %zd at addr 0x%lx "
342 				"addr 0x%lx",
343 				name, (size_t)ELF_R_TYPE(rel.r_info), (size_t)ELF_R_SYM(rel.r_info),
344 				op_loc, link_addr);
345 
346 			/* relocation */
347 			ret = arch_elf_relocate(&rel, op_loc, link_addr, name,
348 					     (uintptr_t)ext->mem[LLEXT_MEM_TEXT]);
349 			if (ret != 0) {
350 				return ret;
351 			}
352 		}
353 	}
354 
355 #ifdef CONFIG_CACHE_MANAGEMENT
356 	/* Make sure changes to memory regions are flushed to RAM */
357 	for (i = 0; i < LLEXT_MEM_COUNT; ++i) {
358 		if (ext->mem[i]) {
359 			sys_cache_data_flush_range(ext->mem[i], ext->mem_size[i]);
360 			sys_cache_instr_invd_range(ext->mem[i], ext->mem_size[i]);
361 		}
362 	}
363 #endif
364 
365 	return 0;
366 }
367