📄 module.c
字号:
/* Copyright (C) 2002 Richard Henderson Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM. This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include <linux/module.h>#include <linux/moduleloader.h>#include <linux/init.h>#include <linux/kallsyms.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/elf.h>#include <linux/seq_file.h>#include <linux/syscalls.h>#include <linux/fcntl.h>#include <linux/rcupdate.h>#include <linux/capability.h>#include <linux/cpu.h>#include <linux/moduleparam.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/vermagic.h>#include <linux/notifier.h>#include <linux/sched.h>#include <linux/stop_machine.h>#include <linux/device.h>#include <linux/string.h>#include <linux/mutex.h>#include <linux/unwind.h>#include <asm/uaccess.h>#include <asm/semaphore.h>#include <asm/cacheflush.h>#include <linux/license.h>extern int module_sysfs_initialized;#if 0#define DEBUGP printk#else#define DEBUGP(fmt , a...)#endif#ifndef ARCH_SHF_SMALL#define ARCH_SHF_SMALL 0#endif/* If this is set, the section belongs in the init part of the module */#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))/* Protects module list */static DEFINE_SPINLOCK(modlist_lock);/* List of modules, protected by module_mutex AND modlist_lock */static DEFINE_MUTEX(module_mutex);static LIST_HEAD(modules);static BLOCKING_NOTIFIER_HEAD(module_notify_list);int register_module_notifier(struct notifier_block * nb){ return blocking_notifier_chain_register(&module_notify_list, nb);}EXPORT_SYMBOL(register_module_notifier);int unregister_module_notifier(struct notifier_block * nb){ return blocking_notifier_chain_unregister(&module_notify_list, nb);}EXPORT_SYMBOL(unregister_module_notifier);/* We require a truly strong try_module_get() */static inline int strong_try_module_get(struct module *mod){ if (mod && mod->state == MODULE_STATE_COMING) return 0; return try_module_get(mod);}static inline void add_taint_module(struct module *mod, unsigned flag){ add_taint(flag); mod->taints |= flag;}/* * A thread that wants to hold a reference to a module only while it * is running can call this to safely exit. nfsd and lockd use this. */void __module_put_and_exit(struct module *mod, long code){ module_put(mod); do_exit(code);}EXPORT_SYMBOL(__module_put_and_exit); /* Find a module section: 0 means not found. */static unsigned int find_sec(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings, const char *name){ unsigned int i; for (i = 1; i < hdr->e_shnum; i++) /* Alloc bit cleared means "ignore it." */ if ((sechdrs[i].sh_flags & SHF_ALLOC) && strcmp(secstrings+sechdrs[i].sh_name, name) == 0) return i; return 0;}/* Provided by the linker */extern const struct kernel_symbol __start___ksymtab[];extern const struct kernel_symbol __stop___ksymtab[];extern const struct kernel_symbol __start___ksymtab_gpl[];extern const struct kernel_symbol __stop___ksymtab_gpl[];extern const struct kernel_symbol __start___ksymtab_gpl_future[];extern const struct kernel_symbol __stop___ksymtab_gpl_future[];extern const struct kernel_symbol __start___ksymtab_unused[];extern const struct kernel_symbol __stop___ksymtab_unused[];extern const struct kernel_symbol __start___ksymtab_unused_gpl[];extern const struct kernel_symbol __stop___ksymtab_unused_gpl[];extern const struct kernel_symbol __start___ksymtab_gpl_future[];extern const struct kernel_symbol __stop___ksymtab_gpl_future[];extern const unsigned long __start___kcrctab[];extern const unsigned long __start___kcrctab_gpl[];extern const unsigned long __start___kcrctab_gpl_future[];extern const unsigned long __start___kcrctab_unused[];extern const unsigned long __start___kcrctab_unused_gpl[];#ifndef CONFIG_MODVERSIONS#define symversion(base, idx) NULL#else#define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL)#endif/* lookup symbol in given range of kernel_symbols */static const struct kernel_symbol *lookup_symbol(const char *name, const struct kernel_symbol *start, const struct kernel_symbol *stop){ const struct kernel_symbol *ks = start; for (; ks < stop; ks++) if (strcmp(ks->name, name) == 0) return ks; return NULL;}static void printk_unused_warning(const char *name){ printk(KERN_WARNING "Symbol %s is marked as UNUSED, " "however this module is using it.\n", name); printk(KERN_WARNING "This symbol will go away in the future.\n"); printk(KERN_WARNING "Please evalute if this is the right api to use, " "and if it really is, submit a report the linux kernel " "mailinglist together with submitting your code for " "inclusion.\n");}/* Find a symbol, return value, crc and module which owns it */static unsigned long __find_symbol(const char *name, struct module **owner, const unsigned long **crc, int gplok){ struct module *mod; const struct kernel_symbol *ks; /* Core kernel first. */ *owner = NULL; ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab); if (ks) { *crc = symversion(__start___kcrctab, (ks - __start___ksymtab)); return ks->value; } if (gplok) { ks = lookup_symbol(name, __start___ksymtab_gpl, __stop___ksymtab_gpl); if (ks) { *crc = symversion(__start___kcrctab_gpl, (ks - __start___ksymtab_gpl)); return ks->value; } } ks = lookup_symbol(name, __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future); if (ks) { if (!gplok) { printk(KERN_WARNING "Symbol %s is being used " "by a non-GPL module, which will not " "be allowed in the future\n", name); printk(KERN_WARNING "Please see the file " "Documentation/feature-removal-schedule.txt " "in the kernel source tree for more " "details.\n"); } *crc = symversion(__start___kcrctab_gpl_future, (ks - __start___ksymtab_gpl_future)); return ks->value; } ks = lookup_symbol(name, __start___ksymtab_unused, __stop___ksymtab_unused); if (ks) { printk_unused_warning(name); *crc = symversion(__start___kcrctab_unused, (ks - __start___ksymtab_unused)); return ks->value; } if (gplok) ks = lookup_symbol(name, __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl); if (ks) { printk_unused_warning(name); *crc = symversion(__start___kcrctab_unused_gpl, (ks - __start___ksymtab_unused_gpl)); return ks->value; } /* Now try modules. */ list_for_each_entry(mod, &modules, list) { *owner = mod; ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms); if (ks) { *crc = symversion(mod->crcs, (ks - mod->syms)); return ks->value; } if (gplok) { ks = lookup_symbol(name, mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms); if (ks) { *crc = symversion(mod->gpl_crcs, (ks - mod->gpl_syms)); return ks->value; } } ks = lookup_symbol(name, mod->unused_syms, mod->unused_syms + mod->num_unused_syms); if (ks) { printk_unused_warning(name); *crc = symversion(mod->unused_crcs, (ks - mod->unused_syms)); return ks->value; } if (gplok) { ks = lookup_symbol(name, mod->unused_gpl_syms, mod->unused_gpl_syms + mod->num_unused_gpl_syms); if (ks) { printk_unused_warning(name); *crc = symversion(mod->unused_gpl_crcs, (ks - mod->unused_gpl_syms)); return ks->value; } } ks = lookup_symbol(name, mod->gpl_future_syms, (mod->gpl_future_syms + mod->num_gpl_future_syms)); if (ks) { if (!gplok) { printk(KERN_WARNING "Symbol %s is being used " "by a non-GPL module, which will not " "be allowed in the future\n", name); printk(KERN_WARNING "Please see the file " "Documentation/feature-removal-schedule.txt " "in the kernel source tree for more " "details.\n"); } *crc = symversion(mod->gpl_future_crcs, (ks - mod->gpl_future_syms)); return ks->value; } } DEBUGP("Failed to find symbol %s\n", name); return 0;}/* Search for module by name: must hold module_mutex. */static struct module *find_module(const char *name){ struct module *mod; list_for_each_entry(mod, &modules, list) { if (strcmp(mod->name, name) == 0) return mod; } return NULL;}#ifdef CONFIG_SMP/* Number of blocks used and allocated. */static unsigned int pcpu_num_used, pcpu_num_allocated;/* Size of each block. -ve means used. */static int *pcpu_size;static int split_block(unsigned int i, unsigned short size){ /* Reallocation required? */ if (pcpu_num_used + 1 > pcpu_num_allocated) { int *new; new = krealloc(pcpu_size, sizeof(new[0])*pcpu_num_allocated*2, GFP_KERNEL); if (!new) return 0; pcpu_num_allocated *= 2; pcpu_size = new; } /* Insert a new subblock */ memmove(&pcpu_size[i+1], &pcpu_size[i], sizeof(pcpu_size[0]) * (pcpu_num_used - i)); pcpu_num_used++; pcpu_size[i+1] -= size; pcpu_size[i] = size; return 1;}static inline unsigned int block_size(int val){ if (val < 0) return -val; return val;}/* Created by linker magic */extern char __per_cpu_start[], __per_cpu_end[];static void *percpu_modalloc(unsigned long size, unsigned long align, const char *name){ unsigned long extra; unsigned int i; void *ptr; if (align > PAGE_SIZE) { printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n", name, align, PAGE_SIZE); align = PAGE_SIZE; } ptr = __per_cpu_start; for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) { /* Extra for alignment requirement. */ extra = ALIGN((unsigned long)ptr, align) - (unsigned long)ptr; BUG_ON(i == 0 && extra != 0); if (pcpu_size[i] < 0 || pcpu_size[i] < extra + size) continue; /* Transfer extra to previous block. */ if (pcpu_size[i-1] < 0) pcpu_size[i-1] -= extra; else pcpu_size[i-1] += extra; pcpu_size[i] -= extra; ptr += extra; /* Split block if warranted */ if (pcpu_size[i] - size > sizeof(unsigned long)) if (!split_block(i, size)) return NULL; /* Mark allocated */ pcpu_size[i] = -pcpu_size[i]; return ptr; } printk(KERN_WARNING "Could not allocate %lu bytes percpu data\n", size); return NULL;}static void percpu_modfree(void *freeme){ unsigned int i; void *ptr = __per_cpu_start + block_size(pcpu_size[0]); /* First entry is core kernel percpu data. */ for (i = 1; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) { if (ptr == freeme) { pcpu_size[i] = -pcpu_size[i]; goto free; } } BUG(); free: /* Merge with previous? */ if (pcpu_size[i-1] >= 0) { pcpu_size[i-1] += pcpu_size[i]; pcpu_num_used--; memmove(&pcpu_size[i], &pcpu_size[i+1], (pcpu_num_used - i) * sizeof(pcpu_size[0])); i--; } /* Merge with next? */ if (i+1 < pcpu_num_used && pcpu_size[i+1] >= 0) { pcpu_size[i] += pcpu_size[i+1]; pcpu_num_used--; memmove(&pcpu_size[i+1], &pcpu_size[i+2], (pcpu_num_used - (i+1)) * sizeof(pcpu_size[0])); }}static unsigned int find_pcpusec(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings){ return find_sec(hdr, sechdrs, secstrings, ".data.percpu");}static int percpu_modinit(void){ pcpu_num_used = 2; pcpu_num_allocated = 2; pcpu_size = kmalloc(sizeof(pcpu_size[0]) * pcpu_num_allocated, GFP_KERNEL); /* Static in-kernel percpu data (used). */ pcpu_size[0] = -(__per_cpu_end-__per_cpu_start); /* Free room. */ pcpu_size[1] = PERCPU_ENOUGH_ROOM + pcpu_size[0]; if (pcpu_size[1] < 0) { printk(KERN_ERR "No per-cpu room for modules.\n"); pcpu_num_used = 1; } return 0;} __initcall(percpu_modinit);#else /* ... !CONFIG_SMP */static inline void *percpu_modalloc(unsigned long size, unsigned long align, const char *name){ return NULL;}static inline void percpu_modfree(void *pcpuptr){ BUG();}static inline unsigned int find_pcpusec(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings){ return 0;}static inline void percpu_modcopy(void *pcpudst, const void *src, unsigned long size){ /* pcpusec should be 0, and size of that section should be 0. */ BUG_ON(size != 0);}#endif /* CONFIG_SMP */#define MODINFO_ATTR(field) \static void setup_modinfo_##field(struct module *mod, const char *s) \{ \ mod->field = kstrdup(s, GFP_KERNEL); \} \static ssize_t show_modinfo_##field(struct module_attribute *mattr, \ struct module *mod, char *buffer) \{ \ return sprintf(buffer, "%s\n", mod->field); \} \static int modinfo_##field##_exists(struct module *mod) \{ \ return mod->field != NULL; \} \static void free_modinfo_##field(struct module *mod) \{ \ kfree(mod->field); \ mod->field = NULL; \} \static struct module_attribute modinfo_##field = { \ .attr = { .name = __stringify(field), .mode = 0444, \ .owner = THIS_MODULE }, \ .show = show_modinfo_##field, \ .setup = setup_modinfo_##field, \ .test = modinfo_##field##_exists, \ .free = free_modinfo_##field, \};MODINFO_ATTR(version);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -