]>
Commit | Line | Data |
---|---|---|
6a161fa9 | 1 | /* dl.c - loadable module support */ |
2 | /* | |
4b13b216 | 3 | * GRUB -- GRand Unified Bootloader |
8231fb77 | 4 | * Copyright (C) 2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. |
6a161fa9 | 5 | * |
5a79f472 | 6 | * GRUB is free software: you can redistribute it and/or modify |
6a161fa9 | 7 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 8 | * the Free Software Foundation, either version 3 of the License, or |
6a161fa9 | 9 | * (at your option) any later version. |
10 | * | |
5a79f472 | 11 | * GRUB is distributed in the hope that it will be useful, |
6a161fa9 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
5a79f472 | 17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
6a161fa9 | 18 | */ |
19 | ||
17dc3751 | 20 | /* Force native word size */ |
21 | #define GRUB_TARGET_WORDSIZE (8 * GRUB_CPU_SIZEOF_VOID_P) | |
22 | ||
6a161fa9 | 23 | #include <config.h> |
4b13b216 | 24 | #include <grub/elf.h> |
25 | #include <grub/dl.h> | |
26 | #include <grub/misc.h> | |
27 | #include <grub/mm.h> | |
28 | #include <grub/err.h> | |
29 | #include <grub/types.h> | |
30 | #include <grub/symbol.h> | |
31 | #include <grub/file.h> | |
32 | #include <grub/env.h> | |
924b6140 | 33 | #include <grub/cache.h> |
9c4b5c13 | 34 | #include <grub/i18n.h> |
8231fb77 | 35 | |
36 | /* Platforms where modules are in a readonly area of memory. */ | |
37 | #if defined(GRUB_MACHINE_QEMU) | |
38 | #define GRUB_MODULES_MACHINE_READONLY | |
39 | #endif | |
4b13b216 | 40 | |
6a161fa9 | 41 | \f |
42 | ||
ebcecdf1 VS |
43 | #pragma GCC diagnostic ignored "-Wcast-align" |
44 | ||
fcaae9ec | 45 | grub_dl_t grub_dl_head = 0; |
6a161fa9 | 46 | |
ba287dd8 VS |
47 | grub_err_t |
48 | grub_dl_add (grub_dl_t mod); | |
49 | ||
7cd0df84 | 50 | /* Keep global so that GDB scripts work. */ |
ba287dd8 | 51 | grub_err_t |
4b13b216 | 52 | grub_dl_add (grub_dl_t mod) |
6a161fa9 | 53 | { |
4b13b216 | 54 | if (grub_dl_get (mod->name)) |
55 | return grub_error (GRUB_ERR_BAD_MODULE, | |
6a161fa9 | 56 | "`%s' is already loaded", mod->name); |
b39f9d20 | 57 | |
4b13b216 | 58 | return GRUB_ERR_NONE; |
6a161fa9 | 59 | } |
60 | ||
61 | static void | |
4b13b216 | 62 | grub_dl_remove (grub_dl_t mod) |
6a161fa9 | 63 | { |
fcaae9ec | 64 | grub_dl_t *p, q; |
6a161fa9 | 65 | |
4b13b216 | 66 | for (p = &grub_dl_head, q = *p; q; p = &q->next, q = *p) |
fcaae9ec | 67 | if (q == mod) |
6a161fa9 | 68 | { |
69 | *p = q->next; | |
6a161fa9 | 70 | return; |
71 | } | |
72 | } | |
73 | ||
6a161fa9 | 74 | \f |
75 | ||
4b13b216 | 76 | struct grub_symbol |
6a161fa9 | 77 | { |
4b13b216 | 78 | struct grub_symbol *next; |
6a161fa9 | 79 | const char *name; |
80 | void *addr; | |
7b58e65f | 81 | int isfunc; |
4b13b216 | 82 | grub_dl_t mod; /* The module to which this symbol belongs. */ |
6a161fa9 | 83 | }; |
4b13b216 | 84 | typedef struct grub_symbol *grub_symbol_t; |
6a161fa9 | 85 | |
86 | /* The size of the symbol table. */ | |
4b13b216 | 87 | #define GRUB_SYMTAB_SIZE 509 |
6a161fa9 | 88 | |
89 | /* The symbol table (using an open-hash). */ | |
4b13b216 | 90 | static struct grub_symbol *grub_symtab[GRUB_SYMTAB_SIZE]; |
6a161fa9 | 91 | |
92 | /* Simple hash function. */ | |
93 | static unsigned | |
4b13b216 | 94 | grub_symbol_hash (const char *s) |
6a161fa9 | 95 | { |
96 | unsigned key = 0; | |
97 | ||
98 | while (*s) | |
99 | key = key * 65599 + *s++; | |
100 | ||
4b13b216 | 101 | return (key + (key >> 5)) % GRUB_SYMTAB_SIZE; |
6a161fa9 | 102 | } |
103 | ||
104 | /* Resolve the symbol name NAME and return the address. | |
105 | Return NULL, if not found. */ | |
7b58e65f | 106 | static grub_symbol_t |
4b13b216 | 107 | grub_dl_resolve_symbol (const char *name) |
6a161fa9 | 108 | { |
4b13b216 | 109 | grub_symbol_t sym; |
6a161fa9 | 110 | |
4b13b216 | 111 | for (sym = grub_symtab[grub_symbol_hash (name)]; sym; sym = sym->next) |
112 | if (grub_strcmp (sym->name, name) == 0) | |
7b58e65f | 113 | return sym; |
6a161fa9 | 114 | |
115 | return 0; | |
116 | } | |
117 | ||
118 | /* Register a symbol with the name NAME and the address ADDR. */ | |
4b13b216 | 119 | grub_err_t |
7b58e65f VS |
120 | grub_dl_register_symbol (const char *name, void *addr, int isfunc, |
121 | grub_dl_t mod) | |
6a161fa9 | 122 | { |
4b13b216 | 123 | grub_symbol_t sym; |
6a161fa9 | 124 | unsigned k; |
b39f9d20 | 125 | |
4b13b216 | 126 | sym = (grub_symbol_t) grub_malloc (sizeof (*sym)); |
6a161fa9 | 127 | if (! sym) |
4b13b216 | 128 | return grub_errno; |
6a161fa9 | 129 | |
130 | if (mod) | |
131 | { | |
4b13b216 | 132 | sym->name = grub_strdup (name); |
6a161fa9 | 133 | if (! sym->name) |
134 | { | |
4b13b216 | 135 | grub_free (sym); |
136 | return grub_errno; | |
6a161fa9 | 137 | } |
138 | } | |
139 | else | |
140 | sym->name = name; | |
b39f9d20 | 141 | |
6a161fa9 | 142 | sym->addr = addr; |
143 | sym->mod = mod; | |
7b58e65f | 144 | sym->isfunc = isfunc; |
b39f9d20 | 145 | |
4b13b216 | 146 | k = grub_symbol_hash (name); |
147 | sym->next = grub_symtab[k]; | |
148 | grub_symtab[k] = sym; | |
6a161fa9 | 149 | |
4b13b216 | 150 | return GRUB_ERR_NONE; |
6a161fa9 | 151 | } |
152 | ||
153 | /* Unregister all the symbols defined in the module MOD. */ | |
154 | static void | |
4b13b216 | 155 | grub_dl_unregister_symbols (grub_dl_t mod) |
6a161fa9 | 156 | { |
157 | unsigned i; | |
158 | ||
159 | if (! mod) | |
4b13b216 | 160 | grub_fatal ("core symbols cannot be unregistered"); |
b39f9d20 | 161 | |
4b13b216 | 162 | for (i = 0; i < GRUB_SYMTAB_SIZE; i++) |
6a161fa9 | 163 | { |
4b13b216 | 164 | grub_symbol_t sym, *p, q; |
6a161fa9 | 165 | |
4b13b216 | 166 | for (p = &grub_symtab[i], sym = *p; sym; sym = q) |
6a161fa9 | 167 | { |
168 | q = sym->next; | |
169 | if (sym->mod == mod) | |
170 | { | |
171 | *p = q; | |
4b13b216 | 172 | grub_free ((void *) sym->name); |
173 | grub_free (sym); | |
6a161fa9 | 174 | } |
175 | else | |
176 | p = &sym->next; | |
177 | } | |
178 | } | |
179 | } | |
180 | ||
181 | /* Return the address of a section whose index is N. */ | |
182 | static void * | |
4b13b216 | 183 | grub_dl_get_section_addr (grub_dl_t mod, unsigned n) |
6a161fa9 | 184 | { |
4b13b216 | 185 | grub_dl_segment_t seg; |
6a161fa9 | 186 | |
187 | for (seg = mod->segment; seg; seg = seg->next) | |
188 | if (seg->section == n) | |
189 | return seg->addr; | |
190 | ||
191 | return 0; | |
192 | } | |
193 | ||
c642636f | 194 | /* Check if EHDR is a valid ELF header. */ |
f76ce889 | 195 | static grub_err_t |
c642636f | 196 | grub_dl_check_header (void *ehdr, grub_size_t size) |
197 | { | |
198 | Elf_Ehdr *e = ehdr; | |
9c4b5c13 | 199 | grub_err_t err; |
c642636f | 200 | |
201 | /* Check the header size. */ | |
202 | if (size < sizeof (Elf_Ehdr)) | |
203 | return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected"); | |
204 | ||
205 | /* Check the magic numbers. */ | |
9c4b5c13 | 206 | if (e->e_ident[EI_MAG0] != ELFMAG0 |
c642636f | 207 | || e->e_ident[EI_MAG1] != ELFMAG1 |
208 | || e->e_ident[EI_MAG2] != ELFMAG2 | |
209 | || e->e_ident[EI_MAG3] != ELFMAG3 | |
210 | || e->e_ident[EI_VERSION] != EV_CURRENT | |
211 | || e->e_version != EV_CURRENT) | |
67093bc0 | 212 | return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-independent ELF magic")); |
9c4b5c13 VS |
213 | |
214 | err = grub_arch_dl_check_header (ehdr); | |
215 | if (err) | |
216 | return err; | |
c642636f | 217 | |
218 | return GRUB_ERR_NONE; | |
219 | } | |
220 | ||
6a161fa9 | 221 | /* Load all segments from memory specified by E. */ |
4b13b216 | 222 | static grub_err_t |
223 | grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) | |
6a161fa9 | 224 | { |
225 | unsigned i; | |
05126706 | 226 | const Elf_Shdr *s; |
f49157df | 227 | grub_size_t tsize = 0, talign = 1; |
7a148da6 | 228 | #if !defined (__i386__) && !defined (__x86_64__) |
f49157df | 229 | grub_size_t tramp; |
230 | grub_size_t got; | |
49060520 | 231 | grub_err_t err; |
f49157df | 232 | #endif |
233 | char *ptr; | |
234 | ||
05126706 | 235 | for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); |
f49157df | 236 | i < e->e_shnum; |
05126706 | 237 | i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) |
f49157df | 238 | { |
10b64f1c | 239 | tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size; |
f49157df | 240 | if (talign < s->sh_addralign) |
241 | talign = s->sh_addralign; | |
242 | } | |
243 | ||
7a148da6 | 244 | #if !defined (__i386__) && !defined (__x86_64__) |
49060520 VS |
245 | err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); |
246 | if (err) | |
247 | return err; | |
f49157df | 248 | tsize += ALIGN_UP (tramp, GRUB_ARCH_DL_TRAMP_ALIGN); |
249 | if (talign < GRUB_ARCH_DL_TRAMP_ALIGN) | |
250 | talign = GRUB_ARCH_DL_TRAMP_ALIGN; | |
251 | tsize += ALIGN_UP (got, GRUB_ARCH_DL_GOT_ALIGN); | |
252 | if (talign < GRUB_ARCH_DL_GOT_ALIGN) | |
253 | talign = GRUB_ARCH_DL_GOT_ALIGN; | |
254 | #endif | |
255 | ||
256 | #ifdef GRUB_MACHINE_EMU | |
a19293cb VS |
257 | mod->base = grub_osdep_dl_memalign (talign, tsize); |
258 | #else | |
f49157df | 259 | mod->base = grub_memalign (talign, tsize); |
a19293cb | 260 | #endif |
f49157df | 261 | if (!mod->base) |
262 | return grub_errno; | |
ed64e9e2 | 263 | mod->sz = tsize; |
f49157df | 264 | ptr = mod->base; |
265 | ||
6a161fa9 | 266 | for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff); |
267 | i < e->e_shnum; | |
268 | i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize)) | |
269 | { | |
270 | if (s->sh_flags & SHF_ALLOC) | |
271 | { | |
4b13b216 | 272 | grub_dl_segment_t seg; |
6a161fa9 | 273 | |
4b13b216 | 274 | seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg)); |
6a161fa9 | 275 | if (! seg) |
4b13b216 | 276 | return grub_errno; |
b39f9d20 | 277 | |
6a161fa9 | 278 | if (s->sh_size) |
279 | { | |
280 | void *addr; | |
281 | ||
f49157df | 282 | ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign); |
283 | addr = ptr; | |
284 | ptr += s->sh_size; | |
6a161fa9 | 285 | |
286 | switch (s->sh_type) | |
287 | { | |
288 | case SHT_PROGBITS: | |
4b13b216 | 289 | grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size); |
6a161fa9 | 290 | break; |
291 | case SHT_NOBITS: | |
4b13b216 | 292 | grub_memset (addr, 0, s->sh_size); |
6a161fa9 | 293 | break; |
294 | } | |
295 | ||
296 | seg->addr = addr; | |
297 | } | |
298 | else | |
299 | seg->addr = 0; | |
300 | ||
301 | seg->size = s->sh_size; | |
302 | seg->section = i; | |
303 | seg->next = mod->segment; | |
304 | mod->segment = seg; | |
305 | } | |
306 | } | |
7a148da6 | 307 | #if !defined (__i386__) && !defined (__x86_64__) |
f49157df | 308 | ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN); |
309 | mod->tramp = ptr; | |
8c534b85 | 310 | mod->trampptr = ptr; |
f49157df | 311 | ptr += tramp; |
312 | ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN); | |
313 | mod->got = ptr; | |
8c534b85 | 314 | mod->gotptr = ptr; |
f49157df | 315 | ptr += got; |
316 | #endif | |
6a161fa9 | 317 | |
4b13b216 | 318 | return GRUB_ERR_NONE; |
6a161fa9 | 319 | } |
320 | ||
4b13b216 | 321 | static grub_err_t |
322 | grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) | |
6a161fa9 | 323 | { |
324 | unsigned i; | |
325 | Elf_Shdr *s; | |
326 | Elf_Sym *sym; | |
327 | const char *str; | |
328 | Elf_Word size, entsize; | |
b39f9d20 | 329 | |
6a161fa9 | 330 | for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); |
331 | i < e->e_shnum; | |
332 | i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) | |
333 | if (s->sh_type == SHT_SYMTAB) | |
334 | break; | |
335 | ||
67dba97e AB |
336 | /* Module without symbol table may still be used to pull in dependencies. |
337 | We verify at build time that such modules do not contain any relocations | |
338 | that may reference symbol table. */ | |
6a161fa9 | 339 | if (i == e->e_shnum) |
67dba97e | 340 | return GRUB_ERR_NONE; |
6a161fa9 | 341 | |
8231fb77 | 342 | #ifdef GRUB_MODULES_MACHINE_READONLY |
343 | mod->symtab = grub_malloc (s->sh_size); | |
cdeb8324 VS |
344 | if (!mod->symtab) |
345 | return grub_errno; | |
7ea452a1 | 346 | grub_memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size); |
8231fb77 | 347 | #else |
348 | mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset); | |
349 | #endif | |
8c534b85 | 350 | mod->symsize = s->sh_entsize; |
8231fb77 | 351 | sym = mod->symtab; |
6a161fa9 | 352 | size = s->sh_size; |
353 | entsize = s->sh_entsize; | |
b39f9d20 | 354 | |
6a161fa9 | 355 | s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link); |
356 | str = (char *) e + s->sh_offset; | |
357 | ||
358 | for (i = 0; | |
359 | i < size / entsize; | |
360 | i++, sym = (Elf_Sym *) ((char *) sym + entsize)) | |
361 | { | |
362 | unsigned char type = ELF_ST_TYPE (sym->st_info); | |
363 | unsigned char bind = ELF_ST_BIND (sym->st_info); | |
364 | const char *name = str + sym->st_name; | |
b39f9d20 | 365 | |
6a161fa9 | 366 | switch (type) |
367 | { | |
368 | case STT_NOTYPE: | |
50739170 | 369 | case STT_OBJECT: |
6a161fa9 | 370 | /* Resolve a global symbol. */ |
371 | if (sym->st_name != 0 && sym->st_shndx == 0) | |
372 | { | |
7b58e65f VS |
373 | grub_symbol_t nsym = grub_dl_resolve_symbol (name); |
374 | if (! nsym) | |
4b13b216 | 375 | return grub_error (GRUB_ERR_BAD_MODULE, |
9c4b5c13 | 376 | N_("symbol `%s' not found"), name); |
7b58e65f VS |
377 | sym->st_value = (Elf_Addr) nsym->addr; |
378 | if (nsym->isfunc) | |
379 | sym->st_info = ELF_ST_INFO (bind, STT_FUNC); | |
6a161fa9 | 380 | } |
381 | else | |
50739170 | 382 | { |
383 | sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod, | |
384 | sym->st_shndx); | |
385 | if (bind != STB_LOCAL) | |
7b58e65f | 386 | if (grub_dl_register_symbol (name, (void *) sym->st_value, 0, mod)) |
50739170 | 387 | return grub_errno; |
388 | } | |
6a161fa9 | 389 | break; |
390 | ||
391 | case STT_FUNC: | |
4b13b216 | 392 | sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod, |
6a161fa9 | 393 | sym->st_shndx); |
73911575 | 394 | #ifdef __ia64__ |
bbbf8435 | 395 | { |
73911575 | 396 | /* FIXME: free descriptor once it's not used anymore. */ |
397 | char **desc; | |
398 | desc = grub_malloc (2 * sizeof (char *)); | |
399 | if (!desc) | |
400 | return grub_errno; | |
401 | desc[0] = (void *) sym->st_value; | |
f49157df | 402 | desc[1] = mod->base; |
bbbf8435 | 403 | sym->st_value = (grub_addr_t) desc; |
404 | } | |
405 | #endif | |
6a161fa9 | 406 | if (bind != STB_LOCAL) |
7b58e65f | 407 | if (grub_dl_register_symbol (name, (void *) sym->st_value, 1, mod)) |
4b13b216 | 408 | return grub_errno; |
4b13b216 | 409 | if (grub_strcmp (name, "grub_mod_init") == 0) |
410 | mod->init = (void (*) (grub_dl_t)) sym->st_value; | |
411 | else if (grub_strcmp (name, "grub_mod_fini") == 0) | |
a5ffe966 | 412 | mod->fini = (void (*) (void)) sym->st_value; |
6a161fa9 | 413 | break; |
414 | ||
415 | case STT_SECTION: | |
4b13b216 | 416 | sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod, |
6a161fa9 | 417 | sym->st_shndx); |
418 | break; | |
419 | ||
420 | case STT_FILE: | |
421 | sym->st_value = 0; | |
422 | break; | |
423 | ||
424 | default: | |
4b13b216 | 425 | return grub_error (GRUB_ERR_BAD_MODULE, |
6a161fa9 | 426 | "unknown symbol type `%d'", (int) type); |
427 | } | |
428 | } | |
429 | ||
4b13b216 | 430 | return GRUB_ERR_NONE; |
6a161fa9 | 431 | } |
432 | ||
80e8b13a VS |
433 | static Elf_Shdr * |
434 | grub_dl_find_section (Elf_Ehdr *e, const char *name) | |
e745cf0c VS |
435 | { |
436 | Elf_Shdr *s; | |
437 | const char *str; | |
438 | unsigned i; | |
439 | ||
440 | s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); | |
441 | str = (char *) e + s->sh_offset; | |
442 | ||
443 | for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); | |
444 | i < e->e_shnum; | |
445 | i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) | |
80e8b13a VS |
446 | if (grub_strcmp (str + s->sh_name, name) == 0) |
447 | return s; | |
448 | return NULL; | |
449 | } | |
e745cf0c | 450 | |
80e8b13a VS |
451 | /* Me, Vladimir Serbinenko, hereby I add this module check as per new |
452 | GNU module policy. Note that this license check is informative only. | |
453 | Modules have to be licensed under GPLv3 or GPLv3+ (optionally | |
454 | multi-licensed under other licences as well) independently of the | |
455 | presence of this check and solely by linking (module loading in GRUB | |
456 | constitutes linking) and GRUB core being licensed under GPLv3+. | |
457 | Be sure to understand your license obligations. | |
458 | */ | |
459 | static grub_err_t | |
460 | grub_dl_check_license (Elf_Ehdr *e) | |
461 | { | |
462 | Elf_Shdr *s = grub_dl_find_section (e, ".module_license"); | |
463 | if (s && (grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3") == 0 | |
464 | || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3+") == 0 | |
465 | || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0)) | |
466 | return GRUB_ERR_NONE; | |
e745cf0c VS |
467 | return grub_error (GRUB_ERR_BAD_MODULE, "incompatible license"); |
468 | } | |
469 | ||
4b13b216 | 470 | static grub_err_t |
471 | grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e) | |
6a161fa9 | 472 | { |
473 | Elf_Shdr *s; | |
6a161fa9 | 474 | |
80e8b13a VS |
475 | s = grub_dl_find_section (e, ".modname"); |
476 | if (!s) | |
4b13b216 | 477 | return grub_error (GRUB_ERR_BAD_MODULE, "no module name found"); |
80e8b13a VS |
478 | |
479 | mod->name = grub_strdup ((char *) e + s->sh_offset); | |
480 | if (! mod->name) | |
481 | return grub_errno; | |
6a161fa9 | 482 | |
4b13b216 | 483 | return GRUB_ERR_NONE; |
6a161fa9 | 484 | } |
485 | ||
4b13b216 | 486 | static grub_err_t |
487 | grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e) | |
6a161fa9 | 488 | { |
489 | Elf_Shdr *s; | |
6a161fa9 | 490 | |
80e8b13a | 491 | s = grub_dl_find_section (e, ".moddeps"); |
b39f9d20 | 492 | |
80e8b13a VS |
493 | if (!s) |
494 | return GRUB_ERR_NONE; | |
6a161fa9 | 495 | |
80e8b13a VS |
496 | const char *name = (char *) e + s->sh_offset; |
497 | const char *max = name + s->sh_size; | |
b39f9d20 | 498 | |
80e8b13a VS |
499 | while ((name < max) && (*name)) |
500 | { | |
501 | grub_dl_t m; | |
502 | grub_dl_dep_t dep; | |
a5ffe966 | 503 | |
80e8b13a VS |
504 | m = grub_dl_load (name); |
505 | if (! m) | |
506 | return grub_errno; | |
b39f9d20 | 507 | |
80e8b13a | 508 | grub_dl_ref (m); |
b39f9d20 | 509 | |
80e8b13a VS |
510 | dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep)); |
511 | if (! dep) | |
512 | return grub_errno; | |
b39f9d20 | 513 | |
80e8b13a VS |
514 | dep->mod = m; |
515 | dep->next = mod->dep; | |
516 | mod->dep = dep; | |
517 | ||
518 | name += grub_strlen (name) + 1; | |
519 | } | |
6a161fa9 | 520 | |
4b13b216 | 521 | return GRUB_ERR_NONE; |
6a161fa9 | 522 | } |
523 | ||
a5ffe966 | 524 | int |
4b13b216 | 525 | grub_dl_ref (grub_dl_t mod) |
a5ffe966 | 526 | { |
4b13b216 | 527 | grub_dl_dep_t dep; |
ce5bf700 | 528 | |
4c7085f8 BC |
529 | if (!mod) |
530 | return 0; | |
531 | ||
ce5bf700 | 532 | for (dep = mod->dep; dep; dep = dep->next) |
4b13b216 | 533 | grub_dl_ref (dep->mod); |
b39f9d20 | 534 | |
a5ffe966 | 535 | return ++mod->ref_count; |
536 | } | |
537 | ||
538 | int | |
4b13b216 | 539 | grub_dl_unref (grub_dl_t mod) |
a5ffe966 | 540 | { |
4b13b216 | 541 | grub_dl_dep_t dep; |
a5ffe966 | 542 | |
4c7085f8 BC |
543 | if (!mod) |
544 | return 0; | |
545 | ||
ce5bf700 | 546 | for (dep = mod->dep; dep; dep = dep->next) |
4b13b216 | 547 | grub_dl_unref (dep->mod); |
b39f9d20 | 548 | |
ce5bf700 | 549 | return --mod->ref_count; |
a5ffe966 | 550 | } |
551 | ||
924b6140 | 552 | static void |
553 | grub_dl_flush_cache (grub_dl_t mod) | |
554 | { | |
ed64e9e2 VS |
555 | grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n", |
556 | (unsigned long) mod->sz, mod->base); | |
557 | grub_arch_sync_caches (mod->base, mod->sz); | |
924b6140 | 558 | } |
559 | ||
8c534b85 VS |
560 | static grub_err_t |
561 | grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) | |
562 | { | |
563 | Elf_Ehdr *e = ehdr; | |
564 | Elf_Shdr *s; | |
565 | unsigned i; | |
566 | ||
567 | for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); | |
568 | i < e->e_shnum; | |
569 | i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) | |
570 | if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA) | |
571 | { | |
572 | grub_dl_segment_t seg; | |
573 | grub_err_t err; | |
574 | ||
575 | /* Find the target segment. */ | |
576 | for (seg = mod->segment; seg; seg = seg->next) | |
577 | if (seg->section == s->sh_info) | |
578 | break; | |
579 | ||
580 | if (seg) | |
581 | { | |
67dba97e AB |
582 | if (!mod->symtab) |
583 | return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table"); | |
584 | ||
8c534b85 VS |
585 | err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg); |
586 | if (err) | |
587 | return err; | |
588 | } | |
589 | } | |
590 | ||
591 | return GRUB_ERR_NONE; | |
592 | } | |
593 | ||
6a161fa9 | 594 | /* Load a module from core memory. */ |
4b13b216 | 595 | grub_dl_t |
7cd0df84 | 596 | grub_dl_load_core_noinit (void *addr, grub_size_t size) |
6a161fa9 | 597 | { |
598 | Elf_Ehdr *e; | |
4b13b216 | 599 | grub_dl_t mod; |
be973c1b | 600 | |
e4e8eaa5 | 601 | grub_dprintf ("modules", "module at %p, size 0x%lx\n", addr, |
602 | (unsigned long) size); | |
6a161fa9 | 603 | e = addr; |
c642636f | 604 | if (grub_dl_check_header (e, size)) |
605 | return 0; | |
be973c1b | 606 | |
c642636f | 607 | if (e->e_type != ET_REL) |
6a161fa9 | 608 | { |
9c4b5c13 | 609 | grub_error (GRUB_ERR_BAD_MODULE, N_("this ELF file is not of the right type")); |
6a161fa9 | 610 | return 0; |
611 | } | |
c642636f | 612 | |
613 | /* Make sure that every section is within the core. */ | |
4f8fe948 | 614 | if (size < e->e_shoff + (grub_uint32_t) e->e_shentsize * e->e_shnum) |
c642636f | 615 | { |
616 | grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core"); | |
617 | return 0; | |
618 | } | |
619 | ||
eab58da2 | 620 | mod = (grub_dl_t) grub_zalloc (sizeof (*mod)); |
6a161fa9 | 621 | if (! mod) |
622 | return 0; | |
623 | ||
6a161fa9 | 624 | mod->ref_count = 1; |
6a161fa9 | 625 | |
be973c1b | 626 | grub_dprintf ("modules", "relocating to %p\n", mod); |
e745cf0c VS |
627 | /* Me, Vladimir Serbinenko, hereby I add this module check as per new |
628 | GNU module policy. Note that this license check is informative only. | |
629 | Modules have to be licensed under GPLv3 or GPLv3+ (optionally | |
630 | multi-licensed under other licences as well) independently of the | |
631 | presence of this check and solely by linking (module loading in GRUB | |
632 | constitutes linking) and GRUB core being licensed under GPLv3+. | |
633 | Be sure to understand your license obligations. | |
634 | */ | |
635 | if (grub_dl_check_license (e) | |
636 | || grub_dl_resolve_name (mod, e) | |
4b13b216 | 637 | || grub_dl_resolve_dependencies (mod, e) |
638 | || grub_dl_load_segments (mod, e) | |
639 | || grub_dl_resolve_symbols (mod, e) | |
8c534b85 | 640 | || grub_dl_relocate_symbols (mod, e)) |
6a161fa9 | 641 | { |
642 | mod->fini = 0; | |
4b13b216 | 643 | grub_dl_unload (mod); |
6a161fa9 | 644 | return 0; |
645 | } | |
646 | ||
924b6140 | 647 | grub_dl_flush_cache (mod); |
be973c1b | 648 | |
649 | grub_dprintf ("modules", "module name: %s\n", mod->name); | |
650 | grub_dprintf ("modules", "init function: %p\n", mod->init); | |
e744219b | 651 | |
4b13b216 | 652 | if (grub_dl_add (mod)) |
6a161fa9 | 653 | { |
4b13b216 | 654 | grub_dl_unload (mod); |
6a161fa9 | 655 | return 0; |
656 | } | |
be973c1b | 657 | |
6a161fa9 | 658 | return mod; |
659 | } | |
660 | ||
7cd0df84 VS |
661 | grub_dl_t |
662 | grub_dl_load_core (void *addr, grub_size_t size) | |
663 | { | |
664 | grub_dl_t mod; | |
665 | ||
313fb3ce VS |
666 | grub_boot_time ("Parsing module"); |
667 | ||
7cd0df84 VS |
668 | mod = grub_dl_load_core_noinit (addr, size); |
669 | ||
670 | if (!mod) | |
671 | return NULL; | |
672 | ||
673 | grub_boot_time ("Initing module %s", mod->name); | |
674 | grub_dl_init (mod); | |
675 | grub_boot_time ("Module %s inited", mod->name); | |
676 | ||
677 | return mod; | |
678 | } | |
679 | ||
6a161fa9 | 680 | /* Load a module from the file FILENAME. */ |
4b13b216 | 681 | grub_dl_t |
682 | grub_dl_load_file (const char *filename) | |
6a161fa9 | 683 | { |
1d7a72fd | 684 | grub_file_t file = NULL; |
4b13b216 | 685 | grub_ssize_t size; |
6a161fa9 | 686 | void *core = 0; |
4b13b216 | 687 | grub_dl_t mod = 0; |
b39f9d20 | 688 | |
313fb3ce VS |
689 | grub_boot_time ("Loading module %s", filename); |
690 | ||
ca0a4f68 | 691 | file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE); |
6a161fa9 | 692 | if (! file) |
693 | return 0; | |
694 | ||
4b13b216 | 695 | size = grub_file_size (file); |
696 | core = grub_malloc (size); | |
6a161fa9 | 697 | if (! core) |
1d7a72fd | 698 | { |
699 | grub_file_close (file); | |
700 | return 0; | |
701 | } | |
6a161fa9 | 702 | |
4b13b216 | 703 | if (grub_file_read (file, core, size) != (int) size) |
1d7a72fd | 704 | { |
705 | grub_file_close (file); | |
706 | grub_free (core); | |
707 | return 0; | |
708 | } | |
709 | ||
710 | /* We must close this before we try to process dependencies. | |
711 | Some disk backends do not handle gracefully multiple concurrent | |
712 | opens of the same device. */ | |
713 | grub_file_close (file); | |
6a161fa9 | 714 | |
4b13b216 | 715 | mod = grub_dl_load_core (core, size); |
c2aa00f0 | 716 | grub_free (core); |
b38551da | 717 | if (! mod) |
c2aa00f0 | 718 | return 0; |
b39f9d20 | 719 | |
57688121 | 720 | mod->ref_count--; |
6a161fa9 | 721 | return mod; |
722 | } | |
723 | ||
6a161fa9 | 724 | /* Load a module using a symbolic name. */ |
4b13b216 | 725 | grub_dl_t |
726 | grub_dl_load (const char *name) | |
6a161fa9 | 727 | { |
728 | char *filename; | |
4b13b216 | 729 | grub_dl_t mod; |
d35d0d37 | 730 | const char *grub_dl_dir = grub_env_get ("prefix"); |
6a161fa9 | 731 | |
4b13b216 | 732 | mod = grub_dl_get (name); |
6a161fa9 | 733 | if (mod) |
a5ffe966 | 734 | return mod; |
b39f9d20 | 735 | |
a6393224 VS |
736 | if (grub_no_modules) |
737 | return 0; | |
738 | ||
2033f53e | 739 | if (! grub_dl_dir) { |
9c4b5c13 | 740 | grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); |
2033f53e | 741 | return 0; |
742 | } | |
6a161fa9 | 743 | |
92cd0f6e VS |
744 | filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM "/%s.mod", |
745 | grub_dl_dir, name); | |
6a161fa9 | 746 | if (! filename) |
747 | return 0; | |
b39f9d20 | 748 | |
4b13b216 | 749 | mod = grub_dl_load_file (filename); |
750 | grub_free (filename); | |
6a161fa9 | 751 | |
752 | if (! mod) | |
753 | return 0; | |
b39f9d20 | 754 | |
4b13b216 | 755 | if (grub_strcmp (mod->name, name) != 0) |
756 | grub_error (GRUB_ERR_BAD_MODULE, "mismatched names"); | |
b39f9d20 | 757 | |
6a161fa9 | 758 | return mod; |
759 | } | |
760 | ||
761 | /* Unload the module MOD. */ | |
a5ffe966 | 762 | int |
4b13b216 | 763 | grub_dl_unload (grub_dl_t mod) |
6a161fa9 | 764 | { |
4b13b216 | 765 | grub_dl_dep_t dep, depn; |
6a161fa9 | 766 | |
a5ffe966 | 767 | if (mod->ref_count > 0) |
768 | return 0; | |
6a161fa9 | 769 | |
770 | if (mod->fini) | |
771 | (mod->fini) (); | |
b39f9d20 | 772 | |
4b13b216 | 773 | grub_dl_remove (mod); |
774 | grub_dl_unregister_symbols (mod); | |
b39f9d20 | 775 | |
6a161fa9 | 776 | for (dep = mod->dep; dep; dep = depn) |
777 | { | |
778 | depn = dep->next; | |
b39f9d20 | 779 | |
3d2c7e35 | 780 | grub_dl_unload (dep->mod); |
b39f9d20 | 781 | |
4b13b216 | 782 | grub_free (dep); |
6a161fa9 | 783 | } |
784 | ||
a19293cb VS |
785 | #ifdef GRUB_MACHINE_EMU |
786 | grub_dl_osdep_dl_free (mod->base); | |
787 | #else | |
bb416954 | 788 | grub_free (mod->base); |
a19293cb | 789 | #endif |
4b13b216 | 790 | grub_free (mod->name); |
8231fb77 | 791 | #ifdef GRUB_MODULES_MACHINE_READONLY |
792 | grub_free (mod->symtab); | |
793 | #endif | |
4b13b216 | 794 | grub_free (mod); |
a5ffe966 | 795 | return 1; |
6a161fa9 | 796 | } |
797 | ||
a5ffe966 | 798 | /* Unload unneeded modules. */ |
6a161fa9 | 799 | void |
4b13b216 | 800 | grub_dl_unload_unneeded (void) |
a5ffe966 | 801 | { |
4b13b216 | 802 | /* Because grub_dl_remove modifies the list of modules, this |
a5ffe966 | 803 | implementation is tricky. */ |
fcaae9ec | 804 | grub_dl_t p = grub_dl_head; |
b39f9d20 | 805 | |
a5ffe966 | 806 | while (p) |
807 | { | |
fcaae9ec | 808 | if (grub_dl_unload (p)) |
a5ffe966 | 809 | { |
4b13b216 | 810 | p = grub_dl_head; |
a5ffe966 | 811 | continue; |
812 | } | |
813 | ||
814 | p = p->next; | |
815 | } | |
816 | } |