1 /*
2  * Copyright (c) 2023 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
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_REGISTER(llext, CONFIG_LLEXT_LOG_LEVEL);
17 
18 #include <string.h>
19 
20 #include "llext_priv.h"
21 
22 sys_slist_t llext_list = SYS_SLIST_STATIC_INIT(&llext_list);
23 
24 struct k_mutex llext_lock = Z_MUTEX_INITIALIZER(llext_lock);
25 
llext_section_shndx(const struct llext_loader * ldr,const struct llext * ext,const char * sect_name)26 int llext_section_shndx(const struct llext_loader *ldr, const struct llext *ext,
27 			const char *sect_name)
28 {
29 	unsigned int i;
30 
31 	for (i = 1; i < ext->sect_cnt; i++) {
32 		const char *name = llext_section_name(ldr, ext, ext->sect_hdrs + i);
33 
34 		if (!strcmp(name, sect_name)) {
35 			return i;
36 		}
37 	}
38 
39 	return -ENOENT;
40 }
41 
llext_get_section_header(struct llext_loader * ldr,struct llext * ext,const char * search_name,elf_shdr_t * shdr)42 int llext_get_section_header(struct llext_loader *ldr, struct llext *ext, const char *search_name,
43 			     elf_shdr_t *shdr)
44 {
45 	int ret;
46 
47 	ret = llext_section_shndx(ldr, ext, search_name);
48 	if (ret < 0) {
49 		return ret;
50 	}
51 
52 	*shdr = ext->sect_hdrs[ret];
53 	return 0;
54 }
55 
llext_find_section(struct llext_loader * ldr,const char * search_name)56 ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name)
57 {
58 	elf_shdr_t *shdr;
59 	unsigned int i;
60 	size_t pos;
61 
62 	for (i = 0, pos = ldr->hdr.e_shoff;
63 	     i < ldr->hdr.e_shnum;
64 	     i++, pos += ldr->hdr.e_shentsize) {
65 		shdr = llext_peek(ldr, pos);
66 		if (!shdr) {
67 			/* The peek() method isn't supported */
68 			return -ENOTSUP;
69 		}
70 
71 		const char *name = llext_peek(ldr,
72 					      ldr->sects[LLEXT_MEM_SHSTRTAB].sh_offset +
73 					      shdr->sh_name);
74 
75 		if (!strcmp(name, search_name)) {
76 			return shdr->sh_offset;
77 		}
78 	}
79 
80 	return -ENOENT;
81 }
82 
83 /*
84  * Note, that while we protect the global llext list while searching, we release
85  * the lock before returning the found extension to the caller. Therefore it's
86  * a responsibility of the caller to protect against races with a freeing
87  * context when calling this function.
88  */
llext_by_name(const char * name)89 struct llext *llext_by_name(const char *name)
90 {
91 	k_mutex_lock(&llext_lock, K_FOREVER);
92 
93 	for (sys_snode_t *node = sys_slist_peek_head(&llext_list);
94 	     node != NULL;
95 	     node = sys_slist_peek_next(node)) {
96 		struct llext *ext = CONTAINER_OF(node, struct llext, llext_list);
97 
98 		if (strncmp(ext->name, name, LLEXT_MAX_NAME_LEN) == 0) {
99 			k_mutex_unlock(&llext_lock);
100 			return ext;
101 		}
102 	}
103 
104 	k_mutex_unlock(&llext_lock);
105 	return NULL;
106 }
107 
llext_iterate(int (* fn)(struct llext * ext,void * arg),void * arg)108 int llext_iterate(int (*fn)(struct llext *ext, void *arg), void *arg)
109 {
110 	sys_snode_t *node;
111 	int ret = 0;
112 
113 	k_mutex_lock(&llext_lock, K_FOREVER);
114 
115 	for (node = sys_slist_peek_head(&llext_list);
116 	     node;
117 	     node = sys_slist_peek_next(node)) {
118 		struct llext *ext = CONTAINER_OF(node, struct llext, llext_list);
119 
120 		ret = fn(ext, arg);
121 		if (ret) {
122 			break;
123 		}
124 	}
125 
126 	k_mutex_unlock(&llext_lock);
127 	return ret;
128 }
129 
llext_find_sym(const struct llext_symtable * sym_table,const char * sym_name)130 const void *llext_find_sym(const struct llext_symtable *sym_table, const char *sym_name)
131 {
132 	if (sym_table == NULL) {
133 		/* Built-in symbol table */
134 #ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
135 		/* 'sym_name' is actually a SLID to search for */
136 		uintptr_t slid = (uintptr_t)sym_name;
137 
138 		/* TODO: perform a binary search instead of linear.
139 		 * Note that - as of writing - the llext_const_symbol_area
140 		 * section is sorted in ascending SLID order.
141 		 * (see scripts/build/llext_prepare_exptab.py)
142 		 */
143 		STRUCT_SECTION_FOREACH(llext_const_symbol, sym) {
144 			if (slid == sym->slid) {
145 				return sym->addr;
146 			}
147 		}
148 #else
149 		STRUCT_SECTION_FOREACH(llext_const_symbol, sym) {
150 			if (strcmp(sym->name, sym_name) == 0) {
151 				return sym->addr;
152 			}
153 		}
154 #endif
155 	} else {
156 		/* find symbols in module */
157 		for (size_t i = 0; i < sym_table->sym_cnt; i++) {
158 			if (strcmp(sym_table->syms[i].name, sym_name) == 0) {
159 				return sym_table->syms[i].addr;
160 			}
161 		}
162 	}
163 
164 	return NULL;
165 }
166 
llext_load(struct llext_loader * ldr,const char * name,struct llext ** ext,const struct llext_load_param * ldr_parm)167 int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext,
168 	       const struct llext_load_param *ldr_parm)
169 {
170 	int ret;
171 
172 	*ext = llext_by_name(name);
173 
174 	k_mutex_lock(&llext_lock, K_FOREVER);
175 
176 	if (*ext) {
177 		/* The use count is at least 1 */
178 		ret = (*ext)->use_count++;
179 		goto out;
180 	}
181 
182 	*ext = llext_alloc_data(sizeof(struct llext));
183 	if (*ext == NULL) {
184 		LOG_ERR("Not enough memory for extension metadata");
185 		ret = -ENOMEM;
186 		goto out;
187 	}
188 
189 	ret = do_llext_load(ldr, *ext, ldr_parm);
190 	if (ret < 0) {
191 		llext_free(*ext);
192 		*ext = NULL;
193 		goto out;
194 	}
195 
196 	/* The (*ext)->name array is LLEXT_MAX_NAME_LEN + 1 bytes long */
197 	strncpy((*ext)->name, name, LLEXT_MAX_NAME_LEN);
198 	(*ext)->name[LLEXT_MAX_NAME_LEN] = '\0';
199 	(*ext)->use_count++;
200 
201 	sys_slist_append(&llext_list, &(*ext)->llext_list);
202 	LOG_INF("Loaded extension %s", (*ext)->name);
203 
204 out:
205 	k_mutex_unlock(&llext_lock);
206 	return ret;
207 }
208 
209 #include <zephyr/logging/log_ctrl.h>
210 
llext_unload(struct llext ** ext)211 int llext_unload(struct llext **ext)
212 {
213 	__ASSERT(*ext, "Expected non-null extension");
214 	struct llext *tmp = *ext;
215 
216 	/* Flush pending log messages, as the deferred formatting may be referencing
217 	 * strings/args in the extension we are about to unload
218 	 */
219 	log_flush();
220 
221 	k_mutex_lock(&llext_lock, K_FOREVER);
222 
223 	__ASSERT(tmp->use_count, "A valid LLEXT cannot have a zero use-count!");
224 
225 	if (tmp->use_count-- != 1) {
226 		unsigned int ret = tmp->use_count;
227 
228 		k_mutex_unlock(&llext_lock);
229 		return ret;
230 	}
231 
232 	/* FIXME: protect the global list */
233 	sys_slist_find_and_remove(&llext_list, &tmp->llext_list);
234 
235 	llext_dependency_remove_all(tmp);
236 
237 	*ext = NULL;
238 	k_mutex_unlock(&llext_lock);
239 
240 	if (tmp->sect_hdrs_on_heap) {
241 		llext_free(tmp->sect_hdrs);
242 	}
243 
244 	llext_free_regions(tmp);
245 	llext_free(tmp->sym_tab.syms);
246 	llext_free(tmp->exp_tab.syms);
247 	llext_free(tmp);
248 
249 	return 0;
250 }
251 
llext_call_fn(struct llext * ext,const char * sym_name)252 int llext_call_fn(struct llext *ext, const char *sym_name)
253 {
254 	void (*fn)(void);
255 
256 	fn = llext_find_sym(&ext->exp_tab, sym_name);
257 	if (fn == NULL) {
258 		return -ENOENT;
259 	}
260 	fn();
261 
262 	return 0;
263 }
264 
call_fn_table(struct llext * ext,bool is_init)265 static int call_fn_table(struct llext *ext, bool is_init)
266 {
267 	ssize_t ret;
268 
269 	ret = llext_get_fn_table(ext, is_init, NULL, 0);
270 	if (ret < 0) {
271 		LOG_ERR("Failed to get table size: %d", (int)ret);
272 		return ret;
273 	}
274 
275 	typedef void (*elf_void_fn_t)(void);
276 
277 	int fn_count = ret / sizeof(elf_void_fn_t);
278 	elf_void_fn_t fn_table[fn_count];
279 
280 	ret = llext_get_fn_table(ext, is_init, &fn_table, sizeof(fn_table));
281 	if (ret < 0) {
282 		LOG_ERR("Failed to get function table: %d", (int)ret);
283 		return ret;
284 	}
285 
286 	for (int i = 0; i < fn_count; i++) {
287 		LOG_DBG("calling %s function %p()",
288 			is_init ? "bringup" : "teardown", (void *)fn_table[i]);
289 		fn_table[i]();
290 	}
291 
292 	return 0;
293 }
294 
llext_bringup(struct llext * ext)295 inline int llext_bringup(struct llext *ext)
296 {
297 	return call_fn_table(ext, true);
298 }
299 
llext_teardown(struct llext * ext)300 inline int llext_teardown(struct llext *ext)
301 {
302 	return call_fn_table(ext, false);
303 }
304 
llext_bootstrap(struct llext * ext,llext_entry_fn_t entry_fn,void * user_data)305 void llext_bootstrap(struct llext *ext, llext_entry_fn_t entry_fn, void *user_data)
306 {
307 	int ret;
308 
309 	/* Call initialization functions */
310 	ret = llext_bringup(ext);
311 	if (ret < 0) {
312 		LOG_ERR("Failed to call init functions: %d", ret);
313 		return;
314 	}
315 
316 	/* Start extension main function */
317 	LOG_DBG("calling entry function %p(%p)", (void *)entry_fn, user_data);
318 	entry_fn(user_data);
319 
320 	/* Call de-initialization functions */
321 	ret = llext_teardown(ext);
322 	if (ret < 0) {
323 		LOG_ERR("Failed to call de-init functions: %d", ret);
324 		return;
325 	}
326 }
327