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 static sys_slist_t _llext_list = SYS_SLIST_STATIC_INIT(&_llext_list);
23
24 static 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, sizeof(ext->name)) == 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(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 strncpy((*ext)->name, name, sizeof((*ext)->name));
197 (*ext)->name[sizeof((*ext)->name) - 1] = '\0';
198 (*ext)->use_count++;
199
200 sys_slist_append(&_llext_list, &(*ext)->_llext_list);
201 LOG_INF("Loaded extension %s", (*ext)->name);
202
203 out:
204 k_mutex_unlock(&llext_lock);
205 return ret;
206 }
207
208 #include <zephyr/logging/log_ctrl.h>
209
llext_unload(struct llext ** ext)210 int llext_unload(struct llext **ext)
211 {
212 __ASSERT(*ext, "Expected non-null extension");
213 struct llext *tmp = *ext;
214
215 /* Flush pending log messages, as the deferred formatting may be referencing
216 * strings/args in the extension we are about to unload
217 */
218 log_flush();
219
220 k_mutex_lock(&llext_lock, K_FOREVER);
221
222 __ASSERT(tmp->use_count, "A valid LLEXT cannot have a zero use-count!");
223
224 if (tmp->use_count-- != 1) {
225 unsigned int ret = tmp->use_count;
226
227 k_mutex_unlock(&llext_lock);
228 return ret;
229 }
230
231 /* FIXME: protect the global list */
232 sys_slist_find_and_remove(&_llext_list, &tmp->_llext_list);
233
234 llext_dependency_remove_all(tmp);
235
236 *ext = NULL;
237 k_mutex_unlock(&llext_lock);
238
239 if (tmp->sect_hdrs_on_heap) {
240 llext_free(tmp->sect_hdrs);
241 }
242
243 llext_free_regions(tmp);
244 llext_free(tmp->sym_tab.syms);
245 llext_free(tmp->exp_tab.syms);
246 llext_free(tmp);
247
248 return 0;
249 }
250
llext_call_fn(struct llext * ext,const char * sym_name)251 int llext_call_fn(struct llext *ext, const char *sym_name)
252 {
253 void (*fn)(void);
254
255 fn = llext_find_sym(&ext->exp_tab, sym_name);
256 if (fn == NULL) {
257 return -ENOENT;
258 }
259 fn();
260
261 return 0;
262 }
263
call_fn_table(struct llext * ext,bool is_init)264 static int call_fn_table(struct llext *ext, bool is_init)
265 {
266 ssize_t ret;
267
268 ret = llext_get_fn_table(ext, is_init, NULL, 0);
269 if (ret < 0) {
270 LOG_ERR("Failed to get table size: %d", (int)ret);
271 return ret;
272 }
273
274 typedef void (*elf_void_fn_t)(void);
275
276 int fn_count = ret / sizeof(elf_void_fn_t);
277 elf_void_fn_t fn_table[fn_count];
278
279 ret = llext_get_fn_table(ext, is_init, &fn_table, sizeof(fn_table));
280 if (ret < 0) {
281 LOG_ERR("Failed to get function table: %d", (int)ret);
282 return ret;
283 }
284
285 for (int i = 0; i < fn_count; i++) {
286 LOG_DBG("calling %s function %p()",
287 is_init ? "bringup" : "teardown", (void *)fn_table[i]);
288 fn_table[i]();
289 }
290
291 return 0;
292 }
293
llext_bringup(struct llext * ext)294 inline int llext_bringup(struct llext *ext)
295 {
296 return call_fn_table(ext, true);
297 }
298
llext_teardown(struct llext * ext)299 inline int llext_teardown(struct llext *ext)
300 {
301 return call_fn_table(ext, false);
302 }
303
llext_bootstrap(struct llext * ext,llext_entry_fn_t entry_fn,void * user_data)304 void llext_bootstrap(struct llext *ext, llext_entry_fn_t entry_fn, void *user_data)
305 {
306 int ret;
307
308 /* Call initialization functions */
309 ret = llext_bringup(ext);
310 if (ret < 0) {
311 LOG_ERR("Failed to call init functions: %d", ret);
312 return;
313 }
314
315 /* Start extension main function */
316 LOG_DBG("calling entry function %p(%p)", (void *)entry_fn, user_data);
317 entry_fn(user_data);
318
319 /* Call de-initialization functions */
320 ret = llext_teardown(ext);
321 if (ret < 0) {
322 LOG_ERR("Failed to call de-init functions: %d", ret);
323 return;
324 }
325 }
326