1 /* dl.c - loadable module support */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002, 2003, 2004, 2005 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_CPU_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_CPU_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 /* Check if EHDR is a valid ELF header. */
243 grub_dl_check_header (void *ehdr
, grub_size_t size
)
247 /* Check the header size. */
248 if (size
< sizeof (Elf_Ehdr
))
249 return grub_error (GRUB_ERR_BAD_OS
, "ELF header smaller than expected");
251 /* Check the magic numbers. */
252 if (grub_arch_dl_check_header (ehdr
)
253 || e
->e_ident
[EI_MAG0
] != ELFMAG0
254 || e
->e_ident
[EI_MAG1
] != ELFMAG1
255 || e
->e_ident
[EI_MAG2
] != ELFMAG2
256 || e
->e_ident
[EI_MAG3
] != ELFMAG3
257 || e
->e_ident
[EI_VERSION
] != EV_CURRENT
258 || e
->e_version
!= EV_CURRENT
)
259 return grub_error (GRUB_ERR_BAD_OS
, "invalid arch independent ELF magic");
261 return GRUB_ERR_NONE
;
264 /* Load all segments from memory specified by E. */
266 grub_dl_load_segments (grub_dl_t mod
, const Elf_Ehdr
*e
)
271 for (i
= 0, s
= (Elf_Shdr
*)((char *) e
+ e
->e_shoff
);
273 i
++, s
= (Elf_Shdr
*)((char *) s
+ e
->e_shentsize
))
275 if (s
->sh_flags
& SHF_ALLOC
)
277 grub_dl_segment_t seg
;
279 seg
= (grub_dl_segment_t
) grub_malloc (sizeof (*seg
));
287 addr
= grub_memalign (s
->sh_addralign
, s
->sh_size
);
297 grub_memcpy (addr
, (char *) e
+ s
->sh_offset
, s
->sh_size
);
300 grub_memset (addr
, 0, s
->sh_size
);
309 seg
->size
= s
->sh_size
;
311 seg
->next
= mod
->segment
;
316 return GRUB_ERR_NONE
;
320 grub_dl_resolve_symbols (grub_dl_t mod
, Elf_Ehdr
*e
)
326 Elf_Word size
, entsize
;
328 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
330 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
331 if (s
->sh_type
== SHT_SYMTAB
)
335 return grub_error (GRUB_ERR_BAD_MODULE
, "no symbol table");
337 sym
= (Elf_Sym
*) ((char *) e
+ s
->sh_offset
);
339 entsize
= s
->sh_entsize
;
341 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shentsize
* s
->sh_link
);
342 str
= (char *) e
+ s
->sh_offset
;
346 i
++, sym
= (Elf_Sym
*) ((char *) sym
+ entsize
))
348 unsigned char type
= ELF_ST_TYPE (sym
->st_info
);
349 unsigned char bind
= ELF_ST_BIND (sym
->st_info
);
350 const char *name
= str
+ sym
->st_name
;
355 /* Resolve a global symbol. */
356 if (sym
->st_name
!= 0 && sym
->st_shndx
== 0)
358 sym
->st_value
= (Elf_Addr
) grub_dl_resolve_symbol (name
);
360 return grub_error (GRUB_ERR_BAD_MODULE
,
361 "the symbol `%s' not found", name
);
368 sym
->st_value
+= (Elf_Addr
) grub_dl_get_section_addr (mod
,
370 if (bind
!= STB_LOCAL
)
371 if (grub_dl_register_symbol (name
, (void *) sym
->st_value
, mod
))
376 sym
->st_value
+= (Elf_Addr
) grub_dl_get_section_addr (mod
,
378 if (bind
!= STB_LOCAL
)
379 if (grub_dl_register_symbol (name
, (void *) sym
->st_value
, mod
))
382 if (grub_strcmp (name
, "grub_mod_init") == 0)
383 mod
->init
= (void (*) (grub_dl_t
)) sym
->st_value
;
384 else if (grub_strcmp (name
, "grub_mod_fini") == 0)
385 mod
->fini
= (void (*) (void)) sym
->st_value
;
389 sym
->st_value
= (Elf_Addr
) grub_dl_get_section_addr (mod
,
398 return grub_error (GRUB_ERR_BAD_MODULE
,
399 "unknown symbol type `%d'", (int) type
);
403 return GRUB_ERR_NONE
;
407 grub_dl_call_init (grub_dl_t mod
)
414 grub_dl_resolve_name (grub_dl_t mod
, Elf_Ehdr
*e
)
420 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shstrndx
* e
->e_shentsize
);
421 str
= (char *) e
+ s
->sh_offset
;
423 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
425 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
426 if (grub_strcmp (str
+ s
->sh_name
, ".modname") == 0)
428 mod
->name
= grub_strdup ((char *) e
+ s
->sh_offset
);
435 return grub_error (GRUB_ERR_BAD_MODULE
, "no module name found");
437 return GRUB_ERR_NONE
;
441 grub_dl_resolve_dependencies (grub_dl_t mod
, Elf_Ehdr
*e
)
447 s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
+ e
->e_shstrndx
* e
->e_shentsize
);
448 str
= (char *) e
+ s
->sh_offset
;
450 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ e
->e_shoff
);
452 i
++, s
= (Elf_Shdr
*) ((char *) s
+ e
->e_shentsize
))
453 if (grub_strcmp (str
+ s
->sh_name
, ".moddeps") == 0)
455 const char *name
= (char *) e
+ s
->sh_offset
;
456 const char *max
= name
+ s
->sh_size
;
463 m
= grub_dl_load (name
);
469 dep
= (grub_dl_dep_t
) grub_malloc (sizeof (*dep
));
474 dep
->next
= mod
->dep
;
477 name
+= grub_strlen (name
) + 1;
481 return GRUB_ERR_NONE
;
485 grub_dl_ref (grub_dl_t mod
)
489 for (dep
= mod
->dep
; dep
; dep
= dep
->next
)
490 grub_dl_ref (dep
->mod
);
492 return ++mod
->ref_count
;
496 grub_dl_unref (grub_dl_t mod
)
500 for (dep
= mod
->dep
; dep
; dep
= dep
->next
)
501 grub_dl_unref (dep
->mod
);
503 return --mod
->ref_count
;
507 grub_dl_flush_cache (grub_dl_t mod
)
509 grub_dl_segment_t seg
;
511 for (seg
= mod
->segment
; seg
; seg
= seg
->next
) {
513 grub_dprintf ("modules", "flushing 0x%x bytes at %p\n", seg
->size
,
515 grub_arch_sync_caches (seg
->addr
, seg
->size
);
520 /* Load a module from core memory. */
522 grub_dl_load_core (void *addr
, grub_size_t size
)
527 grub_dprintf ("modules", "module at %p, size 0x%x\n", addr
, 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_fatal ("module dir is not initialized yet");
632 filename
= (char *) grub_malloc (grub_strlen (grub_dl_dir
) + 1
633 + grub_strlen (name
) + 4 + 1);
637 grub_sprintf (filename
, "%s/%s.mod", grub_dl_dir
, name
);
638 mod
= grub_dl_load_file (filename
);
639 grub_free (filename
);
644 if (grub_strcmp (mod
->name
, name
) != 0)
645 grub_error (GRUB_ERR_BAD_MODULE
, "mismatched names");
650 /* Unload the module MOD. */
652 grub_dl_unload (grub_dl_t mod
)
654 grub_dl_dep_t dep
, depn
;
655 grub_dl_segment_t seg
, segn
;
657 if (mod
->ref_count
> 0)
663 grub_dl_remove (mod
);
664 grub_dl_unregister_symbols (mod
);
666 for (dep
= mod
->dep
; dep
; dep
= depn
)
670 if (! grub_dl_unref (dep
->mod
))
671 grub_dl_unload (dep
->mod
);
676 for (seg
= mod
->segment
; seg
; seg
= segn
)
679 grub_free (seg
->addr
);
683 grub_free (mod
->name
);
688 /* Unload unneeded modules. */
690 grub_dl_unload_unneeded (void)
692 /* Because grub_dl_remove modifies the list of modules, this
693 implementation is tricky. */
694 grub_dl_list_t p
= grub_dl_head
;
698 if (grub_dl_unload (p
->mod
))
708 /* Unload all modules. */
710 grub_dl_unload_all (void)
716 grub_dl_unload_unneeded ();
718 /* Force to decrement the ref count. This will purge pre-loaded
719 modules and manually inserted modules. */
720 for (p
= grub_dl_head
; p
; p
= p
->next
)