1 /* dl.c - loadable module support */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002, 2003, 2004 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 2 of the License, or
9 * (at your option) any later version.
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.
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.
24 #include <grub/misc.h>
27 #include <grub/types.h>
28 #include <grub/symbol.h>
29 #include <grub/file.h>
31 #include <grub/cache.h>
33 #if GRUB_HOST_SIZEOF_VOID_P == 4
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
;
41 # define ELF_ST_BIND(val) ELF32_ST_BIND (val)
42 # define ELF_ST_TYPE(val) ELF32_ST_TYPE (val)
44 #elif GRUB_HOST_SIZEOF_VOID_P == 8
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
;
52 # define ELF_ST_BIND(val) ELF64_ST_BIND (val)
53 # define ELF_ST_TYPE(val) ELF64_ST_TYPE (val)
61 struct grub_dl_list
*next
;
64 typedef struct grub_dl_list
*grub_dl_list_t
;
66 static grub_dl_list_t grub_dl_head
;
69 grub_dl_add (grub_dl_t mod
)
73 if (grub_dl_get (mod
->name
))
74 return grub_error (GRUB_ERR_BAD_MODULE
,
75 "`%s' is already loaded", mod
->name
);
77 l
= (grub_dl_list_t
) grub_malloc (sizeof (*l
));
82 l
->next
= grub_dl_head
;
89 grub_dl_remove (grub_dl_t mod
)
93 for (p
= &grub_dl_head
, q
= *p
; q
; p
= &q
->next
, q
= *p
)
103 grub_dl_get (const char *name
)
107 for (l
= grub_dl_head
; l
; l
= l
->next
)
108 if (grub_strcmp (name
, l
->mod
->name
) == 0)
115 grub_dl_iterate (int (*hook
) (grub_dl_t mod
))
119 for (l
= grub_dl_head
; l
; l
= l
->next
)
128 struct grub_symbol
*next
;
131 grub_dl_t mod
; /* The module to which this symbol belongs. */
133 typedef struct grub_symbol
*grub_symbol_t
;
135 /* The size of the symbol table. */
136 #define GRUB_SYMTAB_SIZE 509
138 /* The symbol table (using an open-hash). */
139 static struct grub_symbol
*grub_symtab
[GRUB_SYMTAB_SIZE
];
141 /* Simple hash function. */
143 grub_symbol_hash (const char *s
)
148 key
= key
* 65599 + *s
++;
150 return (key
+ (key
>> 5)) % GRUB_SYMTAB_SIZE
;
153 /* Resolve the symbol name NAME and return the address.
154 Return NULL, if not found. */
156 grub_dl_resolve_symbol (const char *name
)
160 for (sym
= grub_symtab
[grub_symbol_hash (name
)]; sym
; sym
= sym
->next
)
161 if (grub_strcmp (sym
->name
, name
) == 0)
167 /* Register a symbol with the name NAME and the address ADDR. */
169 grub_dl_register_symbol (const char *name
, void *addr
, grub_dl_t mod
)
174 sym
= (grub_symbol_t
) grub_malloc (sizeof (*sym
));
180 sym
->name
= grub_strdup (name
);
193 k
= grub_symbol_hash (name
);
194 sym
->next
= grub_symtab
[k
];
195 grub_symtab
[k
] = sym
;
197 return GRUB_ERR_NONE
;
200 /* Unregister all the symbols defined in the module MOD. */
202 grub_dl_unregister_symbols (grub_dl_t mod
)
207 grub_fatal ("core symbols cannot be unregistered");
209 for (i
= 0; i
< GRUB_SYMTAB_SIZE
; i
++)
211 grub_symbol_t sym
, *p
, q
;
213 for (p
= &grub_symtab
[i
], sym
= *p
; sym
; sym
= q
)
219 grub_free ((void *) sym
->name
);
228 /* Return the address of a section whose index is N. */
230 grub_dl_get_section_addr (grub_dl_t mod
, unsigned n
)
232 grub_dl_segment_t seg
;
234 for (seg
= mod
->segment
; seg
; seg
= seg
->next
)
235 if (seg
->section
== n
)
241 /* Load all segments from memory specified by E. */
243 grub_dl_load_segments (grub_dl_t mod
, const Elf_Ehdr
*e
)
248 for (i
= 0, s
= (Elf_Shdr
*)((char *) e
+ e
->e_shoff
);
250 i
++, s
= (Elf_Shdr
*)((char *) s
+ e
->e_shentsize
))
252 if (s
->sh_flags
& SHF_ALLOC
)
254 grub_dl_segment_t seg
;
256 seg
= (grub_dl_segment_t
) grub_malloc (sizeof (*seg
));
264 addr
= grub_memalign (s
->sh_addralign
, s
->sh_size
);
274 grub_memcpy (addr
, (char *) e
+ s
->sh_offset
, s
->sh_size
);
277 grub_memset (addr
, 0, s
->sh_size
);
286 seg
->size
= s
->sh_size
;
288 seg
->next
= mod
->segment
;
293 return GRUB_ERR_NONE
;
297 grub_dl_resolve_symbols (grub_dl_t mod
, Elf_Ehdr
*e
)
303 Elf_Word size
, entsize
;
305 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
307 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
308 if (s
->sh_type
== SHT_SYMTAB
)
312 return grub_error (GRUB_ERR_BAD_MODULE
, "no symbol table");
314 sym
= (Elf_Sym
*) ((char *) e
+ s
->sh_offset
);
316 entsize
= s
->sh_entsize
;
318 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shentsize
* s
->sh_link
);
319 str
= (char *) e
+ s
->sh_offset
;
323 i
++, sym
= (Elf_Sym
*) ((char *) sym
+ entsize
))
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
;
332 /* Resolve a global symbol. */
333 if (sym
->st_name
!= 0 && sym
->st_shndx
== 0)
335 sym
->st_value
= (Elf_Addr
) grub_dl_resolve_symbol (name
);
337 return grub_error (GRUB_ERR_BAD_MODULE
,
338 "the symbol `%s' not found", name
);
345 sym
->st_value
+= (Elf_Addr
) grub_dl_get_section_addr (mod
,
347 if (bind
!= STB_LOCAL
)
348 if (grub_dl_register_symbol (name
, (void *) sym
->st_value
, mod
))
353 sym
->st_value
+= (Elf_Addr
) grub_dl_get_section_addr (mod
,
355 if (bind
!= STB_LOCAL
)
356 if (grub_dl_register_symbol (name
, (void *) sym
->st_value
, mod
))
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
;
366 sym
->st_value
= (Elf_Addr
) grub_dl_get_section_addr (mod
,
375 return grub_error (GRUB_ERR_BAD_MODULE
,
376 "unknown symbol type `%d'", (int) type
);
380 return GRUB_ERR_NONE
;
384 grub_dl_call_init (grub_dl_t mod
)
391 grub_dl_resolve_name (grub_dl_t mod
, Elf_Ehdr
*e
)
397 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shstrndx
* e
->e_shentsize
);
398 str
= (char *) e
+ s
->sh_offset
;
400 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
402 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
403 if (grub_strcmp (str
+ s
->sh_name
, ".modname") == 0)
405 mod
->name
= grub_strdup ((char *) e
+ s
->sh_offset
);
412 return grub_error (GRUB_ERR_BAD_MODULE
, "no module name found");
414 return GRUB_ERR_NONE
;
418 grub_dl_resolve_dependencies (grub_dl_t mod
, Elf_Ehdr
*e
)
424 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shstrndx
* e
->e_shentsize
);
425 str
= (char *) e
+ s
->sh_offset
;
427 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
429 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
430 if (grub_strcmp (str
+ s
->sh_name
, ".moddeps") == 0)
432 const char *name
= (char *) e
+ s
->sh_offset
;
433 const char *max
= name
+ s
->sh_size
;
440 m
= grub_dl_load (name
);
446 dep
= (grub_dl_dep_t
) grub_malloc (sizeof (*dep
));
451 dep
->next
= mod
->dep
;
454 name
+= grub_strlen (name
) + 1;
458 return GRUB_ERR_NONE
;
462 grub_dl_ref (grub_dl_t mod
)
466 for (dep
= mod
->dep
; dep
; dep
= dep
->next
)
467 grub_dl_ref (dep
->mod
);
469 return ++mod
->ref_count
;
473 grub_dl_unref (grub_dl_t mod
)
477 for (dep
= mod
->dep
; dep
; dep
= dep
->next
)
478 grub_dl_unref (dep
->mod
);
480 return --mod
->ref_count
;
484 grub_dl_flush_cache (grub_dl_t mod
)
486 grub_dl_segment_t seg
;
488 for (seg
= mod
->segment
; seg
; seg
= seg
->next
)
489 grub_arch_sync_caches (seg
->addr
, seg
->size
);
492 /* Load a module from core memory. */
494 grub_dl_load_core (void *addr
, grub_size_t size
)
500 if (! grub_arch_dl_check_header (e
, size
))
502 grub_error (GRUB_ERR_BAD_MODULE
, "invalid ELF header");
506 mod
= (grub_dl_t
) grub_malloc (sizeof (*mod
));
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
))
524 grub_dl_unload (mod
);
528 grub_dl_flush_cache (mod
);
530 grub_dl_call_init (mod
);
532 if (grub_dl_add (mod
))
534 grub_dl_unload (mod
);
541 /* Load a module from the file FILENAME. */
543 grub_dl_load_file (const char *filename
)
550 file
= grub_file_open (filename
);
554 size
= grub_file_size (file
);
555 core
= grub_malloc (size
);
559 if (grub_file_read (file
, core
, size
) != (int) size
)
562 mod
= grub_dl_load_core (core
, size
);
566 grub_file_close (file
);
572 /* Load a module using a symbolic name. */
574 grub_dl_load (const char *name
)
578 char *grub_dl_dir
= grub_env_get ("prefix");
580 mod
= grub_dl_get (name
);
585 grub_fatal ("module dir is not initialized yet");
587 filename
= (char *) grub_malloc (grub_strlen (grub_dl_dir
) + 1
588 + grub_strlen (name
) + 4 + 1);
592 grub_sprintf (filename
, "%s/%s.mod", grub_dl_dir
, name
);
593 mod
= grub_dl_load_file (filename
);
594 grub_free (filename
);
599 if (grub_strcmp (mod
->name
, name
) != 0)
600 grub_error (GRUB_ERR_BAD_MODULE
, "mismatched names");
605 /* Unload the module MOD. */
607 grub_dl_unload (grub_dl_t mod
)
609 grub_dl_dep_t dep
, depn
;
610 grub_dl_segment_t seg
, segn
;
612 if (mod
->ref_count
> 0)
618 grub_dl_remove (mod
);
619 grub_dl_unregister_symbols (mod
);
621 for (dep
= mod
->dep
; dep
; dep
= depn
)
625 if (! grub_dl_unref (dep
->mod
))
626 grub_dl_unload (dep
->mod
);
631 for (seg
= mod
->segment
; seg
; seg
= segn
)
634 grub_free (seg
->addr
);
638 grub_free (mod
->name
);
643 /* Unload unneeded modules. */
645 grub_dl_unload_unneeded (void)
647 /* Because grub_dl_remove modifies the list of modules, this
648 implementation is tricky. */
649 grub_dl_list_t p
= grub_dl_head
;
653 if (grub_dl_unload (p
->mod
))
663 /* Unload all modules. */
665 grub_dl_unload_all (void)
671 grub_dl_unload_unneeded ();
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
)