/*
 * depmod
 *
 * Create dependency file for modprobe, the kernel loadable modules loader.
 *
 * Copyright 1994, 1995, 1996, 1997 Jacques Gelinas <jack@solucorp.qc.ca>
 * Additional modifications: Björn Ekwall <bj0rn@blox.se> February, March 1999
 *
 * This file is part of the Linux modutils.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

  /*
     Fixes:

     Add -r flag: Keith Owens <kaos@ocs.com.au> October 1999.

     Rationalize common code for 32/64 bit architectures.
     Fix error message for modules.dep.
       Keith Owens <kaos@ocs.com.au> December 1999
     Add arch64().
       Keith Owens <kaos@ocs.com.au> December 1999.
     Ignore any genksyms suffix when using -F.
       Keith Owens <kaos@ocs.com.au> April 2000.
   */

#ident "$Id: depmod.c 1.13 Thu, 20 Apr 2000 14:31:49 +1000 kaos $"

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <limits.h>
#include <elf.h>
#include ELF_MACHINE_H

#include "module.h"
#include "version.h"
#include "util.h"
#include "obj.h"
#include "config.h"
#include "modstat.h"

#define ALLOC_MODULE	5000	/* Number of modules we can handle... */
#define ALLOC_SYM	10000	/* Number of symbols allocated per chunk */

typedef struct SYMBOL{
	struct SYMBOL *next;	/* List connected via hashing */
	struct MODULE *module;	/* Module declaring this symbol */
	const char *name;
	unsigned short hashval;
	unsigned short hashval_nosuffix;
	unsigned short length_nosuffix;
	char status;
} SYMBOL;

typedef struct MODULE {
	char *name;
	int resolved;
	struct {	/* Defined + required symbols for this module */
		SYMBOL **symtab;
		int n_syms;
	} defsym, undefs;
} MODULE;

typedef enum SYM_STATUS {
	SYM_UNDEF,	/* Required by another module */
	SYM_DEFINED,	/* We have found a module that knows about it */
	SYM_RESOLVED
} SYM_STATUS;

typedef struct LIST_SYMBOL {
	struct LIST_SYMBOL *next;
	SYMBOL alloc[ALLOC_SYM];
} LIST_SYMBOL;

/*==================================================*/

static MODULE modules[ALLOC_MODULE];
static int n_modules;
static SYMBOL *symhash[2048];
static SYMBOL *symavail;
static SYMBOL *maxsyms;
static LIST_SYMBOL *chunk;

static int quiet;	/* Don't print errors */
static int showerror;	/* Shows undefined symbols */
static int verbose;	/* Print all modules visiteds */

extern int depmod_main(int argc, char **argv);
extern int depmod_main_32(int argc, char **argv);
extern int depmod_main_64(int argc, char **argv);

extern int quick;	/* Option -A */


/*
 *	Create a symbol definition.
 *	Add defined symbol to the head of the list of defined symbols.
 *	Return the new symbol.
 */
static SYMBOL *addsym(const char *name, MODULE *module, SYM_STATUS status,
		      int ignore_suffix)
{
	SYMBOL added;
	SYMBOL *me;
	unsigned short hashval = 0;
	const char *pt = name, *pt_R;
	char *p;
	int l;

	for (pt = name; *pt; ++pt)
		hashval = (hashval << 1) ^ *pt;
	hashval %= 2048;

	memset(&added, 0, sizeof(SYMBOL));
	added.module = module;
	added.name = xstrdup(name);
	added.hashval = hashval;
	added.status = status;

	/* Compute the hash without the trailing genksym suffix if required */
	if (ignore_suffix && (pt_R = strstr(name, "_R"))) {
		l = strlen(pt_R);
		if (l >= 10) {	/* May be _R.*xxxxxxxx */
			(void)strtoul(pt_R+l-8, &p, 16);
			if (!*p) { /* It is _R.*xxxxxxxx */
				added.length_nosuffix = pt_R - name;
				hashval = 0;
				for (pt = name; pt < pt_R; ++pt)
					hashval = (hashval << 1) ^ *pt;
				hashval %= 2048;
				added.hashval_nosuffix = hashval;
			}
		}
	}
	else {
		added.hashval_nosuffix = 0;
		added.length_nosuffix = 0;
	}

	if (status == SYM_DEFINED) {
		if (symavail == NULL ||
		    symavail == maxsyms) {
			/*
			 * Allocate a new buffer for the collection of symbols
			 */
			LIST_SYMBOL *list = (LIST_SYMBOL *)xmalloc(sizeof(LIST_SYMBOL));
			list->next = chunk;
			chunk = list;
			symavail = list->alloc;
			maxsyms = list->alloc + ALLOC_SYM;
		}
		me = symavail++;
		*me = added;
		me->next = symhash[added.hashval];
		symhash[added.hashval] = me;
	} else {
		me = (SYMBOL *)xmalloc(sizeof(SYMBOL));
		*me = added;
	}

	return me;
}

/************************************************************
 * Module.c
 */

static void resolve(void)
{
	MODULE *mod;
	SYMBOL **sym;
	int i;
	int n;
	int resolved;

	/*
	 * Resolve, starting with the most recently added
	 * module symbol definitions.
	 */
	if (n_modules <= 0)
		return;
	/* else */
	for (i = n_modules - 1, mod = modules + i; i >= 0; --i, --mod) {
		if (mod->resolved)
			continue;

		for (resolved = 1, n = 0, sym = mod->undefs.symtab;
		     n < mod->undefs.n_syms; ++sym, ++n) {
			SYMBOL *psym = *sym, *find;

			if (psym->status != SYM_UNDEF)
				continue;

			/* This is an undefined symbol.   Find first
			   definition.  Symbol hash linked list has
			   newest first, so we want the last match in
			   the chain.  In particular, this prefers
			   kernel symbols to that of any module.
			   
			   If we are ignoring the genksyms suffix in this
			   comparision then there are two hashes to try.
			 */

			find = symhash[psym->hashval];
			while (find) {
				if (strcmp(find->name, psym->name) == 0) {
					psym->status = SYM_RESOLVED;
					psym->module = find->module;
				}
				find = find->next;
			}
			if (psym->status == SYM_UNDEF && psym->length_nosuffix) {
				/* Try again ignoring the suffix */
				find = symhash[psym->hashval_nosuffix];
				while (find) {
					if (strncmp(find->name, psym->name,
					    psym->length_nosuffix) == 0 &&
					    !find->name[psym->length_nosuffix]) {
						psym->status = SYM_RESOLVED;
						psym->module = find->module;
					}
					find = find->next;
				}
			}
			if (psym->status == SYM_UNDEF)
				resolved = 0;
		}
		mod->resolved = resolved;
	}
}

/*
 *	Read the symbols in an object and register them in the symbol table.
 *	Return -1 if there is an error.
 */
static int loadobj(const char *objname, int ignore_suffix)
{
	static char *currdir;
	static int currdir_len;
	FILE *fp;
	MODULE *mod;
	SYMBOL *undefs[5000];
	SYMBOL *defsym[5000];
	struct obj_file *f;
	struct obj_section *sect;
	struct obj_symbol *objsym;
	int i;
	int is_2_2;
	int ksymtab;
	int len;
	int n_undefs = 0;
	int n_defsym = 0;
	char *p;

	p = strrchr(objname, '/');
	len = 1 + (int)(p - objname);

	if ((fp = fopen(objname, "r")) == NULL)
		return 1;

	if (!(f = obj_load(fp, ET_REL))) {
		fclose(fp);
		return -1;
	}
	fclose(fp);

	/*
	 * If we have changed base directory
	 * then use the defined symbols from modules
	 * in the _same_ directory to resolve whatever
	 * undefined symbols there are.
	 *
	 * This strategy ensures that we will have
	 * as correct dependencies as possible,
	 * even if the same symbol is defined by
	 * other modules in other directories.
	 */
	if (currdir_len != len || currdir == NULL ||
	    strncmp(currdir, objname, len) != 0) {
		if (currdir)
			resolve();
		currdir = xstrdup(objname);
		currdir_len = len;
	}

	mod = modules + n_modules++;
	mod->name = xstrdup(objname);

	if ((sect = obj_find_section(f, "__ksymtab")) != NULL)
		ksymtab = sect->idx; /* Only in 2.2 (or at least not 2.0) */
	else
		ksymtab = -1;

	if (sect ||
	    obj_find_section(f, ".modinfo") ||
	    obj_find_symbol(f, "__this_module"))
		is_2_2 = 1;
	else
		is_2_2 = 0;

	for (i = 0; i < HASH_BUCKETS; ++i) {
		for (objsym = f->symtab[i]; objsym; objsym = objsym->next) {
			if (objsym->secidx == SHN_UNDEF) {
				if (ELFW(ST_BIND)(objsym->info) != STB_WEAK) {
					undefs[n_undefs++] = addsym(objsym->name,
								    mod,
								    SYM_UNDEF,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			if (is_2_2 && ksymtab != -1) {
				/* A 2.2 module using EXPORT_SYMBOL */
				if (objsym->secidx == ksymtab &&
				    ELFW(ST_BIND)(objsym->info) == STB_GLOBAL) {
					/* Ignore leading "__ksymtab_" */
					defsym[n_defsym++] = addsym(objsym->name + 10,
								    mod,
								    SYM_DEFINED,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			if (is_2_2) {
				/*
				 * A 2.2 module _not_ using EXPORT_SYMBOL
				 * It might still want to export symbols,
				 * although strictly speaking it shouldn't...
				 * (Seen in pcmcia)
				 */
				if (ELFW(ST_BIND)(objsym->info) == STB_GLOBAL) {
					defsym[n_defsym++] = addsym(objsym->name,
								    mod,
								    SYM_DEFINED,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			/* Not undefined or 2.2 ksymtab entry */
			if (objsym->secidx < SHN_LORESERVE
			/*
			 * The test below is removed for 2.0 compatibility
			 * since some 2.0-modules (correctly) hide the
			 * symbols it exports via register_symtab()
			 */
			/* && ELFW(ST_BIND)(objsym->info) == STB_GLOBAL */
			     ) {
				defsym[n_defsym++] = addsym(objsym->name,
							    mod,
							    SYM_DEFINED,
							    ignore_suffix);
			}
		}
	}

	/*
	 *	Finalize the information about a module.
	 */
	mod->defsym.n_syms = n_defsym;
	if (n_defsym > 0) {
		int size = n_defsym * sizeof(SYMBOL *);

		mod->defsym.symtab = (SYMBOL **)xmalloc(size);
		memcpy(mod->defsym.symtab, defsym, size);
	} else
		mod->defsym.symtab = NULL;

	mod->undefs.n_syms = n_undefs;
	if (n_undefs > 0) {
		int size = n_undefs * sizeof(SYMBOL *);

		mod->undefs.symtab = (SYMBOL **) xmalloc(size);
		memcpy(mod->undefs.symtab, undefs, size);
		mod->resolved = 0;
	} else {
		mod->undefs.symtab = NULL;
		mod->resolved = 1;
	}

	obj_free(f);

	return 0;
}

/*
 * Add the symbol defined in the kernel, simulating a pseudo module.
 * Create a dummy module entry in the list of modules.
 * This module is used to handle the exported kernel symbols.
 *
 * Load from the currently running kernel
 * OR
 * from a file containing kernel symbols from a different kernel.
 * The file can be either a "System.map" file or a copy of "/proc/ksyms".
 * Use a copy of "/proc/ksyms" if you are using versioned symbols!
 *
 * Return -1 if any error.
 */
static int addksyms(char *file_syms)
{
	FILE *fp;
	MODULE *mod = modules + n_modules++;
	SYMBOL *symtab[10000];
	struct module_symbol *ksym;
	unsigned int so_far = 0;
	int is_mapfile = 0;
	int n_syms = 0;
	int size;
	char line[PATH_MAX];
	char *p;

	mod->name = "-";
	mod->defsym.n_syms = 0;
	mod->defsym.symtab = NULL;
	mod->undefs.n_syms = 0;
	mod->undefs.symtab = NULL;

	/* Fake the _mod_use_count_ symbol provided by insmod */
	/* Dummy */
	symtab[n_syms++] = addsym("mod_use_count_", mod, SYM_DEFINED, 0);
	symtab[n_syms++] = addsym("__this_module", mod, SYM_DEFINED, 0);

	/*
	 * Specification: depmod / kernel syms only
	 * When initialising its symbol table from the kernel
	 * depmod silently discards all symbol from loaded modules.
	 *
	 * This means that depmod may be used at any time to compute
	 * the dependancy table, even if there are modules already
	 * loaded.
	 *
	 * depmod use the kernel system call to obtain the
	 * symbol table, not /proc/ksyms.
	 */

	if (file_syms) {
		if ((fp = fopen(file_syms, "r")) == NULL) {
			error("Can't read %s", file_syms);
			return -1;
		}
		if (!fgets(line, sizeof(line), fp)) {
			fclose(fp);
			error("premature EOF on %s", file_syms);
			return -1;
		}

		if (!strtok(line, " \t\n")) {
			fclose(fp);
			error("Illegal format of %s", file_syms);
			return -1;
		}
		p = strtok(NULL, " \t\n");
		/* The second word is either the symbol name or a type */
		if (p && strlen(p) == 1) { /* System.map */
			is_mapfile = 1;
			if (!isupper(*p))
				p = NULL;
			else
				p = strtok(NULL, " \t\n");
		} else { /* /proc/ksyms copy */
			if (p && strtok(NULL, " \t\n"))
				p = NULL;
		}

		if (p)
			symtab[n_syms++] = addsym(p, mod, SYM_DEFINED, 0);

		while (fgets(line, sizeof(line), fp)) {
			if (!is_mapfile && strchr(line, '['))
				continue;
			strtok(line, " \t\n"); /* Skip first word */
			p = strtok(NULL, " \n");
			if (is_mapfile) {
				if (!isupper(*p))
					continue;
				p = strtok(NULL, " \t\n");
			}
			assert(n_syms < 10000);
			symtab[n_syms++] = addsym(p, mod, SYM_DEFINED, 0);
		}
		fclose(fp);
	} else { /* Use the exported symbols from currently running kernel */
		if (!get_kernel_info(K_SYMBOLS))
			return -1;

		for (ksym = ksyms; so_far < nksyms; ++so_far, ksym++) {
			assert(n_syms < 10000);
			symtab[n_syms++] = addsym((char *)ksym->name, mod, SYM_DEFINED, 0);
		}
	}

	size = n_syms * sizeof(SYMBOL *);
	mod->defsym.symtab = (SYMBOL **)xmalloc(size);
	mod->defsym.n_syms = n_syms;
	memcpy(mod->defsym.symtab, symtab, size);

	return 0;
}

/*
 *	Format the dependancy list of a module into a simple makefile.
 *	Print the dependancies in the depfile (or stdout if depfile is NULL).
 */
static void prtdepend(const char *pdepfile, char *base_dir)
{
	FILE *out = stdout;
	MODULE **tbdep;
	MODULE *ptmod = modules;
	int i;
	int skipchars;	/* For depmod -a in image of a tree */

	if (pdepfile != NULL) {
		if ((out = fopen(pdepfile, "w")) == NULL) {
			error("Can't open %s for writing", pdepfile);
			exit(-1);
		}
	}

	skipchars = (base_dir ? strlen(base_dir) : 0);
	tbdep = (MODULE **)alloca(sizeof(MODULE) * n_modules);

	for (i = 0; i < n_modules; i++, ptmod++) {
		SYMBOL **undefs = ptmod->undefs.symtab;
		int n_undefs = ptmod->undefs.n_syms;
		int nbdepmod = 0;
		int nberr = 0;
		int e;
		int m;

		/* Don't print a dependency list for the _kernel_ ... */
		if (strcmp(ptmod->name, "-") == 0)
			continue;

		/* Look for unresolved symbols in this module */
		for (e = 0; e < n_undefs; e++, undefs++) {
			MODULE *mod = (*undefs)->module;

			if ((*undefs)->status == SYM_RESOLVED) {
				/* Resolved by self or exported kernel symbol */
				if (strcmp(mod->name, ptmod->name) == 0 ||
				    strcmp(mod->name, "-") == 0)
					continue;

				/* No duplicate dependencies, please */
				for (m = 0; m < nbdepmod; m++) {
					if (tbdep[m] == mod)
						break;
				}
				if (m == nbdepmod)
					tbdep[nbdepmod++] = mod;
			} else {
				if (!quiet && nberr == 0)
					error("*** Unresolved symbols in %s",
					      ptmod->name);
				if (!quiet && showerror)
						error("\t%s", (*undefs)->name);
				nberr++;
			}
		}

		if (verbose)
			printf("%s\n", ptmod->name + skipchars);

		fprintf(out, "%s:", ptmod->name + skipchars);
		for (m = 0; m < nbdepmod; m++) {
			if (m != 0 /*&& (m & 3) == 0*/)
				fprintf(out, " \\\n");
			fprintf(out, "\t%s", tbdep[m]->name + skipchars);
		}
		fprintf(out, "\n\n");
	}
}

/* For common 3264, only compile the usage message once, in the 64 bit version */
#if defined(COMMON_3264) && defined(ONLY_32)
extern void depmod_usage(void);         /* Use the copy in the 64 bit version */
#else   /* Common 64 bit version or any non common code - compile usage routine */
void depmod_usage(void)
{
	fprintf(stderr,
	"depmod " MODUTILS_VERSION "\n"
	"depmod -[aA] [-n -e -v -q -V]\n"
	"      [-C configfile] [-F kernelsyms] [-b basedirectory] [forced_version]\n"
	"depmod [-n -e -v -q] [-f kernelsyms] module1.o module2.o ...\n"
	"If no arguments (except options) are given, \"depmod -a\" is assumed\n"
	"\n"
	"depmod will output a dependancy list suitable for the modprobe utility.\n"
	"depmod -a will find the list of modules to probe from the file\n"
	ETC_MODULES_CONF ". It will output the result into the depfile specified\n"
	"in this configuration file\n"
	"\n"
	"depmod -A is the same as depmod -a, but will first compare the timestamps\n"
	"of the files involved to see if the depfile needs updating.\n"
	"\n"
	"Normally depmod operates silently, reporting only the list of modules that\n"
	"won't load properly (missing symbols).\n"
	"\n"
	"Options:\n"
	"\t-a, --all            Probe modules listed in " ETC_MODULES_CONF "\n"
	"\t-A                   Like -a, compares timestamps first\n"
	"\t-q, --quiet          Don't report missing symbols\n"
	"\t-e, --errsyms        List unresolved symbols for the given module\n"
	"\t-s, --syslog         Report errors using syslog\n"
	"\t-v, --verbose        Print all visited modules\n"
	"\t-n, --show           Write the dependency file on stdout only\n"
	"\t-r, --root           Allow root to allow modules not owned by root\n"
	"\t-V, --version        Print the release version\n"
	"\t-h, --help           Print this usage message\n"
	"\n"
	"The following options are useful for people managing distributions:\n"
	"\t-b basedirectory\n"
	"\t    --basedir basedirectory    Use an image of a module tree.\n"
	"\t-C configfile\n"
	"\t    --config configfile        Use the file instead of\n"
	"\t                               " ETC_MODULES_CONF ".\n"
	"\t-F kernelsyms\n"
	"\t    --filesyms kernelsyms      Use the file instead of the\n"
	"\t                               current kernel symbols.\n"
	);
}
#endif  /* defined(COMMON_3264) && defined(ONLY_32) */

#if defined(COMMON_3264) && defined(ONLY_32)
#define DEPMOD_MAIN depmod_main_32	/* 32 bit version */
#elif defined(COMMON_3264) && defined(ONLY_64)
#define DEPMOD_MAIN depmod_main_64	/* 64 bit version */
#else
#define DEPMOD_MAIN depmod_main		/* Not common 3264 code */
#endif

/* For common 3264 code, add an overall depmod_main, in the 64 bit version. */
#if defined(COMMON_3264) && defined(ONLY_64)
int depmod_main(int argc, char **argv)
{
	if (arch64())
		return depmod_main_64(argc, argv);
	else
		return depmod_main_32(argc, argv);
}
#endif  /* defined(COMMON_3264) && defined(ONLY_64) */

int DEPMOD_MAIN(int argc, char **argv)
{
	int ret = -1;
	int stdmode = 0;
	int err = 0;
	int nflag = 0;
	int o;
	char *conf_file = NULL;
	char *file_syms = NULL;
	char *base_dir = NULL;
	int ignore_suffix = 0;	/* Ignore genksyms suffix on resolve? */

	struct option long_opts[] = {
		{"all", 0, 0, 'a'},
		{"basedir", 1, 0, 'b'},
		{"config", 1, 0, 'C'},
		{"errsyms", 0, 0, 'e'},
		{"filesyms", 1, 0, 'F'},
		{"help", 0, 0, 'h'},
		{"show", 0, 0, 'n'},
		{"quiet", 0, 0, 'q'},
		{"syslog", 0, 0, 's'},
		{"verbose", 0, 0, 'v'},
		{"version", 0, 0, 'V'},
		{"root", 0, 0, 'r'},
		{0, 0, 0, 0 }
	};

	error_file = "depmod";

	while ((o = getopt_long(argc, argv, "aAb:C:eF:hnqsvVr",
				&long_opts[0], NULL)) != EOF) {
		switch (o) {
		case 'A':
		case 'a':
			quick = o == 'A';
			stdmode = 1;	/* Probe standard directory */
			break;		/* using the config file */

		case 'e':
			showerror = 1;
			break;

		case 'b':
			base_dir = optarg;
			break;

		case '?':
		case 'h':
			depmod_usage();
			return 0;
			break;

		case 'C':
			conf_file = optarg;
			break;

		case 'F':
			file_syms = optarg;
			break;

		case 'n':
			nflag = 1;
			break;

		case 'q':
			quiet = 1;
			break;

		case 's':
			setsyslog("depmod");
			break;

		case 'v':
			verbose = 1;
			break;

		case 'V':
			printf("depmod version %s\n", MODUTILS_VERSION);
			break;

		case 'r':
			root_check_off = 1;
			break;

		default:
			err = 1;
			break;
		}
	}

	if (err) {
		error("Aborting");
		return -1;
	}
	/* else */
	argc -= optind;
	argv += optind;

	ignore_suffix = file_syms != NULL;

	if (stdmode || argc == 0) {
		/* option -a is the default without arguments */
		if (argc > 0) {
			if (config_read(0, *argv, base_dir, conf_file) == -1) {
				error("%s does not exist", depfile);
				return -1;
			}
		} else if (config_read(0, NULL, base_dir, conf_file) == -1) {
			return -1;
		}

		if ((ret = addksyms(file_syms)) != -1) {
			int i;
			GLOB_LIST *g = config_lstmod(NULL, NULL, 0);
			for (i = 0; g && i < g->pathc; i++)
				loadobj(g->pathv[i], ignore_suffix);
			resolve();
			prtdepend(nflag ? NULL : depfile, base_dir);
		}
	} else {
		/* not stdmode */
		if ((ret = addksyms(file_syms)) != -1) {
			for (; argc > 0 && ret != -1; ++argv, --argc)
				loadobj(*argv, ignore_suffix);
			resolve();
			prtdepend(NULL, base_dir);
		}
	}

	return ret;
}

/* For common 3264 code, only compile main in the 64 bit version. */
#if defined(COMMON_3264) && defined(ONLY_32)
/* Use the main in the 64 bit version */
#else
int main(int argc, char **argv)
{
	return depmod_main(argc, argv);
}
#endif	/* defined(COMMON_3264) && defined(ONLY_32) */
