📄 module.c
字号:
mod = load_module(umod, len, uargs); if (IS_ERR(mod)) { mutex_unlock(&module_mutex); return PTR_ERR(mod); } /* Now sew it into the lists. They won't access us, since strong_try_module_get() will fail. */ stop_machine_run(__link_module, mod, NR_CPUS); /* 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 = mod->init(); if (ret < 0) { /* Init routine failed: abort. Try to protect us from buggy refcounters. */ mod->state = MODULE_STATE_GOING; synchronize_sched(); if (mod->unsafe) printk(KERN_ERR "%s: module is now stuck!\n", mod->name); else { module_put(mod); mutex_lock(&module_mutex); free_module(mod); mutex_unlock(&module_mutex); } return ret; } /* Now it's a first class citizen! */ mutex_lock(&module_mutex); mod->state = MODULE_STATE_LIVE; /* 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. We don't lock, as this is used for oops resolution and races are a lesser concern. */const char *module_address_lookup(unsigned long addr, unsigned long *size, unsigned long *offset, char **modname){ struct module *mod; 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; return get_ksymbol(mod, addr, size, offset); } } return NULL;}int lookup_module_symbol_name(unsigned long addr, char *symname){ struct module *mod; mutex_lock(&module_mutex); 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 + 1); mutex_unlock(&module_mutex); return 0; } }out: mutex_unlock(&module_mutex); return -ERANGE;}int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, unsigned long *offset, char *modname, char *name){ struct module *mod; mutex_lock(&module_mutex); 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 + 1); if (name) strlcpy(name, sym, KSYM_NAME_LEN + 1); mutex_unlock(&module_mutex); return 0; } }out: mutex_unlock(&module_mutex); return -ERANGE;}int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, char *name, char *module_name, int *exported){ struct module *mod; mutex_lock(&module_mutex); list_for_each_entry(mod, &modules, list) { if (symnum < mod->num_symtab) { *value = mod->symtab[symnum].st_value; *type = mod->symtab[symnum].st_info; strlcpy(name, mod->strtab + mod->symtab[symnum].st_name, KSYM_NAME_LEN + 1); strlcpy(module_name, mod->name, MODULE_NAME_LEN + 1); *exported = is_exported(name, mod); mutex_unlock(&module_mutex); return 0; } symnum -= mod->num_symtab; } mutex_unlock(&module_mutex); return -ERANGE;}static unsigned long mod_find_symname(struct module *mod, const char *name){ unsigned int i; for (i = 0; i < mod->num_symtab; i++) if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0 && mod->symtab[i].st_info != 'U') return mod->symtab[i].st_value; return 0;}/* Look for this name: can be of form module:name. */unsigned long module_kallsyms_lookup_name(const char *name){ struct module *mod; char *colon; unsigned long ret = 0; /* Don't lock: we're in enough trouble already. */ if ((colon = strchr(name, ':')) != NULL) { *colon = '\0'; if ((mod = find_module(name)) != NULL) ret = mod_find_symname(mod, colon+1); *colon = ':'; } else { list_for_each_entry(mod, &modules, list) if ((ret = mod_find_symname(mod, name)) != 0) break; } return ret;}#endif /* CONFIG_KALLSYMS *//* Called by the /proc file system to return a list of modules. */static void *m_start(struct seq_file *m, loff_t *pos){ struct list_head *i; loff_t n = 0; mutex_lock(&module_mutex); list_for_each(i, &modules) { if (n++ == *pos) break; } if (i == &modules) return NULL; return i;}static void *m_next(struct seq_file *m, void *p, loff_t *pos){ struct list_head *i = p; (*pos)++; if (i->next == &modules) return NULL; return i->next;}static void m_stop(struct seq_file *m, void *p){ mutex_unlock(&module_mutex);}static char *taint_flags(unsigned int taints, char *buf){ int bx = 0; if (taints) { buf[bx++] = '('; if (taints & TAINT_PROPRIETARY_MODULE) buf[bx++] = 'P'; if (taints & TAINT_FORCED_MODULE) buf[bx++] = 'F'; /* * TAINT_FORCED_RMMOD: could be added. * TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't * apply to modules. */ buf[bx++] = ')'; } buf[bx] = '\0'; return buf;}static int m_show(struct seq_file *m, void *p){ struct module *mod = list_entry(p, struct module, list); char buf[8]; seq_printf(m, "%s %lu", mod->name, mod->init_size + mod->core_size); print_unload_info(m, mod); /* Informative for users. */ seq_printf(m, " %s", mod->state == MODULE_STATE_GOING ? "Unloading": mod->state == MODULE_STATE_COMING ? "Loading": "Live"); /* Used by oprofile and other similar tools. */ seq_printf(m, " 0x%p", mod->module_core); /* Taints info */ if (mod->taints) seq_printf(m, " %s", taint_flags(mod->taints, buf)); seq_printf(m, "\n"); return 0;}/* Format: modulename size refcount deps address Where refcount is a number or -, and deps is a comma-separated list of depends or -.*/const struct seq_operations modules_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = m_show};/* Given an address, look for it in the module exception tables. */const struct exception_table_entry *search_module_extables(unsigned long addr){ unsigned long flags; const struct exception_table_entry *e = NULL; struct module *mod; spin_lock_irqsave(&modlist_lock, flags); list_for_each_entry(mod, &modules, list) { if (mod->num_exentries == 0) continue; e = search_extable(mod->extable, mod->extable + mod->num_exentries - 1, addr); if (e) break; } spin_unlock_irqrestore(&modlist_lock, flags); /* Now, if we found one, we are running inside it now, hence we cannot unload the module, hence no refcnt needed. */ return e;}/* * Is this a valid module address? */int is_module_address(unsigned long addr){ unsigned long flags; struct module *mod; spin_lock_irqsave(&modlist_lock, flags); list_for_each_entry(mod, &modules, list) { if (within(addr, mod->module_core, mod->core_size)) { spin_unlock_irqrestore(&modlist_lock, flags); return 1; } } spin_unlock_irqrestore(&modlist_lock, flags); return 0;}/* Is this a valid kernel address? We don't grab the lock: we are oopsing. */struct module *__module_text_address(unsigned long addr){ struct module *mod; list_for_each_entry(mod, &modules, list) if (within(addr, mod->module_init, mod->init_text_size) || within(addr, mod->module_core, mod->core_text_size)) return mod; return NULL;}struct module *module_text_address(unsigned long addr){ struct module *mod; unsigned long flags; spin_lock_irqsave(&modlist_lock, flags); mod = __module_text_address(addr); spin_unlock_irqrestore(&modlist_lock, flags); return mod;}/* Don't grab lock, we're oopsing. */void print_modules(void){ struct module *mod; char buf[8]; printk("Modules linked in:"); list_for_each_entry(mod, &modules, list) printk(" %s%s", mod->name, taint_flags(mod->taints, buf)); printk("\n");}#ifdef CONFIG_SYSFSstatic char *make_driver_name(struct device_driver *drv){ char *driver_name; driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2, GFP_KERNEL); if (!driver_name) return NULL; sprintf(driver_name, "%s:%s", drv->bus->name, drv->name); return driver_name;}static void module_create_drivers_dir(struct module_kobject *mk){ if (!mk || mk->drivers_dir) return; mk->drivers_dir = kobject_add_dir(&mk->kobj, "drivers");}void module_add_driver(struct module *mod, struct device_driver *drv){ char *driver_name; int no_warn; struct module_kobject *mk = NULL; if (!drv) return; if (mod) mk = &mod->mkobj; else if (drv->mod_name) { struct kobject *mkobj; /* Lookup built-in module entry in /sys/modules */ mkobj = kset_find_obj(&module_subsys, drv->mod_name); if (mkobj) { mk = container_of(mkobj, struct module_kobject, kobj); /* remember our module structure */ drv->mkobj = mk; /* kset_find_obj took a reference */ kobject_put(mkobj); } } if (!mk) return; /* Don't check return codes; these calls are idempotent */ no_warn = sysfs_create_link(&drv->kobj, &mk->kobj, "module"); driver_name = make_driver_name(drv); if (driver_name) { module_create_drivers_dir(mk); no_warn = sysfs_create_link(mk->drivers_dir, &drv->kobj, driver_name); kfree(driver_name); }}EXPORT_SYMBOL(module_add_driver);void module_remove_driver(struct device_driver *drv){ struct module_kobject *mk = NULL; char *driver_name; if (!drv) return; sysfs_remove_link(&drv->kobj, "module"); if (drv->owner) mk = &drv->owner->mkobj; else if (drv->mkobj) mk = drv->mkobj; if (mk && mk->drivers_dir) { driver_name = make_driver_name(drv); if (driver_name) { sysfs_remove_link(mk->drivers_dir, driver_name); kfree(driver_name); } }}EXPORT_SYMBOL(module_r
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -