xref: /freebsd-src/usr.sbin/kldxref/kldxref.c (revision 9c6f92408c29a8980e7aec26a2590087c3b3d8ed)
1*9c6f9240SPeter Wemm /*
2*9c6f9240SPeter Wemm  * Copyright (c) 2000, Boris Popov
3*9c6f9240SPeter Wemm  * All rights reserved.
4*9c6f9240SPeter Wemm  *
5*9c6f9240SPeter Wemm  * Redistribution and use in source and binary forms, with or without
6*9c6f9240SPeter Wemm  * modification, are permitted provided that the following conditions
7*9c6f9240SPeter Wemm  * are met:
8*9c6f9240SPeter Wemm  * 1. Redistributions of source code must retain the above copyright
9*9c6f9240SPeter Wemm  *    notice, this list of conditions and the following disclaimer.
10*9c6f9240SPeter Wemm  * 2. Redistributions in binary form must reproduce the above copyright
11*9c6f9240SPeter Wemm  *    notice, this list of conditions and the following disclaimer in the
12*9c6f9240SPeter Wemm  *    documentation and/or other materials provided with the distribution.
13*9c6f9240SPeter Wemm  * 3. All advertising materials mentioning features or use of this software
14*9c6f9240SPeter Wemm  *    must display the following acknowledgement:
15*9c6f9240SPeter Wemm  *    This product includes software developed by Boris Popov.
16*9c6f9240SPeter Wemm  * 4. Neither the name of the author nor the names of any co-contributors
17*9c6f9240SPeter Wemm  *    may be used to endorse or promote products derived from this software
18*9c6f9240SPeter Wemm  *    without specific prior written permission.
19*9c6f9240SPeter Wemm  *
20*9c6f9240SPeter Wemm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21*9c6f9240SPeter Wemm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*9c6f9240SPeter Wemm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*9c6f9240SPeter Wemm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24*9c6f9240SPeter Wemm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25*9c6f9240SPeter Wemm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26*9c6f9240SPeter Wemm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*9c6f9240SPeter Wemm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28*9c6f9240SPeter Wemm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29*9c6f9240SPeter Wemm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30*9c6f9240SPeter Wemm  * SUCH DAMAGE.
31*9c6f9240SPeter Wemm  *
32*9c6f9240SPeter Wemm  * $FreeBSD$
33*9c6f9240SPeter Wemm  */
34*9c6f9240SPeter Wemm 
35*9c6f9240SPeter Wemm #include <sys/param.h>
36*9c6f9240SPeter Wemm #include <sys/exec.h>
37*9c6f9240SPeter Wemm #include <sys/queue.h>
38*9c6f9240SPeter Wemm #include <sys/kernel.h>
39*9c6f9240SPeter Wemm #include <sys/reboot.h>
40*9c6f9240SPeter Wemm #include <sys/linker.h>
41*9c6f9240SPeter Wemm #include <sys/stat.h>
42*9c6f9240SPeter Wemm #include <sys/module.h>
43*9c6f9240SPeter Wemm #define FREEBSD_ELF
44*9c6f9240SPeter Wemm #include <link.h>
45*9c6f9240SPeter Wemm #include <err.h>
46*9c6f9240SPeter Wemm #include <fts.h>
47*9c6f9240SPeter Wemm #include <string.h>
48*9c6f9240SPeter Wemm #include <machine/bootinfo.h>
49*9c6f9240SPeter Wemm #include <machine/elf.h>
50*9c6f9240SPeter Wemm #include <stdio.h>
51*9c6f9240SPeter Wemm #include <stdlib.h>
52*9c6f9240SPeter Wemm #include <unistd.h>
53*9c6f9240SPeter Wemm #include <errno.h>
54*9c6f9240SPeter Wemm 
55*9c6f9240SPeter Wemm #include "ef.h"
56*9c6f9240SPeter Wemm 
57*9c6f9240SPeter Wemm #define	MAXRECSIZE	1024
58*9c6f9240SPeter Wemm #define check(val)	if ((error = (val)) != 0) break
59*9c6f9240SPeter Wemm 
60*9c6f9240SPeter Wemm #ifndef min
61*9c6f9240SPeter Wemm #define	min(a,b)	(((a)<(b)) ? (a) : (b))
62*9c6f9240SPeter Wemm #endif
63*9c6f9240SPeter Wemm 
64*9c6f9240SPeter Wemm struct mod_info {
65*9c6f9240SPeter Wemm 	char*	mi_name;
66*9c6f9240SPeter Wemm 	int	mi_ver;
67*9c6f9240SPeter Wemm 	SLIST_ENTRY(mod_info) mi_next;
68*9c6f9240SPeter Wemm };
69*9c6f9240SPeter Wemm 
70*9c6f9240SPeter Wemm #ifdef notnow
71*9c6f9240SPeter Wemm struct kld_info {
72*9c6f9240SPeter Wemm 	char*	k_filename;
73*9c6f9240SPeter Wemm 	SLIST_HEAD(mod_list_head, mod_info) k_modules;
74*9c6f9240SPeter Wemm 	SLIST_ENTRY(kld_info) k_next;
75*9c6f9240SPeter Wemm };
76*9c6f9240SPeter Wemm 
77*9c6f9240SPeter Wemm SLIST_HEAD(kld_list_head, kld_info) kldlist;
78*9c6f9240SPeter Wemm #endif
79*9c6f9240SPeter Wemm 
80*9c6f9240SPeter Wemm static int dflag, verbose;
81*9c6f9240SPeter Wemm 
82*9c6f9240SPeter Wemm FILE *fxref;
83*9c6f9240SPeter Wemm 
84*9c6f9240SPeter Wemm static char *xref_file = "linker.hints";
85*9c6f9240SPeter Wemm 
86*9c6f9240SPeter Wemm static char recbuf[MAXRECSIZE];
87*9c6f9240SPeter Wemm static int recpos, reccnt;
88*9c6f9240SPeter Wemm 
89*9c6f9240SPeter Wemm static void usage(void);
90*9c6f9240SPeter Wemm 
91*9c6f9240SPeter Wemm static void
92*9c6f9240SPeter Wemm intalign(void)
93*9c6f9240SPeter Wemm {
94*9c6f9240SPeter Wemm 	recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1);
95*9c6f9240SPeter Wemm }
96*9c6f9240SPeter Wemm 
97*9c6f9240SPeter Wemm static void
98*9c6f9240SPeter Wemm record_start(void)
99*9c6f9240SPeter Wemm {
100*9c6f9240SPeter Wemm 	recpos = 0;
101*9c6f9240SPeter Wemm 	memset(recbuf, 0, MAXRECSIZE);
102*9c6f9240SPeter Wemm }
103*9c6f9240SPeter Wemm 
104*9c6f9240SPeter Wemm static int
105*9c6f9240SPeter Wemm record_end(void)
106*9c6f9240SPeter Wemm {
107*9c6f9240SPeter Wemm 	if (dflag || recpos == 0)
108*9c6f9240SPeter Wemm 		return 0;
109*9c6f9240SPeter Wemm 	reccnt++;
110*9c6f9240SPeter Wemm 	intalign();
111*9c6f9240SPeter Wemm 	fwrite(&recpos, sizeof(recpos), 1, fxref);
112*9c6f9240SPeter Wemm 	return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0;
113*9c6f9240SPeter Wemm }
114*9c6f9240SPeter Wemm 
115*9c6f9240SPeter Wemm static int
116*9c6f9240SPeter Wemm record_buf(const void *buf, int size)
117*9c6f9240SPeter Wemm {
118*9c6f9240SPeter Wemm 	if (MAXRECSIZE - recpos < size)
119*9c6f9240SPeter Wemm 		errx(1, "record buffer overflow");
120*9c6f9240SPeter Wemm 	memcpy(recbuf + recpos, buf, size);
121*9c6f9240SPeter Wemm 	recpos += size;
122*9c6f9240SPeter Wemm 	return 0;
123*9c6f9240SPeter Wemm }
124*9c6f9240SPeter Wemm 
125*9c6f9240SPeter Wemm static int
126*9c6f9240SPeter Wemm record_int(int val)
127*9c6f9240SPeter Wemm {
128*9c6f9240SPeter Wemm 	intalign();
129*9c6f9240SPeter Wemm 	return record_buf(&val, sizeof(val));
130*9c6f9240SPeter Wemm }
131*9c6f9240SPeter Wemm 
132*9c6f9240SPeter Wemm static int
133*9c6f9240SPeter Wemm record_byte(u_char val)
134*9c6f9240SPeter Wemm {
135*9c6f9240SPeter Wemm 	return record_buf(&val, sizeof(val));
136*9c6f9240SPeter Wemm }
137*9c6f9240SPeter Wemm 
138*9c6f9240SPeter Wemm static int
139*9c6f9240SPeter Wemm record_string(const char *str)
140*9c6f9240SPeter Wemm {
141*9c6f9240SPeter Wemm 	int len = strlen(str);
142*9c6f9240SPeter Wemm 	int error;
143*9c6f9240SPeter Wemm 
144*9c6f9240SPeter Wemm 	if (dflag)
145*9c6f9240SPeter Wemm 		return 0;
146*9c6f9240SPeter Wemm 	error = record_byte(len);
147*9c6f9240SPeter Wemm 	if (error)
148*9c6f9240SPeter Wemm 		return error;
149*9c6f9240SPeter Wemm 	return record_buf(str, len);
150*9c6f9240SPeter Wemm }
151*9c6f9240SPeter Wemm 
152*9c6f9240SPeter Wemm static int
153*9c6f9240SPeter Wemm parse_entry(struct mod_metadata *md, const char *cval,
154*9c6f9240SPeter Wemm     struct elf_file *ef, const char *kldname)
155*9c6f9240SPeter Wemm {
156*9c6f9240SPeter Wemm 	struct mod_depend mdp;
157*9c6f9240SPeter Wemm 	struct mod_version mdv;
158*9c6f9240SPeter Wemm 	Elf_Off data = (Elf_Off)md->md_data;
159*9c6f9240SPeter Wemm 	int error = 0;
160*9c6f9240SPeter Wemm 
161*9c6f9240SPeter Wemm 	record_start();
162*9c6f9240SPeter Wemm 	switch (md->md_type) {
163*9c6f9240SPeter Wemm 	case MDT_DEPEND:
164*9c6f9240SPeter Wemm 		if (!dflag)
165*9c6f9240SPeter Wemm 			break;
166*9c6f9240SPeter Wemm 		check(ef_seg_read(ef, data, sizeof(mdp), (void**)&mdp));
167*9c6f9240SPeter Wemm 		printf("  depends on %s.%d (%d,%d)\n", cval,
168*9c6f9240SPeter Wemm 		    mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum);
169*9c6f9240SPeter Wemm 		break;
170*9c6f9240SPeter Wemm 	case MDT_VERSION:
171*9c6f9240SPeter Wemm 		check(ef_seg_read(ef, data, sizeof(mdv), (void**)&mdv));
172*9c6f9240SPeter Wemm 		record_int(MDT_VERSION);
173*9c6f9240SPeter Wemm 		record_string(cval);
174*9c6f9240SPeter Wemm 		record_int(mdv.mv_version);
175*9c6f9240SPeter Wemm 		record_string(kldname);
176*9c6f9240SPeter Wemm 		if (!dflag)
177*9c6f9240SPeter Wemm 			break;
178*9c6f9240SPeter Wemm 		printf("  interface %s.%d\n", cval, mdv.mv_version);
179*9c6f9240SPeter Wemm 		break;
180*9c6f9240SPeter Wemm 	case MDT_MODULE:
181*9c6f9240SPeter Wemm 		record_int(MDT_MODULE);
182*9c6f9240SPeter Wemm 		record_string(cval);
183*9c6f9240SPeter Wemm 		record_string(kldname);
184*9c6f9240SPeter Wemm 		if (!dflag)
185*9c6f9240SPeter Wemm 			break;
186*9c6f9240SPeter Wemm 		printf("  module %s\n", cval);
187*9c6f9240SPeter Wemm 		break;
188*9c6f9240SPeter Wemm 	default:
189*9c6f9240SPeter Wemm 		warnx("unknown metdata record %d in file %s", md->md_type, kldname);
190*9c6f9240SPeter Wemm 	}
191*9c6f9240SPeter Wemm 	if (!error)
192*9c6f9240SPeter Wemm 		record_end();
193*9c6f9240SPeter Wemm 	return error;
194*9c6f9240SPeter Wemm }
195*9c6f9240SPeter Wemm 
196*9c6f9240SPeter Wemm static int
197*9c6f9240SPeter Wemm read_kld(char *filename, char *kldname)
198*9c6f9240SPeter Wemm {
199*9c6f9240SPeter Wemm 	struct mod_metadata md;
200*9c6f9240SPeter Wemm 	struct elf_file ef;
201*9c6f9240SPeter Wemm /*	struct kld_info *kip;
202*9c6f9240SPeter Wemm 	struct mod_info *mip;*/
203*9c6f9240SPeter Wemm 	void **p, **orgp;
204*9c6f9240SPeter Wemm 	int error, nmlen;
205*9c6f9240SPeter Wemm 	long start, finish, entries;
206*9c6f9240SPeter Wemm 	Elf_Sym *sym;
207*9c6f9240SPeter Wemm 	char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp;
208*9c6f9240SPeter Wemm 
209*9c6f9240SPeter Wemm 	if (verbose || dflag)
210*9c6f9240SPeter Wemm 		printf("%s\n", filename);
211*9c6f9240SPeter Wemm 	error = ef_open(filename, &ef, verbose);
212*9c6f9240SPeter Wemm 	if (error)
213*9c6f9240SPeter Wemm 		return error;
214*9c6f9240SPeter Wemm 	if (ef.ef_type != EFT_KLD && ef.ef_type != EFT_KERNEL)  {
215*9c6f9240SPeter Wemm 		ef_close(&ef);
216*9c6f9240SPeter Wemm 		return 0;
217*9c6f9240SPeter Wemm 	}
218*9c6f9240SPeter Wemm 	if (!dflag) {
219*9c6f9240SPeter Wemm 		cp = strrchr(kldname, '.');
220*9c6f9240SPeter Wemm 		nmlen = cp ? min(MAXMODNAME, cp - kldname) :
221*9c6f9240SPeter Wemm 		    min(MAXMODNAME, strlen(kldname));
222*9c6f9240SPeter Wemm 		strncpy(kldmodname, kldname, nmlen);
223*9c6f9240SPeter Wemm 		kldmodname[nmlen] = '\0';
224*9c6f9240SPeter Wemm /*		fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/
225*9c6f9240SPeter Wemm 	}
226*9c6f9240SPeter Wemm 	do {
227*9c6f9240SPeter Wemm 		check(ef_lookup_symbol(&ef, "__start_set_" MDT_SETNAME, &sym));
228*9c6f9240SPeter Wemm 		start = sym->st_value;
229*9c6f9240SPeter Wemm 		check(ef_lookup_symbol(&ef, "__stop_set_" MDT_SETNAME, &sym));
230*9c6f9240SPeter Wemm 		finish = sym->st_value;
231*9c6f9240SPeter Wemm 		entries = (finish - start) / sizeof(void *);
232*9c6f9240SPeter Wemm 		check(ef_seg_read_entry(&ef, start, sizeof(*p) * entries, (void**)&p));
233*9c6f9240SPeter Wemm 		orgp = p;
234*9c6f9240SPeter Wemm 		while(entries--) {
235*9c6f9240SPeter Wemm 			check(ef_seg_read(&ef, (Elf_Off)*p, sizeof(md), &md));
236*9c6f9240SPeter Wemm 			p++;
237*9c6f9240SPeter Wemm 			check(ef_seg_read(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval));
238*9c6f9240SPeter Wemm 			cval[MAXMODNAME] = '\0';
239*9c6f9240SPeter Wemm 			parse_entry(&md, cval, &ef, kldname);
240*9c6f9240SPeter Wemm 		}
241*9c6f9240SPeter Wemm 		if (error)
242*9c6f9240SPeter Wemm 			warnc(error, "error while reading %s", filename);
243*9c6f9240SPeter Wemm 		free(orgp);
244*9c6f9240SPeter Wemm 	} while(0);
245*9c6f9240SPeter Wemm 	ef_close(&ef);
246*9c6f9240SPeter Wemm 	return error;
247*9c6f9240SPeter Wemm }
248*9c6f9240SPeter Wemm 
249*9c6f9240SPeter Wemm void
250*9c6f9240SPeter Wemm maketempfile(char *dest, const char *root)
251*9c6f9240SPeter Wemm {
252*9c6f9240SPeter Wemm 	char *p;
253*9c6f9240SPeter Wemm 
254*9c6f9240SPeter Wemm 	strncpy(dest, root, MAXPATHLEN - 1);
255*9c6f9240SPeter Wemm 	dest[MAXPATHLEN] = '\0';
256*9c6f9240SPeter Wemm 
257*9c6f9240SPeter Wemm 	if ((p = strrchr(dest, '/')) != 0)
258*9c6f9240SPeter Wemm 		p++;
259*9c6f9240SPeter Wemm 	else
260*9c6f9240SPeter Wemm 		p = dest;
261*9c6f9240SPeter Wemm 	strcpy(p, "lhint.XXXXXX");
262*9c6f9240SPeter Wemm 	if (mkstemp(dest) == -1)
263*9c6f9240SPeter Wemm 		err(1, "%s", dest);
264*9c6f9240SPeter Wemm }
265*9c6f9240SPeter Wemm 
266*9c6f9240SPeter Wemm static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
267*9c6f9240SPeter Wemm 
268*9c6f9240SPeter Wemm int
269*9c6f9240SPeter Wemm main(int argc, char *argv[])
270*9c6f9240SPeter Wemm {
271*9c6f9240SPeter Wemm 	FTS *ftsp;
272*9c6f9240SPeter Wemm 	FTSENT *p;
273*9c6f9240SPeter Wemm 	int opt, fts_options, ival;
274*9c6f9240SPeter Wemm 
275*9c6f9240SPeter Wemm 	fts_options = FTS_PHYSICAL;
276*9c6f9240SPeter Wemm /*	SLIST_INIT(&kldlist);*/
277*9c6f9240SPeter Wemm 
278*9c6f9240SPeter Wemm 	while ((opt = getopt(argc, argv, "Rdf:v")) != -1) {
279*9c6f9240SPeter Wemm 		switch (opt) {
280*9c6f9240SPeter Wemm 		case 'd':
281*9c6f9240SPeter Wemm 			dflag = 1;
282*9c6f9240SPeter Wemm 			break;
283*9c6f9240SPeter Wemm 		case 'f':
284*9c6f9240SPeter Wemm 			xref_file = optarg;
285*9c6f9240SPeter Wemm 			break;
286*9c6f9240SPeter Wemm 		case 'v':
287*9c6f9240SPeter Wemm 			verbose++;
288*9c6f9240SPeter Wemm 			break;
289*9c6f9240SPeter Wemm 		case 'R':
290*9c6f9240SPeter Wemm 			fts_options |= FTS_COMFOLLOW;
291*9c6f9240SPeter Wemm 			break;
292*9c6f9240SPeter Wemm 		default:
293*9c6f9240SPeter Wemm 			usage();
294*9c6f9240SPeter Wemm 			/* NOTREACHED */
295*9c6f9240SPeter Wemm 		}
296*9c6f9240SPeter Wemm 	}
297*9c6f9240SPeter Wemm 	if (argc - optind < 1)
298*9c6f9240SPeter Wemm 		usage();
299*9c6f9240SPeter Wemm 	argc -= optind;
300*9c6f9240SPeter Wemm 	argv += optind;
301*9c6f9240SPeter Wemm 
302*9c6f9240SPeter Wemm 	ftsp = fts_open(argv, fts_options, 0);
303*9c6f9240SPeter Wemm 	if (ftsp == NULL)
304*9c6f9240SPeter Wemm 		exit(1);
305*9c6f9240SPeter Wemm 
306*9c6f9240SPeter Wemm 	for (;;) {
307*9c6f9240SPeter Wemm 		p = fts_read(ftsp);
308*9c6f9240SPeter Wemm 		if ((p == NULL || p->fts_info == FTS_D) && !dflag && fxref) {
309*9c6f9240SPeter Wemm 			fclose(fxref);
310*9c6f9240SPeter Wemm 			if (reccnt) {
311*9c6f9240SPeter Wemm 				rename(tempname, xrefname);
312*9c6f9240SPeter Wemm 			} else {
313*9c6f9240SPeter Wemm 				unlink(tempname);
314*9c6f9240SPeter Wemm 				unlink(xrefname);
315*9c6f9240SPeter Wemm 			}
316*9c6f9240SPeter Wemm 		}
317*9c6f9240SPeter Wemm 		if (p == NULL)
318*9c6f9240SPeter Wemm 			break;
319*9c6f9240SPeter Wemm 		if (p && p->fts_info == FTS_D && !dflag) {
320*9c6f9240SPeter Wemm 			snprintf(xrefname, sizeof(xrefname), "%s/%s",
321*9c6f9240SPeter Wemm 			    ftsp->fts_path, xref_file);
322*9c6f9240SPeter Wemm 			maketempfile(tempname, ftsp->fts_path);
323*9c6f9240SPeter Wemm 			fxref = fopen(tempname, "w+t");
324*9c6f9240SPeter Wemm 			if (fxref == NULL)
325*9c6f9240SPeter Wemm 				err(1, "can't create %s", tempname);
326*9c6f9240SPeter Wemm 			ival = 1;
327*9c6f9240SPeter Wemm 			fwrite(&ival, sizeof(ival), 1, fxref);
328*9c6f9240SPeter Wemm 			reccnt = 0;
329*9c6f9240SPeter Wemm 		}
330*9c6f9240SPeter Wemm 		if (p->fts_info != FTS_F)
331*9c6f9240SPeter Wemm 			continue;
332*9c6f9240SPeter Wemm 		read_kld(p->fts_path, p->fts_name);
333*9c6f9240SPeter Wemm 	}
334*9c6f9240SPeter Wemm 	fts_close(ftsp);
335*9c6f9240SPeter Wemm 	return 0;
336*9c6f9240SPeter Wemm }
337*9c6f9240SPeter Wemm 
338*9c6f9240SPeter Wemm static void
339*9c6f9240SPeter Wemm usage(void)
340*9c6f9240SPeter Wemm {
341*9c6f9240SPeter Wemm 
342*9c6f9240SPeter Wemm 	fprintf(stderr, "%s\n",
343*9c6f9240SPeter Wemm 	    "Usage: kldxref [-Rdv] [-f hintfile] path [path..]"
344*9c6f9240SPeter Wemm 	);
345*9c6f9240SPeter Wemm 	exit(1);
346*9c6f9240SPeter Wemm }
347