kbuild: export-type enhancement to modpost.c

This patch provides the ability to identify the export-type of each
exported symbols in Module.symvers.

NOTE: It updates the Module.symvers file with the additional
information as shown below.

0x0f8b92af      platform_device_add_resources   vmlinux EXPORT_SYMBOL_GPL
0xcf7efb2a      ethtool_op_set_tx_csum          vmlinux EXPORT_SYMBOL

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: Ram Pai <linuxram@us.ibm.com>
Signed-off-by: Avantika Mathur <mathur@us.ibm.com>
Signed-off-by: Valdis Kletnieks <valdis.kletnieks@vt.edu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index a70f5dd..ba2e4fc 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -22,6 +22,8 @@
 static int all_versions = 0;
 /* If we are modposting external module set to 1 */
 static int external_module = 0;
+/* How a symbol is exported */
+enum export {export_plain, export_gpl, export_gpl_future, export_unknown};
 
 void fatal(const char *fmt, ...)
 {
@@ -118,6 +120,7 @@
 	unsigned int kernel:1;     /* 1 if symbol is from kernel
 				    *  (only for external modules) **/
 	unsigned int preloaded:1;  /* 1 if symbol from Module.symvers */
+	enum export  export;       /* Type of export */
 	char name[0];
 };
 
@@ -153,7 +156,8 @@
 }
 
 /* For the hash of exported symbols */
-static struct symbol *new_symbol(const char *name, struct module *module)
+static struct symbol *new_symbol(const char *name, struct module *module,
+				 enum export export)
 {
 	unsigned int hash;
 	struct symbol *new;
@@ -161,6 +165,7 @@
 	hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
 	new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]);
 	new->module = module;
+	new->export = export;
 	return new;
 }
 
@@ -179,16 +184,55 @@
 	return NULL;
 }
 
+static struct {
+	const char *str;
+	enum export export;
+} export_list[] = {
+	{ .str = "EXPORT_SYMBOL",            .export = export_plain },
+	{ .str = "EXPORT_SYMBOL_GPL",        .export = export_gpl },
+	{ .str = "EXPORT_SYMBOL_GPL_FUTURE", .export = export_gpl_future },
+	{ .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;
+	for (i = 0; export_list[i].export != export_unknown; i++) {
+		if (strcmp(export_list[i].str, s) == 0)
+			return export_list[i].export;
+	}
+	return export_unknown;
+}
+
+static enum export export_from_sec(struct elf_info *elf, Elf_Section sec)
+{
+	if (sec == elf->export_sec)
+		return export_plain;
+	else if (sec == elf->export_gpl_sec)
+		return export_gpl;
+	else if (sec == elf->export_gpl_future_sec)
+		return export_gpl_future;
+	else
+		return export_unknown;
+}
+
 /**
  * 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)
+static struct symbol *sym_add_exported(const char *name, struct module *mod,
+				       enum export export)
 {
 	struct symbol *s = find_symbol(name);
 
 	if (!s) {
-		s = new_symbol(name, mod);
+		s = new_symbol(name, mod, export);
 	} else {
 		if (!s->preloaded) {
 			warn("%s: '%s' exported twice. Previous export "
@@ -200,16 +244,17 @@
 	s->preloaded = 0;
 	s->vmlinux   = is_vmlinux(mod->name);
 	s->kernel    = 0;
+	s->export    = export;
 	return s;
 }
 
 static void sym_update_crc(const char *name, struct module *mod,
-			   unsigned int crc)
+			   unsigned int crc, enum export export)
 {
 	struct symbol *s = find_symbol(name);
 
 	if (!s)
-		s = new_symbol(name, mod);
+		s = new_symbol(name, mod, export);
 	s->crc = crc;
 	s->crc_valid = 1;
 }
@@ -309,13 +354,21 @@
 	for (i = 1; i < hdr->e_shnum; i++) {
 		const char *secstrings
 			= (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+		const char *secname;
 
 		if (sechdrs[i].sh_offset > info->size)
 			goto truncated;
-		if (strcmp(secstrings+sechdrs[i].sh_name, ".modinfo") == 0) {
+		secname = secstrings + sechdrs[i].sh_name;
+		if (strcmp(secname, ".modinfo") == 0) {
 			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;
+		else if (strcmp(secname, "__ksymtab_gpl_future") == 0)
+			info->export_gpl_future_sec = i;
+
 		if (sechdrs[i].sh_type != SHT_SYMTAB)
 			continue;
 
@@ -353,6 +406,7 @@
 			       Elf_Sym *sym, const char *symname)
 {
 	unsigned int crc;
+	enum export export = export_from_sec(info, sym->st_shndx);
 
 	switch (sym->st_shndx) {
 	case SHN_COMMON:
@@ -362,7 +416,8 @@
 		/* CRC'd symbol */
 		if (memcmp(symname, CRC_PFX, strlen(CRC_PFX)) == 0) {
 			crc = (unsigned int) sym->st_value;
-			sym_update_crc(symname + strlen(CRC_PFX), mod, crc);
+			sym_update_crc(symname + strlen(CRC_PFX), mod, crc,
+					export);
 		}
 		break;
 	case SHN_UNDEF:
@@ -406,7 +461,8 @@
 	default:
 		/* All exported symbols */
 		if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) {
-			sym_add_exported(symname + strlen(KSYMTAB_PFX), mod);
+			sym_add_exported(symname + strlen(KSYMTAB_PFX), mod,
+					export);
 		}
 		if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0)
 			mod->has_init = 1;
@@ -1146,6 +1202,9 @@
 	fclose(file);
 }
 
+/* parse Module.symvers file. line format:
+ * 0x12345678<tab>symbol<tab>module[<tab>export]
+ **/
 static void read_dump(const char *fname, unsigned int kernel)
 {
 	unsigned long size, pos = 0;
@@ -1157,7 +1216,7 @@
 		return;
 
 	while ((line = get_next_line(&pos, file, size))) {
-		char *symname, *modname, *d;
+		char *symname, *modname, *d, *export;
 		unsigned int crc;
 		struct module *mod;
 		struct symbol *s;
@@ -1168,8 +1227,9 @@
 		if (!(modname = strchr(symname, '\t')))
 			goto fail;
 		*modname++ = '\0';
-		if (strchr(modname, '\t'))
-			goto fail;
+		if (!(export = strchr(modname, '\t')))
+			*export++ = '\0';
+
 		crc = strtoul(line, &d, 16);
 		if (*symname == '\0' || *modname == '\0' || *d != '\0')
 			goto fail;
@@ -1181,10 +1241,10 @@
 			mod = new_module(NOFAIL(strdup(modname)));
 			mod->skip = 1;
 		}
-		s = sym_add_exported(symname, mod);
+		s = sym_add_exported(symname, mod, export_no(export));
 		s->kernel    = kernel;
 		s->preloaded = 1;
-		sym_update_crc(symname, mod, crc);
+		sym_update_crc(symname, mod, crc, export_no(export));
 	}
 	return;
 fail:
@@ -1214,9 +1274,10 @@
 		symbol = symbolhash[n];
 		while (symbol) {
 			if (dump_sym(symbol))
-				buf_printf(&buf, "0x%08x\t%s\t%s\n",
+				buf_printf(&buf, "0x%08x\t%s\t%s\t%s\n",
 					symbol->crc, symbol->name,
-					symbol->module->name);
+					symbol->module->name,
+					export_str(symbol->export));
 			symbol = symbol->next;
 		}
 	}