2009-07-01 Pavel Roskin <proski@gnu.org>
[grub2/bean.git] / kern / dl.c
blob6c863be6f0d770507460752d35541ca18fcb5dfd
1 /* dl.c - loadable module support */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
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.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <config.h>
21 #include <grub/elf.h>
22 #include <grub/dl.h>
23 #include <grub/misc.h>
24 #include <grub/mm.h>
25 #include <grub/err.h>
26 #include <grub/types.h>
27 #include <grub/symbol.h>
28 #include <grub/file.h>
29 #include <grub/env.h>
30 #include <grub/cache.h>
31 #include <grub/machine/machine.h>
33 /* Platforms where modules are in a readonly area of memory. */
34 #if defined(GRUB_MACHINE_QEMU)
35 #define GRUB_MODULES_MACHINE_READONLY
36 #endif
40 struct grub_dl_list
42 struct grub_dl_list *next;
43 grub_dl_t mod;
45 typedef struct grub_dl_list *grub_dl_list_t;
47 static grub_dl_list_t grub_dl_head;
49 static grub_err_t
50 grub_dl_add (grub_dl_t mod)
52 grub_dl_list_t l;
54 if (grub_dl_get (mod->name))
55 return grub_error (GRUB_ERR_BAD_MODULE,
56 "`%s' is already loaded", mod->name);
58 l = (grub_dl_list_t) grub_malloc (sizeof (*l));
59 if (! l)
60 return grub_errno;
62 l->mod = mod;
63 l->next = grub_dl_head;
64 grub_dl_head = l;
66 return GRUB_ERR_NONE;
69 static void
70 grub_dl_remove (grub_dl_t mod)
72 grub_dl_list_t *p, q;
74 for (p = &grub_dl_head, q = *p; q; p = &q->next, q = *p)
75 if (q->mod == mod)
77 *p = q->next;
78 grub_free (q);
79 return;
83 grub_dl_t
84 grub_dl_get (const char *name)
86 grub_dl_list_t l;
88 for (l = grub_dl_head; l; l = l->next)
89 if (grub_strcmp (name, l->mod->name) == 0)
90 return l->mod;
92 return 0;
95 void
96 grub_dl_iterate (int (*hook) (grub_dl_t mod))
98 grub_dl_list_t l;
100 for (l = grub_dl_head; l; l = l->next)
101 if (hook (l->mod))
102 break;
107 struct grub_symbol
109 struct grub_symbol *next;
110 const char *name;
111 void *addr;
112 grub_dl_t mod; /* The module to which this symbol belongs. */
114 typedef struct grub_symbol *grub_symbol_t;
116 /* The size of the symbol table. */
117 #define GRUB_SYMTAB_SIZE 509
119 /* The symbol table (using an open-hash). */
120 static struct grub_symbol *grub_symtab[GRUB_SYMTAB_SIZE];
122 /* Simple hash function. */
123 static unsigned
124 grub_symbol_hash (const char *s)
126 unsigned key = 0;
128 while (*s)
129 key = key * 65599 + *s++;
131 return (key + (key >> 5)) % GRUB_SYMTAB_SIZE;
134 /* Resolve the symbol name NAME and return the address.
135 Return NULL, if not found. */
136 static void *
137 grub_dl_resolve_symbol (const char *name)
139 grub_symbol_t sym;
141 for (sym = grub_symtab[grub_symbol_hash (name)]; sym; sym = sym->next)
142 if (grub_strcmp (sym->name, name) == 0)
143 return sym->addr;
145 return 0;
148 /* Register a symbol with the name NAME and the address ADDR. */
149 grub_err_t
150 grub_dl_register_symbol (const char *name, void *addr, grub_dl_t mod)
152 grub_symbol_t sym;
153 unsigned k;
155 sym = (grub_symbol_t) grub_malloc (sizeof (*sym));
156 if (! sym)
157 return grub_errno;
159 if (mod)
161 sym->name = grub_strdup (name);
162 if (! sym->name)
164 grub_free (sym);
165 return grub_errno;
168 else
169 sym->name = name;
171 sym->addr = addr;
172 sym->mod = mod;
174 k = grub_symbol_hash (name);
175 sym->next = grub_symtab[k];
176 grub_symtab[k] = sym;
178 return GRUB_ERR_NONE;
181 /* Unregister all the symbols defined in the module MOD. */
182 static void
183 grub_dl_unregister_symbols (grub_dl_t mod)
185 unsigned i;
187 if (! mod)
188 grub_fatal ("core symbols cannot be unregistered");
190 for (i = 0; i < GRUB_SYMTAB_SIZE; i++)
192 grub_symbol_t sym, *p, q;
194 for (p = &grub_symtab[i], sym = *p; sym; sym = q)
196 q = sym->next;
197 if (sym->mod == mod)
199 *p = q;
200 grub_free ((void *) sym->name);
201 grub_free (sym);
203 else
204 p = &sym->next;
209 /* Return the address of a section whose index is N. */
210 static void *
211 grub_dl_get_section_addr (grub_dl_t mod, unsigned n)
213 grub_dl_segment_t seg;
215 for (seg = mod->segment; seg; seg = seg->next)
216 if (seg->section == n)
217 return seg->addr;
219 return 0;
222 /* Check if EHDR is a valid ELF header. */
223 grub_err_t
224 grub_dl_check_header (void *ehdr, grub_size_t size)
226 Elf_Ehdr *e = ehdr;
228 /* Check the header size. */
229 if (size < sizeof (Elf_Ehdr))
230 return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected");
232 /* Check the magic numbers. */
233 if (grub_arch_dl_check_header (ehdr)
234 || e->e_ident[EI_MAG0] != ELFMAG0
235 || e->e_ident[EI_MAG1] != ELFMAG1
236 || e->e_ident[EI_MAG2] != ELFMAG2
237 || e->e_ident[EI_MAG3] != ELFMAG3
238 || e->e_ident[EI_VERSION] != EV_CURRENT
239 || e->e_version != EV_CURRENT)
240 return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
242 return GRUB_ERR_NONE;
245 /* Load all segments from memory specified by E. */
246 static grub_err_t
247 grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
249 unsigned i;
250 Elf_Shdr *s;
252 for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
253 i < e->e_shnum;
254 i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
256 if (s->sh_flags & SHF_ALLOC)
258 grub_dl_segment_t seg;
260 seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
261 if (! seg)
262 return grub_errno;
264 if (s->sh_size)
266 void *addr;
268 addr = grub_memalign (s->sh_addralign, s->sh_size);
269 if (! addr)
271 grub_free (seg);
272 return grub_errno;
275 switch (s->sh_type)
277 case SHT_PROGBITS:
278 grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
279 break;
280 case SHT_NOBITS:
281 grub_memset (addr, 0, s->sh_size);
282 break;
285 seg->addr = addr;
287 else
288 seg->addr = 0;
290 seg->size = s->sh_size;
291 seg->section = i;
292 seg->next = mod->segment;
293 mod->segment = seg;
297 return GRUB_ERR_NONE;
300 static grub_err_t
301 grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
303 unsigned i;
304 Elf_Shdr *s;
305 Elf_Sym *sym;
306 const char *str;
307 Elf_Word size, entsize;
309 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
310 i < e->e_shnum;
311 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
312 if (s->sh_type == SHT_SYMTAB)
313 break;
315 if (i == e->e_shnum)
316 return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
318 #ifdef GRUB_MODULES_MACHINE_READONLY
319 mod->symtab = grub_malloc (s->sh_size);
320 memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size);
321 #else
322 mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
323 #endif
324 sym = mod->symtab;
325 size = s->sh_size;
326 entsize = s->sh_entsize;
328 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
329 str = (char *) e + s->sh_offset;
331 for (i = 0;
332 i < size / entsize;
333 i++, sym = (Elf_Sym *) ((char *) sym + entsize))
335 unsigned char type = ELF_ST_TYPE (sym->st_info);
336 unsigned char bind = ELF_ST_BIND (sym->st_info);
337 const char *name = str + sym->st_name;
339 switch (type)
341 case STT_NOTYPE:
342 /* Resolve a global symbol. */
343 if (sym->st_name != 0 && sym->st_shndx == 0)
345 sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name);
346 if (! sym->st_value)
347 return grub_error (GRUB_ERR_BAD_MODULE,
348 "the symbol `%s' not found", name);
350 else
351 sym->st_value = 0;
352 break;
354 case STT_OBJECT:
355 sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
356 sym->st_shndx);
357 if (bind != STB_LOCAL)
358 if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
359 return grub_errno;
360 break;
362 case STT_FUNC:
363 sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
364 sym->st_shndx);
365 if (bind != STB_LOCAL)
366 if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
367 return grub_errno;
369 if (grub_strcmp (name, "grub_mod_init") == 0)
370 mod->init = (void (*) (grub_dl_t)) sym->st_value;
371 else if (grub_strcmp (name, "grub_mod_fini") == 0)
372 mod->fini = (void (*) (void)) sym->st_value;
373 break;
375 case STT_SECTION:
376 sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
377 sym->st_shndx);
378 break;
380 case STT_FILE:
381 sym->st_value = 0;
382 break;
384 default:
385 return grub_error (GRUB_ERR_BAD_MODULE,
386 "unknown symbol type `%d'", (int) type);
390 return GRUB_ERR_NONE;
393 static void
394 grub_dl_call_init (grub_dl_t mod)
396 if (mod->init)
397 (mod->init) (mod);
400 static grub_err_t
401 grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
403 Elf_Shdr *s;
404 const char *str;
405 unsigned i;
407 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
408 str = (char *) e + s->sh_offset;
410 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
411 i < e->e_shnum;
412 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
413 if (grub_strcmp (str + s->sh_name, ".modname") == 0)
415 mod->name = grub_strdup ((char *) e + s->sh_offset);
416 if (! mod->name)
417 return grub_errno;
418 break;
421 if (i == e->e_shnum)
422 return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
424 return GRUB_ERR_NONE;
427 static grub_err_t
428 grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
430 Elf_Shdr *s;
431 const char *str;
432 unsigned i;
434 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
435 str = (char *) e + s->sh_offset;
437 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
438 i < e->e_shnum;
439 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
440 if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
442 const char *name = (char *) e + s->sh_offset;
443 const char *max = name + s->sh_size;
445 while ((name < max) && (*name))
447 grub_dl_t m;
448 grub_dl_dep_t dep;
450 m = grub_dl_load (name);
451 if (! m)
452 return grub_errno;
454 grub_dl_ref (m);
456 dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
457 if (! dep)
458 return grub_errno;
460 dep->mod = m;
461 dep->next = mod->dep;
462 mod->dep = dep;
464 name += grub_strlen (name) + 1;
468 return GRUB_ERR_NONE;
471 #ifndef GRUB_UTIL
473 grub_dl_ref (grub_dl_t mod)
475 grub_dl_dep_t dep;
477 for (dep = mod->dep; dep; dep = dep->next)
478 grub_dl_ref (dep->mod);
480 return ++mod->ref_count;
484 grub_dl_unref (grub_dl_t mod)
486 grub_dl_dep_t dep;
488 for (dep = mod->dep; dep; dep = dep->next)
489 grub_dl_unref (dep->mod);
491 return --mod->ref_count;
493 #endif
495 static void
496 grub_dl_flush_cache (grub_dl_t mod)
498 grub_dl_segment_t seg;
500 for (seg = mod->segment; seg; seg = seg->next) {
501 if (seg->size) {
502 grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n",
503 (unsigned long) seg->size, seg->addr);
504 grub_arch_sync_caches (seg->addr, seg->size);
509 /* Load a module from core memory. */
510 grub_dl_t
511 grub_dl_load_core (void *addr, grub_size_t size)
513 Elf_Ehdr *e;
514 grub_dl_t mod;
516 grub_dprintf ("modules", "module at %p, size 0x%lx\n", addr,
517 (unsigned long) size);
518 e = addr;
519 if (grub_dl_check_header (e, size))
520 return 0;
522 if (e->e_type != ET_REL)
524 grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
525 return 0;
528 /* Make sure that every section is within the core. */
529 if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
531 grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
532 return 0;
535 mod = (grub_dl_t) grub_malloc (sizeof (*mod));
536 if (! mod)
537 return 0;
539 mod->name = 0;
540 mod->ref_count = 1;
541 mod->dep = 0;
542 mod->segment = 0;
543 mod->init = 0;
544 mod->fini = 0;
546 grub_dprintf ("modules", "relocating to %p\n", mod);
547 if (grub_dl_resolve_name (mod, e)
548 || grub_dl_resolve_dependencies (mod, e)
549 || grub_dl_load_segments (mod, e)
550 || grub_dl_resolve_symbols (mod, e)
551 || grub_arch_dl_relocate_symbols (mod, e))
553 mod->fini = 0;
554 grub_dl_unload (mod);
555 return 0;
558 grub_dl_flush_cache (mod);
560 grub_dprintf ("modules", "module name: %s\n", mod->name);
561 grub_dprintf ("modules", "init function: %p\n", mod->init);
562 grub_dl_call_init (mod);
564 if (grub_dl_add (mod))
566 grub_dl_unload (mod);
567 return 0;
570 return mod;
573 /* Load a module from the file FILENAME. */
574 grub_dl_t
575 grub_dl_load_file (const char *filename)
577 grub_file_t file = NULL;
578 grub_ssize_t size;
579 void *core = 0;
580 grub_dl_t mod = 0;
582 file = grub_file_open (filename);
583 if (! file)
584 return 0;
586 size = grub_file_size (file);
587 core = grub_malloc (size);
588 if (! core)
590 grub_file_close (file);
591 return 0;
594 if (grub_file_read (file, core, size) != (int) size)
596 grub_file_close (file);
597 grub_free (core);
598 return 0;
601 /* We must close this before we try to process dependencies.
602 Some disk backends do not handle gracefully multiple concurrent
603 opens of the same device. */
604 grub_file_close (file);
606 mod = grub_dl_load_core (core, size);
607 if (! mod)
609 grub_free (core);
610 return 0;
613 mod->ref_count = 0;
614 return mod;
617 /* Load a module using a symbolic name. */
618 grub_dl_t
619 grub_dl_load (const char *name)
621 char *filename;
622 grub_dl_t mod;
623 char *grub_dl_dir = grub_env_get ("prefix");
625 mod = grub_dl_get (name);
626 if (mod)
627 return mod;
629 if (! grub_dl_dir) {
630 grub_error (GRUB_ERR_FILE_NOT_FOUND, "\"prefix\" is not set");
631 return 0;
634 filename = (char *) grub_malloc (grub_strlen (grub_dl_dir) + 1
635 + grub_strlen (name) + 4 + 1);
636 if (! filename)
637 return 0;
639 grub_sprintf (filename, "%s/%s.mod", grub_dl_dir, name);
640 mod = grub_dl_load_file (filename);
641 grub_free (filename);
643 if (! mod)
644 return 0;
646 if (grub_strcmp (mod->name, name) != 0)
647 grub_error (GRUB_ERR_BAD_MODULE, "mismatched names");
649 return mod;
652 /* Unload the module MOD. */
654 grub_dl_unload (grub_dl_t mod)
656 grub_dl_dep_t dep, depn;
657 grub_dl_segment_t seg, segn;
659 if (mod->ref_count > 0)
660 return 0;
662 if (mod->fini)
663 (mod->fini) ();
665 grub_dl_remove (mod);
666 grub_dl_unregister_symbols (mod);
668 for (dep = mod->dep; dep; dep = depn)
670 depn = dep->next;
672 if (! grub_dl_unref (dep->mod))
673 grub_dl_unload (dep->mod);
675 grub_free (dep);
678 for (seg = mod->segment; seg; seg = segn)
680 segn = seg->next;
681 grub_free (seg->addr);
682 grub_free (seg);
685 grub_free (mod->name);
686 #ifdef GRUB_MODULES_MACHINE_READONLY
687 grub_free (mod->symtab);
688 #endif
689 grub_free (mod);
690 return 1;
693 /* Unload unneeded modules. */
694 void
695 grub_dl_unload_unneeded (void)
697 /* Because grub_dl_remove modifies the list of modules, this
698 implementation is tricky. */
699 grub_dl_list_t p = grub_dl_head;
701 while (p)
703 if (grub_dl_unload (p->mod))
705 p = grub_dl_head;
706 continue;
709 p = p->next;
713 /* Unload all modules. */
714 void
715 grub_dl_unload_all (void)
717 while (grub_dl_head)
719 grub_dl_list_t p;
721 grub_dl_unload_unneeded ();
723 /* Force to decrement the ref count. This will purge pre-loaded
724 modules and manually inserted modules. */
725 for (p = grub_dl_head; p; p = p->next)
726 p->mod->ref_count--;