]> git.proxmox.com Git - grub2.git/blob - kern/dl.c
2006-05-29 Yoshinori K. Okuji <okuji@enbug.org>
[grub2.git] / kern / dl.c
1 /* dl.c - loadable module support */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
5 *
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 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <config.h>
22 #include <grub/elf.h>
23 #include <grub/dl.h>
24 #include <grub/misc.h>
25 #include <grub/mm.h>
26 #include <grub/err.h>
27 #include <grub/types.h>
28 #include <grub/symbol.h>
29 #include <grub/file.h>
30 #include <grub/env.h>
31 #include <grub/cache.h>
32
33 #if GRUB_CPU_SIZEOF_VOID_P == 4
34
35 typedef Elf32_Word Elf_Word;
36 typedef Elf32_Addr Elf_Addr;
37 typedef Elf32_Ehdr Elf_Ehdr;
38 typedef Elf32_Shdr Elf_Shdr;
39 typedef Elf32_Sym Elf_Sym;
40
41 # define ELF_ST_BIND(val) ELF32_ST_BIND (val)
42 # define ELF_ST_TYPE(val) ELF32_ST_TYPE (val)
43
44 #elif GRUB_CPU_SIZEOF_VOID_P == 8
45
46 typedef Elf64_Word Elf_Word;
47 typedef Elf64_Addr Elf_Addr;
48 typedef Elf64_Ehdr Elf_Ehdr;
49 typedef Elf64_Shdr Elf_Shdr;
50 typedef Elf64_Sym Elf_Sym;
51
52 # define ELF_ST_BIND(val) ELF64_ST_BIND (val)
53 # define ELF_ST_TYPE(val) ELF64_ST_TYPE (val)
54
55 #endif
56
57 \f
58
59 struct grub_dl_list
60 {
61 struct grub_dl_list *next;
62 grub_dl_t mod;
63 };
64 typedef struct grub_dl_list *grub_dl_list_t;
65
66 static grub_dl_list_t grub_dl_head;
67
68 static grub_err_t
69 grub_dl_add (grub_dl_t mod)
70 {
71 grub_dl_list_t l;
72
73 if (grub_dl_get (mod->name))
74 return grub_error (GRUB_ERR_BAD_MODULE,
75 "`%s' is already loaded", mod->name);
76
77 l = (grub_dl_list_t) grub_malloc (sizeof (*l));
78 if (! l)
79 return grub_errno;
80
81 l->mod = mod;
82 l->next = grub_dl_head;
83 grub_dl_head = l;
84
85 return GRUB_ERR_NONE;
86 }
87
88 static void
89 grub_dl_remove (grub_dl_t mod)
90 {
91 grub_dl_list_t *p, q;
92
93 for (p = &grub_dl_head, q = *p; q; p = &q->next, q = *p)
94 if (q->mod == mod)
95 {
96 *p = q->next;
97 grub_free (q);
98 return;
99 }
100 }
101
102 grub_dl_t
103 grub_dl_get (const char *name)
104 {
105 grub_dl_list_t l;
106
107 for (l = grub_dl_head; l; l = l->next)
108 if (grub_strcmp (name, l->mod->name) == 0)
109 return l->mod;
110
111 return 0;
112 }
113
114 void
115 grub_dl_iterate (int (*hook) (grub_dl_t mod))
116 {
117 grub_dl_list_t l;
118
119 for (l = grub_dl_head; l; l = l->next)
120 if (hook (l->mod))
121 break;
122 }
123
124 \f
125
126 struct grub_symbol
127 {
128 struct grub_symbol *next;
129 const char *name;
130 void *addr;
131 grub_dl_t mod; /* The module to which this symbol belongs. */
132 };
133 typedef struct grub_symbol *grub_symbol_t;
134
135 /* The size of the symbol table. */
136 #define GRUB_SYMTAB_SIZE 509
137
138 /* The symbol table (using an open-hash). */
139 static struct grub_symbol *grub_symtab[GRUB_SYMTAB_SIZE];
140
141 /* Simple hash function. */
142 static unsigned
143 grub_symbol_hash (const char *s)
144 {
145 unsigned key = 0;
146
147 while (*s)
148 key = key * 65599 + *s++;
149
150 return (key + (key >> 5)) % GRUB_SYMTAB_SIZE;
151 }
152
153 /* Resolve the symbol name NAME and return the address.
154 Return NULL, if not found. */
155 void *
156 grub_dl_resolve_symbol (const char *name)
157 {
158 grub_symbol_t sym;
159
160 for (sym = grub_symtab[grub_symbol_hash (name)]; sym; sym = sym->next)
161 if (grub_strcmp (sym->name, name) == 0)
162 return sym->addr;
163
164 return 0;
165 }
166
167 /* Register a symbol with the name NAME and the address ADDR. */
168 grub_err_t
169 grub_dl_register_symbol (const char *name, void *addr, grub_dl_t mod)
170 {
171 grub_symbol_t sym;
172 unsigned k;
173
174 sym = (grub_symbol_t) grub_malloc (sizeof (*sym));
175 if (! sym)
176 return grub_errno;
177
178 if (mod)
179 {
180 sym->name = grub_strdup (name);
181 if (! sym->name)
182 {
183 grub_free (sym);
184 return grub_errno;
185 }
186 }
187 else
188 sym->name = name;
189
190 sym->addr = addr;
191 sym->mod = mod;
192
193 k = grub_symbol_hash (name);
194 sym->next = grub_symtab[k];
195 grub_symtab[k] = sym;
196
197 return GRUB_ERR_NONE;
198 }
199
200 /* Unregister all the symbols defined in the module MOD. */
201 static void
202 grub_dl_unregister_symbols (grub_dl_t mod)
203 {
204 unsigned i;
205
206 if (! mod)
207 grub_fatal ("core symbols cannot be unregistered");
208
209 for (i = 0; i < GRUB_SYMTAB_SIZE; i++)
210 {
211 grub_symbol_t sym, *p, q;
212
213 for (p = &grub_symtab[i], sym = *p; sym; sym = q)
214 {
215 q = sym->next;
216 if (sym->mod == mod)
217 {
218 *p = q;
219 grub_free ((void *) sym->name);
220 grub_free (sym);
221 }
222 else
223 p = &sym->next;
224 }
225 }
226 }
227
228 /* Return the address of a section whose index is N. */
229 static void *
230 grub_dl_get_section_addr (grub_dl_t mod, unsigned n)
231 {
232 grub_dl_segment_t seg;
233
234 for (seg = mod->segment; seg; seg = seg->next)
235 if (seg->section == n)
236 return seg->addr;
237
238 return 0;
239 }
240
241 /* Check if EHDR is a valid ELF header. */
242 grub_err_t
243 grub_dl_check_header (void *ehdr, grub_size_t size)
244 {
245 Elf_Ehdr *e = ehdr;
246
247 /* Check the header size. */
248 if (size < sizeof (Elf_Ehdr))
249 return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected");
250
251 /* Check the magic numbers. */
252 if (grub_arch_dl_check_header (ehdr)
253 || e->e_ident[EI_MAG0] != ELFMAG0
254 || e->e_ident[EI_MAG1] != ELFMAG1
255 || e->e_ident[EI_MAG2] != ELFMAG2
256 || e->e_ident[EI_MAG3] != ELFMAG3
257 || e->e_ident[EI_VERSION] != EV_CURRENT
258 || e->e_version != EV_CURRENT)
259 return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
260
261 return GRUB_ERR_NONE;
262 }
263
264 /* Load all segments from memory specified by E. */
265 static grub_err_t
266 grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
267 {
268 unsigned i;
269 Elf_Shdr *s;
270
271 for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
272 i < e->e_shnum;
273 i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
274 {
275 if (s->sh_flags & SHF_ALLOC)
276 {
277 grub_dl_segment_t seg;
278
279 seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
280 if (! seg)
281 return grub_errno;
282
283 if (s->sh_size)
284 {
285 void *addr;
286
287 addr = grub_memalign (s->sh_addralign, s->sh_size);
288 if (! addr)
289 {
290 grub_free (seg);
291 return grub_errno;
292 }
293
294 switch (s->sh_type)
295 {
296 case SHT_PROGBITS:
297 grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
298 break;
299 case SHT_NOBITS:
300 grub_memset (addr, 0, s->sh_size);
301 break;
302 }
303
304 seg->addr = addr;
305 }
306 else
307 seg->addr = 0;
308
309 seg->size = s->sh_size;
310 seg->section = i;
311 seg->next = mod->segment;
312 mod->segment = seg;
313 }
314 }
315
316 return GRUB_ERR_NONE;
317 }
318
319 static grub_err_t
320 grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
321 {
322 unsigned i;
323 Elf_Shdr *s;
324 Elf_Sym *sym;
325 const char *str;
326 Elf_Word size, entsize;
327
328 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
329 i < e->e_shnum;
330 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
331 if (s->sh_type == SHT_SYMTAB)
332 break;
333
334 if (i == e->e_shnum)
335 return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
336
337 sym = (Elf_Sym *) ((char *) e + s->sh_offset);
338 size = s->sh_size;
339 entsize = s->sh_entsize;
340
341 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
342 str = (char *) e + s->sh_offset;
343
344 for (i = 0;
345 i < size / entsize;
346 i++, sym = (Elf_Sym *) ((char *) sym + entsize))
347 {
348 unsigned char type = ELF_ST_TYPE (sym->st_info);
349 unsigned char bind = ELF_ST_BIND (sym->st_info);
350 const char *name = str + sym->st_name;
351
352 switch (type)
353 {
354 case STT_NOTYPE:
355 /* Resolve a global symbol. */
356 if (sym->st_name != 0 && sym->st_shndx == 0)
357 {
358 sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name);
359 if (! sym->st_value)
360 return grub_error (GRUB_ERR_BAD_MODULE,
361 "the symbol `%s' not found", name);
362 }
363 else
364 sym->st_value = 0;
365 break;
366
367 case STT_OBJECT:
368 sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
369 sym->st_shndx);
370 if (bind != STB_LOCAL)
371 if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
372 return grub_errno;
373 break;
374
375 case STT_FUNC:
376 sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
377 sym->st_shndx);
378 if (bind != STB_LOCAL)
379 if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
380 return grub_errno;
381
382 if (grub_strcmp (name, "grub_mod_init") == 0)
383 mod->init = (void (*) (grub_dl_t)) sym->st_value;
384 else if (grub_strcmp (name, "grub_mod_fini") == 0)
385 mod->fini = (void (*) (void)) sym->st_value;
386 break;
387
388 case STT_SECTION:
389 sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
390 sym->st_shndx);
391 break;
392
393 case STT_FILE:
394 sym->st_value = 0;
395 break;
396
397 default:
398 return grub_error (GRUB_ERR_BAD_MODULE,
399 "unknown symbol type `%d'", (int) type);
400 }
401 }
402
403 return GRUB_ERR_NONE;
404 }
405
406 static void
407 grub_dl_call_init (grub_dl_t mod)
408 {
409 if (mod->init)
410 (mod->init) (mod);
411 }
412
413 static grub_err_t
414 grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
415 {
416 Elf_Shdr *s;
417 const char *str;
418 unsigned i;
419
420 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
421 str = (char *) e + s->sh_offset;
422
423 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
424 i < e->e_shnum;
425 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
426 if (grub_strcmp (str + s->sh_name, ".modname") == 0)
427 {
428 mod->name = grub_strdup ((char *) e + s->sh_offset);
429 if (! mod->name)
430 return grub_errno;
431 break;
432 }
433
434 if (i == e->e_shnum)
435 return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
436
437 return GRUB_ERR_NONE;
438 }
439
440 static grub_err_t
441 grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
442 {
443 Elf_Shdr *s;
444 const char *str;
445 unsigned i;
446
447 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
448 str = (char *) e + s->sh_offset;
449
450 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
451 i < e->e_shnum;
452 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
453 if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
454 {
455 const char *name = (char *) e + s->sh_offset;
456 const char *max = name + s->sh_size;
457
458 while (name < max)
459 {
460 grub_dl_t m;
461 grub_dl_dep_t dep;
462
463 m = grub_dl_load (name);
464 if (! m)
465 return grub_errno;
466
467 grub_dl_ref (m);
468
469 dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
470 if (! dep)
471 return grub_errno;
472
473 dep->mod = m;
474 dep->next = mod->dep;
475 mod->dep = dep;
476
477 name += grub_strlen (name) + 1;
478 }
479 }
480
481 return GRUB_ERR_NONE;
482 }
483
484 int
485 grub_dl_ref (grub_dl_t mod)
486 {
487 grub_dl_dep_t dep;
488
489 for (dep = mod->dep; dep; dep = dep->next)
490 grub_dl_ref (dep->mod);
491
492 return ++mod->ref_count;
493 }
494
495 int
496 grub_dl_unref (grub_dl_t mod)
497 {
498 grub_dl_dep_t dep;
499
500 for (dep = mod->dep; dep; dep = dep->next)
501 grub_dl_unref (dep->mod);
502
503 return --mod->ref_count;
504 }
505
506 static void
507 grub_dl_flush_cache (grub_dl_t mod)
508 {
509 grub_dl_segment_t seg;
510
511 for (seg = mod->segment; seg; seg = seg->next) {
512 if (seg->size) {
513 grub_dprintf ("modules", "flushing 0x%x bytes at %p\n", seg->size,
514 seg->addr);
515 grub_arch_sync_caches (seg->addr, seg->size);
516 }
517 }
518 }
519
520 /* Load a module from core memory. */
521 grub_dl_t
522 grub_dl_load_core (void *addr, grub_size_t size)
523 {
524 Elf_Ehdr *e;
525 grub_dl_t mod;
526
527 grub_dprintf ("modules", "module at %p, size 0x%x\n", addr, size);
528 e = addr;
529 if (grub_dl_check_header (e, size))
530 return 0;
531
532 if (e->e_type != ET_REL)
533 {
534 grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
535 return 0;
536 }
537
538 /* Make sure that every section is within the core. */
539 if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
540 {
541 grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
542 return 0;
543 }
544
545 mod = (grub_dl_t) grub_malloc (sizeof (*mod));
546 if (! mod)
547 return 0;
548
549 mod->name = 0;
550 mod->ref_count = 1;
551 mod->dep = 0;
552 mod->segment = 0;
553 mod->init = 0;
554 mod->fini = 0;
555
556 grub_dprintf ("modules", "relocating to %p\n", mod);
557 if (grub_dl_resolve_name (mod, e)
558 || grub_dl_resolve_dependencies (mod, e)
559 || grub_dl_load_segments (mod, e)
560 || grub_dl_resolve_symbols (mod, e)
561 || grub_arch_dl_relocate_symbols (mod, e))
562 {
563 mod->fini = 0;
564 grub_dl_unload (mod);
565 return 0;
566 }
567
568 grub_dl_flush_cache (mod);
569
570 grub_dprintf ("modules", "module name: %s\n", mod->name);
571 grub_dprintf ("modules", "init function: %p\n", mod->init);
572 grub_dl_call_init (mod);
573
574 if (grub_dl_add (mod))
575 {
576 grub_dl_unload (mod);
577 return 0;
578 }
579
580 return mod;
581 }
582
583 /* Load a module from the file FILENAME. */
584 grub_dl_t
585 grub_dl_load_file (const char *filename)
586 {
587 grub_file_t file;
588 grub_ssize_t size;
589 void *core = 0;
590 grub_dl_t mod = 0;
591
592 file = grub_file_open (filename);
593 if (! file)
594 return 0;
595
596 size = grub_file_size (file);
597 core = grub_malloc (size);
598 if (! core)
599 goto failed;
600
601 if (grub_file_read (file, core, size) != (int) size)
602 goto failed;
603
604 mod = grub_dl_load_core (core, size);
605 if (! mod)
606 goto failed;
607
608 mod->ref_count = 0;
609
610 failed:
611 grub_file_close (file);
612 grub_free (core);
613
614 return mod;
615 }
616
617 /* Load a module using a symbolic name. */
618 grub_dl_t
619 grub_dl_load (const char *name)
620 {
621 char *filename;
622 grub_dl_t mod;
623 char *grub_dl_dir = grub_env_get ("prefix");
624
625 mod = grub_dl_get (name);
626 if (mod)
627 return mod;
628
629 if (! grub_dl_dir)
630 grub_fatal ("module dir is not initialized yet");
631
632 filename = (char *) grub_malloc (grub_strlen (grub_dl_dir) + 1
633 + grub_strlen (name) + 4 + 1);
634 if (! filename)
635 return 0;
636
637 grub_sprintf (filename, "%s/%s.mod", grub_dl_dir, name);
638 mod = grub_dl_load_file (filename);
639 grub_free (filename);
640
641 if (! mod)
642 return 0;
643
644 if (grub_strcmp (mod->name, name) != 0)
645 grub_error (GRUB_ERR_BAD_MODULE, "mismatched names");
646
647 return mod;
648 }
649
650 /* Unload the module MOD. */
651 int
652 grub_dl_unload (grub_dl_t mod)
653 {
654 grub_dl_dep_t dep, depn;
655 grub_dl_segment_t seg, segn;
656
657 if (mod->ref_count > 0)
658 return 0;
659
660 if (mod->fini)
661 (mod->fini) ();
662
663 grub_dl_remove (mod);
664 grub_dl_unregister_symbols (mod);
665
666 for (dep = mod->dep; dep; dep = depn)
667 {
668 depn = dep->next;
669
670 if (! grub_dl_unref (dep->mod))
671 grub_dl_unload (dep->mod);
672
673 grub_free (dep);
674 }
675
676 for (seg = mod->segment; seg; seg = segn)
677 {
678 segn = seg->next;
679 grub_free (seg->addr);
680 grub_free (seg);
681 }
682
683 grub_free (mod->name);
684 grub_free (mod);
685 return 1;
686 }
687
688 /* Unload unneeded modules. */
689 void
690 grub_dl_unload_unneeded (void)
691 {
692 /* Because grub_dl_remove modifies the list of modules, this
693 implementation is tricky. */
694 grub_dl_list_t p = grub_dl_head;
695
696 while (p)
697 {
698 if (grub_dl_unload (p->mod))
699 {
700 p = grub_dl_head;
701 continue;
702 }
703
704 p = p->next;
705 }
706 }
707
708 /* Unload all modules. */
709 void
710 grub_dl_unload_all (void)
711 {
712 while (grub_dl_head)
713 {
714 grub_dl_list_t p;
715
716 grub_dl_unload_unneeded ();
717
718 /* Force to decrement the ref count. This will purge pre-loaded
719 modules and manually inserted modules. */
720 for (p = grub_dl_head; p; p = p->next)
721 p->mod->ref_count--;
722 }
723 }