2N/A/* dl.c - loadable module support */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
2N/A * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A/* Force native word size */
2N/A#define GRUB_TARGET_WORDSIZE (8 * GRUB_CPU_SIZEOF_VOID_P)
2N/A
2N/A#include <config.h>
2N/A#include <grub/elf.h>
2N/A#include <grub/dl.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/err.h>
2N/A#include <grub/types.h>
2N/A#include <grub/symbol.h>
2N/A#include <grub/file.h>
2N/A#include <grub/env.h>
2N/A#include <grub/cache.h>
2N/A
2N/A/* Platforms where modules are in a readonly area of memory. */
2N/A#if defined(GRUB_MACHINE_QEMU)
2N/A#define GRUB_MODULES_MACHINE_READONLY
2N/A#endif
2N/A
2N/A#ifdef GRUB_MACHINE_EMU
2N/A#include <sys/mman.h>
2N/A#endif
2N/A
2N/A
2N/A
2N/Agrub_dl_t grub_dl_head = 0;
2N/A
2N/Astatic grub_err_t
2N/Agrub_dl_add (grub_dl_t mod)
2N/A{
2N/A if (grub_dl_get (mod->name))
2N/A return grub_error (GRUB_ERR_BAD_MODULE,
2N/A "`%s' is already loaded", mod->name);
2N/A
2N/A mod->next = grub_dl_head;
2N/A grub_dl_head = mod;
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic void
2N/Agrub_dl_remove (grub_dl_t mod)
2N/A{
2N/A grub_dl_t *p, q;
2N/A
2N/A for (p = &grub_dl_head, q = *p; q; p = &q->next, q = *p)
2N/A if (q == mod)
2N/A {
2N/A *p = q->next;
2N/A return;
2N/A }
2N/A}
2N/A
2N/Agrub_dl_t
2N/Agrub_dl_get (const char *name)
2N/A{
2N/A grub_dl_t l;
2N/A
2N/A for (l = grub_dl_head; l; l = l->next)
2N/A if (grub_strcmp (name, l->name) == 0)
2N/A return l;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A
2N/Astruct grub_symbol
2N/A{
2N/A struct grub_symbol *next;
2N/A const char *name;
2N/A void *addr;
2N/A int isfunc;
2N/A grub_dl_t mod; /* The module to which this symbol belongs. */
2N/A};
2N/Atypedef struct grub_symbol *grub_symbol_t;
2N/A
2N/A/* The size of the symbol table. */
2N/A#define GRUB_SYMTAB_SIZE 509
2N/A
2N/A/* The symbol table (using an open-hash). */
2N/Astatic struct grub_symbol *grub_symtab[GRUB_SYMTAB_SIZE];
2N/A
2N/A/* Simple hash function. */
2N/Astatic unsigned
2N/Agrub_symbol_hash (const char *s)
2N/A{
2N/A unsigned key = 0;
2N/A
2N/A while (*s)
2N/A key = key * 65599 + *s++;
2N/A
2N/A return (key + (key >> 5)) % GRUB_SYMTAB_SIZE;
2N/A}
2N/A
2N/A/* Resolve the symbol name NAME and return the address.
2N/A Return NULL, if not found. */
2N/Astatic grub_symbol_t
2N/Agrub_dl_resolve_symbol (const char *name)
2N/A{
2N/A grub_symbol_t sym;
2N/A
2N/A for (sym = grub_symtab[grub_symbol_hash (name)]; sym; sym = sym->next)
2N/A if (grub_strcmp (sym->name, name) == 0)
2N/A return sym;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/* Register a symbol with the name NAME and the address ADDR. */
2N/Agrub_err_t
2N/Agrub_dl_register_symbol (const char *name, void *addr, int isfunc,
2N/A grub_dl_t mod)
2N/A{
2N/A grub_symbol_t sym;
2N/A unsigned k;
2N/A
2N/A sym = (grub_symbol_t) grub_malloc (sizeof (*sym));
2N/A if (! sym)
2N/A return grub_errno;
2N/A
2N/A if (mod)
2N/A {
2N/A sym->name = grub_strdup (name);
2N/A if (! sym->name)
2N/A {
2N/A grub_free (sym);
2N/A return grub_errno;
2N/A }
2N/A }
2N/A else
2N/A sym->name = name;
2N/A
2N/A sym->addr = addr;
2N/A sym->mod = mod;
2N/A sym->isfunc = isfunc;
2N/A
2N/A k = grub_symbol_hash (name);
2N/A sym->next = grub_symtab[k];
2N/A grub_symtab[k] = sym;
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/* Unregister all the symbols defined in the module MOD. */
2N/Astatic void
2N/Agrub_dl_unregister_symbols (grub_dl_t mod)
2N/A{
2N/A unsigned i;
2N/A
2N/A if (! mod)
2N/A grub_fatal ("core symbols cannot be unregistered");
2N/A
2N/A for (i = 0; i < GRUB_SYMTAB_SIZE; i++)
2N/A {
2N/A grub_symbol_t sym, *p, q;
2N/A
2N/A for (p = &grub_symtab[i], sym = *p; sym; sym = q)
2N/A {
2N/A q = sym->next;
2N/A if (sym->mod == mod)
2N/A {
2N/A *p = q;
2N/A grub_free ((void *) sym->name);
2N/A grub_free (sym);
2N/A }
2N/A else
2N/A p = &sym->next;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/* Return the address of a section whose index is N. */
2N/Astatic void *
2N/Agrub_dl_get_section_addr (grub_dl_t mod, unsigned n)
2N/A{
2N/A grub_dl_segment_t seg;
2N/A
2N/A for (seg = mod->segment; seg; seg = seg->next)
2N/A if (seg->section == n)
2N/A return seg->addr;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/* Check if EHDR is a valid ELF header. */
2N/Astatic grub_err_t
2N/Agrub_dl_check_header (void *ehdr, grub_size_t size)
2N/A{
2N/A Elf_Ehdr *e = ehdr;
2N/A
2N/A /* Check the header size. */
2N/A if (size < sizeof (Elf_Ehdr))
2N/A return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected");
2N/A
2N/A /* Check the magic numbers. */
2N/A if (grub_arch_dl_check_header (ehdr)
2N/A || e->e_ident[EI_MAG0] != ELFMAG0
2N/A || e->e_ident[EI_MAG1] != ELFMAG1
2N/A || e->e_ident[EI_MAG2] != ELFMAG2
2N/A || e->e_ident[EI_MAG3] != ELFMAG3
2N/A || e->e_ident[EI_VERSION] != EV_CURRENT
2N/A || e->e_version != EV_CURRENT)
2N/A return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/* Load all segments from memory specified by E. */
2N/Astatic grub_err_t
2N/Agrub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
2N/A{
2N/A unsigned i;
2N/A Elf_Shdr *s;
2N/A grub_size_t tsize = 0, talign = 1;
2N/A#if defined (__ia64__) || defined (__powerpc__)
2N/A grub_size_t tramp;
2N/A grub_size_t got;
2N/A#endif
2N/A char *ptr;
2N/A
2N/A for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
2N/A i < e->e_shnum;
2N/A i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
2N/A {
2N/A tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size;
2N/A if (talign < s->sh_addralign)
2N/A talign = s->sh_addralign;
2N/A }
2N/A
2N/A#if defined (__ia64__) || defined (__powerpc__)
2N/A grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
2N/A tramp *= GRUB_ARCH_DL_TRAMP_SIZE;
2N/A got *= sizeof (grub_uint64_t);
2N/A tsize += ALIGN_UP (tramp, GRUB_ARCH_DL_TRAMP_ALIGN);
2N/A if (talign < GRUB_ARCH_DL_TRAMP_ALIGN)
2N/A talign = GRUB_ARCH_DL_TRAMP_ALIGN;
2N/A tsize += ALIGN_UP (got, GRUB_ARCH_DL_GOT_ALIGN);
2N/A if (talign < GRUB_ARCH_DL_GOT_ALIGN)
2N/A talign = GRUB_ARCH_DL_GOT_ALIGN;
2N/A#endif
2N/A
2N/A#ifdef GRUB_MACHINE_EMU
2N/A if (talign < 8192 * 16)
2N/A talign = 8192 * 16;
2N/A tsize = ALIGN_UP (tsize, 8192 * 16);
2N/A#endif
2N/A
2N/A mod->base = grub_memalign (talign, tsize);
2N/A if (!mod->base)
2N/A return grub_errno;
2N/A mod->sz = tsize;
2N/A ptr = mod->base;
2N/A
2N/A#ifdef GRUB_MACHINE_EMU
2N/A mprotect (mod->base, tsize, PROT_READ | PROT_WRITE | PROT_EXEC);
2N/A#endif
2N/A
2N/A for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
2N/A i < e->e_shnum;
2N/A i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
2N/A {
2N/A if (s->sh_flags & SHF_ALLOC)
2N/A {
2N/A grub_dl_segment_t seg;
2N/A
2N/A seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
2N/A if (! seg)
2N/A return grub_errno;
2N/A
2N/A if (s->sh_size)
2N/A {
2N/A void *addr;
2N/A
2N/A ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign);
2N/A addr = ptr;
2N/A ptr += s->sh_size;
2N/A
2N/A switch (s->sh_type)
2N/A {
2N/A case SHT_PROGBITS:
2N/A grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
2N/A break;
2N/A case SHT_NOBITS:
2N/A grub_memset (addr, 0, s->sh_size);
2N/A break;
2N/A }
2N/A
2N/A seg->addr = addr;
2N/A }
2N/A else
2N/A seg->addr = 0;
2N/A
2N/A seg->size = s->sh_size;
2N/A seg->section = i;
2N/A seg->next = mod->segment;
2N/A mod->segment = seg;
2N/A }
2N/A }
2N/A#if defined (__ia64__) || defined (__powerpc__)
2N/A ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
2N/A mod->tramp = ptr;
2N/A ptr += tramp;
2N/A ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN);
2N/A mod->got = ptr;
2N/A ptr += got;
2N/A#endif
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
2N/A{
2N/A unsigned i;
2N/A Elf_Shdr *s;
2N/A Elf_Sym *sym;
2N/A const char *str;
2N/A Elf_Word size, entsize;
2N/A
2N/A for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
2N/A i < e->e_shnum;
2N/A i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
2N/A if (s->sh_type == SHT_SYMTAB)
2N/A break;
2N/A
2N/A if (i == e->e_shnum)
2N/A return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
2N/A
2N/A#ifdef GRUB_MODULES_MACHINE_READONLY
2N/A mod->symtab = grub_malloc (s->sh_size);
2N/A memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size);
2N/A#else
2N/A mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
2N/A#endif
2N/A sym = mod->symtab;
2N/A size = s->sh_size;
2N/A entsize = s->sh_entsize;
2N/A
2N/A s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
2N/A str = (char *) e + s->sh_offset;
2N/A
2N/A for (i = 0;
2N/A i < size / entsize;
2N/A i++, sym = (Elf_Sym *) ((char *) sym + entsize))
2N/A {
2N/A unsigned char type = ELF_ST_TYPE (sym->st_info);
2N/A unsigned char bind = ELF_ST_BIND (sym->st_info);
2N/A const char *name = str + sym->st_name;
2N/A
2N/A switch (type)
2N/A {
2N/A case STT_NOTYPE:
2N/A case STT_OBJECT:
2N/A /* Resolve a global symbol. */
2N/A if (sym->st_name != 0 && sym->st_shndx == 0)
2N/A {
2N/A grub_symbol_t nsym = grub_dl_resolve_symbol (name);
2N/A if (! nsym)
2N/A return grub_error (GRUB_ERR_BAD_MODULE,
2N/A "symbol not found: `%s'", name);
2N/A sym->st_value = (Elf_Addr) nsym->addr;
2N/A if (nsym->isfunc)
2N/A sym->st_info = ELF_ST_INFO (bind, STT_FUNC);
2N/A }
2N/A else
2N/A {
2N/A sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
2N/A sym->st_shndx);
2N/A if (bind != STB_LOCAL)
2N/A if (grub_dl_register_symbol (name, (void *) sym->st_value, 0, mod))
2N/A return grub_errno;
2N/A }
2N/A break;
2N/A
2N/A case STT_FUNC:
2N/A sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
2N/A sym->st_shndx);
2N/A#ifdef __ia64__
2N/A {
2N/A /* FIXME: free descriptor once it's not used anymore. */
2N/A char **desc;
2N/A desc = grub_malloc (2 * sizeof (char *));
2N/A if (!desc)
2N/A return grub_errno;
2N/A desc[0] = (void *) sym->st_value;
2N/A desc[1] = mod->base;
2N/A sym->st_value = (grub_addr_t) desc;
2N/A }
2N/A#endif
2N/A if (bind != STB_LOCAL)
2N/A if (grub_dl_register_symbol (name, (void *) sym->st_value, 1, mod))
2N/A return grub_errno;
2N/A if (grub_strcmp (name, "grub_mod_init") == 0)
2N/A mod->init = (void (*) (grub_dl_t)) sym->st_value;
2N/A else if (grub_strcmp (name, "grub_mod_fini") == 0)
2N/A mod->fini = (void (*) (void)) sym->st_value;
2N/A break;
2N/A
2N/A case STT_SECTION:
2N/A sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
2N/A sym->st_shndx);
2N/A break;
2N/A
2N/A case STT_FILE:
2N/A sym->st_value = 0;
2N/A break;
2N/A
2N/A default:
2N/A return grub_error (GRUB_ERR_BAD_MODULE,
2N/A "unknown symbol type `%d'", (int) type);
2N/A }
2N/A }
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic void
2N/Agrub_dl_call_init (grub_dl_t mod)
2N/A{
2N/A if (mod->init)
2N/A (mod->init) (mod);
2N/A}
2N/A
2N/A/* Me, Vladimir Serbinenko, hereby I add this module check as per new
2N/A GNU module policy. Note that this license check is informative only.
2N/A Modules have to be licensed under GPLv3 or GPLv3+ (optionally
2N/A multi-licensed under other licences as well) independently of the
2N/A presence of this check and solely by linking (module loading in GRUB
2N/A constitutes linking) and GRUB core being licensed under GPLv3+.
2N/A Be sure to understand your license obligations.
2N/A*/
2N/Astatic grub_err_t
2N/Agrub_dl_check_license (char *modname, Elf_Ehdr *e)
2N/A{
2N/A Elf_Shdr *s;
2N/A const char *str;
2N/A unsigned i;
2N/A char *licensep = 0;
2N/A
2N/A s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
2N/A str = (char *) e + s->sh_offset;
2N/A
2N/A for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
2N/A i < e->e_shnum;
2N/A i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
2N/A if (grub_strcmp (str + s->sh_name, ".module_license") == 0)
2N/A {
2N/A licensep = (char *) e + s->sh_offset;
2N/A if (grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3") == 0
2N/A || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3+") == 0
2N/A || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0)
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A return grub_error (GRUB_ERR_BAD_MODULE, "incompatible license (%s) for module %s", licensep ? : "(none present)", modname);
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
2N/A{
2N/A Elf_Shdr *s;
2N/A const char *str;
2N/A unsigned i;
2N/A
2N/A s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
2N/A str = (char *) e + s->sh_offset;
2N/A
2N/A for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
2N/A i < e->e_shnum;
2N/A i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
2N/A if (grub_strcmp (str + s->sh_name, ".modname") == 0)
2N/A {
2N/A mod->name = grub_strdup ((char *) e + s->sh_offset);
2N/A if (! mod->name)
2N/A return grub_errno;
2N/A break;
2N/A }
2N/A
2N/A if (i == e->e_shnum)
2N/A return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
2N/A{
2N/A Elf_Shdr *s;
2N/A const char *str;
2N/A unsigned i;
2N/A
2N/A s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
2N/A str = (char *) e + s->sh_offset;
2N/A
2N/A for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
2N/A i < e->e_shnum;
2N/A i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
2N/A if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
2N/A {
2N/A const char *name = (char *) e + s->sh_offset;
2N/A const char *max = name + s->sh_size;
2N/A
2N/A while ((name < max) && (*name))
2N/A {
2N/A grub_dl_t m;
2N/A grub_dl_dep_t dep;
2N/A
2N/A m = grub_dl_load (name);
2N/A if (! m)
2N/A return grub_errno;
2N/A
2N/A grub_dl_ref (m);
2N/A
2N/A dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
2N/A if (! dep)
2N/A return grub_errno;
2N/A
2N/A dep->mod = m;
2N/A dep->next = mod->dep;
2N/A mod->dep = dep;
2N/A
2N/A name += grub_strlen (name) + 1;
2N/A }
2N/A }
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Aint
2N/Agrub_dl_ref (grub_dl_t mod)
2N/A{
2N/A grub_dl_dep_t dep;
2N/A
2N/A if (!mod)
2N/A return 0;
2N/A
2N/A for (dep = mod->dep; dep; dep = dep->next)
2N/A grub_dl_ref (dep->mod);
2N/A
2N/A return ++mod->ref_count;
2N/A}
2N/A
2N/Aint
2N/Agrub_dl_unref (grub_dl_t mod)
2N/A{
2N/A grub_dl_dep_t dep;
2N/A
2N/A if (!mod)
2N/A return 0;
2N/A
2N/A for (dep = mod->dep; dep; dep = dep->next)
2N/A grub_dl_unref (dep->mod);
2N/A
2N/A return --mod->ref_count;
2N/A}
2N/A
2N/Astatic void
2N/Agrub_dl_flush_cache (grub_dl_t mod)
2N/A{
2N/A grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n",
2N/A (unsigned long) mod->sz, mod->base);
2N/A grub_arch_sync_caches (mod->base, mod->sz);
2N/A}
2N/A
2N/A/* Load a module from core memory. */
2N/Agrub_dl_t
2N/Agrub_dl_load_core (void *addr, grub_size_t size)
2N/A{
2N/A Elf_Ehdr *e;
2N/A grub_dl_t mod;
2N/A
2N/A grub_dprintf ("modules", "module at %p, size 0x%lx\n", addr,
2N/A (unsigned long) size);
2N/A e = addr;
2N/A if (grub_dl_check_header (e, size))
2N/A return 0;
2N/A
2N/A if (e->e_type != ET_REL)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
2N/A return 0;
2N/A }
2N/A
2N/A /* Make sure that every section is within the core. */
2N/A if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
2N/A return 0;
2N/A }
2N/A
2N/A mod = (grub_dl_t) grub_zalloc (sizeof (*mod));
2N/A if (! mod)
2N/A return 0;
2N/A
2N/A mod->ref_count = 1;
2N/A
2N/A grub_dprintf ("modules", "relocating to %p\n", mod);
2N/A /* Me, Vladimir Serbinenko, hereby I add this module check as per new
2N/A GNU module policy. Note that this license check is informative only.
2N/A Modules have to be licensed under GPLv3 or GPLv3+ (optionally
2N/A multi-licensed under other licences as well) independently of the
2N/A presence of this check and solely by linking (module loading in GRUB
2N/A constitutes linking) and GRUB core being licensed under GPLv3+.
2N/A Be sure to understand your license obligations.
2N/A */
2N/A if (grub_dl_resolve_name (mod, e)
2N/A || grub_dl_check_license (mod->name, e)
2N/A || grub_dl_resolve_dependencies (mod, e)
2N/A || grub_dl_load_segments (mod, e)
2N/A || grub_dl_resolve_symbols (mod, e)
2N/A || grub_arch_dl_relocate_symbols (mod, e))
2N/A {
2N/A mod->fini = 0;
2N/A grub_dl_unload (mod);
2N/A return 0;
2N/A }
2N/A
2N/A grub_dl_flush_cache (mod);
2N/A
2N/A grub_dprintf ("modules", "module name: %s\n", mod->name);
2N/A grub_dprintf ("modules", "init function: %p\n", mod->init);
2N/A grub_dl_call_init (mod);
2N/A
2N/A if (grub_dl_add (mod))
2N/A {
2N/A grub_dl_unload (mod);
2N/A return 0;
2N/A }
2N/A
2N/A return mod;
2N/A}
2N/A
2N/A/* Load a module from the file FILENAME. */
2N/Agrub_dl_t
2N/Agrub_dl_load_file (const char *filename)
2N/A{
2N/A grub_file_t file = NULL;
2N/A grub_ssize_t size;
2N/A void *core = 0;
2N/A grub_dl_t mod = 0;
2N/A
2N/A file = grub_file_open (filename);
2N/A if (! file)
2N/A return 0;
2N/A
2N/A size = grub_file_size (file);
2N/A core = grub_malloc (size);
2N/A if (! core)
2N/A {
2N/A grub_file_close (file);
2N/A return 0;
2N/A }
2N/A
2N/A if (grub_file_read (file, core, size) != (int) size)
2N/A {
2N/A grub_file_close (file);
2N/A grub_free (core);
2N/A return 0;
2N/A }
2N/A
2N/A /* We must close this before we try to process dependencies.
2N/A Some disk backends do not handle gracefully multiple concurrent
2N/A opens of the same device. */
2N/A grub_file_close (file);
2N/A
2N/A mod = grub_dl_load_core (core, size);
2N/A grub_free (core);
2N/A if (! mod)
2N/A return 0;
2N/A
2N/A mod->ref_count--;
2N/A return mod;
2N/A}
2N/A
2N/A/* Load a module using a symbolic name. */
2N/Agrub_dl_t
2N/Agrub_dl_load (const char *name)
2N/A{
2N/A char *filename;
2N/A grub_dl_t mod;
2N/A const char *grub_dl_dir = grub_env_get ("prefix");
2N/A
2N/A mod = grub_dl_get (name);
2N/A if (mod)
2N/A return mod;
2N/A
2N/A if (! grub_dl_dir) {
2N/A grub_error (GRUB_ERR_FILE_NOT_FOUND, "\"prefix\" is not set");
2N/A return 0;
2N/A }
2N/A
2N/A filename = grub_xasprintf ("%s/%s.mod", grub_dl_dir, name);
2N/A if (! filename)
2N/A return 0;
2N/A
2N/A mod = grub_dl_load_file (filename);
2N/A grub_free (filename);
2N/A
2N/A if (! mod)
2N/A return 0;
2N/A
2N/A if (grub_strcmp (mod->name, name) != 0)
2N/A grub_error (GRUB_ERR_BAD_MODULE, "mismatched names");
2N/A
2N/A return mod;
2N/A}
2N/A
2N/A/* Unload the module MOD. */
2N/Aint
2N/Agrub_dl_unload (grub_dl_t mod)
2N/A{
2N/A grub_dl_dep_t dep, depn;
2N/A
2N/A if (mod->ref_count > 0)
2N/A return 0;
2N/A
2N/A if (mod->fini)
2N/A (mod->fini) ();
2N/A
2N/A grub_dl_remove (mod);
2N/A grub_dl_unregister_symbols (mod);
2N/A
2N/A for (dep = mod->dep; dep; dep = depn)
2N/A {
2N/A depn = dep->next;
2N/A
2N/A grub_dl_unload (dep->mod);
2N/A
2N/A grub_free (dep);
2N/A }
2N/A
2N/A grub_free (mod->base);
2N/A grub_free (mod->name);
2N/A#ifdef GRUB_MODULES_MACHINE_READONLY
2N/A grub_free (mod->symtab);
2N/A#endif
2N/A grub_free (mod);
2N/A return 1;
2N/A}
2N/A
2N/A/* Unload unneeded modules. */
2N/Avoid
2N/Agrub_dl_unload_unneeded (void)
2N/A{
2N/A /* Because grub_dl_remove modifies the list of modules, this
2N/A implementation is tricky. */
2N/A grub_dl_t p = grub_dl_head;
2N/A
2N/A while (p)
2N/A {
2N/A if (grub_dl_unload (p))
2N/A {
2N/A p = grub_dl_head;
2N/A continue;
2N/A }
2N/A
2N/A p = p->next;
2N/A }
2N/A}