struct module * load_module(void *mod_image, unsigned long len) { struct module *module = NULL; Elf32_Ehdr *ehdr; /* Elf header structure pointer */ Elf32_Shdr *sechdrs; /* Section header structure pointer */ Elf32_Sym *sym; unsigned int numsyms; char *strtab = 0; /* String table pointer */ int i; /* Loop counter */ unsigned int strindex = 0; unsigned int symindex = 0; unsigned int modindex; char *secstrings; void *ptr = NULL; int err; int cmdindex; if (len < sizeof(*ehdr)) return NULL; ehdr = (Elf32_Ehdr *)mod_image; if (len < ehdr->e_shoff + ehdr->e_shnum * sizeof(Elf_Shdr)) return NULL; /* Find the section header string table for output info */ sechdrs = (Elf32_Shdr *) (mod_image + ehdr->e_shoff + (ehdr->e_shstrndx * sizeof (Elf32_Shdr))); if (sechdrs->sh_type == SHT_SYMTAB) strtab = (char *) (mod_image + sechdrs->sh_offset); /* Convenience variables */ sechdrs = (void *)ehdr + ehdr->e_shoff; secstrings = (void *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset; sechdrs[0].sh_addr = 0; for (i = 0; i < ehdr->e_shnum; i++) { debug("%d addr: 0x%08x size: 0x%08x ofs: 0x%08x\n", i, sechdrs[i].sh_addr, sechdrs[i].sh_size, sechdrs[i].sh_offset); /* Mark all sections sh_addr with their address in the temporary image. */ sechdrs[i].sh_addr = (size_t)ehdr + sechdrs[i].sh_offset; if (sechdrs[i].sh_type == SHT_SYMTAB) { symindex = i; strindex = sechdrs[i].sh_link; strtab = mod_image + sechdrs[strindex].sh_offset; } } modindex = find_sec(ehdr, sechdrs, secstrings, ".gnu.linkonce.this_module"); if (!modindex) { printf("No module found in object\n"); err = -ENOEXEC; goto cleanup; } module = (void *)sechdrs[modindex].sh_addr; if (symindex == 0) { printf("module has no symbols (stripped?)\n"); err = -ENOEXEC; goto cleanup; } /* Determine total sizes, and put offsets in sh_entsize. For now this is done generically; there doesn't appear to be any special cases for the architectures. */ layout_sections(module, ehdr, sechdrs, secstrings); ptr = xzalloc(module->core_size); module->module_core = ptr; /* Transfer each section which specifies SHF_ALLOC */ debug("final section addresses:\n"); for (i = 0; i < ehdr->e_shnum; i++) { void *dest; if (!(sechdrs[i].sh_flags & SHF_ALLOC)) continue; dest = module->module_core + sechdrs[i].sh_entsize; debug("0x%08x dest 0x%p\n", sechdrs[i].sh_addr, dest); if (sechdrs[i].sh_type != SHT_NOBITS) memcpy(dest, (void *)sechdrs[i].sh_addr, sechdrs[i].sh_size); /* Update sh_addr to point to copy in image. */ sechdrs[i].sh_addr = (unsigned long)dest; } /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(sechdrs, symindex, strtab); if (err < 0) goto cleanup; for (i = 0; i < ehdr->e_shnum; i++) { if (sechdrs[i].sh_type == SHT_REL) { apply_relocate(sechdrs, strtab, symindex, i, module); } if (sechdrs[i].sh_type == SHT_RELA) apply_relocate_add(sechdrs, strtab, symindex, i, module); } numsyms = sechdrs[symindex].sh_size / sizeof(Elf32_Sym); sym = (void *)sechdrs[symindex].sh_addr; cmdindex = find_sec(ehdr, sechdrs, secstrings, ".barebox_cmd"); if (cmdindex) { struct command *cmd =(struct command *)sechdrs[cmdindex].sh_addr; for (i = 0; i < sechdrs[cmdindex].sh_size / sizeof(struct command); i++) { register_command(cmd); cmd++; } } for (i = 0; i < numsyms; i++) { if (!strcmp(strtab + sym[i].st_name, MODULE_SYMBOL_PREFIX "init_module")) { printf("found init_module() at 0x%08x\n", sym[i].st_value); module->init = (void *)sym[i].st_value; } } list_add_tail(&module->list, &module_list); return module; cleanup: if (ptr) free(ptr); if (module) free(module); return NULL; }
static int relocate_rel(unsigned char *data, address_t offset, const Elf32_Rel *rel, const Elf32_Shdr *rel_progbits, const Elf32_Sym *sym, const char *strtab) { const Elf32_Ehdr *hdr = (const struct elf32_hdr*)data; const Elf32_Shdr *sechdrs = (const Elf32_Shdr*)(data + hdr->e_shoff); address_t address = rel_progbits->sh_offset + rel->r_offset; address_t *p = (address_t*)(data + address); address_t value; const char *name; switch (ELF32_ST_TYPE(sym->st_info)) { case STT_NOTYPE: /* symbol from kernel */ /* get symbol name */ name = strtab + sym->st_name; /* resolve the symbol using kernel symbol table */ value = get_symbol(0, name); if (value == 0) { fprintf(stderr, "cannot to resolve %s\n", name); return -1; } break; case STT_SECTION: /* local symbol I */ if (sym->st_shndx >= hdr->e_shnum) { fprintf(stderr, "sym->st_shndx out of bounds\n"); return -1; } value = offset + sechdrs[sym->st_shndx].sh_offset; break; case STT_OBJECT: case STT_FUNC: /* local symbol II */ if (sym->st_shndx >= hdr->e_shnum) { fprintf(stderr, "sym->st_shndx out of bounds\n"); return -1; } value = offset + sechdrs[sym->st_shndx].sh_offset + sym->st_value; break; default: fprintf(stderr, "unkown ST_TYPE: %d\n", ELF32_ST_TYPE(sym->st_info)); return -1; } /* relocate it */ apply_relocate(rel, value, p, offset + address); return 0; }
static noinline struct module *load_module(void __user * umod, unsigned long len, const char __user * uargs) { struct elfhdr *hdr; struct secthdr *sechdrs; char *secstrings, *args, *modmagic, *strtab = NULL; //char *staging; unsigned int i; unsigned int symindex = 0; unsigned int strindex = 0; unsigned int modindex, versindex, infoindex, pcpuindex; struct module *mod; long err = 0; void *ptr = NULL; kprintf("load_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs); if (len < sizeof(*hdr)) return NULL; if (len > 64 * 1024 * 1024 || (hdr = kmalloc(len)) == NULL) return NULL; kprintf("load_module: copy_from_user\n"); struct mm_struct *mm = current->mm; lock_mm(mm); if (!copy_from_user(mm, hdr, umod, len, 1)) { unlock_mm(mm); goto free_hdr; } unlock_mm(mm); kprintf("load_module: hdr:%p\n", hdr); // sanity check if (memcmp(&(hdr->e_magic), ELFMAG, SELFMAG) != 0 || hdr->e_type != ET_REL || !elf_check_arch(hdr) || hdr->e_shentsize != sizeof(*sechdrs)) { kprintf("load_module: sanity check failed.\n"); goto free_hdr; } if (len < hdr->e_shoff + hdr->e_shnum * sizeof(*sechdrs)) goto truncated; sechdrs = (void *)hdr + hdr->e_shoff; secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; sechdrs[0].sh_addr = 0; for (i = 1; i < hdr->e_shnum; i++) { if (sechdrs[i].sh_type != SHT_NOBITS && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) goto truncated; // mark sh_addr sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset; if (sechdrs[i].sh_type == SHT_SYMTAB) { symindex = i; strindex = sechdrs[i].sh_link; strtab = (char *)hdr + sechdrs[strindex].sh_offset; } } modindex = find_sec(hdr, sechdrs, secstrings, ".gnu.linkonce.this_module"); if (!modindex) { kprintf("load_module: No module found in object.\n"); goto free_hdr; } // temp: point mod into copy of data mod = (void *)sechdrs[modindex].sh_addr; if (symindex == 0) { kprintf("load_module: %s module has no symbols (stripped?).\n", mod->name); goto free_hdr; } versindex = find_sec(hdr, sechdrs, secstrings, "__versions"); infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo"); pcpuindex = 0;//find_pcpusec(hdr, sechdrs, secstrings); // don't keep modinfo and version sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; sechdrs[versindex].sh_flags &= ~(unsigned long)SHF_ALLOC; // keep symbol and string tables sechdrs[symindex].sh_flags |= SHF_ALLOC; sechdrs[strindex].sh_flags |= SHF_ALLOC; /*if (!check_modstruct_version(sechdrs, versindex, mod)) { goto free_hdr; }*/ /* modmagic = get_modinfo(sechdrs, infoindex, "vermagic"); if (!modmagic) { kprintf("load_module: bad vermagic\n"); goto free_hdr; } else if (!same_magic(modmagic, vermagic, versindex)) { ; // TODO: module magic is left for future use. } */ //staging = get_modinfo(sechdrs, infoindex, "staging"); // TODO: staging is left for future use. if (find_module(mod->name)) { kprintf("load_module: module %s exists\n", mod->name); goto free_mod; } mod->state = MODULE_STATE_COMING; // err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod); // TODO: we do not need it for x86 or arm // TODO: percpu is no longer needed. layout_sections(mod, hdr, sechdrs, secstrings); ptr = module_alloc_update_bounds(mod->core_size); if (!ptr) { goto free_percpu; } memset(ptr, 0, mod->core_size); mod->module_core = ptr; ptr = module_alloc_update_bounds(mod->init_size); if (!ptr && mod->init_size) { goto free_core; } memset(ptr, 0, mod->init_size); mod->module_init = ptr; kprintf("load_module: final section addresses:\n"); for (i = 0; i < hdr->e_shnum; i++) { void *dest; if (!(sechdrs[i].sh_flags & SHF_ALLOC)) { kprintf("\tSkipped %s\n", secstrings + sechdrs[i].sh_name); continue; } if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK) dest = mod->module_init + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK); else dest = mod->module_core + sechdrs[i].sh_entsize; if (sechdrs[i].sh_type != SHT_NOBITS) memcpy(dest, (void *)sechdrs[i].sh_addr, sechdrs[i].sh_size); sechdrs[i].sh_addr = (unsigned long)dest; kprintf("\t0x%lx %s\n", sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name); } /* Module has been moved. */ mod = (void *)sechdrs[modindex].sh_addr; /* Now we've moved module, initialize linked lists, etc. */ module_unload_init(mod); /* Set up license info based on the info section */ set_license(mod, get_modinfo(sechdrs, infoindex, "license")); err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex, mod); if (err < 0) goto cleanup; mod->syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab", sizeof(*mod->syms), &mod->num_syms); mod->crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab"); // relocations for (i = 1; i < hdr->e_shnum; i++) { const char *strtab = (char *)sechdrs[strindex].sh_addr; unsigned int info = sechdrs[i].sh_info; /* Not a valid relocation section */ if (info >= hdr->e_shnum) continue; /* Don't bother with non-allocated sections */ if (!(sechdrs[info].sh_flags & SHF_ALLOC)) continue; if (sechdrs[i].sh_type == SHT_REL) err = apply_relocate(sechdrs, strtab, symindex, i, mod); else if (sechdrs[i].sh_type == SHT_RELA) err = apply_relocate_add(sechdrs, strtab, symindex, i, mod); if (err < 0) goto cleanup; } err = verify_export_symbols(mod); if (err < 0) goto cleanup; // TODO: kallsyms is left for future use. //add_kallsyms(mod, sechdrs, symindex, strindex, secstrings); err = module_finalize(hdr, sechdrs, mod); if (err < 0) goto cleanup; list_add(&modules, &mod->list); kfree(hdr); return mod; cleanup: module_unload_free(mod); free_init: module_free(mod, mod->module_init); free_core: module_free(mod, mod->module_core); free_percpu: free_mod: free_hdr: kfree(hdr); return NULL; truncated: kprintf("load_module: module len %lu truncated.\n"); goto free_hdr; }