📄 module.c
字号:
goto free_mod; } sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC; mod->percpu = percpu; } /* 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(mod, hdr, sechdrs, secstrings); /* Do the allocs. */ ptr = module_alloc_update_bounds(mod->core_size); if (!ptr) { err = -ENOMEM; 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) { err = -ENOMEM; goto free_core; } memset(ptr, 0, mod->init_size); mod->module_init = ptr; /* Transfer each section which specifies SHF_ALLOC */ DEBUGP("final section addresses:\n"); for (i = 0; i < hdr->e_shnum; i++) { void *dest; if (!(sechdrs[i].sh_flags & SHF_ALLOC)) 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); /* Update sh_addr to point to copy in image. */ sechdrs[i].sh_addr = (unsigned long)dest; DEBUGP("\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); /* add kobject, so we can reference it. */ err = mod_sysfs_init(mod); if (err) goto free_unload; /* Set up license info based on the info section */ set_license(mod, get_modinfo(sechdrs, infoindex, "license")); /* * ndiswrapper is under GPL by itself, but loads proprietary modules. * Don't use add_taint_module(), as it would prevent ndiswrapper from * using GPL-only symbols it needs. */ if (strcmp(mod->name, "ndiswrapper") == 0) add_taint(TAINT_PROPRIETARY_MODULE); /* driverloader was caught wrongly pretending to be under GPL */ if (strcmp(mod->name, "driverloader") == 0) add_taint_module(mod, TAINT_PROPRIETARY_MODULE); /* Set up MODINFO_ATTR fields */ setup_modinfo(mod, sechdrs, infoindex); /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex, mod); if (err < 0) goto cleanup; /* Set up EXPORTed & EXPORT_GPLed symbols (section 0 is 0 length) */ mod->num_syms = sechdrs[exportindex].sh_size / sizeof(*mod->syms); mod->syms = (void *)sechdrs[exportindex].sh_addr; if (crcindex) mod->crcs = (void *)sechdrs[crcindex].sh_addr; mod->num_gpl_syms = sechdrs[gplindex].sh_size / sizeof(*mod->gpl_syms); mod->gpl_syms = (void *)sechdrs[gplindex].sh_addr; if (gplcrcindex) mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr; mod->num_gpl_future_syms = sechdrs[gplfutureindex].sh_size / sizeof(*mod->gpl_future_syms); mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr; if (gplfuturecrcindex) mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr;#ifdef CONFIG_UNUSED_SYMBOLS mod->num_unused_syms = sechdrs[unusedindex].sh_size / sizeof(*mod->unused_syms); mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size / sizeof(*mod->unused_gpl_syms); mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr; if (unusedcrcindex) mod->unused_crcs = (void *)sechdrs[unusedcrcindex].sh_addr; mod->unused_gpl_syms = (void *)sechdrs[unusedgplindex].sh_addr; if (unusedgplcrcindex) mod->unused_gpl_crcs = (void *)sechdrs[unusedgplcrcindex].sh_addr;#endif#ifdef CONFIG_MODVERSIONS if ((mod->num_syms && !crcindex) || (mod->num_gpl_syms && !gplcrcindex) || (mod->num_gpl_future_syms && !gplfuturecrcindex)#ifdef CONFIG_UNUSED_SYMBOLS || (mod->num_unused_syms && !unusedcrcindex) || (mod->num_unused_gpl_syms && !unusedgplcrcindex)#endif ) { printk(KERN_WARNING "%s: No versions for exported symbols.\n", mod->name); err = try_to_force_load(mod, "nocrc"); if (err) goto cleanup; }#endif markersindex = find_sec(hdr, sechdrs, secstrings, "__markers"); markersstringsindex = find_sec(hdr, sechdrs, secstrings, "__markers_strings"); /* Now do 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; }#ifdef CONFIG_MARKERS mod->markers = (void *)sechdrs[markersindex].sh_addr; mod->num_markers = sechdrs[markersindex].sh_size / sizeof(*mod->markers);#endif /* Find duplicate symbols */ err = verify_export_symbols(mod); if (err < 0) goto cleanup; /* Set up and sort exception table */ mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable); mod->extable = extable = (void *)sechdrs[exindex].sh_addr; sort_extable(extable, extable + mod->num_exentries); /* Finally, copy percpu area over. */ percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr, sechdrs[pcpuindex].sh_size); add_kallsyms(mod, sechdrs, symindex, strindex, secstrings);#ifdef CONFIG_MARKERS if (!mod->taints) marker_update_probe_range(mod->markers, mod->markers + mod->num_markers);#endif err = module_finalize(hdr, sechdrs, mod); if (err < 0) goto cleanup; /* flush the icache in correct context */ old_fs = get_fs(); set_fs(KERNEL_DS); /* * Flush the instruction cache, since we've played with text. * Do it before processing of module parameters, so the module * can provide parameter accessor functions of its own. */ if (mod->module_init) flush_icache_range((unsigned long)mod->module_init, (unsigned long)mod->module_init + mod->init_size); flush_icache_range((unsigned long)mod->module_core, (unsigned long)mod->module_core + mod->core_size); set_fs(old_fs); mod->args = args; if (obsparmindex) printk(KERN_WARNING "%s: Ignoring obsolete parameters\n", mod->name); /* Now sew it into the lists so we can get lockdep and oops * info during argument parsing. Noone should access us, since * strong_try_module_get() will fail. */ stop_machine(__link_module, mod, NULL); /* Size of section 0 is 0, so this works well if no params */ err = parse_args(mod->name, mod->args, (struct kernel_param *) sechdrs[setupindex].sh_addr, sechdrs[setupindex].sh_size / sizeof(struct kernel_param), NULL); if (err < 0) goto unlink; err = mod_sysfs_setup(mod, (struct kernel_param *) sechdrs[setupindex].sh_addr, sechdrs[setupindex].sh_size / sizeof(struct kernel_param)); if (err < 0) goto unlink; add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs); /* Size of section 0 is 0, so this works well if no unwind info. */ mod->unwind_info = unwind_add_table(mod, (void *)sechdrs[unwindex].sh_addr, sechdrs[unwindex].sh_size); /* Get rid of temporary copy */ vfree(hdr); /* Done! */ return mod; unlink: stop_machine(__unlink_module, mod, NULL); module_arch_cleanup(mod); cleanup: kobject_del(&mod->mkobj.kobj); kobject_put(&mod->mkobj.kobj); free_unload: module_unload_free(mod); module_free(mod, mod->module_init); free_core: module_free(mod, mod->module_core); free_percpu: if (percpu) percpu_modfree(percpu); free_mod: kfree(args); free_hdr: vfree(hdr); return ERR_PTR(err); truncated: printk(KERN_ERR "Module len %lu truncated\n", len); err = -ENOEXEC; goto free_hdr;}/* This is where the real work happens */asmlinkage longsys_init_module(void __user *umod, unsigned long len, const char __user *uargs){ struct module *mod; int ret = 0; /* Must have permission */ if (!capable(CAP_SYS_MODULE)) return -EPERM; /* Only one module load at a time, please */ if (mutex_lock_interruptible(&module_mutex) != 0) return -EINTR; /* Do all the hard work */ mod = load_module(umod, len, uargs); if (IS_ERR(mod)) { mutex_unlock(&module_mutex); return PTR_ERR(mod); } /* Drop lock so they can recurse */ mutex_unlock(&module_mutex); blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod); /* Start the module */ if (mod->init != NULL) ret = do_one_initcall(mod->init); if (ret < 0) { /* Init routine failed: abort. Try to protect us from buggy refcounters. */ mod->state = MODULE_STATE_GOING; synchronize_sched(); module_put(mod); blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod); mutex_lock(&module_mutex); free_module(mod); mutex_unlock(&module_mutex); wake_up(&module_wq); return ret; } if (ret > 0) { printk(KERN_WARNING "%s: '%s'->init suspiciously returned %d, " "it should follow 0/-E convention\n" KERN_WARNING "%s: loading module anyway...\n", __func__, mod->name, ret, __func__); dump_stack(); } /* Now it's a first class citizen! Wake up anyone waiting for it. */ mod->state = MODULE_STATE_LIVE; wake_up(&module_wq); mutex_lock(&module_mutex); /* Drop initial reference. */ module_put(mod); unwind_remove_table(mod->unwind_info, 1); module_free(mod, mod->module_init); mod->module_init = NULL; mod->init_size = 0; mod->init_text_size = 0; mutex_unlock(&module_mutex); return 0;}static inline int within(unsigned long addr, void *start, unsigned long size){ return ((void *)addr >= start && (void *)addr < start + size);}#ifdef CONFIG_KALLSYMS/* * This ignores the intensely annoying "mapping symbols" found * in ARM ELF files: $a, $t and $d. */static inline int is_arm_mapping_symbol(const char *str){ return str[0] == '$' && strchr("atd", str[1]) && (str[2] == '\0' || str[2] == '.');}static const char *get_ksymbol(struct module *mod, unsigned long addr, unsigned long *size, unsigned long *offset){ unsigned int i, best = 0; unsigned long nextval; /* At worse, next value is at end of module */ if (within(addr, mod->module_init, mod->init_size)) nextval = (unsigned long)mod->module_init+mod->init_text_size; else nextval = (unsigned long)mod->module_core+mod->core_text_size; /* Scan for closest preceeding symbol, and next symbol. (ELF starts real symbols at 1). */ for (i = 1; i < mod->num_symtab; i++) { if (mod->symtab[i].st_shndx == SHN_UNDEF) continue; /* We ignore unnamed symbols: they're uninformative * and inserted at a whim. */ if (mod->symtab[i].st_value <= addr && mod->symtab[i].st_value > mod->symtab[best].st_value && *(mod->strtab + mod->symtab[i].st_name) != '\0' && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name)) best = i; if (mod->symtab[i].st_value > addr && mod->symtab[i].st_value < nextval && *(mod->strtab + mod->symtab[i].st_name) != '\0' && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name)) nextval = mod->symtab[i].st_value; } if (!best) return NULL; if (size) *size = nextval - mod->symtab[best].st_value; if (offset) *offset = addr - mod->symtab[best].st_value; return mod->strtab + mod->symtab[best].st_name;}/* For kallsyms to ask for address resolution. NULL means not found. Careful * not to lock to avoid deadlock on oopses, simply disable preemption. */const char *module_address_lookup(unsigned long addr, unsigned long *size, unsigned long *offset, char **modname, char *namebuf){ struct module *mod; const char *ret = NULL; preempt_disable(); list_for_each_entry(mod, &modules, list) { if (within(addr, mod->module_init, mod->init_size) || within(addr, mod->module_core, mod->core_size)) { if (modname) *modname = mod->name; ret = get_ksymbol(mod, addr, size, offset); break; } } /* Make a copy in here where it's safe */ if (ret) { strncpy(namebuf, ret, KSYM_NAME_LEN - 1); ret = namebuf; } preempt_enable(); return ret;}int lookup_module_symbol_name(unsigned long addr, char *symname){ struct module *mod; preempt_disable(); list_for_each_entry(mod, &modules, list) { if (within(addr, mod->module_init, mod->init_size) || within(addr, mod->module_core, mod->core_size)) { const char *sym; sym = get_ksymbol(mod, addr, NULL, NULL); if (!sym) goto out; strlcpy(symname, sym, KSYM_NAME_LEN); preempt_enable(); return 0; } }out: preempt_enable(); return -ERANGE;}int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, unsigned long *offset, char *modname, char *name){ struct module *mod; preempt_disable(); list_for_each_entry(mod, &modules, list) { if (within(addr, mod->module_init, mod->init_size) || within(addr, mod->module_core, mod->core_size)) { const char *sym; sym = get_ksymbol(mod, addr, size, offset); if (!sym) goto out; if (modname) strlcpy(modname, mod->name, MODULE_NAME_LEN); if (name) strlcpy(name, sym, KSYM_NAME_LEN); preempt_enable(); return 0; } }out: preempt_enable(); return -ERANGE;}int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, char *name, char *module_name, int *exported){ struct module *mod; preempt_disable(); list_for_each_entry(mod, &modules, list) { if (symnum < mod->num_symtab) { *value = mod->symtab[symnum].st_value; *type = mod->symtab[symnum].st_info;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -