]> git.proxmox.com Git - grub2.git/blob - kern/dl.c
2004-12-27 Marco Gerards <metgerards@student.han.nl>
[grub2.git] / kern / dl.c
1 /* dl.c - loadable module support */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002, 2003, 2004 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_HOST_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_HOST_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 /* Load all segments from memory specified by E. */
242 static grub_err_t
243 grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
244 {
245 unsigned i;
246 Elf_Shdr *s;
247
248 for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
249 i < e->e_shnum;
250 i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
251 {
252 if (s->sh_flags & SHF_ALLOC)
253 {
254 grub_dl_segment_t seg;
255
256 seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
257 if (! seg)
258 return grub_errno;
259
260 if (s->sh_size)
261 {
262 void *addr;
263
264 addr = grub_memalign (s->sh_addralign, s->sh_size);
265 if (! addr)
266 {
267 grub_free (seg);
268 return grub_errno;
269 }
270
271 switch (s->sh_type)
272 {
273 case SHT_PROGBITS:
274 grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
275 break;
276 case SHT_NOBITS:
277 grub_memset (addr, 0, s->sh_size);
278 break;
279 }
280
281 seg->addr = addr;
282 }
283 else
284 seg->addr = 0;
285
286 seg->size = s->sh_size;
287 seg->section = i;
288 seg->next = mod->segment;
289 mod->segment = seg;
290 }
291 }
292
293 return GRUB_ERR_NONE;
294 }
295
296 static grub_err_t
297 grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
298 {
299 unsigned i;
300 Elf_Shdr *s;
301 Elf_Sym *sym;
302 const char *str;
303 Elf_Word size, entsize;
304
305 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
306 i < e->e_shnum;
307 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
308 if (s->sh_type == SHT_SYMTAB)
309 break;
310
311 if (i == e->e_shnum)
312 return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
313
314 sym = (Elf_Sym *) ((char *) e + s->sh_offset);
315 size = s->sh_size;
316 entsize = s->sh_entsize;
317
318 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
319 str = (char *) e + s->sh_offset;
320
321 for (i = 0;
322 i < size / entsize;
323 i++, sym = (Elf_Sym *) ((char *) sym + entsize))
324 {
325 unsigned char type = ELF_ST_TYPE (sym->st_info);
326 unsigned char bind = ELF_ST_BIND (sym->st_info);
327 const char *name = str + sym->st_name;
328
329 switch (type)
330 {
331 case STT_NOTYPE:
332 /* Resolve a global symbol. */
333 if (sym->st_name != 0 && sym->st_shndx == 0)
334 {
335 sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name);
336 if (! sym->st_value)
337 return grub_error (GRUB_ERR_BAD_MODULE,
338 "the symbol `%s' not found", name);
339 }
340 else
341 sym->st_value = 0;
342 break;
343
344 case STT_OBJECT:
345 sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
346 sym->st_shndx);
347 if (bind != STB_LOCAL)
348 if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
349 return grub_errno;
350 break;
351
352 case STT_FUNC:
353 sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
354 sym->st_shndx);
355 if (bind != STB_LOCAL)
356 if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
357 return grub_errno;
358
359 if (grub_strcmp (name, "grub_mod_init") == 0)
360 mod->init = (void (*) (grub_dl_t)) sym->st_value;
361 else if (grub_strcmp (name, "grub_mod_fini") == 0)
362 mod->fini = (void (*) (void)) sym->st_value;
363 break;
364
365 case STT_SECTION:
366 sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
367 sym->st_shndx);
368 break;
369
370 case STT_FILE:
371 sym->st_value = 0;
372 break;
373
374 default:
375 return grub_error (GRUB_ERR_BAD_MODULE,
376 "unknown symbol type `%d'", (int) type);
377 }
378 }
379
380 return GRUB_ERR_NONE;
381 }
382
383 static void
384 grub_dl_call_init (grub_dl_t mod)
385 {
386 if (mod->init)
387 (mod->init) (mod);
388 }
389
390 static grub_err_t
391 grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
392 {
393 Elf_Shdr *s;
394 const char *str;
395 unsigned i;
396
397 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
398 str = (char *) e + s->sh_offset;
399
400 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
401 i < e->e_shnum;
402 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
403 if (grub_strcmp (str + s->sh_name, ".modname") == 0)
404 {
405 mod->name = grub_strdup ((char *) e + s->sh_offset);
406 if (! mod->name)
407 return grub_errno;
408 break;
409 }
410
411 if (i == e->e_shnum)
412 return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
413
414 return GRUB_ERR_NONE;
415 }
416
417 static grub_err_t
418 grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
419 {
420 Elf_Shdr *s;
421 const char *str;
422 unsigned i;
423
424 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
425 str = (char *) e + s->sh_offset;
426
427 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
428 i < e->e_shnum;
429 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
430 if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
431 {
432 const char *name = (char *) e + s->sh_offset;
433 const char *max = name + s->sh_size;
434
435 while (name < max)
436 {
437 grub_dl_t m;
438 grub_dl_dep_t dep;
439
440 m = grub_dl_load (name);
441 if (! m)
442 return grub_errno;
443
444 grub_dl_ref (m);
445
446 dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
447 if (! dep)
448 return grub_errno;
449
450 dep->mod = m;
451 dep->next = mod->dep;
452 mod->dep = dep;
453
454 name += grub_strlen (name) + 1;
455 }
456 }
457
458 return GRUB_ERR_NONE;
459 }
460
461 int
462 grub_dl_ref (grub_dl_t mod)
463 {
464 grub_dl_dep_t dep;
465
466 for (dep = mod->dep; dep; dep = dep->next)
467 grub_dl_ref (dep->mod);
468
469 return ++mod->ref_count;
470 }
471
472 int
473 grub_dl_unref (grub_dl_t mod)
474 {
475 grub_dl_dep_t dep;
476
477 for (dep = mod->dep; dep; dep = dep->next)
478 grub_dl_unref (dep->mod);
479
480 return --mod->ref_count;
481 }
482
483 static void
484 grub_dl_flush_cache (grub_dl_t mod)
485 {
486 grub_dl_segment_t seg;
487
488 for (seg = mod->segment; seg; seg = seg->next)
489 grub_arch_sync_caches (seg->addr, seg->size);
490 }
491
492 /* Load a module from core memory. */
493 grub_dl_t
494 grub_dl_load_core (void *addr, grub_size_t size)
495 {
496 Elf_Ehdr *e;
497 grub_dl_t mod;
498
499 e = addr;
500 if (! grub_arch_dl_check_header (e, size))
501 {
502 grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF header");
503 return 0;
504 }
505
506 mod = (grub_dl_t) grub_malloc (sizeof (*mod));
507 if (! mod)
508 return 0;
509
510 mod->name = 0;
511 mod->ref_count = 1;
512 mod->dep = 0;
513 mod->segment = 0;
514 mod->init = 0;
515 mod->fini = 0;
516
517 if (grub_dl_resolve_name (mod, e)
518 || grub_dl_resolve_dependencies (mod, e)
519 || grub_dl_load_segments (mod, e)
520 || grub_dl_resolve_symbols (mod, e)
521 || grub_arch_dl_relocate_symbols (mod, e))
522 {
523 mod->fini = 0;
524 grub_dl_unload (mod);
525 return 0;
526 }
527
528 grub_dl_flush_cache (mod);
529
530 grub_dl_call_init (mod);
531
532 if (grub_dl_add (mod))
533 {
534 grub_dl_unload (mod);
535 return 0;
536 }
537
538 return mod;
539 }
540
541 /* Load a module from the file FILENAME. */
542 grub_dl_t
543 grub_dl_load_file (const char *filename)
544 {
545 grub_file_t file;
546 grub_ssize_t size;
547 void *core = 0;
548 grub_dl_t mod = 0;
549
550 file = grub_file_open (filename);
551 if (! file)
552 return 0;
553
554 size = grub_file_size (file);
555 core = grub_malloc (size);
556 if (! core)
557 goto failed;
558
559 if (grub_file_read (file, core, size) != (int) size)
560 goto failed;
561
562 mod = grub_dl_load_core (core, size);
563 mod->ref_count = 0;
564
565 failed:
566 grub_file_close (file);
567 grub_free (core);
568
569 return mod;
570 }
571
572 /* Load a module using a symbolic name. */
573 grub_dl_t
574 grub_dl_load (const char *name)
575 {
576 char *filename;
577 grub_dl_t mod;
578 char *grub_dl_dir = grub_env_get ("prefix");
579
580 mod = grub_dl_get (name);
581 if (mod)
582 return mod;
583
584 if (! grub_dl_dir)
585 grub_fatal ("module dir is not initialized yet");
586
587 filename = (char *) grub_malloc (grub_strlen (grub_dl_dir) + 1
588 + grub_strlen (name) + 4 + 1);
589 if (! filename)
590 return 0;
591
592 grub_sprintf (filename, "%s/%s.mod", grub_dl_dir, name);
593 mod = grub_dl_load_file (filename);
594 grub_free (filename);
595
596 if (! mod)
597 return 0;
598
599 if (grub_strcmp (mod->name, name) != 0)
600 grub_error (GRUB_ERR_BAD_MODULE, "mismatched names");
601
602 return mod;
603 }
604
605 /* Unload the module MOD. */
606 int
607 grub_dl_unload (grub_dl_t mod)
608 {
609 grub_dl_dep_t dep, depn;
610 grub_dl_segment_t seg, segn;
611
612 if (mod->ref_count > 0)
613 return 0;
614
615 if (mod->fini)
616 (mod->fini) ();
617
618 grub_dl_remove (mod);
619 grub_dl_unregister_symbols (mod);
620
621 for (dep = mod->dep; dep; dep = depn)
622 {
623 depn = dep->next;
624
625 if (! grub_dl_unref (dep->mod))
626 grub_dl_unload (dep->mod);
627
628 grub_free (dep);
629 }
630
631 for (seg = mod->segment; seg; seg = segn)
632 {
633 segn = seg->next;
634 grub_free (seg->addr);
635 grub_free (seg);
636 }
637
638 grub_free (mod->name);
639 grub_free (mod);
640 return 1;
641 }
642
643 /* Unload unneeded modules. */
644 void
645 grub_dl_unload_unneeded (void)
646 {
647 /* Because grub_dl_remove modifies the list of modules, this
648 implementation is tricky. */
649 grub_dl_list_t p = grub_dl_head;
650
651 while (p)
652 {
653 if (grub_dl_unload (p->mod))
654 {
655 p = grub_dl_head;
656 continue;
657 }
658
659 p = p->next;
660 }
661 }
662
663 /* Unload all modules. */
664 void
665 grub_dl_unload_all (void)
666 {
667 while (grub_dl_head)
668 {
669 grub_dl_list_t p;
670
671 grub_dl_unload_unneeded ();
672
673 /* Force to decrement the ref count. This will purge pre-loaded
674 modules and manually inserted modules. */
675 for (p = grub_dl_head; p; p = p->next)
676 p->mod->ref_count--;
677 }
678 }