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