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