xref: /linux-6.15/scripts/mod/modpost.c (revision fededcd2)
11da177e4SLinus Torvalds /* Postprocess module symbol versions
21da177e4SLinus Torvalds  *
31da177e4SLinus Torvalds  * Copyright 2003       Kai Germaschewski
41da177e4SLinus Torvalds  * Copyright 2002-2004  Rusty Russell, IBM Corporation
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Based in part on module-init-tools/depmod.c,file2alias
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * This software may be used and distributed according to the terms
91da177e4SLinus Torvalds  * of the GNU General Public License, incorporated herein by reference.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Usage: modpost vmlinux module1.o module2.o ...
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <ctype.h>
151da177e4SLinus Torvalds #include "modpost.h"
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /* Are we using CONFIG_MODVERSIONS? */
181da177e4SLinus Torvalds int modversions = 0;
191da177e4SLinus Torvalds /* Warn about undefined symbols? (do so if we have vmlinux) */
201da177e4SLinus Torvalds int have_vmlinux = 0;
211da177e4SLinus Torvalds /* Is CONFIG_MODULE_SRCVERSION_ALL set? */
221da177e4SLinus Torvalds static int all_versions = 0;
23040fcc81SSam Ravnborg /* If we are modposting external module set to 1 */
24040fcc81SSam Ravnborg static int external_module = 0;
251da177e4SLinus Torvalds 
265c3ead8cSSam Ravnborg void fatal(const char *fmt, ...)
271da177e4SLinus Torvalds {
281da177e4SLinus Torvalds 	va_list arglist;
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds 	fprintf(stderr, "FATAL: ");
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds 	va_start(arglist, fmt);
331da177e4SLinus Torvalds 	vfprintf(stderr, fmt, arglist);
341da177e4SLinus Torvalds 	va_end(arglist);
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds 	exit(1);
371da177e4SLinus Torvalds }
381da177e4SLinus Torvalds 
395c3ead8cSSam Ravnborg void warn(const char *fmt, ...)
401da177e4SLinus Torvalds {
411da177e4SLinus Torvalds 	va_list arglist;
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 	fprintf(stderr, "WARNING: ");
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 	va_start(arglist, fmt);
461da177e4SLinus Torvalds 	vfprintf(stderr, fmt, arglist);
471da177e4SLinus Torvalds 	va_end(arglist);
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds 
50040fcc81SSam Ravnborg static int is_vmlinux(const char *modname)
51040fcc81SSam Ravnborg {
52040fcc81SSam Ravnborg 	const char *myname;
53040fcc81SSam Ravnborg 
54040fcc81SSam Ravnborg 	if ((myname = strrchr(modname, '/')))
55040fcc81SSam Ravnborg 		myname++;
56040fcc81SSam Ravnborg 	else
57040fcc81SSam Ravnborg 		myname = modname;
58040fcc81SSam Ravnborg 
59040fcc81SSam Ravnborg 	return strcmp(myname, "vmlinux") == 0;
60040fcc81SSam Ravnborg }
61040fcc81SSam Ravnborg 
621da177e4SLinus Torvalds void *do_nofail(void *ptr, const char *expr)
631da177e4SLinus Torvalds {
641da177e4SLinus Torvalds 	if (!ptr) {
651da177e4SLinus Torvalds 		fatal("modpost: Memory allocation failure: %s.\n", expr);
661da177e4SLinus Torvalds 	}
671da177e4SLinus Torvalds 	return ptr;
681da177e4SLinus Torvalds }
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds /* A list of all modules we processed */
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds static struct module *modules;
731da177e4SLinus Torvalds 
745c3ead8cSSam Ravnborg static struct module *find_module(char *modname)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds 	struct module *mod;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	for (mod = modules; mod; mod = mod->next)
791da177e4SLinus Torvalds 		if (strcmp(mod->name, modname) == 0)
801da177e4SLinus Torvalds 			break;
811da177e4SLinus Torvalds 	return mod;
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds 
845c3ead8cSSam Ravnborg static struct module *new_module(char *modname)
851da177e4SLinus Torvalds {
861da177e4SLinus Torvalds 	struct module *mod;
871da177e4SLinus Torvalds 	char *p, *s;
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	mod = NOFAIL(malloc(sizeof(*mod)));
901da177e4SLinus Torvalds 	memset(mod, 0, sizeof(*mod));
911da177e4SLinus Torvalds 	p = NOFAIL(strdup(modname));
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	/* strip trailing .o */
941da177e4SLinus Torvalds 	if ((s = strrchr(p, '.')) != NULL)
951da177e4SLinus Torvalds 		if (strcmp(s, ".o") == 0)
961da177e4SLinus Torvalds 			*s = '\0';
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	/* add to list */
991da177e4SLinus Torvalds 	mod->name = p;
1001da177e4SLinus Torvalds 	mod->next = modules;
1011da177e4SLinus Torvalds 	modules = mod;
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	return mod;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds /* A hash of all exported symbols,
1071da177e4SLinus Torvalds  * struct symbol is also used for lists of unresolved symbols */
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds #define SYMBOL_HASH_SIZE 1024
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds struct symbol {
1121da177e4SLinus Torvalds 	struct symbol *next;
1131da177e4SLinus Torvalds 	struct module *module;
1141da177e4SLinus Torvalds 	unsigned int crc;
1151da177e4SLinus Torvalds 	int crc_valid;
1161da177e4SLinus Torvalds 	unsigned int weak:1;
117040fcc81SSam Ravnborg 	unsigned int vmlinux:1;    /* 1 if symbol is defined in vmlinux */
118040fcc81SSam Ravnborg 	unsigned int kernel:1;     /* 1 if symbol is from kernel
119040fcc81SSam Ravnborg 				    *  (only for external modules) **/
1208e70c458SSam Ravnborg 	unsigned int preloaded:1;  /* 1 if symbol from Module.symvers */
1211da177e4SLinus Torvalds 	char name[0];
1221da177e4SLinus Torvalds };
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds static struct symbol *symbolhash[SYMBOL_HASH_SIZE];
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds /* This is based on the hash agorithm from gdbm, via tdb */
1271da177e4SLinus Torvalds static inline unsigned int tdb_hash(const char *name)
1281da177e4SLinus Torvalds {
1291da177e4SLinus Torvalds 	unsigned value;	/* Used to compute the hash value.  */
1301da177e4SLinus Torvalds 	unsigned   i;	/* Used to cycle through random values. */
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 	/* Set the initial value from the key size. */
1331da177e4SLinus Torvalds 	for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++)
1341da177e4SLinus Torvalds 		value = (value + (((unsigned char *)name)[i] << (i*5 % 24)));
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 	return (1103515243 * value + 12345);
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds 
1395c3ead8cSSam Ravnborg /**
1405c3ead8cSSam Ravnborg  * Allocate a new symbols for use in the hash of exported symbols or
1415c3ead8cSSam Ravnborg  * the list of unresolved symbols per module
1425c3ead8cSSam Ravnborg  **/
1435c3ead8cSSam Ravnborg static struct symbol *alloc_symbol(const char *name, unsigned int weak,
1445c3ead8cSSam Ravnborg 				   struct symbol *next)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 	memset(s, 0, sizeof(*s));
1491da177e4SLinus Torvalds 	strcpy(s->name, name);
1501da177e4SLinus Torvalds 	s->weak = weak;
1511da177e4SLinus Torvalds 	s->next = next;
1521da177e4SLinus Torvalds 	return s;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds /* For the hash of exported symbols */
156040fcc81SSam Ravnborg static struct symbol *new_symbol(const char *name, struct module *module)
1571da177e4SLinus Torvalds {
1581da177e4SLinus Torvalds 	unsigned int hash;
1591da177e4SLinus Torvalds 	struct symbol *new;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
1621da177e4SLinus Torvalds 	new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]);
1631da177e4SLinus Torvalds 	new->module = module;
164040fcc81SSam Ravnborg 	return new;
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds 
1675c3ead8cSSam Ravnborg static struct symbol *find_symbol(const char *name)
1681da177e4SLinus Torvalds {
1691da177e4SLinus Torvalds 	struct symbol *s;
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds 	/* For our purposes, .foo matches foo.  PPC64 needs this. */
1721da177e4SLinus Torvalds 	if (name[0] == '.')
1731da177e4SLinus Torvalds 		name++;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s=s->next) {
1761da177e4SLinus Torvalds 		if (strcmp(s->name, name) == 0)
1771da177e4SLinus Torvalds 			return s;
1781da177e4SLinus Torvalds 	}
1791da177e4SLinus Torvalds 	return NULL;
1801da177e4SLinus Torvalds }
1811da177e4SLinus Torvalds 
1825c3ead8cSSam Ravnborg /**
1835c3ead8cSSam Ravnborg  * Add an exported symbol - it may have already been added without a
1845c3ead8cSSam Ravnborg  * CRC, in this case just update the CRC
1855c3ead8cSSam Ravnborg  **/
186040fcc81SSam Ravnborg static struct symbol *sym_add_exported(const char *name, struct module *mod)
1871da177e4SLinus Torvalds {
1881da177e4SLinus Torvalds 	struct symbol *s = find_symbol(name);
1891da177e4SLinus Torvalds 
1908e70c458SSam Ravnborg 	if (!s) {
191040fcc81SSam Ravnborg 		s = new_symbol(name, mod);
1928e70c458SSam Ravnborg 	} else {
1938e70c458SSam Ravnborg 		if (!s->preloaded) {
1948e70c458SSam Ravnborg 			warn("%s: duplicate symbol '%s' previous definition "
1958e70c458SSam Ravnborg 			     "was in %s%s\n", mod->name, name,
1968e70c458SSam Ravnborg 			     s->module->name,
1978e70c458SSam Ravnborg 			     is_vmlinux(s->module->name) ?"":".ko");
1988e70c458SSam Ravnborg 		}
1998e70c458SSam Ravnborg 	}
2008e70c458SSam Ravnborg 	s->preloaded = 0;
201040fcc81SSam Ravnborg 	s->vmlinux   = is_vmlinux(mod->name);
202040fcc81SSam Ravnborg 	s->kernel    = 0;
203040fcc81SSam Ravnborg 	return s;
2041da177e4SLinus Torvalds }
205040fcc81SSam Ravnborg 
206040fcc81SSam Ravnborg static void sym_update_crc(const char *name, struct module *mod,
207040fcc81SSam Ravnborg 			   unsigned int crc)
208040fcc81SSam Ravnborg {
209040fcc81SSam Ravnborg 	struct symbol *s = find_symbol(name);
210040fcc81SSam Ravnborg 
211040fcc81SSam Ravnborg 	if (!s)
212040fcc81SSam Ravnborg 		s = new_symbol(name, mod);
213040fcc81SSam Ravnborg 	s->crc = crc;
2141da177e4SLinus Torvalds 	s->crc_valid = 1;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
2175c3ead8cSSam Ravnborg void *grab_file(const char *filename, unsigned long *size)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	struct stat st;
2201da177e4SLinus Torvalds 	void *map;
2211da177e4SLinus Torvalds 	int fd;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	fd = open(filename, O_RDONLY);
2241da177e4SLinus Torvalds 	if (fd < 0 || fstat(fd, &st) != 0)
2251da177e4SLinus Torvalds 		return NULL;
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	*size = st.st_size;
2281da177e4SLinus Torvalds 	map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
2291da177e4SLinus Torvalds 	close(fd);
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 	if (map == MAP_FAILED)
2321da177e4SLinus Torvalds 		return NULL;
2331da177e4SLinus Torvalds 	return map;
2341da177e4SLinus Torvalds }
2351da177e4SLinus Torvalds 
2365c3ead8cSSam Ravnborg /**
2375c3ead8cSSam Ravnborg   * Return a copy of the next line in a mmap'ed file.
2385c3ead8cSSam Ravnborg   * spaces in the beginning of the line is trimmed away.
2395c3ead8cSSam Ravnborg   * Return a pointer to a static buffer.
2405c3ead8cSSam Ravnborg   **/
2415c3ead8cSSam Ravnborg char* get_next_line(unsigned long *pos, void *file, unsigned long size)
2421da177e4SLinus Torvalds {
2431da177e4SLinus Torvalds 	static char line[4096];
2441da177e4SLinus Torvalds 	int skip = 1;
2451da177e4SLinus Torvalds 	size_t len = 0;
2461da177e4SLinus Torvalds 	signed char *p = (signed char *)file + *pos;
2471da177e4SLinus Torvalds 	char *s = line;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	for (; *pos < size ; (*pos)++)
2501da177e4SLinus Torvalds 	{
2511da177e4SLinus Torvalds 		if (skip && isspace(*p)) {
2521da177e4SLinus Torvalds 			p++;
2531da177e4SLinus Torvalds 			continue;
2541da177e4SLinus Torvalds 		}
2551da177e4SLinus Torvalds 		skip = 0;
2561da177e4SLinus Torvalds 		if (*p != '\n' && (*pos < size)) {
2571da177e4SLinus Torvalds 			len++;
2581da177e4SLinus Torvalds 			*s++ = *p++;
2591da177e4SLinus Torvalds 			if (len > 4095)
2601da177e4SLinus Torvalds 				break; /* Too long, stop */
2611da177e4SLinus Torvalds 		} else {
2621da177e4SLinus Torvalds 			/* End of string */
2631da177e4SLinus Torvalds 			*s = '\0';
2641da177e4SLinus Torvalds 			return line;
2651da177e4SLinus Torvalds 		}
2661da177e4SLinus Torvalds 	}
2671da177e4SLinus Torvalds 	/* End of buffer */
2681da177e4SLinus Torvalds 	return NULL;
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
2715c3ead8cSSam Ravnborg void release_file(void *file, unsigned long size)
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds 	munmap(file, size);
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
2765c3ead8cSSam Ravnborg static void parse_elf(struct elf_info *info, const char *filename)
2771da177e4SLinus Torvalds {
2781da177e4SLinus Torvalds 	unsigned int i;
2791da177e4SLinus Torvalds 	Elf_Ehdr *hdr = info->hdr;
2801da177e4SLinus Torvalds 	Elf_Shdr *sechdrs;
2811da177e4SLinus Torvalds 	Elf_Sym  *sym;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	hdr = grab_file(filename, &info->size);
2841da177e4SLinus Torvalds 	if (!hdr) {
2851da177e4SLinus Torvalds 		perror(filename);
2861da177e4SLinus Torvalds 		abort();
2871da177e4SLinus Torvalds 	}
2881da177e4SLinus Torvalds 	info->hdr = hdr;
2891da177e4SLinus Torvalds 	if (info->size < sizeof(*hdr))
2901da177e4SLinus Torvalds 		goto truncated;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	/* Fix endianness in ELF header */
2931da177e4SLinus Torvalds 	hdr->e_shoff    = TO_NATIVE(hdr->e_shoff);
2941da177e4SLinus Torvalds 	hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx);
2951da177e4SLinus Torvalds 	hdr->e_shnum    = TO_NATIVE(hdr->e_shnum);
2961da177e4SLinus Torvalds 	hdr->e_machine  = TO_NATIVE(hdr->e_machine);
2971da177e4SLinus Torvalds 	sechdrs = (void *)hdr + hdr->e_shoff;
2981da177e4SLinus Torvalds 	info->sechdrs = sechdrs;
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	/* Fix endianness in section headers */
3011da177e4SLinus Torvalds 	for (i = 0; i < hdr->e_shnum; i++) {
3021da177e4SLinus Torvalds 		sechdrs[i].sh_type   = TO_NATIVE(sechdrs[i].sh_type);
3031da177e4SLinus Torvalds 		sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset);
3041da177e4SLinus Torvalds 		sechdrs[i].sh_size   = TO_NATIVE(sechdrs[i].sh_size);
3051da177e4SLinus Torvalds 		sechdrs[i].sh_link   = TO_NATIVE(sechdrs[i].sh_link);
3061da177e4SLinus Torvalds 		sechdrs[i].sh_name   = TO_NATIVE(sechdrs[i].sh_name);
3071da177e4SLinus Torvalds 	}
3081da177e4SLinus Torvalds 	/* Find symbol table. */
3091da177e4SLinus Torvalds 	for (i = 1; i < hdr->e_shnum; i++) {
3101da177e4SLinus Torvalds 		const char *secstrings
3111da177e4SLinus Torvalds 			= (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 		if (sechdrs[i].sh_offset > info->size)
3141da177e4SLinus Torvalds 			goto truncated;
3151da177e4SLinus Torvalds 		if (strcmp(secstrings+sechdrs[i].sh_name, ".modinfo") == 0) {
3161da177e4SLinus Torvalds 			info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
3171da177e4SLinus Torvalds 			info->modinfo_len = sechdrs[i].sh_size;
3181da177e4SLinus Torvalds 		}
3191da177e4SLinus Torvalds 		if (sechdrs[i].sh_type != SHT_SYMTAB)
3201da177e4SLinus Torvalds 			continue;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 		info->symtab_start = (void *)hdr + sechdrs[i].sh_offset;
3231da177e4SLinus Torvalds 		info->symtab_stop  = (void *)hdr + sechdrs[i].sh_offset
3241da177e4SLinus Torvalds 			                         + sechdrs[i].sh_size;
3251da177e4SLinus Torvalds 		info->strtab       = (void *)hdr +
3261da177e4SLinus Torvalds 			             sechdrs[sechdrs[i].sh_link].sh_offset;
3271da177e4SLinus Torvalds 	}
3281da177e4SLinus Torvalds 	if (!info->symtab_start) {
329cb80514dSSam Ravnborg 		fatal("%s has no symtab?\n", filename);
3301da177e4SLinus Torvalds 	}
3311da177e4SLinus Torvalds 	/* Fix endianness in symbols */
3321da177e4SLinus Torvalds 	for (sym = info->symtab_start; sym < info->symtab_stop; sym++) {
3331da177e4SLinus Torvalds 		sym->st_shndx = TO_NATIVE(sym->st_shndx);
3341da177e4SLinus Torvalds 		sym->st_name  = TO_NATIVE(sym->st_name);
3351da177e4SLinus Torvalds 		sym->st_value = TO_NATIVE(sym->st_value);
3361da177e4SLinus Torvalds 		sym->st_size  = TO_NATIVE(sym->st_size);
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds 	return;
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds  truncated:
341cb80514dSSam Ravnborg 	fatal("%s is truncated.\n", filename);
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds 
3445c3ead8cSSam Ravnborg static void parse_elf_finish(struct elf_info *info)
3451da177e4SLinus Torvalds {
3461da177e4SLinus Torvalds 	release_file(info->hdr, info->size);
3471da177e4SLinus Torvalds }
3481da177e4SLinus Torvalds 
3499572b28fSLuke Yang #define CRC_PFX     "__crc_"
3509572b28fSLuke Yang #define KSYMTAB_PFX "__ksymtab_"
3511da177e4SLinus Torvalds 
3525c3ead8cSSam Ravnborg static void handle_modversions(struct module *mod, struct elf_info *info,
3531da177e4SLinus Torvalds 			       Elf_Sym *sym, const char *symname)
3541da177e4SLinus Torvalds {
3551da177e4SLinus Torvalds 	unsigned int crc;
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 	switch (sym->st_shndx) {
3581da177e4SLinus Torvalds 	case SHN_COMMON:
359cb80514dSSam Ravnborg 		warn("\"%s\" [%s] is COMMON symbol\n", symname, mod->name);
3601da177e4SLinus Torvalds 		break;
3611da177e4SLinus Torvalds 	case SHN_ABS:
3621da177e4SLinus Torvalds 		/* CRC'd symbol */
3631da177e4SLinus Torvalds 		if (memcmp(symname, CRC_PFX, strlen(CRC_PFX)) == 0) {
3641da177e4SLinus Torvalds 			crc = (unsigned int) sym->st_value;
365040fcc81SSam Ravnborg 			sym_update_crc(symname + strlen(CRC_PFX), mod, crc);
3661da177e4SLinus Torvalds 		}
3671da177e4SLinus Torvalds 		break;
3681da177e4SLinus Torvalds 	case SHN_UNDEF:
3691da177e4SLinus Torvalds 		/* undefined symbol */
3701da177e4SLinus Torvalds 		if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
3711da177e4SLinus Torvalds 		    ELF_ST_BIND(sym->st_info) != STB_WEAK)
3721da177e4SLinus Torvalds 			break;
3731da177e4SLinus Torvalds 		/* ignore global offset table */
3741da177e4SLinus Torvalds 		if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0)
3751da177e4SLinus Torvalds 			break;
3761da177e4SLinus Torvalds 		/* ignore __this_module, it will be resolved shortly */
3771da177e4SLinus Torvalds 		if (strcmp(symname, MODULE_SYMBOL_PREFIX "__this_module") == 0)
3781da177e4SLinus Torvalds 			break;
3798d529014SBen Colline /* cope with newer glibc (2.3.4 or higher) STT_ definition in elf.h */
3808d529014SBen Colline #if defined(STT_REGISTER) || defined(STT_SPARC_REGISTER)
3818d529014SBen Colline /* add compatibility with older glibc */
3828d529014SBen Colline #ifndef STT_SPARC_REGISTER
3838d529014SBen Colline #define STT_SPARC_REGISTER STT_REGISTER
3848d529014SBen Colline #endif
3851da177e4SLinus Torvalds 		if (info->hdr->e_machine == EM_SPARC ||
3861da177e4SLinus Torvalds 		    info->hdr->e_machine == EM_SPARCV9) {
3871da177e4SLinus Torvalds 			/* Ignore register directives. */
3888d529014SBen Colline 			if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER)
3891da177e4SLinus Torvalds 				break;
3907caaeabbSAl Viro  			if (symname[0] == '.') {
3917caaeabbSAl Viro  				char *munged = strdup(symname);
3927caaeabbSAl Viro  				munged[0] = '_';
3937caaeabbSAl Viro  				munged[1] = toupper(munged[1]);
3947caaeabbSAl Viro  				symname = munged;
3957caaeabbSAl Viro  			}
3961da177e4SLinus Torvalds 		}
3971da177e4SLinus Torvalds #endif
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 		if (memcmp(symname, MODULE_SYMBOL_PREFIX,
4001da177e4SLinus Torvalds 			   strlen(MODULE_SYMBOL_PREFIX)) == 0)
4011da177e4SLinus Torvalds 			mod->unres = alloc_symbol(symname +
4021da177e4SLinus Torvalds 						  strlen(MODULE_SYMBOL_PREFIX),
4031da177e4SLinus Torvalds 						  ELF_ST_BIND(sym->st_info) == STB_WEAK,
4041da177e4SLinus Torvalds 						  mod->unres);
4051da177e4SLinus Torvalds 		break;
4061da177e4SLinus Torvalds 	default:
4071da177e4SLinus Torvalds 		/* All exported symbols */
4081da177e4SLinus Torvalds 		if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) {
409040fcc81SSam Ravnborg 			sym_add_exported(symname + strlen(KSYMTAB_PFX), mod);
4101da177e4SLinus Torvalds 		}
4111da177e4SLinus Torvalds 		if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0)
4121da177e4SLinus Torvalds 			mod->has_init = 1;
4131da177e4SLinus Torvalds 		if (strcmp(symname, MODULE_SYMBOL_PREFIX "cleanup_module") == 0)
4141da177e4SLinus Torvalds 			mod->has_cleanup = 1;
4151da177e4SLinus Torvalds 		break;
4161da177e4SLinus Torvalds 	}
4171da177e4SLinus Torvalds }
4181da177e4SLinus Torvalds 
4195c3ead8cSSam Ravnborg /**
4205c3ead8cSSam Ravnborg  * Parse tag=value strings from .modinfo section
4215c3ead8cSSam Ravnborg  **/
4221da177e4SLinus Torvalds static char *next_string(char *string, unsigned long *secsize)
4231da177e4SLinus Torvalds {
4241da177e4SLinus Torvalds 	/* Skip non-zero chars */
4251da177e4SLinus Torvalds 	while (string[0]) {
4261da177e4SLinus Torvalds 		string++;
4271da177e4SLinus Torvalds 		if ((*secsize)-- <= 1)
4281da177e4SLinus Torvalds 			return NULL;
4291da177e4SLinus Torvalds 	}
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	/* Skip any zero padding. */
4321da177e4SLinus Torvalds 	while (!string[0]) {
4331da177e4SLinus Torvalds 		string++;
4341da177e4SLinus Torvalds 		if ((*secsize)-- <= 1)
4351da177e4SLinus Torvalds 			return NULL;
4361da177e4SLinus Torvalds 	}
4371da177e4SLinus Torvalds 	return string;
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
4411da177e4SLinus Torvalds 			 const char *tag)
4421da177e4SLinus Torvalds {
4431da177e4SLinus Torvalds 	char *p;
4441da177e4SLinus Torvalds 	unsigned int taglen = strlen(tag);
4451da177e4SLinus Torvalds 	unsigned long size = modinfo_len;
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	for (p = modinfo; p; p = next_string(p, &size)) {
4481da177e4SLinus Torvalds 		if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
4491da177e4SLinus Torvalds 			return p + taglen + 1;
4501da177e4SLinus Torvalds 	}
4511da177e4SLinus Torvalds 	return NULL;
4521da177e4SLinus Torvalds }
4531da177e4SLinus Torvalds 
45493684d3bSSam Ravnborg /**
45593684d3bSSam Ravnborg  * Find symbol based on relocation record info.
45693684d3bSSam Ravnborg  * In some cases the symbol supplied is a valid symbol so
45793684d3bSSam Ravnborg  * return refsym. If st_name != 0 we assume this is a valid symbol.
45893684d3bSSam Ravnborg  * In other cases the symbol needs to be looked up in the symbol table
45993684d3bSSam Ravnborg  * based on section and address.
46093684d3bSSam Ravnborg  *  **/
46193684d3bSSam Ravnborg static Elf_Sym *find_elf_symbol(struct elf_info *elf, Elf_Addr addr,
46293684d3bSSam Ravnborg 				Elf_Sym *relsym)
46393684d3bSSam Ravnborg {
46493684d3bSSam Ravnborg 	Elf_Sym *sym;
46593684d3bSSam Ravnborg 
46693684d3bSSam Ravnborg 	if (relsym->st_name != 0)
46793684d3bSSam Ravnborg 		return relsym;
46893684d3bSSam Ravnborg 	for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
46993684d3bSSam Ravnborg 		if (sym->st_shndx != relsym->st_shndx)
47093684d3bSSam Ravnborg 			continue;
47193684d3bSSam Ravnborg 		if (sym->st_value == addr)
47293684d3bSSam Ravnborg 			return sym;
47393684d3bSSam Ravnborg 	}
47493684d3bSSam Ravnborg 	return NULL;
47593684d3bSSam Ravnborg }
47693684d3bSSam Ravnborg 
477b39927cfSSam Ravnborg /*
478b39927cfSSam Ravnborg  * Find symbols before or equal addr and after addr - in the section sec
479b39927cfSSam Ravnborg  **/
480b39927cfSSam Ravnborg static void find_symbols_between(struct elf_info *elf, Elf_Addr addr,
481b39927cfSSam Ravnborg 				 const char *sec,
482b39927cfSSam Ravnborg 			         Elf_Sym **before, Elf_Sym **after)
483b39927cfSSam Ravnborg {
484b39927cfSSam Ravnborg 	Elf_Sym *sym;
485b39927cfSSam Ravnborg 	Elf_Ehdr *hdr = elf->hdr;
486b39927cfSSam Ravnborg 	Elf_Addr beforediff = ~0;
487b39927cfSSam Ravnborg 	Elf_Addr afterdiff = ~0;
488b39927cfSSam Ravnborg 	const char *secstrings = (void *)hdr +
489b39927cfSSam Ravnborg 				 elf->sechdrs[hdr->e_shstrndx].sh_offset;
490b39927cfSSam Ravnborg 
491b39927cfSSam Ravnborg 	*before = NULL;
492b39927cfSSam Ravnborg 	*after = NULL;
493b39927cfSSam Ravnborg 
494b39927cfSSam Ravnborg 	for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
495b39927cfSSam Ravnborg 		const char *symsec;
496b39927cfSSam Ravnborg 
497b39927cfSSam Ravnborg 		if (sym->st_shndx >= SHN_LORESERVE)
498b39927cfSSam Ravnborg 			continue;
499b39927cfSSam Ravnborg 		symsec = secstrings + elf->sechdrs[sym->st_shndx].sh_name;
500b39927cfSSam Ravnborg 		if (strcmp(symsec, sec) != 0)
501b39927cfSSam Ravnborg 			continue;
502b39927cfSSam Ravnborg 		if (sym->st_value <= addr) {
503b39927cfSSam Ravnborg 			if ((addr - sym->st_value) < beforediff) {
504b39927cfSSam Ravnborg 				beforediff = addr - sym->st_value;
505b39927cfSSam Ravnborg 				*before = sym;
506b39927cfSSam Ravnborg 			}
507b39927cfSSam Ravnborg 		}
508b39927cfSSam Ravnborg 		else
509b39927cfSSam Ravnborg 		{
510b39927cfSSam Ravnborg 			if ((sym->st_value - addr) < afterdiff) {
511b39927cfSSam Ravnborg 				afterdiff = sym->st_value - addr;
512b39927cfSSam Ravnborg 				*after = sym;
513b39927cfSSam Ravnborg 			}
514b39927cfSSam Ravnborg 		}
515b39927cfSSam Ravnborg 	}
516b39927cfSSam Ravnborg }
517b39927cfSSam Ravnborg 
518b39927cfSSam Ravnborg /**
519b39927cfSSam Ravnborg  * Print a warning about a section mismatch.
520b39927cfSSam Ravnborg  * Try to find symbols near it so user can find it.
521b39927cfSSam Ravnborg  **/
522b39927cfSSam Ravnborg static void warn_sec_mismatch(const char *modname, const char *fromsec,
523b39927cfSSam Ravnborg 			      struct elf_info *elf, Elf_Sym *sym, Elf_Rela r)
524b39927cfSSam Ravnborg {
52593684d3bSSam Ravnborg 	const char *refsymname = "";
52693684d3bSSam Ravnborg 	Elf_Sym *before, *after;
52793684d3bSSam Ravnborg 	Elf_Sym *refsym;
528b39927cfSSam Ravnborg 	Elf_Ehdr *hdr = elf->hdr;
529b39927cfSSam Ravnborg 	Elf_Shdr *sechdrs = elf->sechdrs;
530b39927cfSSam Ravnborg 	const char *secstrings = (void *)hdr +
531b39927cfSSam Ravnborg 				 sechdrs[hdr->e_shstrndx].sh_offset;
532b39927cfSSam Ravnborg 	const char *secname = secstrings + sechdrs[sym->st_shndx].sh_name;
533b39927cfSSam Ravnborg 
534b39927cfSSam Ravnborg 	find_symbols_between(elf, r.r_offset, fromsec, &before, &after);
535b39927cfSSam Ravnborg 
53693684d3bSSam Ravnborg 	refsym = find_elf_symbol(elf, r.r_addend, sym);
53793684d3bSSam Ravnborg 	if (refsym && strlen(elf->strtab + refsym->st_name))
53893684d3bSSam Ravnborg 		refsymname = elf->strtab + refsym->st_name;
53993684d3bSSam Ravnborg 
540b39927cfSSam Ravnborg 	if (before && after) {
54193684d3bSSam Ravnborg 		warn("%s - Section mismatch: reference to %s:%s from %s "
54293684d3bSSam Ravnborg 		     "between '%s' (at offset 0x%llx) and '%s'\n",
54393684d3bSSam Ravnborg 		     modname, secname, refsymname, fromsec,
544b39927cfSSam Ravnborg 		     elf->strtab + before->st_name,
54593684d3bSSam Ravnborg 		     (long long)r.r_offset,
546b39927cfSSam Ravnborg 		     elf->strtab + after->st_name);
547b39927cfSSam Ravnborg 	} else if (before) {
54893684d3bSSam Ravnborg 		warn("%s - Section mismatch: reference to %s:%s from %s "
54993684d3bSSam Ravnborg 		     "after '%s' (at offset 0x%llx)\n",
55093684d3bSSam Ravnborg 		     modname, secname, refsymname, fromsec,
551b39927cfSSam Ravnborg 		     elf->strtab + before->st_name,
55293684d3bSSam Ravnborg 		     (long long)r.r_offset);
553b39927cfSSam Ravnborg 	} else if (after) {
55493684d3bSSam Ravnborg 		warn("%s - Section mismatch: reference to %s:%s from %s "
55593684d3bSSam Ravnborg 		     "before '%s' (at offset -0x%llx)\n",
55693684d3bSSam Ravnborg 		     modname, secname, refsymname, fromsec,
557b39927cfSSam Ravnborg 		     elf->strtab + before->st_name,
55893684d3bSSam Ravnborg 		     (long long)r.r_offset);
559b39927cfSSam Ravnborg 	} else {
56093684d3bSSam Ravnborg 		warn("%s - Section mismatch: reference to %s:%s from %s "
56193684d3bSSam Ravnborg 		     "(offset 0x%llx)\n",
56293684d3bSSam Ravnborg 		     modname, secname, fromsec, refsymname,
56393684d3bSSam Ravnborg 		     (long long)r.r_offset);
564b39927cfSSam Ravnborg 	}
565b39927cfSSam Ravnborg }
566b39927cfSSam Ravnborg 
567b39927cfSSam Ravnborg /**
568b39927cfSSam Ravnborg  * A module includes a number of sections that are discarded
569b39927cfSSam Ravnborg  * either when loaded or when used as built-in.
570b39927cfSSam Ravnborg  * For loaded modules all functions marked __init and all data
571b39927cfSSam Ravnborg  * marked __initdata will be discarded when the module has been intialized.
572b39927cfSSam Ravnborg  * Likewise for modules used built-in the sections marked __exit
573b39927cfSSam Ravnborg  * are discarded because __exit marked function are supposed to be called
574b39927cfSSam Ravnborg  * only when a moduel is unloaded which never happes for built-in modules.
575b39927cfSSam Ravnborg  * The check_sec_ref() function traverses all relocation records
576b39927cfSSam Ravnborg  * to find all references to a section that reference a section that will
577b39927cfSSam Ravnborg  * be discarded and warns about it.
578b39927cfSSam Ravnborg  **/
579b39927cfSSam Ravnborg static void check_sec_ref(struct module *mod, const char *modname,
580b39927cfSSam Ravnborg 			  struct elf_info *elf,
581b39927cfSSam Ravnborg 			  int section(const char*),
582b39927cfSSam Ravnborg 			  int section_ref_ok(const char *))
583b39927cfSSam Ravnborg {
584b39927cfSSam Ravnborg 	int i;
585b39927cfSSam Ravnborg 	Elf_Sym  *sym;
586b39927cfSSam Ravnborg 	Elf_Ehdr *hdr = elf->hdr;
587b39927cfSSam Ravnborg 	Elf_Shdr *sechdrs = elf->sechdrs;
588b39927cfSSam Ravnborg 	const char *secstrings = (void *)hdr +
589b39927cfSSam Ravnborg 				 sechdrs[hdr->e_shstrndx].sh_offset;
590b39927cfSSam Ravnborg 
591b39927cfSSam Ravnborg 	/* Walk through all sections */
592b39927cfSSam Ravnborg 	for (i = 0; i < hdr->e_shnum; i++) {
593*fededcd2S[email protected] 		Elf_Rela *rela;
594*fededcd2S[email protected] 		Elf_Rela *start = (void *)hdr + sechdrs[i].sh_offset;
595*fededcd2S[email protected] 		Elf_Rela *stop  = (void*)start + sechdrs[i].sh_size;
596b39927cfSSam Ravnborg 		const char *name = secstrings + sechdrs[i].sh_name +
597b39927cfSSam Ravnborg 						strlen(".rela");
598b39927cfSSam Ravnborg 		/* We want to process only relocation sections and not .init */
599b39927cfSSam Ravnborg 		if (section_ref_ok(name) || (sechdrs[i].sh_type != SHT_RELA))
600b39927cfSSam Ravnborg 			continue;
601b39927cfSSam Ravnborg 
602b39927cfSSam Ravnborg 		for (rela = start; rela < stop; rela++) {
603b39927cfSSam Ravnborg 			Elf_Rela r;
604b39927cfSSam Ravnborg 			const char *secname;
605b39927cfSSam Ravnborg 			r.r_offset = TO_NATIVE(rela->r_offset);
606b39927cfSSam Ravnborg 			r.r_info   = TO_NATIVE(rela->r_info);
60793684d3bSSam Ravnborg 			r.r_addend = TO_NATIVE(rela->r_addend);
608b39927cfSSam Ravnborg 			sym = elf->symtab_start + ELF_R_SYM(r.r_info);
609b39927cfSSam Ravnborg 			/* Skip special sections */
610b39927cfSSam Ravnborg 			if (sym->st_shndx >= SHN_LORESERVE)
611b39927cfSSam Ravnborg 				continue;
612b39927cfSSam Ravnborg 
6138ea80ca4SSam Ravnborg 			secname = secstrings + sechdrs[sym->st_shndx].sh_name;
614b39927cfSSam Ravnborg 			if (section(secname))
615b39927cfSSam Ravnborg 				warn_sec_mismatch(modname, name, elf, sym, r);
616b39927cfSSam Ravnborg 		}
617b39927cfSSam Ravnborg 	}
618b39927cfSSam Ravnborg }
619b39927cfSSam Ravnborg 
620b39927cfSSam Ravnborg /**
621b39927cfSSam Ravnborg  * Functions used only during module init is marked __init and is stored in
622b39927cfSSam Ravnborg  * a .init.text section. Likewise data is marked __initdata and stored in
623b39927cfSSam Ravnborg  * a .init.data section.
624b39927cfSSam Ravnborg  * If this section is one of these sections return 1
625b39927cfSSam Ravnborg  * See include/linux/init.h for the details
626b39927cfSSam Ravnborg  **/
627b39927cfSSam Ravnborg static int init_section(const char *name)
628b39927cfSSam Ravnborg {
629b39927cfSSam Ravnborg 	if (strcmp(name, ".init") == 0)
630b39927cfSSam Ravnborg 		return 1;
631b39927cfSSam Ravnborg 	if (strncmp(name, ".init.", strlen(".init.")) == 0)
632b39927cfSSam Ravnborg 		return 1;
633b39927cfSSam Ravnborg 	return 0;
634b39927cfSSam Ravnborg }
635b39927cfSSam Ravnborg 
636b39927cfSSam Ravnborg /**
637b39927cfSSam Ravnborg  * Identify sections from which references to a .init section is OK.
638b39927cfSSam Ravnborg  *
639b39927cfSSam Ravnborg  * Unfortunately references to read only data that referenced .init
640b39927cfSSam Ravnborg  * sections had to be excluded. Almost all of these are false
641b39927cfSSam Ravnborg  * positives, they are created by gcc. The downside of excluding rodata
642b39927cfSSam Ravnborg  * is that there really are some user references from rodata to
643b39927cfSSam Ravnborg  * init code, e.g. drivers/video/vgacon.c:
644b39927cfSSam Ravnborg  *
645b39927cfSSam Ravnborg  * const struct consw vga_con = {
646b39927cfSSam Ravnborg  *        con_startup:            vgacon_startup,
647b39927cfSSam Ravnborg  *
648b39927cfSSam Ravnborg  * where vgacon_startup is __init.  If you want to wade through the false
649b39927cfSSam Ravnborg  * positives, take out the check for rodata.
650b39927cfSSam Ravnborg  **/
651b39927cfSSam Ravnborg static int init_section_ref_ok(const char *name)
652b39927cfSSam Ravnborg {
653b39927cfSSam Ravnborg 	const char **s;
654b39927cfSSam Ravnborg 	/* Absolute section names */
655b39927cfSSam Ravnborg 	const char *namelist1[] = {
656b39927cfSSam Ravnborg 		".init",
657b39927cfSSam Ravnborg 		".stab",
658b39927cfSSam Ravnborg 		".rodata",
659b39927cfSSam Ravnborg 		".text.lock",
660b39927cfSSam Ravnborg 		".pci_fixup_header",
661b39927cfSSam Ravnborg 		".pci_fixup_final",
662b39927cfSSam Ravnborg 		".pdr",
663b39927cfSSam Ravnborg 		"__param",
664b39927cfSSam Ravnborg 		NULL
665b39927cfSSam Ravnborg 	};
666b39927cfSSam Ravnborg 	/* Start of section names */
667b39927cfSSam Ravnborg 	const char *namelist2[] = {
668b39927cfSSam Ravnborg 		".init.",
669b39927cfSSam Ravnborg 		".altinstructions",
670b39927cfSSam Ravnborg 		".eh_frame",
671b39927cfSSam Ravnborg 		".debug",
672b39927cfSSam Ravnborg 		NULL
673b39927cfSSam Ravnborg 	};
674b39927cfSSam Ravnborg 
675b39927cfSSam Ravnborg 	for (s = namelist1; *s; s++)
676b39927cfSSam Ravnborg 		if (strcmp(*s, name) == 0)
677b39927cfSSam Ravnborg 			return 1;
678b39927cfSSam Ravnborg 	for (s = namelist2; *s; s++)
679b39927cfSSam Ravnborg 		if (strncmp(*s, name, strlen(*s)) == 0)
680b39927cfSSam Ravnborg 			return 1;
681b39927cfSSam Ravnborg 	return 0;
682b39927cfSSam Ravnborg }
683b39927cfSSam Ravnborg 
684b39927cfSSam Ravnborg /*
685b39927cfSSam Ravnborg  * Functions used only during module exit is marked __exit and is stored in
686b39927cfSSam Ravnborg  * a .exit.text section. Likewise data is marked __exitdata and stored in
687b39927cfSSam Ravnborg  * a .exit.data section.
688b39927cfSSam Ravnborg  * If this section is one of these sections return 1
689b39927cfSSam Ravnborg  * See include/linux/init.h for the details
690b39927cfSSam Ravnborg  **/
691b39927cfSSam Ravnborg static int exit_section(const char *name)
692b39927cfSSam Ravnborg {
693b39927cfSSam Ravnborg 	if (strcmp(name, ".exit.text") == 0)
694b39927cfSSam Ravnborg 		return 1;
695b39927cfSSam Ravnborg 	if (strcmp(name, ".exit.data") == 0)
696b39927cfSSam Ravnborg 		return 1;
697b39927cfSSam Ravnborg 	return 0;
698b39927cfSSam Ravnborg 
699b39927cfSSam Ravnborg }
700b39927cfSSam Ravnborg 
701b39927cfSSam Ravnborg /*
702b39927cfSSam Ravnborg  * Identify sections from which references to a .exit section is OK.
703b39927cfSSam Ravnborg  *
704b39927cfSSam Ravnborg  * [OPD] Keith Ownes <[email protected]> commented:
705b39927cfSSam Ravnborg  * For our future {in}sanity, add a comment that this is the ppc .opd
706b39927cfSSam Ravnborg  * section, not the ia64 .opd section.
707b39927cfSSam Ravnborg  * ia64 .opd should not point to discarded sections.
708b39927cfSSam Ravnborg  **/
709b39927cfSSam Ravnborg static int exit_section_ref_ok(const char *name)
710b39927cfSSam Ravnborg {
711b39927cfSSam Ravnborg 	const char **s;
712b39927cfSSam Ravnborg 	/* Absolute section names */
713b39927cfSSam Ravnborg 	const char *namelist1[] = {
714b39927cfSSam Ravnborg 		".exit.text",
715b39927cfSSam Ravnborg 		".exit.data",
716b39927cfSSam Ravnborg 		".init.text",
717b39927cfSSam Ravnborg 		".opd", /* See comment [OPD] */
718b39927cfSSam Ravnborg 		".altinstructions",
719b39927cfSSam Ravnborg 		".pdr",
720b39927cfSSam Ravnborg 		".exitcall.exit",
721b39927cfSSam Ravnborg 		".eh_frame",
722b39927cfSSam Ravnborg 		".stab",
723b39927cfSSam Ravnborg 		NULL
724b39927cfSSam Ravnborg 	};
725b39927cfSSam Ravnborg 	/* Start of section names */
726b39927cfSSam Ravnborg 	const char *namelist2[] = {
727b39927cfSSam Ravnborg 		".debug",
728b39927cfSSam Ravnborg 		NULL
729b39927cfSSam Ravnborg 	};
730b39927cfSSam Ravnborg 
731b39927cfSSam Ravnborg 	for (s = namelist1; *s; s++)
732b39927cfSSam Ravnborg 		if (strcmp(*s, name) == 0)
733b39927cfSSam Ravnborg 			return 1;
734b39927cfSSam Ravnborg 	for (s = namelist2; *s; s++)
735b39927cfSSam Ravnborg 		if (strncmp(*s, name, strlen(*s)) == 0)
736b39927cfSSam Ravnborg 			return 1;
737b39927cfSSam Ravnborg 	return 0;
738b39927cfSSam Ravnborg }
739b39927cfSSam Ravnborg 
7405c3ead8cSSam Ravnborg static void read_symbols(char *modname)
7411da177e4SLinus Torvalds {
7421da177e4SLinus Torvalds 	const char *symname;
7431da177e4SLinus Torvalds 	char *version;
7441da177e4SLinus Torvalds 	struct module *mod;
7451da177e4SLinus Torvalds 	struct elf_info info = { };
7461da177e4SLinus Torvalds 	Elf_Sym *sym;
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds 	parse_elf(&info, modname);
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds 	mod = new_module(modname);
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 	/* When there's no vmlinux, don't print warnings about
7531da177e4SLinus Torvalds 	 * unresolved symbols (since there'll be too many ;) */
7541da177e4SLinus Torvalds 	if (is_vmlinux(modname)) {
7551da177e4SLinus Torvalds 		have_vmlinux = 1;
7561da177e4SLinus Torvalds 		mod->skip = 1;
7571da177e4SLinus Torvalds 	}
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
7601da177e4SLinus Torvalds 		symname = info.strtab + sym->st_name;
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds 		handle_modversions(mod, &info, sym, symname);
7631da177e4SLinus Torvalds 		handle_moddevtable(mod, &info, sym, symname);
7641da177e4SLinus Torvalds 	}
765b39927cfSSam Ravnborg 	check_sec_ref(mod, modname, &info, init_section, init_section_ref_ok);
766b39927cfSSam Ravnborg 	check_sec_ref(mod, modname, &info, exit_section, exit_section_ref_ok);
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds 	version = get_modinfo(info.modinfo, info.modinfo_len, "version");
7691da177e4SLinus Torvalds 	if (version)
7701da177e4SLinus Torvalds 		maybe_frob_rcs_version(modname, version, info.modinfo,
7711da177e4SLinus Torvalds 				       version - (char *)info.hdr);
7721da177e4SLinus Torvalds 	if (version || (all_versions && !is_vmlinux(modname)))
7731da177e4SLinus Torvalds 		get_src_version(modname, mod->srcversion,
7741da177e4SLinus Torvalds 				sizeof(mod->srcversion)-1);
7751da177e4SLinus Torvalds 
7761da177e4SLinus Torvalds 	parse_elf_finish(&info);
7771da177e4SLinus Torvalds 
7781da177e4SLinus Torvalds 	/* Our trick to get versioning for struct_module - it's
7791da177e4SLinus Torvalds 	 * never passed as an argument to an exported function, so
7801da177e4SLinus Torvalds 	 * the automatic versioning doesn't pick it up, but it's really
7811da177e4SLinus Torvalds 	 * important anyhow */
7821da177e4SLinus Torvalds 	if (modversions)
7831da177e4SLinus Torvalds 		mod->unres = alloc_symbol("struct_module", 0, mod->unres);
7841da177e4SLinus Torvalds }
7851da177e4SLinus Torvalds 
7861da177e4SLinus Torvalds #define SZ 500
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds /* We first write the generated file into memory using the
7891da177e4SLinus Torvalds  * following helper, then compare to the file on disk and
7901da177e4SLinus Torvalds  * only update the later if anything changed */
7911da177e4SLinus Torvalds 
7925c3ead8cSSam Ravnborg void __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf,
7935c3ead8cSSam Ravnborg 						      const char *fmt, ...)
7941da177e4SLinus Torvalds {
7951da177e4SLinus Torvalds 	char tmp[SZ];
7961da177e4SLinus Torvalds 	int len;
7971da177e4SLinus Torvalds 	va_list ap;
7981da177e4SLinus Torvalds 
7991da177e4SLinus Torvalds 	va_start(ap, fmt);
8001da177e4SLinus Torvalds 	len = vsnprintf(tmp, SZ, fmt, ap);
8011da177e4SLinus Torvalds 	if (buf->size - buf->pos < len + 1) {
8021da177e4SLinus Torvalds 		buf->size += 128;
8031da177e4SLinus Torvalds 		buf->p = realloc(buf->p, buf->size);
8041da177e4SLinus Torvalds 	}
8051da177e4SLinus Torvalds 	strncpy(buf->p + buf->pos, tmp, len + 1);
8061da177e4SLinus Torvalds 	buf->pos += len;
8071da177e4SLinus Torvalds 	va_end(ap);
8081da177e4SLinus Torvalds }
8091da177e4SLinus Torvalds 
8105c3ead8cSSam Ravnborg void buf_write(struct buffer *buf, const char *s, int len)
8111da177e4SLinus Torvalds {
8121da177e4SLinus Torvalds 	if (buf->size - buf->pos < len) {
8131da177e4SLinus Torvalds 		buf->size += len;
8141da177e4SLinus Torvalds 		buf->p = realloc(buf->p, buf->size);
8151da177e4SLinus Torvalds 	}
8161da177e4SLinus Torvalds 	strncpy(buf->p + buf->pos, s, len);
8171da177e4SLinus Torvalds 	buf->pos += len;
8181da177e4SLinus Torvalds }
8191da177e4SLinus Torvalds 
8205c3ead8cSSam Ravnborg /**
8215c3ead8cSSam Ravnborg  * Header for the generated file
8225c3ead8cSSam Ravnborg  **/
8235c3ead8cSSam Ravnborg static void add_header(struct buffer *b, struct module *mod)
8241da177e4SLinus Torvalds {
8251da177e4SLinus Torvalds 	buf_printf(b, "#include <linux/module.h>\n");
8261da177e4SLinus Torvalds 	buf_printf(b, "#include <linux/vermagic.h>\n");
8271da177e4SLinus Torvalds 	buf_printf(b, "#include <linux/compiler.h>\n");
8281da177e4SLinus Torvalds 	buf_printf(b, "\n");
8291da177e4SLinus Torvalds 	buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
8301da177e4SLinus Torvalds 	buf_printf(b, "\n");
8311da177e4SLinus Torvalds 	buf_printf(b, "struct module __this_module\n");
8321da177e4SLinus Torvalds 	buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n");
833f83b5e32SUstyugov Roman 	buf_printf(b, " .name = KBUILD_MODNAME,\n");
8341da177e4SLinus Torvalds 	if (mod->has_init)
8351da177e4SLinus Torvalds 		buf_printf(b, " .init = init_module,\n");
8361da177e4SLinus Torvalds 	if (mod->has_cleanup)
8371da177e4SLinus Torvalds 		buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n"
8381da177e4SLinus Torvalds 			      " .exit = cleanup_module,\n"
8391da177e4SLinus Torvalds 			      "#endif\n");
8401da177e4SLinus Torvalds 	buf_printf(b, "};\n");
8411da177e4SLinus Torvalds }
8421da177e4SLinus Torvalds 
8435c3ead8cSSam Ravnborg /**
8445c3ead8cSSam Ravnborg  * Record CRCs for unresolved symbols
8455c3ead8cSSam Ravnborg  **/
8465c3ead8cSSam Ravnborg static void add_versions(struct buffer *b, struct module *mod)
8471da177e4SLinus Torvalds {
8481da177e4SLinus Torvalds 	struct symbol *s, *exp;
8491da177e4SLinus Torvalds 
8501da177e4SLinus Torvalds 	for (s = mod->unres; s; s = s->next) {
8511da177e4SLinus Torvalds 		exp = find_symbol(s->name);
8521da177e4SLinus Torvalds 		if (!exp || exp->module == mod) {
8531da177e4SLinus Torvalds 			if (have_vmlinux && !s->weak)
854cb80514dSSam Ravnborg 				warn("\"%s\" [%s.ko] undefined!\n",
855cb80514dSSam Ravnborg 				     s->name, mod->name);
8561da177e4SLinus Torvalds 			continue;
8571da177e4SLinus Torvalds 		}
8581da177e4SLinus Torvalds 		s->module = exp->module;
8591da177e4SLinus Torvalds 		s->crc_valid = exp->crc_valid;
8601da177e4SLinus Torvalds 		s->crc = exp->crc;
8611da177e4SLinus Torvalds 	}
8621da177e4SLinus Torvalds 
8631da177e4SLinus Torvalds 	if (!modversions)
8641da177e4SLinus Torvalds 		return;
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds 	buf_printf(b, "\n");
8671da177e4SLinus Torvalds 	buf_printf(b, "static const struct modversion_info ____versions[]\n");
8681da177e4SLinus Torvalds 	buf_printf(b, "__attribute_used__\n");
8691da177e4SLinus Torvalds 	buf_printf(b, "__attribute__((section(\"__versions\"))) = {\n");
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds 	for (s = mod->unres; s; s = s->next) {
8721da177e4SLinus Torvalds 		if (!s->module) {
8731da177e4SLinus Torvalds 			continue;
8741da177e4SLinus Torvalds 		}
8751da177e4SLinus Torvalds 		if (!s->crc_valid) {
876cb80514dSSam Ravnborg 			warn("\"%s\" [%s.ko] has no CRC!\n",
8771da177e4SLinus Torvalds 				s->name, mod->name);
8781da177e4SLinus Torvalds 			continue;
8791da177e4SLinus Torvalds 		}
8801da177e4SLinus Torvalds 		buf_printf(b, "\t{ %#8x, \"%s\" },\n", s->crc, s->name);
8811da177e4SLinus Torvalds 	}
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 	buf_printf(b, "};\n");
8841da177e4SLinus Torvalds }
8851da177e4SLinus Torvalds 
8865c3ead8cSSam Ravnborg static void add_depends(struct buffer *b, struct module *mod,
8875c3ead8cSSam Ravnborg 			struct module *modules)
8881da177e4SLinus Torvalds {
8891da177e4SLinus Torvalds 	struct symbol *s;
8901da177e4SLinus Torvalds 	struct module *m;
8911da177e4SLinus Torvalds 	int first = 1;
8921da177e4SLinus Torvalds 
8931da177e4SLinus Torvalds 	for (m = modules; m; m = m->next) {
8941da177e4SLinus Torvalds 		m->seen = is_vmlinux(m->name);
8951da177e4SLinus Torvalds 	}
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 	buf_printf(b, "\n");
8981da177e4SLinus Torvalds 	buf_printf(b, "static const char __module_depends[]\n");
8991da177e4SLinus Torvalds 	buf_printf(b, "__attribute_used__\n");
9001da177e4SLinus Torvalds 	buf_printf(b, "__attribute__((section(\".modinfo\"))) =\n");
9011da177e4SLinus Torvalds 	buf_printf(b, "\"depends=");
9021da177e4SLinus Torvalds 	for (s = mod->unres; s; s = s->next) {
9031da177e4SLinus Torvalds 		if (!s->module)
9041da177e4SLinus Torvalds 			continue;
9051da177e4SLinus Torvalds 
9061da177e4SLinus Torvalds 		if (s->module->seen)
9071da177e4SLinus Torvalds 			continue;
9081da177e4SLinus Torvalds 
9091da177e4SLinus Torvalds 		s->module->seen = 1;
9101da177e4SLinus Torvalds 		buf_printf(b, "%s%s", first ? "" : ",",
9111da177e4SLinus Torvalds 			   strrchr(s->module->name, '/') + 1);
9121da177e4SLinus Torvalds 		first = 0;
9131da177e4SLinus Torvalds 	}
9141da177e4SLinus Torvalds 	buf_printf(b, "\";\n");
9151da177e4SLinus Torvalds }
9161da177e4SLinus Torvalds 
9175c3ead8cSSam Ravnborg static void add_srcversion(struct buffer *b, struct module *mod)
9181da177e4SLinus Torvalds {
9191da177e4SLinus Torvalds 	if (mod->srcversion[0]) {
9201da177e4SLinus Torvalds 		buf_printf(b, "\n");
9211da177e4SLinus Torvalds 		buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n",
9221da177e4SLinus Torvalds 			   mod->srcversion);
9231da177e4SLinus Torvalds 	}
9241da177e4SLinus Torvalds }
9251da177e4SLinus Torvalds 
9265c3ead8cSSam Ravnborg static void write_if_changed(struct buffer *b, const char *fname)
9271da177e4SLinus Torvalds {
9281da177e4SLinus Torvalds 	char *tmp;
9291da177e4SLinus Torvalds 	FILE *file;
9301da177e4SLinus Torvalds 	struct stat st;
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds 	file = fopen(fname, "r");
9331da177e4SLinus Torvalds 	if (!file)
9341da177e4SLinus Torvalds 		goto write;
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	if (fstat(fileno(file), &st) < 0)
9371da177e4SLinus Torvalds 		goto close_write;
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 	if (st.st_size != b->pos)
9401da177e4SLinus Torvalds 		goto close_write;
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds 	tmp = NOFAIL(malloc(b->pos));
9431da177e4SLinus Torvalds 	if (fread(tmp, 1, b->pos, file) != b->pos)
9441da177e4SLinus Torvalds 		goto free_write;
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds 	if (memcmp(tmp, b->p, b->pos) != 0)
9471da177e4SLinus Torvalds 		goto free_write;
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds 	free(tmp);
9501da177e4SLinus Torvalds 	fclose(file);
9511da177e4SLinus Torvalds 	return;
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds  free_write:
9541da177e4SLinus Torvalds 	free(tmp);
9551da177e4SLinus Torvalds  close_write:
9561da177e4SLinus Torvalds 	fclose(file);
9571da177e4SLinus Torvalds  write:
9581da177e4SLinus Torvalds 	file = fopen(fname, "w");
9591da177e4SLinus Torvalds 	if (!file) {
9601da177e4SLinus Torvalds 		perror(fname);
9611da177e4SLinus Torvalds 		exit(1);
9621da177e4SLinus Torvalds 	}
9631da177e4SLinus Torvalds 	if (fwrite(b->p, 1, b->pos, file) != b->pos) {
9641da177e4SLinus Torvalds 		perror(fname);
9651da177e4SLinus Torvalds 		exit(1);
9661da177e4SLinus Torvalds 	}
9671da177e4SLinus Torvalds 	fclose(file);
9681da177e4SLinus Torvalds }
9691da177e4SLinus Torvalds 
970040fcc81SSam Ravnborg static void read_dump(const char *fname, unsigned int kernel)
9711da177e4SLinus Torvalds {
9721da177e4SLinus Torvalds 	unsigned long size, pos = 0;
9731da177e4SLinus Torvalds 	void *file = grab_file(fname, &size);
9741da177e4SLinus Torvalds 	char *line;
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds         if (!file)
9771da177e4SLinus Torvalds 		/* No symbol versions, silently ignore */
9781da177e4SLinus Torvalds 		return;
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds 	while ((line = get_next_line(&pos, file, size))) {
9811da177e4SLinus Torvalds 		char *symname, *modname, *d;
9821da177e4SLinus Torvalds 		unsigned int crc;
9831da177e4SLinus Torvalds 		struct module *mod;
984040fcc81SSam Ravnborg 		struct symbol *s;
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds 		if (!(symname = strchr(line, '\t')))
9871da177e4SLinus Torvalds 			goto fail;
9881da177e4SLinus Torvalds 		*symname++ = '\0';
9891da177e4SLinus Torvalds 		if (!(modname = strchr(symname, '\t')))
9901da177e4SLinus Torvalds 			goto fail;
9911da177e4SLinus Torvalds 		*modname++ = '\0';
9921da177e4SLinus Torvalds 		if (strchr(modname, '\t'))
9931da177e4SLinus Torvalds 			goto fail;
9941da177e4SLinus Torvalds 		crc = strtoul(line, &d, 16);
9951da177e4SLinus Torvalds 		if (*symname == '\0' || *modname == '\0' || *d != '\0')
9961da177e4SLinus Torvalds 			goto fail;
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 		if (!(mod = find_module(modname))) {
9991da177e4SLinus Torvalds 			if (is_vmlinux(modname)) {
10001da177e4SLinus Torvalds 				have_vmlinux = 1;
10011da177e4SLinus Torvalds 			}
10021da177e4SLinus Torvalds 			mod = new_module(NOFAIL(strdup(modname)));
10031da177e4SLinus Torvalds 			mod->skip = 1;
10041da177e4SLinus Torvalds 		}
1005040fcc81SSam Ravnborg 		s = sym_add_exported(symname, mod);
1006040fcc81SSam Ravnborg 		s->kernel    = kernel;
10078e70c458SSam Ravnborg 		s->preloaded = 1;
1008040fcc81SSam Ravnborg 		sym_update_crc(symname, mod, crc);
10091da177e4SLinus Torvalds 	}
10101da177e4SLinus Torvalds 	return;
10111da177e4SLinus Torvalds fail:
10121da177e4SLinus Torvalds 	fatal("parse error in symbol dump file\n");
10131da177e4SLinus Torvalds }
10141da177e4SLinus Torvalds 
1015040fcc81SSam Ravnborg /* For normal builds always dump all symbols.
1016040fcc81SSam Ravnborg  * For external modules only dump symbols
1017040fcc81SSam Ravnborg  * that are not read from kernel Module.symvers.
1018040fcc81SSam Ravnborg  **/
1019040fcc81SSam Ravnborg static int dump_sym(struct symbol *sym)
1020040fcc81SSam Ravnborg {
1021040fcc81SSam Ravnborg 	if (!external_module)
1022040fcc81SSam Ravnborg 		return 1;
1023040fcc81SSam Ravnborg 	if (sym->vmlinux || sym->kernel)
1024040fcc81SSam Ravnborg 		return 0;
1025040fcc81SSam Ravnborg 	return 1;
1026040fcc81SSam Ravnborg }
1027040fcc81SSam Ravnborg 
10285c3ead8cSSam Ravnborg static void write_dump(const char *fname)
10291da177e4SLinus Torvalds {
10301da177e4SLinus Torvalds 	struct buffer buf = { };
10311da177e4SLinus Torvalds 	struct symbol *symbol;
10321da177e4SLinus Torvalds 	int n;
10331da177e4SLinus Torvalds 
10341da177e4SLinus Torvalds 	for (n = 0; n < SYMBOL_HASH_SIZE ; n++) {
10351da177e4SLinus Torvalds 		symbol = symbolhash[n];
10361da177e4SLinus Torvalds 		while (symbol) {
1037040fcc81SSam Ravnborg 			if (dump_sym(symbol))
1038040fcc81SSam Ravnborg 				buf_printf(&buf, "0x%08x\t%s\t%s\n",
1039040fcc81SSam Ravnborg 					symbol->crc, symbol->name,
1040040fcc81SSam Ravnborg 					symbol->module->name);
10411da177e4SLinus Torvalds 			symbol = symbol->next;
10421da177e4SLinus Torvalds 		}
10431da177e4SLinus Torvalds 	}
10441da177e4SLinus Torvalds 	write_if_changed(&buf, fname);
10451da177e4SLinus Torvalds }
10461da177e4SLinus Torvalds 
10475c3ead8cSSam Ravnborg int main(int argc, char **argv)
10481da177e4SLinus Torvalds {
10491da177e4SLinus Torvalds 	struct module *mod;
10501da177e4SLinus Torvalds 	struct buffer buf = { };
10511da177e4SLinus Torvalds 	char fname[SZ];
1052040fcc81SSam Ravnborg 	char *kernel_read = NULL, *module_read = NULL;
1053040fcc81SSam Ravnborg 	char *dump_write = NULL;
10541da177e4SLinus Torvalds 	int opt;
10551da177e4SLinus Torvalds 
1056040fcc81SSam Ravnborg 	while ((opt = getopt(argc, argv, "i:I:mo:a")) != -1) {
10571da177e4SLinus Torvalds 		switch(opt) {
10581da177e4SLinus Torvalds 			case 'i':
1059040fcc81SSam Ravnborg 				kernel_read = optarg;
1060040fcc81SSam Ravnborg 				break;
1061040fcc81SSam Ravnborg 			case 'I':
1062040fcc81SSam Ravnborg 				module_read = optarg;
1063040fcc81SSam Ravnborg 				external_module = 1;
10641da177e4SLinus Torvalds 				break;
10651da177e4SLinus Torvalds 			case 'm':
10661da177e4SLinus Torvalds 				modversions = 1;
10671da177e4SLinus Torvalds 				break;
10681da177e4SLinus Torvalds 			case 'o':
10691da177e4SLinus Torvalds 				dump_write = optarg;
10701da177e4SLinus Torvalds 				break;
10711da177e4SLinus Torvalds 			case 'a':
10721da177e4SLinus Torvalds 				all_versions = 1;
10731da177e4SLinus Torvalds 				break;
10741da177e4SLinus Torvalds 			default:
10751da177e4SLinus Torvalds 				exit(1);
10761da177e4SLinus Torvalds 		}
10771da177e4SLinus Torvalds 	}
10781da177e4SLinus Torvalds 
1079040fcc81SSam Ravnborg 	if (kernel_read)
1080040fcc81SSam Ravnborg 		read_dump(kernel_read, 1);
1081040fcc81SSam Ravnborg 	if (module_read)
1082040fcc81SSam Ravnborg 		read_dump(module_read, 0);
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds 	while (optind < argc) {
10851da177e4SLinus Torvalds 		read_symbols(argv[optind++]);
10861da177e4SLinus Torvalds 	}
10871da177e4SLinus Torvalds 
10881da177e4SLinus Torvalds 	for (mod = modules; mod; mod = mod->next) {
10891da177e4SLinus Torvalds 		if (mod->skip)
10901da177e4SLinus Torvalds 			continue;
10911da177e4SLinus Torvalds 
10921da177e4SLinus Torvalds 		buf.pos = 0;
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds 		add_header(&buf, mod);
10951da177e4SLinus Torvalds 		add_versions(&buf, mod);
10961da177e4SLinus Torvalds 		add_depends(&buf, mod, modules);
10971da177e4SLinus Torvalds 		add_moddevtable(&buf, mod);
10981da177e4SLinus Torvalds 		add_srcversion(&buf, mod);
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds 		sprintf(fname, "%s.mod.c", mod->name);
11011da177e4SLinus Torvalds 		write_if_changed(&buf, fname);
11021da177e4SLinus Torvalds 	}
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 	if (dump_write)
11051da177e4SLinus Torvalds 		write_dump(dump_write);
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 	return 0;
11081da177e4SLinus Torvalds }
1109