diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2022-08-02 10:06:12 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2022-08-02 10:06:12 -0700 | 
| commit | 8bb5e7f4dcd9b9ef22a3ea25c9066a8a968f12dd (patch) | |
| tree | 0f1383880607a227142f9388a066959926233ff1 /scripts/mod/modpost.c | |
| parent | 2a96271fb66c499e4a89d76a89d3d01170c10bef (diff) | |
| parent | 7c744d00990ea999d27f306f6db5ccb61b1304b2 (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 5.20 (or 6.0) merge window.
Diffstat (limited to 'scripts/mod/modpost.c')
| -rw-r--r-- | scripts/mod/modpost.c | 828 | 
1 files changed, 395 insertions, 433 deletions
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 6bfa33217914..620dc8c4c814 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -13,6 +13,7 @@  #define _GNU_SOURCE  #include <elf.h> +#include <fnmatch.h>  #include <stdio.h>  #include <ctype.h>  #include <string.h> @@ -23,20 +24,20 @@  #include "../../include/linux/license.h"  /* Are we using CONFIG_MODVERSIONS? */ -static int modversions = 0; +static bool modversions;  /* Is CONFIG_MODULE_SRCVERSION_ALL set? */ -static int all_versions = 0; +static bool all_versions;  /* If we are modposting external module set to 1 */ -static int external_module = 0; +static bool external_module;  /* Only warn about unresolved symbols */ -static int warn_unresolved = 0; -/* How a symbol is exported */ -static int sec_mismatch_count = 0; -static int sec_mismatch_warn_only = true; +static bool warn_unresolved; + +static int sec_mismatch_count; +static bool sec_mismatch_warn_only = true;  /* ignore missing files */ -static int ignore_missing_files; +static bool ignore_missing_files;  /* If set to 1, only warn (instead of error) about missing ns imports */ -static int allow_missing_ns_imports; +static bool allow_missing_ns_imports;  static bool error_occurred; @@ -47,12 +48,6 @@ static bool error_occurred;  #define MAX_UNRESOLVED_REPORTS	10  static unsigned int nr_unresolved; -enum export { -	export_plain, -	export_gpl, -	export_unknown -}; -  /* In kernel, this size is defined in linux/module.h;   * here we use Elf_Addr instead of long for covering cross-compile   */ @@ -165,31 +160,43 @@ char *get_line(char **stringp)  }  /* A list of all modules we processed */ -static struct module *modules; +LIST_HEAD(modules);  static struct module *find_module(const char *modname)  {  	struct module *mod; -	for (mod = modules; mod; mod = mod->next) +	list_for_each_entry(mod, &modules, list) {  		if (strcmp(mod->name, modname) == 0) -			break; -	return mod; +			return mod; +	} +	return NULL;  } -static struct module *new_module(const char *modname) +static struct module *new_module(const char *name, size_t namelen)  {  	struct module *mod; -	mod = NOFAIL(malloc(sizeof(*mod) + strlen(modname) + 1)); +	mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1));  	memset(mod, 0, sizeof(*mod)); -	/* add to list */ -	strcpy(mod->name, modname); -	mod->is_vmlinux = (strcmp(modname, "vmlinux") == 0); -	mod->gpl_compatible = -1; -	mod->next = modules; -	modules = mod; +	INIT_LIST_HEAD(&mod->exported_symbols); +	INIT_LIST_HEAD(&mod->unresolved_symbols); +	INIT_LIST_HEAD(&mod->missing_namespaces); +	INIT_LIST_HEAD(&mod->imported_namespaces); + +	memcpy(mod->name, name, namelen); +	mod->name[namelen] = '\0'; +	mod->is_vmlinux = (strcmp(mod->name, "vmlinux") == 0); + +	/* +	 * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE() +	 * is missing, do not check the use for EXPORT_SYMBOL_GPL() becasue +	 * modpost will exit wiht error anyway. +	 */ +	mod->is_gpl_compatible = true; + +	list_add_tail(&mod->list, &modules);  	return mod;  } @@ -201,13 +208,13 @@ static struct module *new_module(const char *modname)  struct symbol {  	struct symbol *next; +	struct list_head list;	/* link to module::exported_symbols or module::unresolved_symbols */  	struct module *module; -	unsigned int crc; -	int crc_valid;  	char *namespace; -	unsigned int weak:1; -	unsigned int is_static:1;  /* 1 if symbol is not global */ -	enum export  export;       /* Type of export */ +	unsigned int crc; +	bool crc_valid; +	bool weak; +	bool is_gpl_only;	/* exported by EXPORT_SYMBOL_GPL */  	char name[];  }; @@ -230,32 +237,37 @@ static inline unsigned int tdb_hash(const char *name)   * Allocate a new symbols for use in the hash of exported symbols or   * the list of unresolved symbols per module   **/ -static struct symbol *alloc_symbol(const char *name, unsigned int weak, -				   struct symbol *next) +static struct symbol *alloc_symbol(const char *name)  {  	struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));  	memset(s, 0, sizeof(*s));  	strcpy(s->name, name); -	s->weak = weak; -	s->next = next; -	s->is_static = 1; +  	return s;  }  /* For the hash of exported symbols */ -static struct symbol *new_symbol(const char *name, struct module *module, -				 enum export export) +static void hash_add_symbol(struct symbol *sym)  {  	unsigned int hash; -	hash = tdb_hash(name) % SYMBOL_HASH_SIZE; -	symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]); +	hash = tdb_hash(sym->name) % SYMBOL_HASH_SIZE; +	sym->next = symbolhash[hash]; +	symbolhash[hash] = sym; +} -	return symbolhash[hash]; +static void sym_add_unresolved(const char *name, struct module *mod, bool weak) +{ +	struct symbol *sym; + +	sym = alloc_symbol(name); +	sym->weak = weak; + +	list_add_tail(&sym->list, &mod->unresolved_symbols);  } -static struct symbol *find_symbol(const char *name) +static struct symbol *sym_find_with_module(const char *name, struct module *mod)  {  	struct symbol *s; @@ -264,67 +276,44 @@ static struct symbol *find_symbol(const char *name)  		name++;  	for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) { -		if (strcmp(s->name, name) == 0) +		if (strcmp(s->name, name) == 0 && (!mod || s->module == mod))  			return s;  	}  	return NULL;  } -static bool contains_namespace(struct namespace_list *list, -			       const char *namespace) +static struct symbol *find_symbol(const char *name)  { -	for (; list; list = list->next) +	return sym_find_with_module(name, NULL); +} + +struct namespace_list { +	struct list_head list; +	char namespace[]; +}; + +static bool contains_namespace(struct list_head *head, const char *namespace) +{ +	struct namespace_list *list; + +	list_for_each_entry(list, head, list) {  		if (!strcmp(list->namespace, namespace))  			return true; +	}  	return false;  } -static void add_namespace(struct namespace_list **list, const char *namespace) +static void add_namespace(struct list_head *head, const char *namespace)  {  	struct namespace_list *ns_entry; -	if (!contains_namespace(*list, namespace)) { -		ns_entry = NOFAIL(malloc(sizeof(struct namespace_list) + +	if (!contains_namespace(head, namespace)) { +		ns_entry = NOFAIL(malloc(sizeof(*ns_entry) +  					 strlen(namespace) + 1));  		strcpy(ns_entry->namespace, namespace); -		ns_entry->next = *list; -		*list = ns_entry; -	} -} - -static bool module_imports_namespace(struct module *module, -				     const char *namespace) -{ -	return contains_namespace(module->imported_namespaces, namespace); -} - -static const struct { -	const char *str; -	enum export export; -} export_list[] = { -	{ .str = "EXPORT_SYMBOL",            .export = export_plain }, -	{ .str = "EXPORT_SYMBOL_GPL",        .export = export_gpl }, -	{ .str = "(unknown)",                .export = export_unknown }, -}; - - -static const char *export_str(enum export ex) -{ -	return export_list[ex].str; -} - -static enum export export_no(const char *s) -{ -	int i; - -	if (!s) -		return export_unknown; -	for (i = 0; export_list[i].export != export_unknown; i++) { -		if (strcmp(export_list[i].str, s) == 0) -			return export_list[i].export; +		list_add_tail(&ns_entry->list, head);  	} -	return export_unknown;  }  static void *sym_get_data_by_offset(const struct elf_info *info, @@ -357,35 +346,6 @@ static const char *sec_name(const struct elf_info *info, int secindex)  #define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0) -static enum export export_from_secname(struct elf_info *elf, unsigned int sec) -{ -	const char *secname = sec_name(elf, sec); - -	if (strstarts(secname, "___ksymtab+")) -		return export_plain; -	else if (strstarts(secname, "___ksymtab_gpl+")) -		return export_gpl; -	else -		return export_unknown; -} - -static enum export export_from_sec(struct elf_info *elf, unsigned int sec) -{ -	if (sec == elf->export_sec) -		return export_plain; -	else if (sec == elf->export_gpl_sec) -		return export_gpl; -	else -		return export_unknown; -} - -static const char *namespace_from_kstrtabns(const struct elf_info *info, -					    const Elf_Sym *sym) -{ -	const char *value = sym_get_data(info, sym); -	return value[0] ? value : NULL; -} -  static void sym_update_namespace(const char *symname, const char *namespace)  {  	struct symbol *s = find_symbol(symname); @@ -401,47 +361,33 @@ static void sym_update_namespace(const char *symname, const char *namespace)  	}  	free(s->namespace); -	s->namespace = -		namespace && namespace[0] ? NOFAIL(strdup(namespace)) : NULL; +	s->namespace = namespace[0] ? NOFAIL(strdup(namespace)) : NULL;  } -/** - * Add an exported symbol - it may have already been added without a - * CRC, in this case just update the CRC - **/  static struct symbol *sym_add_exported(const char *name, struct module *mod, -				       enum export export) +				       bool gpl_only)  {  	struct symbol *s = find_symbol(name); -	if (!s) { -		s = new_symbol(name, mod, export); -	} else if (!external_module || s->module->is_vmlinux || -		   s->module == mod) { -		warn("%s: '%s' exported twice. Previous export was in %s%s\n", -		     mod->name, name, s->module->name, -		     s->module->is_vmlinux ? "" : ".ko"); -		return s; +	if (s && (!external_module || s->module->is_vmlinux || s->module == mod)) { +		error("%s: '%s' exported twice. Previous export was in %s%s\n", +		      mod->name, name, s->module->name, +		      s->module->is_vmlinux ? "" : ".ko");  	} +	s = alloc_symbol(name);  	s->module = mod; -	s->export    = export; +	s->is_gpl_only = gpl_only; +	list_add_tail(&s->list, &mod->exported_symbols); +	hash_add_symbol(s); +  	return s;  } -static void sym_set_crc(const char *name, unsigned int crc) +static void sym_set_crc(struct symbol *sym, unsigned int crc)  { -	struct symbol *s = find_symbol(name); - -	/* -	 * Ignore stand-alone __crc_*, which might be auto-generated symbols -	 * such as __*_veneer in ARM ELF. -	 */ -	if (!s) -		return; - -	s->crc = crc; -	s->crc_valid = 1; +	sym->crc = crc; +	sym->crc_valid = true;  }  static void *grab_file(const char *filename, size_t *size) @@ -576,10 +522,7 @@ static int parse_elf(struct elf_info *info, const char *filename)  				fatal("%s has NOBITS .modinfo\n", filename);  			info->modinfo = (void *)hdr + sechdrs[i].sh_offset;  			info->modinfo_len = sechdrs[i].sh_size; -		} else if (strcmp(secname, "__ksymtab") == 0) -			info->export_sec = i; -		else if (strcmp(secname, "__ksymtab_gpl") == 0) -			info->export_gpl_sec = i; +		}  		if (sechdrs[i].sh_type == SHT_SYMTAB) {  			unsigned int sh_link_idx; @@ -658,48 +601,18 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname)  		    strstarts(symname, "_savevr_") ||  		    strcmp(symname, ".TOC.") == 0)  			return 1; + +	if (info->hdr->e_machine == EM_S390) +		/* Expoline thunks are linked on all kernel modules during final link of .ko */ +		if (strstarts(symname, "__s390_indirect_jump_r")) +			return 1;  	/* Do not ignore this symbol */  	return 0;  } -static void handle_modversion(const struct module *mod, -			      const struct elf_info *info, -			      const Elf_Sym *sym, const char *symname) -{ -	unsigned int crc; - -	if (sym->st_shndx == SHN_UNDEF) { -		warn("EXPORT symbol \"%s\" [%s%s] version ...\n" -		     "Is \"%s\" prototyped in <asm/asm-prototypes.h>?\n", -		     symname, mod->name, mod->is_vmlinux ? "" : ".ko", -		     symname); - -		return; -	} - -	if (sym->st_shndx == SHN_ABS) { -		crc = sym->st_value; -	} else { -		unsigned int *crcp; - -		/* symbol points to the CRC in the ELF object */ -		crcp = sym_get_data(info, sym); -		crc = TO_NATIVE(*crcp); -	} -	sym_set_crc(symname, crc); -} -  static void handle_symbol(struct module *mod, struct elf_info *info,  			  const Elf_Sym *sym, const char *symname)  { -	enum export export; -	const char *name; - -	if (strstarts(symname, "__ksymtab")) -		export = export_from_secname(info, get_secindex(info, sym)); -	else -		export = export_from_sec(info, get_secindex(info, sym)); -  	switch (sym->st_shndx) {  	case SHN_COMMON:  		if (strstarts(symname, "__gnu_lto_")) { @@ -727,20 +640,26 @@ static void handle_symbol(struct module *mod, struct elf_info *info,  			}  		} -		mod->unres = alloc_symbol(symname, -					  ELF_ST_BIND(sym->st_info) == STB_WEAK, -					  mod->unres); +		sym_add_unresolved(symname, mod, +				   ELF_ST_BIND(sym->st_info) == STB_WEAK);  		break;  	default:  		/* All exported symbols */  		if (strstarts(symname, "__ksymtab_")) { +			const char *name, *secname; +  			name = symname + strlen("__ksymtab_"); -			sym_add_exported(name, mod, export); +			secname = sec_name(info, get_secindex(info, sym)); + +			if (strstarts(secname, "___ksymtab_gpl+")) +				sym_add_exported(name, mod, true); +			else if (strstarts(secname, "___ksymtab+")) +				sym_add_exported(name, mod, false);  		}  		if (strcmp(symname, "init_module") == 0) -			mod->has_init = 1; +			mod->has_init = true;  		if (strcmp(symname, "cleanup_module") == 0) -			mod->has_cleanup = 1; +			mod->has_cleanup = true;  		break;  	}  } @@ -792,29 +711,6 @@ static char *get_modinfo(struct elf_info *info, const char *tag)  	return get_next_modinfo(info, tag, NULL);  } -/** - * Test if string s ends in string sub - * return 0 if match - **/ -static int strrcmp(const char *s, const char *sub) -{ -	int slen, sublen; - -	if (!s || !sub) -		return 1; - -	slen = strlen(s); -	sublen = strlen(sub); - -	if ((slen == 0) || (sublen == 0)) -		return 1; - -	if (sublen > slen) -		return 1; - -	return memcmp(s + slen - sublen, sub, sublen); -} -  static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)  {  	if (sym) @@ -823,46 +719,22 @@ static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)  		return "(unknown)";  } -/* The pattern is an array of simple patterns. - * "foo" will match an exact string equal to "foo" - * "*foo" will match a string that ends with "foo" - * "foo*" will match a string that begins with "foo" - * "*foo*" will match a string that contains "foo" +/* + * Check whether the 'string' argument matches one of the 'patterns', + * an array of shell wildcard patterns (glob). + * + * Return true is there is a match.   */ -static int match(const char *sym, const char * const pat[]) +static bool match(const char *string, const char *const patterns[])  { -	const char *p; -	while (*pat) { -		p = *pat++; -		const char *endp = p + strlen(p) - 1; - -		/* "*foo*" */ -		if (*p == '*' && *endp == '*') { -			char *bare = NOFAIL(strndup(p + 1, strlen(p) - 2)); -			char *here = strstr(sym, bare); +	const char *pattern; -			free(bare); -			if (here != NULL) -				return 1; -		} -		/* "*foo" */ -		else if (*p == '*') { -			if (strrcmp(sym, p + 1) == 0) -				return 1; -		} -		/* "foo*" */ -		else if (*endp == '*') { -			if (strncmp(sym, p, strlen(p) - 1) == 0) -				return 1; -		} -		/* no wildcards */ -		else { -			if (strcmp(p, sym) == 0) -				return 1; -		} +	while ((pattern = *patterns++)) { +		if (!fnmatch(pattern, string, 0)) +			return true;  	} -	/* no match */ -	return 0; + +	return false;  }  /* sections that we do not want to do full section mismatch check on */ @@ -1108,7 +980,7 @@ static const struct sectioncheck sectioncheck[] = {  },  /* Do not export init/exit functions or data */  { -	.fromsec = { "__ksymtab*", NULL }, +	.fromsec = { "___ksymtab*", NULL },  	.bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL },  	.mismatch = EXPORT_TO_INIT_EXIT,  	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, @@ -1129,8 +1001,6 @@ static const struct sectioncheck *section_mismatch(  		const char *fromsec, const char *tosec)  {  	int i; -	int elems = sizeof(sectioncheck) / sizeof(struct sectioncheck); -	const struct sectioncheck *check = §ioncheck[0];  	/*  	 * The target section could be the SHT_NUL section when we're @@ -1141,14 +1011,15 @@ static const struct sectioncheck *section_mismatch(  	if (*tosec == '\0')  		return NULL; -	for (i = 0; i < elems; i++) { +	for (i = 0; i < ARRAY_SIZE(sectioncheck); i++) { +		const struct sectioncheck *check = §ioncheck[i]; +  		if (match(fromsec, check->fromsec)) {  			if (check->bad_tosec[0] && match(tosec, check->bad_tosec))  				return check;  			if (check->good_tosec[0] && !match(tosec, check->good_tosec))  				return check;  		} -		check++;  	}  	return NULL;  } @@ -1260,7 +1131,8 @@ static int secref_whitelist(const struct sectioncheck *mismatch,  static inline int is_arm_mapping_symbol(const char *str)  { -	return str[0] == '$' && strchr("axtd", str[1]) +	return str[0] == '$' && +	       (str[1] == 'a' || str[1] == 'd' || str[1] == 't' || str[1] == 'x')  	       && (str[2] == '\0' || str[2] == '.');  } @@ -1350,13 +1222,9 @@ static Elf_Sym *find_elf_symbol2(struct elf_info *elf, Elf_Addr addr,  			continue;  		if (!is_valid_name(elf, sym))  			continue; -		if (sym->st_value <= addr) { -			if ((addr - sym->st_value) < distance) { -				distance = addr - sym->st_value; -				near = sym; -			} else if ((addr - sym->st_value) == distance) { -				near = sym; -			} +		if (sym->st_value <= addr && addr - sym->st_value <= distance) { +			distance = addr - sym->st_value; +			near = sym;  		}  	}  	return near; @@ -1963,8 +1831,7 @@ static void section_rel(const char *modname, struct elf_info *elf,   * to find all references to a section that reference a section that will   * be discarded and warns about it.   **/ -static void check_sec_ref(struct module *mod, const char *modname, -			  struct elf_info *elf) +static void check_sec_ref(const char *modname, struct elf_info *elf)  {  	int i;  	Elf_Shdr *sechdrs = elf->sechdrs; @@ -1986,16 +1853,110 @@ static char *remove_dot(char *s)  	if (n && s[n]) {  		size_t m = strspn(s + n + 1, "0123456789"); -		if (m && (s[n + m] == '.' || s[n + m] == 0)) +		if (m && (s[n + m + 1] == '.' || s[n + m + 1] == 0))  			s[n] = 0; - -		/* strip trailing .lto */ -		if (strends(s, ".lto")) -			s[strlen(s) - 4] = '\0';  	}  	return s;  } +/* + * The CRCs are recorded in .*.cmd files in the form of: + * #SYMVER <name> <crc> + */ +static void extract_crcs_for_object(const char *object, struct module *mod) +{ +	char cmd_file[PATH_MAX]; +	char *buf, *p; +	const char *base; +	int dirlen, ret; + +	base = strrchr(object, '/'); +	if (base) { +		base++; +		dirlen = base - object; +	} else { +		dirlen = 0; +		base = object; +	} + +	ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd", +		       dirlen, object, base); +	if (ret >= sizeof(cmd_file)) { +		error("%s: too long path was truncated\n", cmd_file); +		return; +	} + +	buf = read_text_file(cmd_file); +	p = buf; + +	while ((p = strstr(p, "\n#SYMVER "))) { +		char *name; +		size_t namelen; +		unsigned int crc; +		struct symbol *sym; + +		name = p + strlen("\n#SYMVER "); + +		p = strchr(name, ' '); +		if (!p) +			break; + +		namelen = p - name; +		p++; + +		if (!isdigit(*p)) +			continue;	/* skip this line */ + +		crc = strtol(p, &p, 0); +		if (*p != '\n') +			continue;	/* skip this line */ + +		name[namelen] = '\0'; + +		/* +		 * sym_find_with_module() may return NULL here. +		 * It typically occurs when CONFIG_TRIM_UNUSED_KSYMS=y. +		 * Since commit e1327a127703, genksyms calculates CRCs of all +		 * symbols, including trimmed ones. Ignore orphan CRCs. +		 */ +		sym = sym_find_with_module(name, mod); +		if (sym) +			sym_set_crc(sym, crc); +	} + +	free(buf); +} + +/* + * The symbol versions (CRC) are recorded in the .*.cmd files. + * Parse them to retrieve CRCs for the current module. + */ +static void mod_set_crcs(struct module *mod) +{ +	char objlist[PATH_MAX]; +	char *buf, *p, *obj; +	int ret; + +	if (mod->is_vmlinux) { +		strcpy(objlist, ".vmlinux.objs"); +	} else { +		/* objects for a module are listed in the *.mod file. */ +		ret = snprintf(objlist, sizeof(objlist), "%s.mod", mod->name); +		if (ret >= sizeof(objlist)) { +			error("%s: too long path was truncated\n", objlist); +			return; +		} +	} + +	buf = read_text_file(objlist); +	p = buf; + +	while ((obj = strsep(&p, "\n")) && obj[0]) +		extract_crcs_for_object(obj, mod); + +	free(buf); +} +  static void read_symbols(const char *modname)  {  	const char *symname; @@ -2009,28 +1970,21 @@ static void read_symbols(const char *modname)  	if (!parse_elf(&info, modname))  		return; -	{ -		char *tmp; - -		/* strip trailing .o */ -		tmp = NOFAIL(strdup(modname)); -		tmp[strlen(tmp) - 2] = '\0'; -		/* strip trailing .lto */ -		if (strends(tmp, ".lto")) -			tmp[strlen(tmp) - 4] = '\0'; -		mod = new_module(tmp); -		free(tmp); +	if (!strends(modname, ".o")) { +		error("%s: filename must be suffixed with .o\n", modname); +		return;  	} +	/* strip trailing .o */ +	mod = new_module(modname, strlen(modname) - strlen(".o")); +  	if (!mod->is_vmlinux) {  		license = get_modinfo(&info, "license");  		if (!license)  			error("missing MODULE_LICENSE() in %s\n", modname);  		while (license) { -			if (license_is_gpl_compatible(license)) -				mod->gpl_compatible = 1; -			else { -				mod->gpl_compatible = 0; +			if (!license_is_gpl_compatible(license)) { +				mod->is_gpl_compatible = false;  				break;  			}  			license = get_next_modinfo(&info, "license", license); @@ -2057,29 +2011,10 @@ static void read_symbols(const char *modname)  		/* Apply symbol namespaces from __kstrtabns_<symbol> entries. */  		if (strstarts(symname, "__kstrtabns_"))  			sym_update_namespace(symname + strlen("__kstrtabns_"), -					     namespace_from_kstrtabns(&info, -								      sym)); - -		if (strstarts(symname, "__crc_")) -			handle_modversion(mod, &info, sym, -					  symname + strlen("__crc_")); -	} - -	// check for static EXPORT_SYMBOL_* functions && global vars -	for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { -		unsigned char bind = ELF_ST_BIND(sym->st_info); - -		if (bind == STB_GLOBAL || bind == STB_WEAK) { -			struct symbol *s = -				find_symbol(remove_dot(info.strtab + -						       sym->st_name)); - -			if (s) -				s->is_static = 0; -		} +					     sym_get_data(&info, sym));  	} -	check_sec_ref(mod, modname, &info); +	check_sec_ref(modname, &info);  	if (!mod->is_vmlinux) {  		version = get_modinfo(&info, "version"); @@ -2090,12 +2025,17 @@ static void read_symbols(const char *modname)  	parse_elf_finish(&info); -	/* Our trick to get versioning for module struct etc. - it's -	 * never passed as an argument to an exported function, so -	 * the automatic versioning doesn't pick it up, but it's really -	 * important anyhow */ -	if (modversions) -		mod->unres = alloc_symbol("module_layout", 0, mod->unres); +	if (modversions) { +		/* +		 * Our trick to get versioning for module struct etc. - it's +		 * never passed as an argument to an exported function, so +		 * the automatic versioning doesn't pick it up, but it's really +		 * important anyhow. +		 */ +		sym_add_unresolved("module_layout", mod, false); + +		mod_set_crcs(mod); +	}  }  static void read_symbols_from_files(const char *filename) @@ -2148,34 +2088,30 @@ void buf_write(struct buffer *buf, const char *s, int len)  	buf->pos += len;  } -static void check_for_gpl_usage(enum export exp, const char *m, const char *s) -{ -	switch (exp) { -	case export_gpl: -		error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", -		      m, s); -		break; -	case export_plain: -	case export_unknown: -		/* ignore */ -		break; -	} -} -  static void check_exports(struct module *mod)  {  	struct symbol *s, *exp; -	for (s = mod->unres; s; s = s->next) { +	list_for_each_entry(s, &mod->unresolved_symbols, list) {  		const char *basename;  		exp = find_symbol(s->name); -		if (!exp || exp->module == mod) { +		if (!exp) {  			if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS)  				modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR,  					    "\"%s\" [%s.ko] undefined!\n",  					    s->name, mod->name);  			continue;  		} +		if (exp->module == mod) { +			error("\"%s\" [%s.ko] was exported without definition\n", +			      s->name, mod->name); +			continue; +		} + +		s->module = exp->module; +		s->crc_valid = exp->crc_valid; +		s->crc = exp->crc; +  		basename = strrchr(mod->name, '/');  		if (basename)  			basename++; @@ -2183,15 +2119,16 @@ static void check_exports(struct module *mod)  			basename = mod->name;  		if (exp->namespace && -		    !module_imports_namespace(mod, exp->namespace)) { +		    !contains_namespace(&mod->imported_namespaces, exp->namespace)) {  			modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR,  				    "module %s uses symbol %s from namespace %s, but does not import it.\n",  				    basename, exp->name, exp->namespace);  			add_namespace(&mod->missing_namespaces, exp->namespace);  		} -		if (!mod->gpl_compatible) -			check_for_gpl_usage(exp->export, basename, exp->name); +		if (!mod->is_gpl_compatible && exp->is_gpl_only) +			error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", +			      basename, exp->name);  	}  } @@ -2221,6 +2158,7 @@ static void add_header(struct buffer *b, struct module *mod)  	buf_printf(b, "#define INCLUDE_VERMAGIC\n");  	buf_printf(b, "#include <linux/build-salt.h>\n");  	buf_printf(b, "#include <linux/elfnote-lto.h>\n"); +	buf_printf(b, "#include <linux/export-internal.h>\n");  	buf_printf(b, "#include <linux/vermagic.h>\n");  	buf_printf(b, "#include <linux/compiler.h>\n");  	buf_printf(b, "\n"); @@ -2241,26 +2179,41 @@ static void add_header(struct buffer *b, struct module *mod)  			      "#endif\n");  	buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n");  	buf_printf(b, "};\n"); -} -static void add_intree_flag(struct buffer *b, int is_intree) -{ -	if (is_intree) +	if (!external_module)  		buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); -} -/* Cannot check for assembler */ -static void add_retpoline(struct buffer *b) -{ -	buf_printf(b, "\n#ifdef CONFIG_RETPOLINE\n"); -	buf_printf(b, "MODULE_INFO(retpoline, \"Y\");\n"); -	buf_printf(b, "#endif\n"); +	buf_printf(b, +		   "\n" +		   "#ifdef CONFIG_RETPOLINE\n" +		   "MODULE_INFO(retpoline, \"Y\");\n" +		   "#endif\n"); + +	if (strstarts(mod->name, "drivers/staging")) +		buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");  } -static void add_staging_flag(struct buffer *b, const char *name) +static void add_exported_symbols(struct buffer *buf, struct module *mod)  { -	if (strstarts(name, "drivers/staging")) -		buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); +	struct symbol *sym; + +	if (!modversions) +		return; + +	/* record CRCs for exported symbols */ +	buf_printf(buf, "\n"); +	list_for_each_entry(sym, &mod->exported_symbols, list) { +		if (!sym->crc_valid) { +			warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" +			     "Is \"%s\" prototyped in <asm/asm-prototypes.h>?\n", +			     sym->name, mod->name, mod->is_vmlinux ? "" : ".ko", +			     sym->name); +			continue; +		} + +		buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n", +			   sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : ""); +	}  }  /** @@ -2268,16 +2221,7 @@ static void add_staging_flag(struct buffer *b, const char *name)   **/  static void add_versions(struct buffer *b, struct module *mod)  { -	struct symbol *s, *exp; - -	for (s = mod->unres; s; s = s->next) { -		exp = find_symbol(s->name); -		if (!exp || exp->module == mod) -			continue; -		s->module = exp->module; -		s->crc_valid = exp->crc_valid; -		s->crc = exp->crc; -	} +	struct symbol *s;  	if (!modversions)  		return; @@ -2286,7 +2230,7 @@ static void add_versions(struct buffer *b, struct module *mod)  	buf_printf(b, "static const struct modversion_info ____versions[]\n");  	buf_printf(b, "__used __section(\"__versions\") = {\n"); -	for (s = mod->unres; s; s = s->next) { +	list_for_each_entry(s, &mod->unresolved_symbols, list) {  		if (!s->module)  			continue;  		if (!s->crc_valid) { @@ -2312,13 +2256,14 @@ static void add_depends(struct buffer *b, struct module *mod)  	int first = 1;  	/* Clear ->seen flag of modules that own symbols needed by this. */ -	for (s = mod->unres; s; s = s->next) +	list_for_each_entry(s, &mod->unresolved_symbols, list) {  		if (s->module)  			s->module->seen = s->module->is_vmlinux; +	}  	buf_printf(b, "\n");  	buf_printf(b, "MODULE_INFO(depends, \""); -	for (s = mod->unres; s; s = s->next) { +	list_for_each_entry(s, &mod->unresolved_symbols, list) {  		const char *p;  		if (!s->module)  			continue; @@ -2326,7 +2271,7 @@ static void add_depends(struct buffer *b, struct module *mod)  		if (s->module->seen)  			continue; -		s->module->seen = 1; +		s->module->seen = true;  		p = strrchr(s->module->name, '/');  		if (p)  			p++; @@ -2351,6 +2296,9 @@ static void write_buf(struct buffer *b, const char *fname)  {  	FILE *file; +	if (error_occurred) +		return; +  	file = fopen(fname, "w");  	if (!file) {  		perror(fname); @@ -2401,6 +2349,47 @@ static void write_if_changed(struct buffer *b, const char *fname)  	write_buf(b, fname);  } +static void write_vmlinux_export_c_file(struct module *mod) +{ +	struct buffer buf = { }; + +	buf_printf(&buf, +		   "#include <linux/export-internal.h>\n"); + +	add_exported_symbols(&buf, mod); +	write_if_changed(&buf, ".vmlinux.export.c"); +	free(buf.p); +} + +/* do sanity checks, and generate *.mod.c file */ +static void write_mod_c_file(struct module *mod) +{ +	struct buffer buf = { }; +	char fname[PATH_MAX]; +	int ret; + +	check_modname_len(mod); +	check_exports(mod); + +	add_header(&buf, mod); +	add_exported_symbols(&buf, mod); +	add_versions(&buf, mod); +	add_depends(&buf, mod); +	add_moddevtable(&buf, mod); +	add_srcversion(&buf, mod); + +	ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); +	if (ret >= sizeof(fname)) { +		error("%s: too long path was truncated\n", fname); +		goto free; +	} + +	write_if_changed(&buf, fname); + +free: +	free(buf.p); +} +  /* parse Module.symvers file. line format:   * 0x12345678<tab>symbol<tab>module<tab>export<tab>namespace   **/ @@ -2420,6 +2409,7 @@ static void read_dump(const char *fname)  		unsigned int crc;  		struct module *mod;  		struct symbol *s; +		bool gpl_only;  		if (!(symname = strchr(line, '\t')))  			goto fail; @@ -2437,14 +2427,23 @@ static void read_dump(const char *fname)  		crc = strtoul(line, &d, 16);  		if (*symname == '\0' || *modname == '\0' || *d != '\0')  			goto fail; + +		if (!strcmp(export, "EXPORT_SYMBOL_GPL")) { +			gpl_only = true; +		} else if (!strcmp(export, "EXPORT_SYMBOL")) { +			gpl_only = false; +		} else { +			error("%s: unknown license %s. skip", symname, export); +			continue; +		} +  		mod = find_module(modname);  		if (!mod) { -			mod = new_module(modname); -			mod->from_dump = 1; +			mod = new_module(modname, strlen(modname)); +			mod->from_dump = true;  		} -		s = sym_add_exported(symname, mod, export_no(export)); -		s->is_static = 0; -		sym_set_crc(symname, crc); +		s = sym_add_exported(symname, mod, gpl_only); +		sym_set_crc(s, crc);  		sym_update_namespace(symname, namespace);  	}  	free(buf); @@ -2457,22 +2456,17 @@ fail:  static void write_dump(const char *fname)  {  	struct buffer buf = { }; -	struct symbol *symbol; -	const char *namespace; -	int n; +	struct module *mod; +	struct symbol *sym; -	for (n = 0; n < SYMBOL_HASH_SIZE ; n++) { -		symbol = symbolhash[n]; -		while (symbol) { -			if (!symbol->module->from_dump) { -				namespace = symbol->namespace; -				buf_printf(&buf, "0x%08x\t%s\t%s\t%s\t%s\n", -					   symbol->crc, symbol->name, -					   symbol->module->name, -					   export_str(symbol->export), -					   namespace ? namespace : ""); -			} -			symbol = symbol->next; +	list_for_each_entry(mod, &modules, list) { +		if (mod->from_dump) +			continue; +		list_for_each_entry(sym, &mod->exported_symbols, list) { +			buf_printf(&buf, "0x%08x\t%s\t%s\tEXPORT_SYMBOL%s\t%s\n", +				   sym->crc, sym->name, mod->name, +				   sym->is_gpl_only ? "_GPL" : "", +				   sym->namespace ?: "");  		}  	}  	write_buf(&buf, fname); @@ -2485,14 +2479,14 @@ static void write_namespace_deps_files(const char *fname)  	struct namespace_list *ns;  	struct buffer ns_deps_buf = {}; -	for (mod = modules; mod; mod = mod->next) { +	list_for_each_entry(mod, &modules, list) { -		if (mod->from_dump || !mod->missing_namespaces) +		if (mod->from_dump || list_empty(&mod->missing_namespaces))  			continue;  		buf_printf(&ns_deps_buf, "%s.ko:", mod->name); -		for (ns = mod->missing_namespaces; ns; ns = ns->next) +		list_for_each_entry(ns, &mod->missing_namespaces, list)  			buf_printf(&ns_deps_buf, " %s", ns->namespace);  		buf_printf(&ns_deps_buf, "\n"); @@ -2503,55 +2497,52 @@ static void write_namespace_deps_files(const char *fname)  }  struct dump_list { -	struct dump_list *next; +	struct list_head list;  	const char *file;  };  int main(int argc, char **argv)  {  	struct module *mod; -	struct buffer buf = { };  	char *missing_namespace_deps = NULL;  	char *dump_write = NULL, *files_source = NULL;  	int opt; -	int n; -	struct dump_list *dump_read_start = NULL; -	struct dump_list **dump_read_iter = &dump_read_start; +	LIST_HEAD(dump_lists); +	struct dump_list *dl, *dl2;  	while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) {  		switch (opt) {  		case 'e': -			external_module = 1; +			external_module = true;  			break;  		case 'i': -			*dump_read_iter = -				NOFAIL(calloc(1, sizeof(**dump_read_iter))); -			(*dump_read_iter)->file = optarg; -			dump_read_iter = &(*dump_read_iter)->next; +			dl = NOFAIL(malloc(sizeof(*dl))); +			dl->file = optarg; +			list_add_tail(&dl->list, &dump_lists);  			break;  		case 'm': -			modversions = 1; +			modversions = true;  			break;  		case 'n': -			ignore_missing_files = 1; +			ignore_missing_files = true;  			break;  		case 'o':  			dump_write = optarg;  			break;  		case 'a': -			all_versions = 1; +			all_versions = true;  			break;  		case 'T':  			files_source = optarg;  			break;  		case 'w': -			warn_unresolved = 1; +			warn_unresolved = true;  			break;  		case 'E':  			sec_mismatch_warn_only = false;  			break;  		case 'N': -			allow_missing_ns_imports = 1; +			allow_missing_ns_imports = true;  			break;  		case 'd':  			missing_namespace_deps = optarg; @@ -2561,13 +2552,10 @@ int main(int argc, char **argv)  		}  	} -	while (dump_read_start) { -		struct dump_list *tmp; - -		read_dump(dump_read_start->file); -		tmp = dump_read_start->next; -		free(dump_read_start); -		dump_read_start = tmp; +	list_for_each_entry_safe(dl, dl2, &dump_lists, list) { +		read_dump(dl->file); +		list_del(&dl->list); +		free(dl);  	}  	while (optind < argc) @@ -2576,28 +2564,14 @@ int main(int argc, char **argv)  	if (files_source)  		read_symbols_from_files(files_source); -	for (mod = modules; mod; mod = mod->next) { -		char fname[PATH_MAX]; - -		if (mod->is_vmlinux || mod->from_dump) +	list_for_each_entry(mod, &modules, list) { +		if (mod->from_dump)  			continue; -		buf.pos = 0; - -		check_modname_len(mod); -		check_exports(mod); - -		add_header(&buf, mod); -		add_intree_flag(&buf, !external_module); -		add_retpoline(&buf); -		add_staging_flag(&buf, mod->name); -		add_versions(&buf, mod); -		add_depends(&buf, mod); -		add_moddevtable(&buf, mod); -		add_srcversion(&buf, mod); - -		sprintf(fname, "%s.mod.c", mod->name); -		write_if_changed(&buf, fname); +		if (mod->is_vmlinux) +			write_vmlinux_export_c_file(mod); +		else +			write_mod_c_file(mod);  	}  	if (missing_namespace_deps) @@ -2608,22 +2582,10 @@ int main(int argc, char **argv)  	if (sec_mismatch_count && !sec_mismatch_warn_only)  		error("Section mismatches detected.\n"  		      "Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n"); -	for (n = 0; n < SYMBOL_HASH_SIZE; n++) { -		struct symbol *s; - -		for (s = symbolhash[n]; s; s = s->next) { -			if (s->is_static) -				error("\"%s\" [%s] is a static %s\n", -				      s->name, s->module->name, -				      export_str(s->export)); -		} -	}  	if (nr_unresolved > MAX_UNRESOLVED_REPORTS)  		warn("suppressed %u unresolved symbol warnings because there were too many)\n",  		     nr_unresolved - MAX_UNRESOLVED_REPORTS); -	free(buf.p); -  	return error_occurred ? 1 : 0;  }  | 
