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