2009-08-17 Robert Millan <rmh.grub@aybabtu.com>
[grub2/phcoder/solaris.git] / kern / dl.c
blob78ebc1e385ff56deb3a5d6a634bd290ca788e860
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 /* Force native word size */
21 #define GRUB_TARGET_WORDSIZE (8 * GRUB_CPU_SIZEOF_VOID_P)
23 #include <config.h>
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>
33 #include <grub/cache.h>
34 #include <grub/machine/machine.h>
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
43 struct grub_dl_list
45 struct grub_dl_list *next;
46 grub_dl_t mod;
48 typedef struct grub_dl_list *grub_dl_list_t;
50 static grub_dl_list_t grub_dl_head;
52 static grub_err_t
53 grub_dl_add (grub_dl_t mod)
55 grub_dl_list_t l;
57 if (grub_dl_get (mod->name))
58 return grub_error (GRUB_ERR_BAD_MODULE,
59 "`%s' is already loaded", mod->name);
61 l = (grub_dl_list_t) grub_malloc (sizeof (*l));
62 if (! l)
63 return grub_errno;
65 l->mod = mod;
66 l->next = grub_dl_head;
67 grub_dl_head = l;
69 return GRUB_ERR_NONE;
72 static void
73 grub_dl_remove (grub_dl_t mod)
75 grub_dl_list_t *p, q;
77 for (p = &grub_dl_head, q = *p; q; p = &q->next, q = *p)
78 if (q->mod == mod)
80 *p = q->next;
81 grub_free (q);
82 return;
86 grub_dl_t
87 grub_dl_get (const char *name)
89 grub_dl_list_t l;
91 for (l = grub_dl_head; l; l = l->next)
92 if (grub_strcmp (name, l->mod->name) == 0)
93 return l->mod;
95 return 0;
98 void
99 grub_dl_iterate (int (*hook) (grub_dl_t mod))
101 grub_dl_list_t l;
103 for (l = grub_dl_head; l; l = l->next)
104 if (hook (l->mod))
105 break;
110 struct grub_symbol
112 struct grub_symbol *next;
113 const char *name;
114 void *addr;
115 grub_dl_t mod; /* The module to which this symbol belongs. */
117 typedef struct grub_symbol *grub_symbol_t;
119 /* The size of the symbol table. */
120 #define GRUB_SYMTAB_SIZE 509
122 /* The symbol table (using an open-hash). */
123 static struct grub_symbol *grub_symtab[GRUB_SYMTAB_SIZE];
125 /* Simple hash function. */
126 static unsigned
127 grub_symbol_hash (const char *s)
129 unsigned key = 0;
131 while (*s)
132 key = key * 65599 + *s++;
134 return (key + (key >> 5)) % GRUB_SYMTAB_SIZE;
137 /* Resolve the symbol name NAME and return the address.
138 Return NULL, if not found. */
139 static void *
140 grub_dl_resolve_symbol (const char *name)
142 grub_symbol_t sym;
144 for (sym = grub_symtab[grub_symbol_hash (name)]; sym; sym = sym->next)
145 if (grub_strcmp (sym->name, name) == 0)
146 return sym->addr;
148 return 0;
151 /* Register a symbol with the name NAME and the address ADDR. */
152 grub_err_t
153 grub_dl_register_symbol (const char *name, void *addr, grub_dl_t mod)
155 grub_symbol_t sym;
156 unsigned k;
158 sym = (grub_symbol_t) grub_malloc (sizeof (*sym));
159 if (! sym)
160 return grub_errno;
162 if (mod)
164 sym->name = grub_strdup (name);
165 if (! sym->name)
167 grub_free (sym);
168 return grub_errno;
171 else
172 sym->name = name;
174 sym->addr = addr;
175 sym->mod = mod;
177 k = grub_symbol_hash (name);
178 sym->next = grub_symtab[k];
179 grub_symtab[k] = sym;
181 return GRUB_ERR_NONE;
184 /* Unregister all the symbols defined in the module MOD. */
185 static void
186 grub_dl_unregister_symbols (grub_dl_t mod)
188 unsigned i;
190 if (! mod)
191 grub_fatal ("core symbols cannot be unregistered");
193 for (i = 0; i < GRUB_SYMTAB_SIZE; i++)
195 grub_symbol_t sym, *p, q;
197 for (p = &grub_symtab[i], sym = *p; sym; sym = q)
199 q = sym->next;
200 if (sym->mod == mod)
202 *p = q;
203 grub_free ((void *) sym->name);
204 grub_free (sym);
206 else
207 p = &sym->next;
212 /* Return the address of a section whose index is N. */
213 static void *
214 grub_dl_get_section_addr (grub_dl_t mod, unsigned n)
216 grub_dl_segment_t seg;
218 for (seg = mod->segment; seg; seg = seg->next)
219 if (seg->section == n)
220 return seg->addr;
222 return 0;
225 /* Check if EHDR is a valid ELF header. */
226 static grub_err_t
227 grub_dl_check_header (void *ehdr, grub_size_t size)
229 Elf_Ehdr *e = ehdr;
231 /* Check the header size. */
232 if (size < sizeof (Elf_Ehdr))
233 return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected");
235 /* Check the magic numbers. */
236 if (grub_arch_dl_check_header (ehdr)
237 || e->e_ident[EI_MAG0] != ELFMAG0
238 || e->e_ident[EI_MAG1] != ELFMAG1
239 || e->e_ident[EI_MAG2] != ELFMAG2
240 || e->e_ident[EI_MAG3] != ELFMAG3
241 || e->e_ident[EI_VERSION] != EV_CURRENT
242 || e->e_version != EV_CURRENT)
243 return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
245 return GRUB_ERR_NONE;
248 /* Load all segments from memory specified by E. */
249 static grub_err_t
250 grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
252 unsigned i;
253 Elf_Shdr *s;
255 for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
256 i < e->e_shnum;
257 i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
259 if (s->sh_flags & SHF_ALLOC)
261 grub_dl_segment_t seg;
263 seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
264 if (! seg)
265 return grub_errno;
267 if (s->sh_size)
269 void *addr;
271 addr = grub_memalign (s->sh_addralign, s->sh_size);
272 if (! addr)
274 grub_free (seg);
275 return grub_errno;
278 switch (s->sh_type)
280 case SHT_PROGBITS:
281 grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
282 break;
283 case SHT_NOBITS:
284 grub_memset (addr, 0, s->sh_size);
285 break;
288 seg->addr = addr;
290 else
291 seg->addr = 0;
293 seg->size = s->sh_size;
294 seg->section = i;
295 seg->next = mod->segment;
296 mod->segment = seg;
300 return GRUB_ERR_NONE;
303 static grub_err_t
304 grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
306 unsigned i;
307 Elf_Shdr *s;
308 Elf_Sym *sym;
309 const char *str;
310 Elf_Word size, entsize;
312 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
313 i < e->e_shnum;
314 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
315 if (s->sh_type == SHT_SYMTAB)
316 break;
318 if (i == e->e_shnum)
319 return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
321 #ifdef GRUB_MODULES_MACHINE_READONLY
322 mod->symtab = grub_malloc (s->sh_size);
323 memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size);
324 #else
325 mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
326 #endif
327 sym = mod->symtab;
328 size = s->sh_size;
329 entsize = s->sh_entsize;
331 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
332 str = (char *) e + s->sh_offset;
334 for (i = 0;
335 i < size / entsize;
336 i++, sym = (Elf_Sym *) ((char *) sym + entsize))
338 unsigned char type = ELF_ST_TYPE (sym->st_info);
339 unsigned char bind = ELF_ST_BIND (sym->st_info);
340 const char *name = str + sym->st_name;
342 switch (type)
344 case STT_NOTYPE:
345 /* Resolve a global symbol. */
346 if (sym->st_name != 0 && sym->st_shndx == 0)
348 sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name);
349 if (! sym->st_value)
350 return grub_error (GRUB_ERR_BAD_MODULE,
351 "the symbol `%s' not found", name);
353 else
354 sym->st_value = 0;
355 break;
357 case STT_OBJECT:
358 sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
359 sym->st_shndx);
360 if (bind != STB_LOCAL)
361 if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
362 return grub_errno;
363 break;
365 case STT_FUNC:
366 sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
367 sym->st_shndx);
368 if (bind != STB_LOCAL)
369 if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
370 return grub_errno;
372 if (grub_strcmp (name, "grub_mod_init") == 0)
373 mod->init = (void (*) (grub_dl_t)) sym->st_value;
374 else if (grub_strcmp (name, "grub_mod_fini") == 0)
375 mod->fini = (void (*) (void)) sym->st_value;
376 break;
378 case STT_SECTION:
379 sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
380 sym->st_shndx);
381 break;
383 case STT_FILE:
384 sym->st_value = 0;
385 break;
387 default:
388 return grub_error (GRUB_ERR_BAD_MODULE,
389 "unknown symbol type `%d'", (int) type);
393 return GRUB_ERR_NONE;
396 static void
397 grub_dl_call_init (grub_dl_t mod)
399 if (mod->init)
400 (mod->init) (mod);
403 static grub_err_t
404 grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
406 Elf_Shdr *s;
407 const char *str;
408 unsigned i;
410 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
411 str = (char *) e + s->sh_offset;
413 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
414 i < e->e_shnum;
415 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
416 if (grub_strcmp (str + s->sh_name, ".modname") == 0)
418 mod->name = grub_strdup ((char *) e + s->sh_offset);
419 if (! mod->name)
420 return grub_errno;
421 break;
424 if (i == e->e_shnum)
425 return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
427 return GRUB_ERR_NONE;
430 static grub_err_t
431 grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
433 Elf_Shdr *s;
434 const char *str;
435 unsigned i;
437 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
438 str = (char *) e + s->sh_offset;
440 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
441 i < e->e_shnum;
442 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
443 if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
445 const char *name = (char *) e + s->sh_offset;
446 const char *max = name + s->sh_size;
448 while ((name < max) && (*name))
450 grub_dl_t m;
451 grub_dl_dep_t dep;
453 m = grub_dl_load (name);
454 if (! m)
455 return grub_errno;
457 grub_dl_ref (m);
459 dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
460 if (! dep)
461 return grub_errno;
463 dep->mod = m;
464 dep->next = mod->dep;
465 mod->dep = dep;
467 name += grub_strlen (name) + 1;
471 return GRUB_ERR_NONE;
474 #ifndef GRUB_UTIL
476 grub_dl_ref (grub_dl_t mod)
478 grub_dl_dep_t dep;
480 for (dep = mod->dep; dep; dep = dep->next)
481 grub_dl_ref (dep->mod);
483 return ++mod->ref_count;
487 grub_dl_unref (grub_dl_t mod)
489 grub_dl_dep_t dep;
491 for (dep = mod->dep; dep; dep = dep->next)
492 grub_dl_unref (dep->mod);
494 return --mod->ref_count;
496 #endif
498 static void
499 grub_dl_flush_cache (grub_dl_t mod)
501 grub_dl_segment_t seg;
503 for (seg = mod->segment; seg; seg = seg->next) {
504 if (seg->size) {
505 grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n",
506 (unsigned long) seg->size, seg->addr);
507 grub_arch_sync_caches (seg->addr, seg->size);
512 /* Load a module from core memory. */
513 grub_dl_t
514 grub_dl_load_core (void *addr, grub_size_t size)
516 Elf_Ehdr *e;
517 grub_dl_t mod;
519 grub_dprintf ("modules", "module at %p, size 0x%lx\n", addr,
520 (unsigned long) size);
521 e = addr;
522 if (grub_dl_check_header (e, size))
523 return 0;
525 if (e->e_type != ET_REL)
527 grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
528 return 0;
531 /* Make sure that every section is within the core. */
532 if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
534 grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
535 return 0;
538 mod = (grub_dl_t) grub_zalloc (sizeof (*mod));
539 if (! mod)
540 return 0;
542 mod->ref_count = 1;
544 grub_dprintf ("modules", "relocating to %p\n", mod);
545 if (grub_dl_resolve_name (mod, e)
546 || grub_dl_resolve_dependencies (mod, e)
547 || grub_dl_load_segments (mod, e)
548 || grub_dl_resolve_symbols (mod, e)
549 || grub_arch_dl_relocate_symbols (mod, e))
551 mod->fini = 0;
552 grub_dl_unload (mod);
553 return 0;
556 grub_dl_flush_cache (mod);
558 grub_dprintf ("modules", "module name: %s\n", mod->name);
559 grub_dprintf ("modules", "init function: %p\n", mod->init);
560 grub_dl_call_init (mod);
562 if (grub_dl_add (mod))
564 grub_dl_unload (mod);
565 return 0;
568 return mod;
571 /* Load a module from the file FILENAME. */
572 grub_dl_t
573 grub_dl_load_file (const char *filename)
575 grub_file_t file = NULL;
576 grub_ssize_t size;
577 void *core = 0;
578 grub_dl_t mod = 0;
580 file = grub_file_open (filename);
581 if (! file)
582 return 0;
584 size = grub_file_size (file);
585 core = grub_malloc (size);
586 if (! core)
588 grub_file_close (file);
589 return 0;
592 if (grub_file_read (file, core, size) != (int) size)
594 grub_file_close (file);
595 grub_free (core);
596 return 0;
599 /* We must close this before we try to process dependencies.
600 Some disk backends do not handle gracefully multiple concurrent
601 opens of the same device. */
602 grub_file_close (file);
604 mod = grub_dl_load_core (core, size);
605 if (! mod)
607 grub_free (core);
608 return 0;
611 mod->ref_count = 0;
612 return mod;
615 /* Load a module using a symbolic name. */
616 grub_dl_t
617 grub_dl_load (const char *name)
619 char *filename;
620 grub_dl_t mod;
621 char *grub_dl_dir = grub_env_get ("prefix");
623 mod = grub_dl_get (name);
624 if (mod)
625 return mod;
627 if (! grub_dl_dir) {
628 grub_error (GRUB_ERR_FILE_NOT_FOUND, "\"prefix\" is not set");
629 return 0;
632 filename = (char *) grub_malloc (grub_strlen (grub_dl_dir) + 1
633 + grub_strlen (name) + 4 + 1);
634 if (! filename)
635 return 0;
637 grub_sprintf (filename, "%s/%s.mod", grub_dl_dir, name);
638 mod = grub_dl_load_file (filename);
639 grub_free (filename);
641 if (! mod)
642 return 0;
644 if (grub_strcmp (mod->name, name) != 0)
645 grub_error (GRUB_ERR_BAD_MODULE, "mismatched names");
647 return mod;
650 /* Unload the module MOD. */
652 grub_dl_unload (grub_dl_t mod)
654 grub_dl_dep_t dep, depn;
655 grub_dl_segment_t seg, segn;
657 if (mod->ref_count > 0)
658 return 0;
660 if (mod->fini)
661 (mod->fini) ();
663 grub_dl_remove (mod);
664 grub_dl_unregister_symbols (mod);
666 for (dep = mod->dep; dep; dep = depn)
668 depn = dep->next;
670 if (! grub_dl_unref (dep->mod))
671 grub_dl_unload (dep->mod);
673 grub_free (dep);
676 for (seg = mod->segment; seg; seg = segn)
678 segn = seg->next;
679 grub_free (seg->addr);
680 grub_free (seg);
683 grub_free (mod->name);
684 #ifdef GRUB_MODULES_MACHINE_READONLY
685 grub_free (mod->symtab);
686 #endif
687 grub_free (mod);
688 return 1;
691 /* Unload unneeded modules. */
692 void
693 grub_dl_unload_unneeded (void)
695 /* Because grub_dl_remove modifies the list of modules, this
696 implementation is tricky. */
697 grub_dl_list_t p = grub_dl_head;
699 while (p)
701 if (grub_dl_unload (p->mod))
703 p = grub_dl_head;
704 continue;
707 p = p->next;
711 /* Unload all modules. */
712 void
713 grub_dl_unload_all (void)
715 while (grub_dl_head)
717 grub_dl_list_t p;
719 grub_dl_unload_unneeded ();
721 /* Force to decrement the ref count. This will purge pre-loaded
722 modules and manually inserted modules. */
723 for (p = grub_dl_head; p; p = p->next)
724 p->mod->ref_count--;