diff options
Diffstat (limited to 'kernel/module.c')
| -rw-r--r-- | kernel/module.c | 319 | 
1 files changed, 171 insertions, 148 deletions
| diff --git a/kernel/module.c b/kernel/module.c index 8d6cccc6c3c..8674a390a2e 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -164,131 +164,140 @@ static const struct kernel_symbol *lookup_symbol(const char *name,  	return NULL;  } -static void printk_unused_warning(const char *name) +static bool always_ok(bool gplok, bool warn, 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"); +	return true;  } -/* 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) +static bool printk_unused_warning(bool gplok, bool warn, const char *name)  { -	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 (warn) { +		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");  	} -	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; +	return true; +} + +static bool gpl_only_unused_warning(bool gplok, bool warn, const char *name) +{ +	if (!gplok) +		return false; +	return printk_unused_warning(gplok, warn, name); +} + +static bool gpl_only(bool gplok, bool warn, const char *name) +{ +	return gplok; +} + +static bool warn_if_not_gpl(bool gplok, bool warn, const char *name) +{ +	if (!gplok && warn) { +		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");  	} +	return true; +} -	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; +struct symsearch { +	const struct kernel_symbol *start, *stop; +	const unsigned long *crcs; +	bool (*check)(bool gplok, bool warn, const char *name); +}; + +/* Look through this array of symbol tables for a symbol match which + * passes the check function. */ +static const struct kernel_symbol *search_symarrays(const struct symsearch *arr, +						    unsigned int num, +						    const char *name, +						    bool gplok, +						    bool warn, +						    const unsigned long **crc) +{ +	unsigned int i; +	const struct kernel_symbol *ks; + +	for (i = 0; i < num; i++) { +		ks = lookup_symbol(name, arr[i].start, arr[i].stop); +		if (!ks || !arr[i].check(gplok, warn, name)) +			continue; + +		if (crc) +			*crc = symversion(arr[i].crcs, ks - arr[i].start); +		return ks;  	} +	return NULL; +} + +/* Find a symbol, return value, (optional) crc and (optional) module + * which owns it */ +static unsigned long find_symbol(const char *name, +				 struct module **owner, +				 const unsigned long **crc, +				 bool gplok, +				 bool warn) +{ +	struct module *mod; +	const struct kernel_symbol *ks; +	const struct symsearch arr[] = { +		{ __start___ksymtab, __stop___ksymtab, __start___kcrctab, +		  always_ok }, +		{ __start___ksymtab_gpl, __stop___ksymtab_gpl, +		  __start___kcrctab_gpl, gpl_only }, +		{ __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, +		  __start___kcrctab_gpl_future, warn_if_not_gpl }, +		{ __start___ksymtab_unused, __stop___ksymtab_unused, +		  __start___kcrctab_unused, printk_unused_warning }, +		{ __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, +		  __start___kcrctab_unused_gpl, gpl_only_unused_warning }, +	}; -	if (gplok) -		ks = lookup_symbol(name, __start___ksymtab_unused_gpl, -				 __stop___ksymtab_unused_gpl); +	/* Core kernel first. */ +	ks = search_symarrays(arr, ARRAY_SIZE(arr), name, gplok, warn, crc);  	if (ks) { -		printk_unused_warning(name); -		*crc = symversion(__start___kcrctab_unused_gpl, -				  (ks - __start___ksymtab_unused_gpl)); +		if (owner) +			*owner = NULL;  		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); +		struct symsearch arr[] = { +			{ mod->syms, mod->syms + mod->num_syms, mod->crcs, +			  always_ok }, +			{ mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms, +			  mod->gpl_crcs, gpl_only }, +			{ mod->gpl_future_syms, +			  mod->gpl_future_syms + mod->num_gpl_future_syms, +			  mod->gpl_future_crcs, warn_if_not_gpl }, +			{ mod->unused_syms, +			  mod->unused_syms + mod->num_unused_syms, +			  mod->unused_crcs, printk_unused_warning }, +			{ mod->unused_gpl_syms, +			  mod->unused_gpl_syms + mod->num_unused_gpl_syms, +			  mod->unused_gpl_crcs, gpl_only_unused_warning }, +		}; + +		ks = search_symarrays(arr, ARRAY_SIZE(arr), +				      name, gplok, warn, crc);  		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)); +			if (owner) +				*owner = mod;  			return ks->value;  		}  	} +  	DEBUGP("Failed to find symbol %s\n", name);  	return -ENOENT;  } @@ -736,12 +745,13 @@ sys_delete_module(const char __user *name_user, unsigned int flags)  	if (!forced && module_refcount(mod) != 0)  		wait_for_zero_refcount(mod); +	mutex_unlock(&module_mutex);  	/* Final destruction now noone is using it. */ -	if (mod->exit != NULL) { -		mutex_unlock(&module_mutex); +	if (mod->exit != NULL)  		mod->exit(); -		mutex_lock(&module_mutex); -	} +	blocking_notifier_call_chain(&module_notify_list, +				     MODULE_STATE_GOING, mod); +	mutex_lock(&module_mutex);  	/* Store the name of the last unloaded module for diagnostic purposes */  	strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));  	free_module(mod); @@ -777,10 +787,9 @@ static void print_unload_info(struct seq_file *m, struct module *mod)  void __symbol_put(const char *symbol)  {  	struct module *owner; -	const unsigned long *crc;  	preempt_disable(); -	if (IS_ERR_VALUE(__find_symbol(symbol, &owner, &crc, 1))) +	if (IS_ERR_VALUE(find_symbol(symbol, &owner, NULL, true, false)))  		BUG();  	module_put(owner);  	preempt_enable(); @@ -924,13 +933,10 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs,  					  struct module *mod)  {  	const unsigned long *crc; -	struct module *owner; -	if (IS_ERR_VALUE(__find_symbol("struct_module", -						&owner, &crc, 1))) +	if (IS_ERR_VALUE(find_symbol("struct_module", NULL, &crc, true, false)))  		BUG(); -	return check_version(sechdrs, versindex, "struct_module", mod, -			     crc); +	return check_version(sechdrs, versindex, "struct_module", mod, crc);  }  /* First part is kernel version, which we ignore. */ @@ -974,8 +980,8 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs,  	unsigned long ret;  	const unsigned long *crc; -	ret = __find_symbol(name, &owner, &crc, -			!(mod->taints & TAINT_PROPRIETARY_MODULE)); +	ret = find_symbol(name, &owner, &crc, +			  !(mod->taints & TAINT_PROPRIETARY_MODULE), true);  	if (!IS_ERR_VALUE(ret)) {  		/* use_module can fail due to OOM,  		   or module initialization or unloading */ @@ -991,6 +997,20 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs,   * J. Corbet <corbet@lwn.net>   */  #if defined(CONFIG_KALLSYMS) && defined(CONFIG_SYSFS) +struct module_sect_attr +{ +	struct module_attribute mattr; +	char *name; +	unsigned long address; +}; + +struct module_sect_attrs +{ +	struct attribute_group grp; +	unsigned int nsections; +	struct module_sect_attr attrs[0]; +}; +  static ssize_t module_sect_show(struct module_attribute *mattr,  				struct module *mod, char *buf)  { @@ -1001,7 +1021,7 @@ static ssize_t module_sect_show(struct module_attribute *mattr,  static void free_sect_attrs(struct module_sect_attrs *sect_attrs)  { -	int section; +	unsigned int section;  	for (section = 0; section < sect_attrs->nsections; section++)  		kfree(sect_attrs->attrs[section].name); @@ -1362,10 +1382,9 @@ void *__symbol_get(const char *symbol)  {  	struct module *owner;  	unsigned long value; -	const unsigned long *crc;  	preempt_disable(); -	value = __find_symbol(symbol, &owner, &crc, 1); +	value = find_symbol(symbol, &owner, NULL, true, true);  	if (IS_ERR_VALUE(value))  		value = 0;  	else if (strong_try_module_get(owner)) @@ -1382,33 +1401,33 @@ EXPORT_SYMBOL_GPL(__symbol_get);   */  static int verify_export_symbols(struct module *mod)  { -	const char *name = NULL; -	unsigned long i, ret = 0; +	unsigned int i;  	struct module *owner; -	const unsigned long *crc; - -	for (i = 0; i < mod->num_syms; i++) -		if (!IS_ERR_VALUE(__find_symbol(mod->syms[i].name, -							&owner, &crc, 1))) { -			name = mod->syms[i].name; -			ret = -ENOEXEC; -			goto dup; -		} +	const struct kernel_symbol *s; +	struct { +		const struct kernel_symbol *sym; +		unsigned int num; +	} arr[] = { +		{ mod->syms, mod->num_syms }, +		{ mod->gpl_syms, mod->num_gpl_syms }, +		{ mod->gpl_future_syms, mod->num_gpl_future_syms }, +		{ mod->unused_syms, mod->num_unused_syms }, +		{ mod->unused_gpl_syms, mod->num_unused_gpl_syms }, +	}; -	for (i = 0; i < mod->num_gpl_syms; i++) -		if (!IS_ERR_VALUE(__find_symbol(mod->gpl_syms[i].name, -							&owner, &crc, 1))) { -			name = mod->gpl_syms[i].name; -			ret = -ENOEXEC; -			goto dup; +	for (i = 0; i < ARRAY_SIZE(arr); i++) { +		for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { +			if (!IS_ERR_VALUE(find_symbol(s->name, &owner, +						      NULL, true, false))) { +				printk(KERN_ERR +				       "%s: exports duplicate symbol %s" +				       " (owned by %s)\n", +				       mod->name, s->name, module_name(owner)); +				return -ENOEXEC; +			}  		} - -dup: -	if (ret) -		printk(KERN_ERR "%s: exports duplicate symbol %s (owned by %s)\n", -			mod->name, name, module_name(owner)); - -	return ret; +	} +	return 0;  }  /* Change all symbols so that st_value encodes the pointer directly. */ @@ -1814,8 +1833,9 @@ static struct module *load_module(void __user *umod,  	unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME);  #endif -	/* Don't keep modinfo section */ +	/* Don't keep modinfo and version sections. */  	sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; +	sechdrs[versindex].sh_flags &= ~(unsigned long)SHF_ALLOC;  #ifdef CONFIG_KALLSYMS  	/* Keep symbol and string tables for decoding later. */  	sechdrs[symindex].sh_flags |= SHF_ALLOC; @@ -1977,7 +1997,8 @@ static struct module *load_module(void __user *umod,  		mod->unused_crcs = (void *)sechdrs[unusedcrcindex].sh_addr;  	mod->unused_gpl_syms = (void *)sechdrs[unusedgplindex].sh_addr;  	if (unusedgplcrcindex) -		mod->unused_crcs = (void *)sechdrs[unusedgplcrcindex].sh_addr; +		mod->unused_gpl_crcs +			= (void *)sechdrs[unusedgplcrcindex].sh_addr;  #ifdef CONFIG_MODVERSIONS  	if ((mod->num_syms && !crcindex) || @@ -2171,6 +2192,8 @@ sys_init_module(void __user *umod,  		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); | 
