1 /* dl.c - loadable module support */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002,2003,2004,2005,2007 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/>.
23 #include <grub/misc.h>
26 #include <grub/types.h>
27 #include <grub/symbol.h>
28 #include <grub/file.h>
30 #include <grub/cache.h>
32 #if GRUB_CPU_SIZEOF_VOID_P == 4
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
;
40 # define ELF_ST_BIND(val) ELF32_ST_BIND (val)
41 # define ELF_ST_TYPE(val) ELF32_ST_TYPE (val)
43 #elif GRUB_CPU_SIZEOF_VOID_P == 8
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
;
51 # define ELF_ST_BIND(val) ELF64_ST_BIND (val)
52 # define ELF_ST_TYPE(val) ELF64_ST_TYPE (val)
60 struct grub_dl_list
*next
;
63 typedef struct grub_dl_list
*grub_dl_list_t
;
65 static grub_dl_list_t grub_dl_head
;
68 grub_dl_add (grub_dl_t mod
)
72 if (grub_dl_get (mod
->name
))
73 return grub_error (GRUB_ERR_BAD_MODULE
,
74 "`%s' is already loaded", mod
->name
);
76 l
= (grub_dl_list_t
) grub_malloc (sizeof (*l
));
81 l
->next
= grub_dl_head
;
88 grub_dl_remove (grub_dl_t mod
)
92 for (p
= &grub_dl_head
, q
= *p
; q
; p
= &q
->next
, q
= *p
)
102 grub_dl_get (const char *name
)
106 for (l
= grub_dl_head
; l
; l
= l
->next
)
107 if (grub_strcmp (name
, l
->mod
->name
) == 0)
114 grub_dl_iterate (int (*hook
) (grub_dl_t mod
))
118 for (l
= grub_dl_head
; l
; l
= l
->next
)
127 struct grub_symbol
*next
;
130 grub_dl_t mod
; /* The module to which this symbol belongs. */
132 typedef struct grub_symbol
*grub_symbol_t
;
134 /* The size of the symbol table. */
135 #define GRUB_SYMTAB_SIZE 509
137 /* The symbol table (using an open-hash). */
138 static struct grub_symbol
*grub_symtab
[GRUB_SYMTAB_SIZE
];
140 /* Simple hash function. */
142 grub_symbol_hash (const char *s
)
147 key
= key
* 65599 + *s
++;
149 return (key
+ (key
>> 5)) % GRUB_SYMTAB_SIZE
;
152 /* Resolve the symbol name NAME and return the address.
153 Return NULL, if not found. */
155 grub_dl_resolve_symbol (const char *name
)
159 for (sym
= grub_symtab
[grub_symbol_hash (name
)]; sym
; sym
= sym
->next
)
160 if (grub_strcmp (sym
->name
, name
) == 0)
166 /* Register a symbol with the name NAME and the address ADDR. */
168 grub_dl_register_symbol (const char *name
, void *addr
, grub_dl_t mod
)
173 sym
= (grub_symbol_t
) grub_malloc (sizeof (*sym
));
179 sym
->name
= grub_strdup (name
);
192 k
= grub_symbol_hash (name
);
193 sym
->next
= grub_symtab
[k
];
194 grub_symtab
[k
] = sym
;
196 return GRUB_ERR_NONE
;
199 /* Unregister all the symbols defined in the module MOD. */
201 grub_dl_unregister_symbols (grub_dl_t mod
)
206 grub_fatal ("core symbols cannot be unregistered");
208 for (i
= 0; i
< GRUB_SYMTAB_SIZE
; i
++)
210 grub_symbol_t sym
, *p
, q
;
212 for (p
= &grub_symtab
[i
], sym
= *p
; sym
; sym
= q
)
218 grub_free ((void *) sym
->name
);
227 /* Return the address of a section whose index is N. */
229 grub_dl_get_section_addr (grub_dl_t mod
, unsigned n
)
231 grub_dl_segment_t seg
;
233 for (seg
= mod
->segment
; seg
; seg
= seg
->next
)
234 if (seg
->section
== n
)
240 /* Check if EHDR is a valid ELF header. */
242 grub_dl_check_header (void *ehdr
, grub_size_t size
)
246 /* Check the header size. */
247 if (size
< sizeof (Elf_Ehdr
))
248 return grub_error (GRUB_ERR_BAD_OS
, "ELF header smaller than expected");
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");
260 return GRUB_ERR_NONE
;
263 /* Load all segments from memory specified by E. */
265 grub_dl_load_segments (grub_dl_t mod
, const Elf_Ehdr
*e
)
270 for (i
= 0, s
= (Elf_Shdr
*)((char *) e
+ e
->e_shoff
);
272 i
++, s
= (Elf_Shdr
*)((char *) s
+ e
->e_shentsize
))
274 if (s
->sh_flags
& SHF_ALLOC
)
276 grub_dl_segment_t seg
;
278 seg
= (grub_dl_segment_t
) grub_malloc (sizeof (*seg
));
286 addr
= grub_memalign (s
->sh_addralign
, s
->sh_size
);
296 grub_memcpy (addr
, (char *) e
+ s
->sh_offset
, s
->sh_size
);
299 grub_memset (addr
, 0, s
->sh_size
);
308 seg
->size
= s
->sh_size
;
310 seg
->next
= mod
->segment
;
315 return GRUB_ERR_NONE
;
319 grub_dl_resolve_symbols (grub_dl_t mod
, Elf_Ehdr
*e
)
325 Elf_Word size
, entsize
;
327 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
329 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
330 if (s
->sh_type
== SHT_SYMTAB
)
334 return grub_error (GRUB_ERR_BAD_MODULE
, "no symbol table");
336 sym
= (Elf_Sym
*) ((char *) e
+ s
->sh_offset
);
338 entsize
= s
->sh_entsize
;
340 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shentsize
* s
->sh_link
);
341 str
= (char *) e
+ s
->sh_offset
;
345 i
++, sym
= (Elf_Sym
*) ((char *) sym
+ entsize
))
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
;
354 /* Resolve a global symbol. */
355 if (sym
->st_name
!= 0 && sym
->st_shndx
== 0)
357 sym
->st_value
= (Elf_Addr
) grub_dl_resolve_symbol (name
);
359 return grub_error (GRUB_ERR_BAD_MODULE
,
360 "the symbol `%s' not found", name
);
367 sym
->st_value
+= (Elf_Addr
) grub_dl_get_section_addr (mod
,
369 if (bind
!= STB_LOCAL
)
370 if (grub_dl_register_symbol (name
, (void *) sym
->st_value
, mod
))
375 sym
->st_value
+= (Elf_Addr
) grub_dl_get_section_addr (mod
,
377 if (bind
!= STB_LOCAL
)
378 if (grub_dl_register_symbol (name
, (void *) sym
->st_value
, mod
))
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
;
388 sym
->st_value
= (Elf_Addr
) grub_dl_get_section_addr (mod
,
397 return grub_error (GRUB_ERR_BAD_MODULE
,
398 "unknown symbol type `%d'", (int) type
);
402 return GRUB_ERR_NONE
;
406 grub_dl_call_init (grub_dl_t mod
)
413 grub_dl_resolve_name (grub_dl_t mod
, Elf_Ehdr
*e
)
419 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shstrndx
* e
->e_shentsize
);
420 str
= (char *) e
+ s
->sh_offset
;
422 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
424 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
425 if (grub_strcmp (str
+ s
->sh_name
, ".modname") == 0)
427 mod
->name
= grub_strdup ((char *) e
+ s
->sh_offset
);
434 return grub_error (GRUB_ERR_BAD_MODULE
, "no module name found");
436 return GRUB_ERR_NONE
;
440 grub_dl_resolve_dependencies (grub_dl_t mod
, Elf_Ehdr
*e
)
446 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shstrndx
* e
->e_shentsize
);
447 str
= (char *) e
+ s
->sh_offset
;
449 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
451 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
452 if (grub_strcmp (str
+ s
->sh_name
, ".moddeps") == 0)
454 const char *name
= (char *) e
+ s
->sh_offset
;
455 const char *max
= name
+ s
->sh_size
;
457 while ((name
< max
) && (*name
))
462 m
= grub_dl_load (name
);
468 dep
= (grub_dl_dep_t
) grub_malloc (sizeof (*dep
));
473 dep
->next
= mod
->dep
;
476 name
+= grub_strlen (name
) + 1;
480 return GRUB_ERR_NONE
;
484 grub_dl_ref (grub_dl_t mod
)
488 for (dep
= mod
->dep
; dep
; dep
= dep
->next
)
489 grub_dl_ref (dep
->mod
);
491 return ++mod
->ref_count
;
495 grub_dl_unref (grub_dl_t mod
)
499 for (dep
= mod
->dep
; dep
; dep
= dep
->next
)
500 grub_dl_unref (dep
->mod
);
502 return --mod
->ref_count
;
506 grub_dl_flush_cache (grub_dl_t mod
)
508 grub_dl_segment_t seg
;
510 for (seg
= mod
->segment
; seg
; seg
= seg
->next
) {
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
);
519 /* Load a module from core memory. */
521 grub_dl_load_core (void *addr
, grub_size_t size
)
526 grub_dprintf ("modules", "module at %p, size 0x%lx\n", addr
,
527 (unsigned long) size
);
529 if (grub_dl_check_header (e
, size
))
532 if (e
->e_type
!= ET_REL
)
534 grub_error (GRUB_ERR_BAD_MODULE
, "invalid ELF file type");
538 /* Make sure that every section is within the core. */
539 if (size
< e
->e_shoff
+ e
->e_shentsize
* e
->e_shnum
)
541 grub_error (GRUB_ERR_BAD_OS
, "ELF sections outside core");
545 mod
= (grub_dl_t
) grub_malloc (sizeof (*mod
));
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
))
564 grub_dl_unload (mod
);
568 grub_dl_flush_cache (mod
);
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
);
574 if (grub_dl_add (mod
))
576 grub_dl_unload (mod
);
583 /* Load a module from the file FILENAME. */
585 grub_dl_load_file (const char *filename
)
592 file
= grub_file_open (filename
);
596 size
= grub_file_size (file
);
597 core
= grub_malloc (size
);
601 if (grub_file_read (file
, core
, size
) != (int) size
)
604 mod
= grub_dl_load_core (core
, size
);
611 grub_file_close (file
);
617 /* Load a module using a symbolic name. */
619 grub_dl_load (const char *name
)
623 char *grub_dl_dir
= grub_env_get ("prefix");
625 mod
= grub_dl_get (name
);
630 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "\"prefix\" is not set");
634 filename
= (char *) grub_malloc (grub_strlen (grub_dl_dir
) + 1
635 + grub_strlen (name
) + 4 + 1);
639 grub_sprintf (filename
, "%s/%s.mod", grub_dl_dir
, name
);
640 mod
= grub_dl_load_file (filename
);
641 grub_free (filename
);
646 if (grub_strcmp (mod
->name
, name
) != 0)
647 grub_error (GRUB_ERR_BAD_MODULE
, "mismatched names");
652 /* Unload the module MOD. */
654 grub_dl_unload (grub_dl_t mod
)
656 grub_dl_dep_t dep
, depn
;
657 grub_dl_segment_t seg
, segn
;
659 if (mod
->ref_count
> 0)
665 grub_dl_remove (mod
);
666 grub_dl_unregister_symbols (mod
);
668 for (dep
= mod
->dep
; dep
; dep
= depn
)
672 if (! grub_dl_unref (dep
->mod
))
673 grub_dl_unload (dep
->mod
);
678 for (seg
= mod
->segment
; seg
; seg
= segn
)
681 grub_free (seg
->addr
);
685 grub_free (mod
->name
);
690 /* Unload unneeded modules. */
692 grub_dl_unload_unneeded (void)
694 /* Because grub_dl_remove modifies the list of modules, this
695 implementation is tricky. */
696 grub_dl_list_t p
= grub_dl_head
;
700 if (grub_dl_unload (p
->mod
))
710 /* Unload all modules. */
712 grub_dl_unload_all (void)
718 grub_dl_unload_unneeded ();
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
)