|
| 1 | +/* |
| 2 | + * Copyright (c) 2025 Intel Corporation |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <zephyr/llext/loader.h> |
| 8 | +#include <zephyr/llext/llext.h> |
| 9 | +#include <zephyr/llext/llext_internal.h> |
| 10 | +#include <zephyr/kernel.h> |
| 11 | +#include <zephyr/sys/slist.h> |
| 12 | + |
| 13 | +#include <zephyr/logging/log.h> |
| 14 | +LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL); |
| 15 | + |
| 16 | +#include <string.h> |
| 17 | + |
| 18 | +#include "llext_priv.h" |
| 19 | + |
| 20 | +/* |
| 21 | + * Prepare a set of extension copies for future restoring. The user has copied |
| 22 | + * multiple extensions and their dependencies into a flat array. We have to |
| 23 | + * relink dependency pointers to copies in this array for later restoring. Note, |
| 24 | + * that all dependencies have to be in this array, if any are missing, restoring |
| 25 | + * will fail, so we return an error in such cases. |
| 26 | + */ |
| 27 | +int llext_relink_dependency(struct llext *ext, unsigned int n_ext) |
| 28 | +{ |
| 29 | + unsigned int i, j, k; |
| 30 | + |
| 31 | + for (i = 0; i < n_ext; i++) { |
| 32 | + for (j = 0; ext[i].dependency[j] && j < ARRAY_SIZE(ext[i].dependency); j++) { |
| 33 | + for (k = 0; k < n_ext; k++) { |
| 34 | + if (!strncmp(ext[k].name, ext[i].dependency[j]->name, |
| 35 | + sizeof(ext[k].name))) { |
| 36 | + ext[i].dependency[j] = ext + k; |
| 37 | + LOG_DBG("backup %s depends on %s", |
| 38 | + ext[i].name, ext[k].name); |
| 39 | + break; |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + if (k == n_ext) { |
| 44 | + return -ENOENT; |
| 45 | + } |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + return 0; |
| 50 | +} |
| 51 | + |
| 52 | +int llext_restore(struct llext **ext, struct llext_loader **ldr, unsigned int n_ext) |
| 53 | +{ |
| 54 | + struct llext_elf_sect_map **map = llext_alloc(sizeof(*map) * n_ext); |
| 55 | + struct llext *next, *tmp, *first = ext[0], *last = ext[n_ext - 1]; |
| 56 | + unsigned int i, j, n_map, n_exp_tab; |
| 57 | + int ret; |
| 58 | + |
| 59 | + if (!map) { |
| 60 | + LOG_ERR("cannot allocate list of maps of %zu", sizeof(*map) * n_ext); |
| 61 | + return -ENOMEM; |
| 62 | + } |
| 63 | + |
| 64 | + /* |
| 65 | + * Each extension has a section map, so if this loop completes |
| 66 | + * successfully in the end n_map == n_ext. But if it's terminated |
| 67 | + * because of an allocation failure, we need to know how many maps have |
| 68 | + * to be freed. |
| 69 | + */ |
| 70 | + for (n_map = 0, n_exp_tab = 0; n_map < n_ext; n_map++) { |
| 71 | + /* Need to allocate individually, because that's how they're freed */ |
| 72 | + map[n_map] = llext_alloc(sizeof(**map) * ext[n_map]->sect_cnt); |
| 73 | + if (!map[n_map]) { |
| 74 | + LOG_ERR("cannot allocate section map of %zu", |
| 75 | + sizeof(**map) * ext[n_map]->sect_cnt); |
| 76 | + ret = -ENOMEM; |
| 77 | + goto free_map; |
| 78 | + } |
| 79 | + |
| 80 | + /* Not every extension exports symbols, count those that do */ |
| 81 | + if (ext[n_map]->exp_tab.sym_cnt) { |
| 82 | + n_exp_tab++; |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + /* Array of pointers to exported symbol tables. Can be NULL if n_exp_tab == 0 */ |
| 87 | + struct llext_symbol **exp_tab = llext_alloc(sizeof(*exp_tab) * n_exp_tab); |
| 88 | + |
| 89 | + if (n_exp_tab) { |
| 90 | + if (!exp_tab) { |
| 91 | + LOG_ERR("cannot allocate list of exported symbol tables of %zu", |
| 92 | + sizeof(*exp_tab) * n_exp_tab); |
| 93 | + ret = -ENOMEM; |
| 94 | + goto free_map; |
| 95 | + } |
| 96 | + |
| 97 | + /* Now actually allocate new exported symbol lists */ |
| 98 | + for (i = 0, j = 0; i < n_ext; i++) { |
| 99 | + if (ext[i]->exp_tab.sym_cnt) { |
| 100 | + size_t size = sizeof(**exp_tab) * ext[i]->exp_tab.sym_cnt; |
| 101 | + |
| 102 | + exp_tab[j] = llext_alloc(size); |
| 103 | + if (!exp_tab[j]) { |
| 104 | + LOG_ERR("cannot allocate exported symbol table of %zu", |
| 105 | + size); |
| 106 | + ret = -ENOMEM; |
| 107 | + goto free_sym; |
| 108 | + } |
| 109 | + memcpy(exp_tab[j++], ext[i]->exp_tab.syms, size); |
| 110 | + } |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + k_mutex_lock(&llext_lock, K_FOREVER); |
| 115 | + |
| 116 | + /* Allocate extensions and add them to the global list */ |
| 117 | + for (i = 0, j = 0; i < n_ext; i++) { |
| 118 | + next = llext_alloc(sizeof(*next)); |
| 119 | + if (!next) { |
| 120 | + LOG_ERR("cannot allocate LLEXT of %zu", sizeof(*next)); |
| 121 | + ret = -ENOMEM; |
| 122 | + goto free_llext; |
| 123 | + } |
| 124 | + |
| 125 | + /* Copy the extension and return a pointer to it to the user */ |
| 126 | + *next = *ext[i]; |
| 127 | + ext[i] = next; |
| 128 | + if (next->exp_tab.sym_cnt) { |
| 129 | + next->exp_tab.syms = exp_tab[j++]; |
| 130 | + } |
| 131 | + |
| 132 | + sys_slist_append(&llext_list, &next->llext_list); |
| 133 | + } |
| 134 | + |
| 135 | + k_mutex_unlock(&llext_lock); |
| 136 | + |
| 137 | + /* Copy section maps */ |
| 138 | + for (i = 0; i < n_ext; i++) { |
| 139 | + size_t map_size = sizeof(struct llext_elf_sect_map) * ext[i]->sect_cnt; |
| 140 | + |
| 141 | + memcpy(map[i], ldr[i]->sect_map, map_size); |
| 142 | + ldr[i]->sect_map = map[i]; |
| 143 | + } |
| 144 | + |
| 145 | + llext_free(map); |
| 146 | + llext_free(exp_tab); |
| 147 | + |
| 148 | + /* Restore dependencies previously saved by llext_relink_dependency() */ |
| 149 | + SYS_SLIST_FOR_EACH_CONTAINER(&llext_list, next, llext_list) { |
| 150 | + for (j = 0; next->dependency[j] && j < ARRAY_SIZE(next->dependency); j++) { |
| 151 | + if (next->dependency[j] < first || next->dependency[j] >= last) { |
| 152 | + /* Inconsistency detected */ |
| 153 | + LOG_ERR("dependency out of range"); |
| 154 | + ret = -EINVAL; |
| 155 | + goto free_locked; |
| 156 | + } |
| 157 | + |
| 158 | + next->dependency[j] = llext_by_name(next->dependency[j]->name); |
| 159 | + if (!next->dependency[j]) { |
| 160 | + /* Bug in the algorithm */ |
| 161 | + LOG_ERR("dependency not found"); |
| 162 | + ret = -EFAULT; |
| 163 | + goto free_locked; |
| 164 | + } |
| 165 | + |
| 166 | + LOG_DBG("restore %s depends on %s", |
| 167 | + next->name, next->dependency[j]->name); |
| 168 | + } |
| 169 | + } |
| 170 | + |
| 171 | + return 0; |
| 172 | + |
| 173 | +free_locked: |
| 174 | + k_mutex_lock(&llext_lock, K_FOREVER); |
| 175 | + |
| 176 | +free_llext: |
| 177 | + /* Free only those extensions, that we've saved into ext[] */ |
| 178 | + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&llext_list, next, tmp, llext_list) { |
| 179 | + for (i = 0; i < n_ext; i++) { |
| 180 | + if (ext[i] == next) { |
| 181 | + sys_slist_remove(&llext_list, NULL, &next->llext_list); |
| 182 | + llext_free(next); |
| 183 | + ext[i] = NULL; |
| 184 | + break; |
| 185 | + } |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + k_mutex_unlock(&llext_lock); |
| 190 | + |
| 191 | +free_sym: |
| 192 | + for (i = 0; i < n_exp_tab && exp_tab[i]; i++) { |
| 193 | + llext_free(exp_tab[i]); |
| 194 | + } |
| 195 | + |
| 196 | + llext_free(exp_tab); |
| 197 | + |
| 198 | +free_map: |
| 199 | + for (i = 0; i < n_map; i++) { |
| 200 | + llext_free(map[i]); |
| 201 | + } |
| 202 | + |
| 203 | + llext_free(map); |
| 204 | + |
| 205 | + return ret; |
| 206 | +} |
0 commit comments