/* dl.c - loadable module support */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. * * VAS_EBOOT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VAS_EBOOT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VAS_EBOOT. If not, see . */ /* Force native word size */ #define VAS_EBOOT_TARGET_WORDSIZE (8 * VAS_EBOOT_CPU_SIZEOF_VOID_P) #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef VAS_EBOOT_MACHINE_EFI #include #endif /* Platforms where modules are in a readonly area of memory. */ #if defined(VAS_EBOOT_MACHINE_QEMU) #define VAS_EBOOT_MODULES_MACHINE_READONLY #endif #ifdef VAS_EBOOT_MACHINE_EFI #define DL_ALIGN VAS_EBOOT_EFI_PAGE_SIZE #else #define DL_ALIGN 1 #endif #pragma GCC diagnostic ignored "-Wcast-align" VasEBoot_dl_t VasEBoot_dl_head = 0; VasEBoot_err_t VasEBoot_dl_add (VasEBoot_dl_t mod); /* Keep global so that GDB scripts work. */ VasEBoot_err_t VasEBoot_dl_add (VasEBoot_dl_t mod) { if (VasEBoot_dl_get (mod->name)) return VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, "`%s' is already loaded", mod->name); return VAS_EBOOT_ERR_NONE; } static void VasEBoot_dl_remove (VasEBoot_dl_t mod) { VasEBoot_dl_t *p, q; for (p = &VasEBoot_dl_head, q = *p; q; p = &q->next, q = *p) if (q == mod) { *p = q->next; return; } } struct VasEBoot_symbol { struct VasEBoot_symbol *next; const char *name; void *addr; int isfunc; VasEBoot_dl_t mod; /* The module to which this symbol belongs. */ }; typedef struct VasEBoot_symbol *VasEBoot_symbol_t; /* The size of the symbol table. */ #define VAS_EBOOT_SYMTAB_SIZE 509 /* The symbol table (using an open-hash). */ static struct VasEBoot_symbol *VasEBoot_symtab[VAS_EBOOT_SYMTAB_SIZE]; /* Simple hash function. */ static unsigned VasEBoot_symbol_hash (const char *s) { unsigned key = 0; while (*s) key = key * 65599 + *s++; return (key + (key >> 5)) % VAS_EBOOT_SYMTAB_SIZE; } /* Resolve the symbol name NAME and return the address. Return NULL, if not found. */ static VasEBoot_symbol_t VasEBoot_dl_resolve_symbol (const char *name) { VasEBoot_symbol_t sym; for (sym = VasEBoot_symtab[VasEBoot_symbol_hash (name)]; sym; sym = sym->next) if (VasEBoot_strcmp (sym->name, name) == 0) return sym; return 0; } /* Register a symbol with the name NAME and the address ADDR. */ VasEBoot_err_t VasEBoot_dl_register_symbol (const char *name, void *addr, int isfunc, VasEBoot_dl_t mod) { VasEBoot_symbol_t sym; unsigned k; sym = (VasEBoot_symbol_t) VasEBoot_malloc (sizeof (*sym)); if (! sym) return VasEBoot_errno; if (mod) { sym->name = VasEBoot_strdup (name); if (! sym->name) { VasEBoot_free (sym); return VasEBoot_errno; } } else sym->name = name; sym->addr = addr; sym->mod = mod; sym->isfunc = isfunc; k = VasEBoot_symbol_hash (name); sym->next = VasEBoot_symtab[k]; VasEBoot_symtab[k] = sym; return VAS_EBOOT_ERR_NONE; } /* Unregister all the symbols defined in the module MOD. */ static void VasEBoot_dl_unregister_symbols (VasEBoot_dl_t mod) { unsigned i; if (! mod) VasEBoot_fatal ("core symbols cannot be unregistered"); for (i = 0; i < VAS_EBOOT_SYMTAB_SIZE; i++) { VasEBoot_symbol_t sym, *p, q; for (p = &VasEBoot_symtab[i], sym = *p; sym; sym = q) { q = sym->next; if (sym->mod == mod) { *p = q; VasEBoot_free ((void *) sym->name); VasEBoot_free (sym); } else p = &sym->next; } } } /* Return the address of a section whose index is N. */ static void * VasEBoot_dl_get_section_addr (VasEBoot_dl_t mod, unsigned n) { VasEBoot_dl_segment_t seg; for (seg = mod->segment; seg; seg = seg->next) if (seg->section == n) return seg->addr; return 0; } /* Check if EHDR is a valid ELF header. */ static VasEBoot_err_t VasEBoot_dl_check_header (void *ehdr, VasEBoot_size_t size) { Elf_Ehdr *e = ehdr; VasEBoot_err_t err; /* Check the header size. */ if (size < sizeof (Elf_Ehdr)) return VasEBoot_error (VAS_EBOOT_ERR_BAD_OS, "ELF header smaller than expected"); /* Check the magic numbers. */ if (e->e_ident[EI_MAG0] != ELFMAG0 || e->e_ident[EI_MAG1] != ELFMAG1 || e->e_ident[EI_MAG2] != ELFMAG2 || e->e_ident[EI_MAG3] != ELFMAG3 || e->e_ident[EI_VERSION] != EV_CURRENT || e->e_version != EV_CURRENT) return VasEBoot_error (VAS_EBOOT_ERR_BAD_OS, N_("invalid arch-independent ELF magic")); err = VasEBoot_arch_dl_check_header (ehdr); if (err) return err; return VAS_EBOOT_ERR_NONE; } /* Load all segments from memory specified by E. */ static VasEBoot_err_t VasEBoot_dl_load_segments (VasEBoot_dl_t mod, const Elf_Ehdr *e) { unsigned i; const Elf_Shdr *s; VasEBoot_size_t tsize = 0, talign = 1, arch_addralign = 1; #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) VasEBoot_size_t tramp; VasEBoot_size_t tramp_align; VasEBoot_size_t got; VasEBoot_size_t got_align; VasEBoot_err_t err; #endif char *ptr; arch_addralign = DL_ALIGN; for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); i < e->e_shnum; i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) { VasEBoot_size_t sh_addralign; VasEBoot_size_t sh_size; if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC)) continue; sh_addralign = ALIGN_UP (s->sh_addralign, arch_addralign); sh_size = ALIGN_UP (s->sh_size, sh_addralign); tsize = ALIGN_UP (tsize, sh_addralign) + sh_size; talign = VasEBoot_max (talign, sh_addralign); } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) err = VasEBoot_arch_dl_get_tramp_got_size (e, &tramp, &got); if (err) return err; tramp_align = VasEBoot_max (VAS_EBOOT_ARCH_DL_TRAMP_ALIGN, arch_addralign); tsize += ALIGN_UP (tramp, tramp_align); talign = VasEBoot_max (talign, tramp_align); got_align = VasEBoot_max (VAS_EBOOT_ARCH_DL_GOT_ALIGN, arch_addralign); tsize += ALIGN_UP (got, got_align); talign = VasEBoot_max (talign, got_align); #endif #ifdef VAS_EBOOT_MACHINE_EMU mod->base = VasEBoot_osdep_dl_memalign (talign, tsize); #else mod->base = VasEBoot_memalign (talign, tsize); #endif if (!mod->base) return VasEBoot_errno; mod->sz = tsize; ptr = mod->base; for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize)) { VasEBoot_size_t sh_addralign = ALIGN_UP (s->sh_addralign, arch_addralign); VasEBoot_size_t sh_size = ALIGN_UP (s->sh_size, sh_addralign); if (s->sh_flags & SHF_ALLOC) { VasEBoot_dl_segment_t seg; seg = (VasEBoot_dl_segment_t) VasEBoot_malloc (sizeof (*seg)); if (! seg) return VasEBoot_errno; if (s->sh_size) { void *addr; ptr = (char *) ALIGN_UP ((VasEBoot_addr_t) ptr, sh_addralign); addr = ptr; ptr += sh_size; switch (s->sh_type) { case SHT_PROGBITS: VasEBoot_memcpy (addr, (char *) e + s->sh_offset, s->sh_size); VasEBoot_memset ((char *) addr + s->sh_size, 0, sh_size - s->sh_size); break; case SHT_NOBITS: VasEBoot_memset (addr, 0, sh_size); break; } seg->addr = addr; } else seg->addr = 0; seg->size = sh_size; seg->section = i; seg->next = mod->segment; mod->segment = seg; } } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) ptr = (char *) ALIGN_UP ((VasEBoot_addr_t) ptr, tramp_align); mod->tramp = ptr; mod->trampptr = ptr; ptr += tramp; ptr = (char *) ALIGN_UP ((VasEBoot_addr_t) ptr, got_align); mod->got = ptr; mod->gotptr = ptr; ptr += got; #endif return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_dl_resolve_symbols (VasEBoot_dl_t mod, Elf_Ehdr *e) { unsigned i; Elf_Shdr *s; Elf_Sym *sym; const char *str; Elf_Word size, entsize; for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) if (s->sh_type == SHT_SYMTAB) break; /* Module without symbol table may still be used to pull in dependencies. We verify at build time that such modules do not contain any relocations that may reference symbol table. */ if (i == e->e_shnum) return VAS_EBOOT_ERR_NONE; #ifdef VAS_EBOOT_MODULES_MACHINE_READONLY mod->symtab = VasEBoot_malloc (s->sh_size); if (!mod->symtab) return VasEBoot_errno; VasEBoot_memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size); #else mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset); #endif mod->symsize = s->sh_entsize; sym = mod->symtab; size = s->sh_size; entsize = s->sh_entsize; s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link); str = (char *) e + s->sh_offset; for (i = 0; i < size / entsize; i++, sym = (Elf_Sym *) ((char *) sym + entsize)) { unsigned char type = ELF_ST_TYPE (sym->st_info); unsigned char bind = ELF_ST_BIND (sym->st_info); const char *name = str + sym->st_name; switch (type) { case STT_NOTYPE: case STT_OBJECT: /* Resolve a global symbol. */ if (sym->st_name != 0 && sym->st_shndx == 0) { VasEBoot_symbol_t nsym = VasEBoot_dl_resolve_symbol (name); if (! nsym) return VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, N_("symbol `%s' not found"), name); sym->st_value = (Elf_Addr) nsym->addr; if (nsym->isfunc) sym->st_info = ELF_ST_INFO (bind, STT_FUNC); } else { sym->st_value += (Elf_Addr) VasEBoot_dl_get_section_addr (mod, sym->st_shndx); if (bind != STB_LOCAL) if (VasEBoot_dl_register_symbol (name, (void *) sym->st_value, 0, mod)) return VasEBoot_errno; } break; case STT_FUNC: sym->st_value += (Elf_Addr) VasEBoot_dl_get_section_addr (mod, sym->st_shndx); #ifdef __ia64__ { /* FIXME: free descriptor once it's not used anymore. */ char **desc; desc = VasEBoot_malloc (2 * sizeof (char *)); if (!desc) return VasEBoot_errno; desc[0] = (void *) sym->st_value; desc[1] = mod->base; sym->st_value = (VasEBoot_addr_t) desc; } #endif if (bind != STB_LOCAL) if (VasEBoot_dl_register_symbol (name, (void *) sym->st_value, 1, mod)) return VasEBoot_errno; if (VasEBoot_strcmp (name, "VasEBoot_mod_init") == 0) mod->init = (void (*) (VasEBoot_dl_t)) sym->st_value; else if (VasEBoot_strcmp (name, "VasEBoot_mod_fini") == 0) mod->fini = (void (*) (void)) sym->st_value; break; case STT_SECTION: sym->st_value = (Elf_Addr) VasEBoot_dl_get_section_addr (mod, sym->st_shndx); break; case STT_FILE: sym->st_value = 0; break; default: return VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, "unknown symbol type `%d'", (int) type); } } return VAS_EBOOT_ERR_NONE; } static Elf_Shdr * VasEBoot_dl_find_section (Elf_Ehdr *e, const char *name) { Elf_Shdr *s; const char *str; unsigned i; s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); str = (char *) e + s->sh_offset; for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) if (VasEBoot_strcmp (str + s->sh_name, name) == 0) return s; return NULL; } /* Me, Vladimir Serbinenko, hereby I add this module check as per new GNU module policy. Note that this license check is informative only. Modules have to be licensed under GPLv3 or GPLv3+ (optionally multi-licensed under other licences as well) independently of the presence of this check and solely by linking (module loading in VAS_EBOOT constitutes linking) and VAS_EBOOT core being licensed under GPLv3+. Be sure to understand your license obligations. */ static VasEBoot_err_t VasEBoot_dl_check_license (VasEBoot_dl_t mod, Elf_Ehdr *e) { Elf_Shdr *s = VasEBoot_dl_find_section (e, ".module_license"); if (s == NULL) return VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, "no license section in module %.63s", mod->name); if (VasEBoot_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3") == 0 || VasEBoot_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3+") == 0 || VasEBoot_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0) return VAS_EBOOT_ERR_NONE; return VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, "incompatible license in module %.63s: %.63s", mod->name, (char *) e + s->sh_offset); } static VasEBoot_err_t VasEBoot_dl_resolve_name (VasEBoot_dl_t mod, Elf_Ehdr *e) { Elf_Shdr *s; s = VasEBoot_dl_find_section (e, ".modname"); if (!s) return VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, "no module name found"); mod->name = VasEBoot_strdup ((char *) e + s->sh_offset); if (! mod->name) return VasEBoot_errno; return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_dl_resolve_dependencies (VasEBoot_dl_t mod, Elf_Ehdr *e) { Elf_Shdr *s; s = VasEBoot_dl_find_section (e, ".moddeps"); if (!s) return VAS_EBOOT_ERR_NONE; const char *name = (char *) e + s->sh_offset; const char *max = name + s->sh_size; while ((name < max) && (*name)) { VasEBoot_dl_t m; VasEBoot_dl_dep_t dep; m = VasEBoot_dl_load (name); if (! m) return VasEBoot_errno; VasEBoot_dl_ref (m); dep = (VasEBoot_dl_dep_t) VasEBoot_malloc (sizeof (*dep)); if (! dep) return VasEBoot_errno; dep->mod = m; dep->next = mod->dep; mod->dep = dep; name += VasEBoot_strlen (name) + 1; } return VAS_EBOOT_ERR_NONE; } VasEBoot_uint64_t VasEBoot_dl_ref (VasEBoot_dl_t mod) { VasEBoot_dl_dep_t dep; if (!mod) return 0; for (dep = mod->dep; dep; dep = dep->next) VasEBoot_dl_ref (dep->mod); if (VasEBoot_add (mod->ref_count, 1, &mod->ref_count)) VasEBoot_fatal ("Module reference count overflow"); return mod->ref_count; } VasEBoot_uint64_t VasEBoot_dl_unref (VasEBoot_dl_t mod) { VasEBoot_dl_dep_t dep; if (!mod) return 0; for (dep = mod->dep; dep; dep = dep->next) VasEBoot_dl_unref (dep->mod); if (VasEBoot_sub (mod->ref_count, 1, &mod->ref_count)) VasEBoot_fatal ("Module reference count underflow"); return mod->ref_count; } VasEBoot_uint64_t VasEBoot_dl_ref_count (VasEBoot_dl_t mod) { if (mod == NULL) return 0; return mod->ref_count; } static void VasEBoot_dl_flush_cache (VasEBoot_dl_t mod) { VasEBoot_dprintf ("modules", "flushing 0x%lx bytes at %p\n", (unsigned long) mod->sz, mod->base); VasEBoot_arch_sync_caches (mod->base, mod->sz); } static VasEBoot_err_t VasEBoot_dl_relocate_symbols (VasEBoot_dl_t mod, void *ehdr) { Elf_Ehdr *e = ehdr; Elf_Shdr *s; unsigned i; for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA) { VasEBoot_dl_segment_t seg; VasEBoot_err_t err; if (!(s->sh_flags & SHF_INFO_LINK)) continue; /* Find the target segment. */ for (seg = mod->segment; seg; seg = seg->next) if (seg->section == s->sh_info) break; if (seg) { if (!mod->symtab) return VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, "relocation without symbol table"); err = VasEBoot_arch_dl_relocate_symbols (mod, ehdr, s, seg); if (err) return err; } } return VAS_EBOOT_ERR_NONE; } /* Only define this on EFI to save space in core. */ #ifdef VAS_EBOOT_MACHINE_EFI static VasEBoot_err_t VasEBoot_dl_set_mem_attrs (VasEBoot_dl_t mod, void *ehdr) { unsigned i; const Elf_Shdr *s; const Elf_Ehdr *e = ehdr; VasEBoot_err_t err; #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) VasEBoot_size_t arch_addralign = DL_ALIGN; VasEBoot_addr_t tgaddr; VasEBoot_size_t tgsz; #endif for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); i < e->e_shnum; i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) { VasEBoot_dl_segment_t seg; VasEBoot_uint64_t set_attrs = VAS_EBOOT_MEM_ATTR_R; VasEBoot_uint64_t clear_attrs = VAS_EBOOT_MEM_ATTR_W | VAS_EBOOT_MEM_ATTR_X; for (seg = mod->segment; seg; seg = seg->next) /* Does this ELF section's index match VAS_EBOOT DL segment? */ if (seg->section == i) break; /* No VAS_EBOOT DL segment found for this ELF section, skip it. */ if (!seg) continue; if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC)) continue; if (s->sh_flags & SHF_WRITE) { set_attrs |= VAS_EBOOT_MEM_ATTR_W; clear_attrs &= ~VAS_EBOOT_MEM_ATTR_W; } if (s->sh_flags & SHF_EXECINSTR) { set_attrs |= VAS_EBOOT_MEM_ATTR_X; clear_attrs &= ~VAS_EBOOT_MEM_ATTR_X; } err = VasEBoot_update_mem_attrs ((VasEBoot_addr_t) seg->addr, seg->size, set_attrs, clear_attrs); if (err != VAS_EBOOT_ERR_NONE) return err; } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) tgaddr = VasEBoot_min ((VasEBoot_addr_t) mod->tramp, (VasEBoot_addr_t) mod->got); tgsz = VasEBoot_max ((VasEBoot_addr_t) mod->trampptr, (VasEBoot_addr_t) mod->gotptr) - tgaddr; if (tgsz) { tgsz = ALIGN_UP (tgsz, arch_addralign); if (tgaddr < (VasEBoot_addr_t) mod->base || tgsz > (VasEBoot_addr_t) -1 - tgaddr || tgaddr + tgsz > (VasEBoot_addr_t) mod->base + mod->sz) return VasEBoot_error (VAS_EBOOT_ERR_BUG, "BUG: trying to protect pages outside of module " "allocation (\"%s\"): module base %p, size 0x%" PRIxVAS_EBOOT_SIZE "; tramp/GOT base 0x%" PRIxVAS_EBOOT_ADDR ", size 0x%" PRIxVAS_EBOOT_SIZE, mod->name, mod->base, mod->sz, tgaddr, tgsz); err = VasEBoot_update_mem_attrs (tgaddr, tgsz, VAS_EBOOT_MEM_ATTR_R | VAS_EBOOT_MEM_ATTR_X, VAS_EBOOT_MEM_ATTR_W); if (err != VAS_EBOOT_ERR_NONE) return err; } #endif return VAS_EBOOT_ERR_NONE; } #else static VasEBoot_err_t VasEBoot_dl_set_mem_attrs (VasEBoot_dl_t mod __attribute__ ((unused)), void *ehdr __attribute__ ((unused))) { return VAS_EBOOT_ERR_NONE; } #endif /* Load a module from core memory. */ VasEBoot_dl_t VasEBoot_dl_load_core_noinit (void *addr, VasEBoot_size_t size) { Elf_Ehdr *e; VasEBoot_dl_t mod; VasEBoot_dprintf ("modules", "module at %p, size 0x%lx\n", addr, (unsigned long) size); e = addr; if (VasEBoot_dl_check_header (e, size)) return 0; if (e->e_type != ET_REL) { VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, N_("this ELF file is not of the right type")); return 0; } /* Make sure that every section is within the core. */ if (size < e->e_shoff + (VasEBoot_uint32_t) e->e_shentsize * e->e_shnum) { VasEBoot_error (VAS_EBOOT_ERR_BAD_OS, "ELF sections outside core"); return 0; } mod = (VasEBoot_dl_t) VasEBoot_zalloc (sizeof (*mod)); if (! mod) return 0; mod->ref_count = 1; VasEBoot_dprintf ("modules", "relocating to %p\n", mod); /* Me, Vladimir Serbinenko, hereby I add this module check as per new GNU module policy. Note that this license check is informative only. Modules have to be licensed under GPLv3 or GPLv3+ (optionally multi-licensed under other licences as well) independently of the presence of this check and solely by linking (module loading in VAS_EBOOT constitutes linking) and VAS_EBOOT core being licensed under GPLv3+. Be sure to understand your license obligations. */ if (VasEBoot_dl_resolve_name (mod, e) || VasEBoot_dl_check_license (mod, e) || VasEBoot_dl_resolve_dependencies (mod, e) || VasEBoot_dl_load_segments (mod, e) || VasEBoot_dl_resolve_symbols (mod, e) || VasEBoot_dl_relocate_symbols (mod, e) || VasEBoot_dl_set_mem_attrs (mod, e)) { mod->fini = 0; VasEBoot_dl_unload (mod); return 0; } VasEBoot_dl_flush_cache (mod); VasEBoot_dprintf ("modules", "module name: %s\n", mod->name); VasEBoot_dprintf ("modules", "init function: %p\n", mod->init); if (VasEBoot_dl_add (mod)) { VasEBoot_dl_unload (mod); return 0; } return mod; } VasEBoot_dl_t VasEBoot_dl_load_core (void *addr, VasEBoot_size_t size) { VasEBoot_dl_t mod; VasEBoot_boot_time ("Parsing module"); mod = VasEBoot_dl_load_core_noinit (addr, size); if (!mod) return NULL; VasEBoot_boot_time ("Initing module %s", mod->name); VasEBoot_dl_init (mod); VasEBoot_boot_time ("Module %s inited", mod->name); return mod; } /* Load a module from the file FILENAME. */ VasEBoot_dl_t VasEBoot_dl_load_file (const char *filename) { VasEBoot_file_t file = NULL; VasEBoot_ssize_t size; void *core = 0; VasEBoot_dl_t mod = 0; VasEBoot_boot_time ("Loading module %s", filename); file = VasEBoot_file_open (filename, VAS_EBOOT_FILE_TYPE_VAS_EBOOT_MODULE); if (! file) return 0; size = VasEBoot_file_size (file); core = VasEBoot_malloc (size); if (! core) { VasEBoot_file_close (file); return 0; } if (VasEBoot_file_read (file, core, size) != (int) size) { VasEBoot_file_close (file); VasEBoot_free (core); return 0; } /* We must close this before we try to process dependencies. Some disk backends do not handle gracefully multiple concurrent opens of the same device. */ VasEBoot_file_close (file); mod = VasEBoot_dl_load_core (core, size); VasEBoot_free (core); if (! mod) return 0; mod->ref_count--; return mod; } /* Load a module using a symbolic name. */ VasEBoot_dl_t VasEBoot_dl_load (const char *name) { char *filename; VasEBoot_dl_t mod; const char *VasEBoot_dl_dir = VasEBoot_env_get ("prefix"); mod = VasEBoot_dl_get (name); if (mod) return mod; if (VasEBoot_no_modules) return 0; if (! VasEBoot_dl_dir) { VasEBoot_error (VAS_EBOOT_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); return 0; } filename = VasEBoot_xasprintf ("%s/" VAS_EBOOT_TARGET_CPU "-" VAS_EBOOT_PLATFORM "/%s.mod", VasEBoot_dl_dir, name); if (! filename) return 0; mod = VasEBoot_dl_load_file (filename); VasEBoot_free (filename); if (! mod) return 0; if (VasEBoot_strcmp (mod->name, name) != 0) VasEBoot_error (VAS_EBOOT_ERR_BAD_MODULE, "mismatched names"); return mod; } /* Unload the module MOD. */ int VasEBoot_dl_unload (VasEBoot_dl_t mod) { VasEBoot_dl_dep_t dep, depn; if (mod->ref_count > 0) return 0; if (mod->fini) (mod->fini) (); VasEBoot_dl_remove (mod); VasEBoot_dl_unregister_symbols (mod); for (dep = mod->dep; dep; dep = depn) { depn = dep->next; VasEBoot_dl_unload (dep->mod); VasEBoot_free (dep); } #ifdef VAS_EBOOT_MACHINE_EMU VasEBoot_dl_osdep_dl_free (mod->base); #else VasEBoot_free (mod->base); #endif VasEBoot_free (mod->name); #ifdef VAS_EBOOT_MODULES_MACHINE_READONLY VasEBoot_free (mod->symtab); #endif VasEBoot_free (mod); return 1; }