xref: /freebsd-src/usr.sbin/kldxref/kldxref.c (revision 9ceddbd532223cd33c22b786a2994dee68be9c8e)
19c6f9240SPeter Wemm /*
29c6f9240SPeter Wemm  * Copyright (c) 2000, Boris Popov
39c6f9240SPeter Wemm  * All rights reserved.
49c6f9240SPeter Wemm  *
59c6f9240SPeter Wemm  * Redistribution and use in source and binary forms, with or without
69c6f9240SPeter Wemm  * modification, are permitted provided that the following conditions
79c6f9240SPeter Wemm  * are met:
89c6f9240SPeter Wemm  * 1. Redistributions of source code must retain the above copyright
99c6f9240SPeter Wemm  *    notice, this list of conditions and the following disclaimer.
109c6f9240SPeter Wemm  * 2. Redistributions in binary form must reproduce the above copyright
119c6f9240SPeter Wemm  *    notice, this list of conditions and the following disclaimer in the
129c6f9240SPeter Wemm  *    documentation and/or other materials provided with the distribution.
139c6f9240SPeter Wemm  * 3. All advertising materials mentioning features or use of this software
149c6f9240SPeter Wemm  *    must display the following acknowledgement:
159c6f9240SPeter Wemm  *    This product includes software developed by Boris Popov.
169c6f9240SPeter Wemm  * 4. Neither the name of the author nor the names of any co-contributors
179c6f9240SPeter Wemm  *    may be used to endorse or promote products derived from this software
189c6f9240SPeter Wemm  *    without specific prior written permission.
199c6f9240SPeter Wemm  *
209c6f9240SPeter Wemm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
219c6f9240SPeter Wemm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
229c6f9240SPeter Wemm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
239c6f9240SPeter Wemm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
249c6f9240SPeter Wemm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
259c6f9240SPeter Wemm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
269c6f9240SPeter Wemm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
279c6f9240SPeter Wemm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
289c6f9240SPeter Wemm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
299c6f9240SPeter Wemm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
309c6f9240SPeter Wemm  * SUCH DAMAGE.
319c6f9240SPeter Wemm  *
329c6f9240SPeter Wemm  * $FreeBSD$
339c6f9240SPeter Wemm  */
349c6f9240SPeter Wemm 
35b7abc67eSMaxim Sobolev #include <sys/types.h>
369c6f9240SPeter Wemm #include <sys/param.h>
379c6f9240SPeter Wemm #include <sys/exec.h>
389c6f9240SPeter Wemm #include <sys/queue.h>
399c6f9240SPeter Wemm #include <sys/kernel.h>
409c6f9240SPeter Wemm #include <sys/reboot.h>
419c6f9240SPeter Wemm #include <sys/linker.h>
429c6f9240SPeter Wemm #include <sys/stat.h>
439c6f9240SPeter Wemm #include <sys/module.h>
449c6f9240SPeter Wemm #define FREEBSD_ELF
459c6f9240SPeter Wemm #include <link.h>
469c6f9240SPeter Wemm #include <err.h>
479c6f9240SPeter Wemm #include <fts.h>
489c6f9240SPeter Wemm #include <string.h>
499c6f9240SPeter Wemm #include <machine/elf.h>
509c6f9240SPeter Wemm #include <stdio.h>
519c6f9240SPeter Wemm #include <stdlib.h>
529c6f9240SPeter Wemm #include <unistd.h>
539c6f9240SPeter Wemm #include <errno.h>
549c6f9240SPeter Wemm 
559c6f9240SPeter Wemm #include "ef.h"
569c6f9240SPeter Wemm 
579c6f9240SPeter Wemm #define	MAXRECSIZE	1024
589c6f9240SPeter Wemm #define check(val)	if ((error = (val)) != 0) break
599c6f9240SPeter Wemm 
609c6f9240SPeter Wemm #ifndef min
619c6f9240SPeter Wemm #define	min(a,b)	(((a)<(b)) ? (a) : (b))
629c6f9240SPeter Wemm #endif
639c6f9240SPeter Wemm 
649c6f9240SPeter Wemm struct mod_info {
659c6f9240SPeter Wemm 	char*	mi_name;
669c6f9240SPeter Wemm 	int	mi_ver;
679c6f9240SPeter Wemm 	SLIST_ENTRY(mod_info) mi_next;
689c6f9240SPeter Wemm };
699c6f9240SPeter Wemm 
709c6f9240SPeter Wemm #ifdef notnow
719c6f9240SPeter Wemm struct kld_info {
729c6f9240SPeter Wemm 	char*	k_filename;
739c6f9240SPeter Wemm 	SLIST_HEAD(mod_list_head, mod_info) k_modules;
749c6f9240SPeter Wemm 	SLIST_ENTRY(kld_info) k_next;
759c6f9240SPeter Wemm };
769c6f9240SPeter Wemm 
779c6f9240SPeter Wemm SLIST_HEAD(kld_list_head, kld_info) kldlist;
789c6f9240SPeter Wemm #endif
799c6f9240SPeter Wemm 
809c6f9240SPeter Wemm static int dflag, verbose;
819c6f9240SPeter Wemm 
829c6f9240SPeter Wemm FILE *fxref;
839c6f9240SPeter Wemm 
8487e5cd7cSMike Heffner static const char *xref_file = "linker.hints";
859c6f9240SPeter Wemm 
869c6f9240SPeter Wemm static char recbuf[MAXRECSIZE];
879c6f9240SPeter Wemm static int recpos, reccnt;
889c6f9240SPeter Wemm 
89*9ceddbd5SMarcel Moolenaar FILE *maketempfile(char *, const char *);
909c6f9240SPeter Wemm static void usage(void);
919c6f9240SPeter Wemm 
929c6f9240SPeter Wemm static void
939c6f9240SPeter Wemm intalign(void)
949c6f9240SPeter Wemm {
959c6f9240SPeter Wemm 	recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1);
969c6f9240SPeter Wemm }
979c6f9240SPeter Wemm 
989c6f9240SPeter Wemm static void
999c6f9240SPeter Wemm record_start(void)
1009c6f9240SPeter Wemm {
1019c6f9240SPeter Wemm 	recpos = 0;
1029c6f9240SPeter Wemm 	memset(recbuf, 0, MAXRECSIZE);
1039c6f9240SPeter Wemm }
1049c6f9240SPeter Wemm 
1059c6f9240SPeter Wemm static int
1069c6f9240SPeter Wemm record_end(void)
1079c6f9240SPeter Wemm {
1089c6f9240SPeter Wemm 	if (dflag || recpos == 0)
1099c6f9240SPeter Wemm 		return 0;
1109c6f9240SPeter Wemm 	reccnt++;
1119c6f9240SPeter Wemm 	intalign();
1129c6f9240SPeter Wemm 	fwrite(&recpos, sizeof(recpos), 1, fxref);
1139c6f9240SPeter Wemm 	return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0;
1149c6f9240SPeter Wemm }
1159c6f9240SPeter Wemm 
1169c6f9240SPeter Wemm static int
1179c6f9240SPeter Wemm record_buf(const void *buf, int size)
1189c6f9240SPeter Wemm {
1199c6f9240SPeter Wemm 	if (MAXRECSIZE - recpos < size)
1209c6f9240SPeter Wemm 		errx(1, "record buffer overflow");
1219c6f9240SPeter Wemm 	memcpy(recbuf + recpos, buf, size);
1229c6f9240SPeter Wemm 	recpos += size;
1239c6f9240SPeter Wemm 	return 0;
1249c6f9240SPeter Wemm }
1259c6f9240SPeter Wemm 
1269c6f9240SPeter Wemm static int
1279c6f9240SPeter Wemm record_int(int val)
1289c6f9240SPeter Wemm {
1299c6f9240SPeter Wemm 	intalign();
1309c6f9240SPeter Wemm 	return record_buf(&val, sizeof(val));
1319c6f9240SPeter Wemm }
1329c6f9240SPeter Wemm 
1339c6f9240SPeter Wemm static int
1349c6f9240SPeter Wemm record_byte(u_char val)
1359c6f9240SPeter Wemm {
1369c6f9240SPeter Wemm 	return record_buf(&val, sizeof(val));
1379c6f9240SPeter Wemm }
1389c6f9240SPeter Wemm 
1399c6f9240SPeter Wemm static int
1409c6f9240SPeter Wemm record_string(const char *str)
1419c6f9240SPeter Wemm {
1429c6f9240SPeter Wemm 	int len = strlen(str);
1439c6f9240SPeter Wemm 	int error;
1449c6f9240SPeter Wemm 
1459c6f9240SPeter Wemm 	if (dflag)
1469c6f9240SPeter Wemm 		return 0;
1479c6f9240SPeter Wemm 	error = record_byte(len);
1489c6f9240SPeter Wemm 	if (error)
1499c6f9240SPeter Wemm 		return error;
1509c6f9240SPeter Wemm 	return record_buf(str, len);
1519c6f9240SPeter Wemm }
1529c6f9240SPeter Wemm 
1539c6f9240SPeter Wemm static int
1549c6f9240SPeter Wemm parse_entry(struct mod_metadata *md, const char *cval,
1559c6f9240SPeter Wemm     struct elf_file *ef, const char *kldname)
1569c6f9240SPeter Wemm {
1579c6f9240SPeter Wemm 	struct mod_depend mdp;
1589c6f9240SPeter Wemm 	struct mod_version mdv;
1599c6f9240SPeter Wemm 	Elf_Off data = (Elf_Off)md->md_data;
1609c6f9240SPeter Wemm 	int error = 0;
1619c6f9240SPeter Wemm 
1629c6f9240SPeter Wemm 	record_start();
1639c6f9240SPeter Wemm 	switch (md->md_type) {
1649c6f9240SPeter Wemm 	case MDT_DEPEND:
1659c6f9240SPeter Wemm 		if (!dflag)
1669c6f9240SPeter Wemm 			break;
1679772dc2aSIan Dowse 		check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp));
1689c6f9240SPeter Wemm 		printf("  depends on %s.%d (%d,%d)\n", cval,
1699c6f9240SPeter Wemm 		    mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum);
1709c6f9240SPeter Wemm 		break;
1719c6f9240SPeter Wemm 	case MDT_VERSION:
1729772dc2aSIan Dowse 		check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv));
1739c6f9240SPeter Wemm 		record_int(MDT_VERSION);
1749c6f9240SPeter Wemm 		record_string(cval);
1759c6f9240SPeter Wemm 		record_int(mdv.mv_version);
1769c6f9240SPeter Wemm 		record_string(kldname);
1779c6f9240SPeter Wemm 		if (!dflag)
1789c6f9240SPeter Wemm 			break;
1799c6f9240SPeter Wemm 		printf("  interface %s.%d\n", cval, mdv.mv_version);
1809c6f9240SPeter Wemm 		break;
1819c6f9240SPeter Wemm 	case MDT_MODULE:
1829c6f9240SPeter Wemm 		record_int(MDT_MODULE);
1839c6f9240SPeter Wemm 		record_string(cval);
1849c6f9240SPeter Wemm 		record_string(kldname);
1859c6f9240SPeter Wemm 		if (!dflag)
1869c6f9240SPeter Wemm 			break;
1879c6f9240SPeter Wemm 		printf("  module %s\n", cval);
1889c6f9240SPeter Wemm 		break;
1899c6f9240SPeter Wemm 	default:
1909f5529b4SRuslan Ermilov 		warnx("unknown metadata record %d in file %s", md->md_type, kldname);
1919c6f9240SPeter Wemm 	}
1929c6f9240SPeter Wemm 	if (!error)
1939c6f9240SPeter Wemm 		record_end();
1949c6f9240SPeter Wemm 	return error;
1959c6f9240SPeter Wemm }
1969c6f9240SPeter Wemm 
1979c6f9240SPeter Wemm static int
1989c6f9240SPeter Wemm read_kld(char *filename, char *kldname)
1999c6f9240SPeter Wemm {
2009c6f9240SPeter Wemm 	struct mod_metadata md;
2019c6f9240SPeter Wemm 	struct elf_file ef;
2029c6f9240SPeter Wemm /*	struct kld_info *kip;
2039c6f9240SPeter Wemm 	struct mod_info *mip;*/
2049c6f9240SPeter Wemm 	void **p, **orgp;
2059772dc2aSIan Dowse 	int error, eftype, nmlen;
2069c6f9240SPeter Wemm 	long start, finish, entries;
2079c6f9240SPeter Wemm 	char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp;
2089c6f9240SPeter Wemm 
2099c6f9240SPeter Wemm 	if (verbose || dflag)
2109c6f9240SPeter Wemm 		printf("%s\n", filename);
2119c6f9240SPeter Wemm 	error = ef_open(filename, &ef, verbose);
2129772dc2aSIan Dowse 	if (error) {
2134a8b7e33SIan Dowse 		error = ef_obj_open(filename, &ef, verbose);
2144a8b7e33SIan Dowse 		if (error) {
2159772dc2aSIan Dowse 			if (verbose)
2169772dc2aSIan Dowse 				warnc(error, "elf_open(%s)", filename);
2179c6f9240SPeter Wemm 			return error;
2189772dc2aSIan Dowse 		}
2194a8b7e33SIan Dowse 	}
2209772dc2aSIan Dowse 	eftype = EF_GET_TYPE(&ef);
2219772dc2aSIan Dowse 	if (eftype != EFT_KLD && eftype != EFT_KERNEL)  {
2229772dc2aSIan Dowse 		EF_CLOSE(&ef);
2239c6f9240SPeter Wemm 		return 0;
2249c6f9240SPeter Wemm 	}
2259c6f9240SPeter Wemm 	if (!dflag) {
2269c6f9240SPeter Wemm 		cp = strrchr(kldname, '.');
2279c6f9240SPeter Wemm 		nmlen = cp ? min(MAXMODNAME, cp - kldname) :
2289c6f9240SPeter Wemm 		    min(MAXMODNAME, strlen(kldname));
2299c6f9240SPeter Wemm 		strncpy(kldmodname, kldname, nmlen);
2309c6f9240SPeter Wemm 		kldmodname[nmlen] = '\0';
2319c6f9240SPeter Wemm /*		fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/
2329c6f9240SPeter Wemm 	}
2339c6f9240SPeter Wemm 	do {
2349772dc2aSIan Dowse 		check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish,
2359772dc2aSIan Dowse 		    &entries));
2369772dc2aSIan Dowse 		check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries,
23776798703SDag-Erling Smørgrav 		    (void *)&p));
2389c6f9240SPeter Wemm 		orgp = p;
2399c6f9240SPeter Wemm 		while(entries--) {
2409772dc2aSIan Dowse 			check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md),
2419772dc2aSIan Dowse 			    &md));
2429c6f9240SPeter Wemm 			p++;
2439772dc2aSIan Dowse 			check(EF_SEG_READ(&ef, (Elf_Off)md.md_cval,
2449772dc2aSIan Dowse 			    sizeof(cval), cval));
2459c6f9240SPeter Wemm 			cval[MAXMODNAME] = '\0';
2469c6f9240SPeter Wemm 			parse_entry(&md, cval, &ef, kldname);
2479c6f9240SPeter Wemm 		}
2489c6f9240SPeter Wemm 		if (error)
2499c6f9240SPeter Wemm 			warnc(error, "error while reading %s", filename);
2509c6f9240SPeter Wemm 		free(orgp);
2519c6f9240SPeter Wemm 	} while(0);
2529772dc2aSIan Dowse 	EF_CLOSE(&ef);
2539c6f9240SPeter Wemm 	return error;
2549c6f9240SPeter Wemm }
2559c6f9240SPeter Wemm 
256*9ceddbd5SMarcel Moolenaar FILE *
2579c6f9240SPeter Wemm maketempfile(char *dest, const char *root)
2589c6f9240SPeter Wemm {
2599c6f9240SPeter Wemm 	char *p;
260*9ceddbd5SMarcel Moolenaar 	int fd;
2619c6f9240SPeter Wemm 
2629c6f9240SPeter Wemm 	strncpy(dest, root, MAXPATHLEN - 1);
2639c6f9240SPeter Wemm 	dest[MAXPATHLEN] = '\0';
2649c6f9240SPeter Wemm 
2659c6f9240SPeter Wemm 	if ((p = strrchr(dest, '/')) != 0)
2669c6f9240SPeter Wemm 		p++;
2679c6f9240SPeter Wemm 	else
2689c6f9240SPeter Wemm 		p = dest;
2699c6f9240SPeter Wemm 	strcpy(p, "lhint.XXXXXX");
270*9ceddbd5SMarcel Moolenaar 	fd = mkstemp(dest);
271*9ceddbd5SMarcel Moolenaar 	return ((fd == -1) ? NULL : fdopen(fd, "w+"));
2729c6f9240SPeter Wemm }
2739c6f9240SPeter Wemm 
2749c6f9240SPeter Wemm static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
2759c6f9240SPeter Wemm 
2769c6f9240SPeter Wemm int
2779c6f9240SPeter Wemm main(int argc, char *argv[])
2789c6f9240SPeter Wemm {
2799c6f9240SPeter Wemm 	FTS *ftsp;
2809c6f9240SPeter Wemm 	FTSENT *p;
2819c6f9240SPeter Wemm 	int opt, fts_options, ival;
282b7abc67eSMaxim Sobolev 	struct stat sb;
2839c6f9240SPeter Wemm 
2849c6f9240SPeter Wemm 	fts_options = FTS_PHYSICAL;
2859c6f9240SPeter Wemm /*	SLIST_INIT(&kldlist);*/
2869c6f9240SPeter Wemm 
2879c6f9240SPeter Wemm 	while ((opt = getopt(argc, argv, "Rdf:v")) != -1) {
2889c6f9240SPeter Wemm 		switch (opt) {
2899c6f9240SPeter Wemm 		case 'd':
2909c6f9240SPeter Wemm 			dflag = 1;
2919c6f9240SPeter Wemm 			break;
2929c6f9240SPeter Wemm 		case 'f':
2939c6f9240SPeter Wemm 			xref_file = optarg;
2949c6f9240SPeter Wemm 			break;
2959c6f9240SPeter Wemm 		case 'v':
2969c6f9240SPeter Wemm 			verbose++;
2979c6f9240SPeter Wemm 			break;
2989c6f9240SPeter Wemm 		case 'R':
2999c6f9240SPeter Wemm 			fts_options |= FTS_COMFOLLOW;
3009c6f9240SPeter Wemm 			break;
3019c6f9240SPeter Wemm 		default:
3029c6f9240SPeter Wemm 			usage();
3039c6f9240SPeter Wemm 			/* NOTREACHED */
3049c6f9240SPeter Wemm 		}
3059c6f9240SPeter Wemm 	}
3069c6f9240SPeter Wemm 	if (argc - optind < 1)
3079c6f9240SPeter Wemm 		usage();
3089c6f9240SPeter Wemm 	argc -= optind;
3099c6f9240SPeter Wemm 	argv += optind;
3109c6f9240SPeter Wemm 
311b7abc67eSMaxim Sobolev 	if (stat(argv[0], &sb) != 0)
312b7abc67eSMaxim Sobolev 		err(1, "%s", argv[0]);
313b7abc67eSMaxim Sobolev 	if ((sb.st_mode & S_IFDIR) == 0) {
314b7abc67eSMaxim Sobolev 		errno = ENOTDIR;
315b7abc67eSMaxim Sobolev 		err(1, "%s", argv[0]);
316b7abc67eSMaxim Sobolev 	}
317b7abc67eSMaxim Sobolev 
3189c6f9240SPeter Wemm 	ftsp = fts_open(argv, fts_options, 0);
3199c6f9240SPeter Wemm 	if (ftsp == NULL)
3209c6f9240SPeter Wemm 		exit(1);
3219c6f9240SPeter Wemm 
3229c6f9240SPeter Wemm 	for (;;) {
3239c6f9240SPeter Wemm 		p = fts_read(ftsp);
3249c6f9240SPeter Wemm 		if ((p == NULL || p->fts_info == FTS_D) && !dflag && fxref) {
3259c6f9240SPeter Wemm 			fclose(fxref);
326*9ceddbd5SMarcel Moolenaar 			fxref = NULL;
3279c6f9240SPeter Wemm 			if (reccnt) {
3289c6f9240SPeter Wemm 				rename(tempname, xrefname);
3299c6f9240SPeter Wemm 			} else {
3309c6f9240SPeter Wemm 				unlink(tempname);
3319c6f9240SPeter Wemm 				unlink(xrefname);
3329c6f9240SPeter Wemm 			}
3339c6f9240SPeter Wemm 		}
3349c6f9240SPeter Wemm 		if (p == NULL)
3359c6f9240SPeter Wemm 			break;
3369c6f9240SPeter Wemm 		if (p && p->fts_info == FTS_D && !dflag) {
3379c6f9240SPeter Wemm 			snprintf(xrefname, sizeof(xrefname), "%s/%s",
3389c6f9240SPeter Wemm 			    ftsp->fts_path, xref_file);
339*9ceddbd5SMarcel Moolenaar 			fxref = maketempfile(tempname, ftsp->fts_path);
3409c6f9240SPeter Wemm 			if (fxref == NULL)
3419c6f9240SPeter Wemm 				err(1, "can't create %s", tempname);
3429c6f9240SPeter Wemm 			ival = 1;
3439c6f9240SPeter Wemm 			fwrite(&ival, sizeof(ival), 1, fxref);
3449c6f9240SPeter Wemm 			reccnt = 0;
3459c6f9240SPeter Wemm 		}
3469c6f9240SPeter Wemm 		if (p->fts_info != FTS_F)
3479c6f9240SPeter Wemm 			continue;
3489f5529b4SRuslan Ermilov 		if (p->fts_namelen >= 8 &&
3499f5529b4SRuslan Ermilov 		    strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0)
3509f5529b4SRuslan Ermilov 			continue;
3519c6f9240SPeter Wemm 		read_kld(p->fts_path, p->fts_name);
3529c6f9240SPeter Wemm 	}
3539c6f9240SPeter Wemm 	fts_close(ftsp);
3549c6f9240SPeter Wemm 	return 0;
3559c6f9240SPeter Wemm }
3569c6f9240SPeter Wemm 
3579c6f9240SPeter Wemm static void
3589c6f9240SPeter Wemm usage(void)
3599c6f9240SPeter Wemm {
3609c6f9240SPeter Wemm 
3619c6f9240SPeter Wemm 	fprintf(stderr, "%s\n",
362cb40c7d1SRuslan Ermilov 	    "usage: kldxref [-Rdv] [-f hintsfile] path ..."
3639c6f9240SPeter Wemm 	);
3649c6f9240SPeter Wemm 	exit(1);
3659c6f9240SPeter Wemm }
366