module.c

来自「Kernel code of linux kernel」· C语言 代码 · 共 2,482 行 · 第 1/5 页

C
2,482
字号
				return -ENOEXEC;			}		}	}	return 0;}/* Change all symbols so that st_value encodes the pointer directly. */static int simplify_symbols(Elf_Shdr *sechdrs,			    unsigned int symindex,			    const char *strtab,			    unsigned int versindex,			    unsigned int pcpuindex,			    struct module *mod){	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;	unsigned long secbase;	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);	int ret = 0;	for (i = 1; i < n; i++) {		switch (sym[i].st_shndx) {		case SHN_COMMON:			/* We compiled with -fno-common.  These are not			   supposed to happen.  */			DEBUGP("Common symbol: %s\n", strtab + sym[i].st_name);			printk("%s: please compile with -fno-common\n",			       mod->name);			ret = -ENOEXEC;			break;		case SHN_ABS:			/* Don't need to do anything */			DEBUGP("Absolute symbol: 0x%08lx\n",			       (long)sym[i].st_value);			break;		case SHN_UNDEF:			sym[i].st_value			  = resolve_symbol(sechdrs, versindex,					   strtab + sym[i].st_name, mod);			/* Ok if resolved.  */			if (!IS_ERR_VALUE(sym[i].st_value))				break;			/* Ok if weak.  */			if (ELF_ST_BIND(sym[i].st_info) == STB_WEAK)				break;			printk(KERN_WARNING "%s: Unknown symbol %s\n",			       mod->name, strtab + sym[i].st_name);			ret = -ENOENT;			break;		default:			/* Divert to percpu allocation if a percpu var. */			if (sym[i].st_shndx == pcpuindex)				secbase = (unsigned long)mod->percpu;			else				secbase = sechdrs[sym[i].st_shndx].sh_addr;			sym[i].st_value += secbase;			break;		}	}	return ret;}/* Update size with this section: return offset. */static long get_offset(unsigned int *size, Elf_Shdr *sechdr){	long ret;	ret = ALIGN(*size, sechdr->sh_addralign ?: 1);	*size = ret + sechdr->sh_size;	return ret;}/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld   might -- code, read-only data, read-write data, small data.  Tally   sizes, and place the offsets into sh_entsize fields: high bit means it   belongs in init. */static void layout_sections(struct module *mod,			    const Elf_Ehdr *hdr,			    Elf_Shdr *sechdrs,			    const char *secstrings){	static unsigned long const masks[][2] = {		/* NOTE: all executable code must be the first section		 * in this array; otherwise modify the text_size		 * finder in the two loops below */		{ SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },		{ SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },		{ SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },		{ ARCH_SHF_SMALL | SHF_ALLOC, 0 }	};	unsigned int m, i;	for (i = 0; i < hdr->e_shnum; i++)		sechdrs[i].sh_entsize = ~0UL;	DEBUGP("Core section allocation order:\n");	for (m = 0; m < ARRAY_SIZE(masks); ++m) {		for (i = 0; i < hdr->e_shnum; ++i) {			Elf_Shdr *s = &sechdrs[i];			if ((s->sh_flags & masks[m][0]) != masks[m][0]			    || (s->sh_flags & masks[m][1])			    || s->sh_entsize != ~0UL			    || strncmp(secstrings + s->sh_name,				       ".init", 5) == 0)				continue;			s->sh_entsize = get_offset(&mod->core_size, s);			DEBUGP("\t%s\n", secstrings + s->sh_name);		}		if (m == 0)			mod->core_text_size = mod->core_size;	}	DEBUGP("Init section allocation order:\n");	for (m = 0; m < ARRAY_SIZE(masks); ++m) {		for (i = 0; i < hdr->e_shnum; ++i) {			Elf_Shdr *s = &sechdrs[i];			if ((s->sh_flags & masks[m][0]) != masks[m][0]			    || (s->sh_flags & masks[m][1])			    || s->sh_entsize != ~0UL			    || strncmp(secstrings + s->sh_name,				       ".init", 5) != 0)				continue;			s->sh_entsize = (get_offset(&mod->init_size, s)					 | INIT_OFFSET_MASK);			DEBUGP("\t%s\n", secstrings + s->sh_name);		}		if (m == 0)			mod->init_text_size = mod->init_size;	}}static void set_license(struct module *mod, const char *license){	if (!license)		license = "unspecified";	if (!license_is_gpl_compatible(license)) {		if (!(tainted & TAINT_PROPRIETARY_MODULE))			printk(KERN_WARNING "%s: module license '%s' taints "				"kernel.\n", mod->name, license);		add_taint_module(mod, TAINT_PROPRIETARY_MODULE);	}}/* Parse tag=value strings from .modinfo section */static char *next_string(char *string, unsigned long *secsize){	/* Skip non-zero chars */	while (string[0]) {		string++;		if ((*secsize)-- <= 1)			return NULL;	}	/* Skip any zero padding. */	while (!string[0]) {		string++;		if ((*secsize)-- <= 1)			return NULL;	}	return string;}static char *get_modinfo(Elf_Shdr *sechdrs,			 unsigned int info,			 const char *tag){	char *p;	unsigned int taglen = strlen(tag);	unsigned long size = sechdrs[info].sh_size;	for (p = (char *)sechdrs[info].sh_addr; p; p = next_string(p, &size)) {		if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')			return p + taglen + 1;	}	return NULL;}static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs,			  unsigned int infoindex){	struct module_attribute *attr;	int i;	for (i = 0; (attr = modinfo_attrs[i]); i++) {		if (attr->setup)			attr->setup(mod,				    get_modinfo(sechdrs,						infoindex,						attr->attr.name));	}}#ifdef CONFIG_KALLSYMS/* 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 int is_exported(const char *name, const struct module *mod){	if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab))		return 1;	else		if (mod && lookup_symbol(name, mod->syms, mod->syms + mod->num_syms))			return 1;		else			return 0;}/* As per nm */static char elf_type(const Elf_Sym *sym,		     Elf_Shdr *sechdrs,		     const char *secstrings,		     struct module *mod){	if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {		if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)			return 'v';		else			return 'w';	}	if (sym->st_shndx == SHN_UNDEF)		return 'U';	if (sym->st_shndx == SHN_ABS)		return 'a';	if (sym->st_shndx >= SHN_LORESERVE)		return '?';	if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR)		return 't';	if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC	    && sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) {		if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE))			return 'r';		else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)			return 'g';		else			return 'd';	}	if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) {		if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)			return 's';		else			return 'b';	}	if (strncmp(secstrings + sechdrs[sym->st_shndx].sh_name,		    ".debug", strlen(".debug")) == 0)		return 'n';	return '?';}static void add_kallsyms(struct module *mod,			 Elf_Shdr *sechdrs,			 unsigned int symindex,			 unsigned int strindex,			 const char *secstrings){	unsigned int i;	mod->symtab = (void *)sechdrs[symindex].sh_addr;	mod->num_symtab = sechdrs[symindex].sh_size / sizeof(Elf_Sym);	mod->strtab = (void *)sechdrs[strindex].sh_addr;	/* Set types up while we still have access to sections. */	for (i = 0; i < mod->num_symtab; i++)		mod->symtab[i].st_info			= elf_type(&mod->symtab[i], sechdrs, secstrings, mod);}#elsestatic inline void add_kallsyms(struct module *mod,				Elf_Shdr *sechdrs,				unsigned int symindex,				unsigned int strindex,				const char *secstrings){}#endif /* CONFIG_KALLSYMS */static void *module_alloc_update_bounds(unsigned long size){	void *ret = module_alloc(size);	if (ret) {		/* Update module bounds. */		if ((unsigned long)ret < module_addr_min)			module_addr_min = (unsigned long)ret;		if ((unsigned long)ret + size > module_addr_max)			module_addr_max = (unsigned long)ret + size;	}	return ret;}/* Allocate and load the module: note that size of section 0 is always   zero, and we rely on this for optional sections. */static noinline struct module *load_module(void __user *umod,				  unsigned long len,				  const char __user *uargs){	Elf_Ehdr *hdr;	Elf_Shdr *sechdrs;	char *secstrings, *args, *modmagic, *strtab = NULL;	unsigned int i;	unsigned int symindex = 0;	unsigned int strindex = 0;	unsigned int setupindex;	unsigned int exindex;	unsigned int exportindex;	unsigned int modindex;	unsigned int obsparmindex;	unsigned int infoindex;	unsigned int gplindex;	unsigned int crcindex;	unsigned int gplcrcindex;	unsigned int versindex;	unsigned int pcpuindex;	unsigned int gplfutureindex;	unsigned int gplfuturecrcindex;	unsigned int unwindex = 0;#ifdef CONFIG_UNUSED_SYMBOLS	unsigned int unusedindex;	unsigned int unusedcrcindex;	unsigned int unusedgplindex;	unsigned int unusedgplcrcindex;#endif	unsigned int markersindex;	unsigned int markersstringsindex;	struct module *mod;	long err = 0;	void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */	struct exception_table_entry *extable;	mm_segment_t old_fs;	DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",	       umod, len, uargs);	if (len < sizeof(*hdr))		return ERR_PTR(-ENOEXEC);	/* Suck in entire file: we'll want most of it. */	/* vmalloc barfs on "unusual" numbers.  Check here */	if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)		return ERR_PTR(-ENOMEM);	if (copy_from_user(hdr, umod, len) != 0) {		err = -EFAULT;		goto free_hdr;	}	/* Sanity checks against insmoding binaries or wrong arch,           weird elf version */	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0	    || hdr->e_type != ET_REL	    || !elf_check_arch(hdr)	    || hdr->e_shentsize != sizeof(*sechdrs)) {		err = -ENOEXEC;		goto free_hdr;	}	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))		goto truncated;	/* Convenience variables */	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 all sections sh_addr with their address in the		   temporary image. */		sechdrs[i].sh_addr = (size_t)hdr + sechdrs[i].sh_offset;		/* Internal symbols and strings. */		if (sechdrs[i].sh_type == SHT_SYMTAB) {			symindex = i;			strindex = sechdrs[i].sh_link;			strtab = (char *)hdr + sechdrs[strindex].sh_offset;		}#ifndef CONFIG_MODULE_UNLOAD		/* Don't load .exit sections */		if (strncmp(secstrings+sechdrs[i].sh_name, ".exit", 5) == 0)			sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;#endif	}	modindex = find_sec(hdr, sechdrs, secstrings,			    ".gnu.linkonce.this_module");	if (!modindex) {		printk(KERN_WARNING "No module found in object\n");		err = -ENOEXEC;		goto free_hdr;	}	mod = (void *)sechdrs[modindex].sh_addr;	if (symindex == 0) {		printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",		       mod->name);		err = -ENOEXEC;		goto free_hdr;	}	/* Optional sections */	exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab");	gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl");	gplfutureindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl_future");	crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab");	gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl");	gplfuturecrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl_future");#ifdef CONFIG_UNUSED_SYMBOLS	unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused");	unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl");	unusedcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused");	unusedgplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused_gpl");#endif	setupindex = find_sec(hdr, sechdrs, secstrings, "__param");	exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table");	obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm");	versindex = find_sec(hdr, sechdrs, secstrings, "__versions");	infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");	pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);#ifdef ARCH_UNWIND_SECTION_NAME	unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME);#endif	/* 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;	sechdrs[strindex].sh_flags |= SHF_ALLOC;#endif	if (unwindex)		sechdrs[unwindex].sh_flags |= SHF_ALLOC;	/* Check module struct version now, before we try to use module. */	if (!check_modstruct_version(sechdrs, versindex, mod)) {		err = -ENOEXEC;		goto free_hdr;	}	modmagic = get_modinfo(sechdrs, infoindex, "vermagic");	/* This is allowed: modprobe --force will invalidate it. */	if (!modmagic) {		err = try_to_force_load(mod, "magic");		if (err)			goto free_hdr;	} else if (!same_magic(modmagic, vermagic, versindex)) {		printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",		       mod->name, modmagic, vermagic);		err = -ENOEXEC;		goto free_hdr;	}	/* Now copy in args */	args = strndup_user(uargs, ~0UL >> 1);	if (IS_ERR(args)) {		err = PTR_ERR(args);		goto free_hdr;	}	if (find_module(mod->name)) {		err = -EEXIST;		goto free_mod;	}	mod->state = MODULE_STATE_COMING;	/* Allow arches to frob section contents and sizes.  */	err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);	if (err < 0)		goto free_mod;	if (pcpuindex) {		/* We have a special allocation for this section. */		percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,					 sechdrs[pcpuindex].sh_addralign,					 mod->name);		if (!percpu) {			err = -ENOMEM;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?