xref: /freebsd/usr.sbin/kldxref/kldxref.c (revision 83dff030abeb6f61ea7bae43524f29a83e97be99)
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/elf.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <errno.h>
53 
54 #include "ef.h"
55 
56 #define	MAXRECSIZE	1024
57 #define check(val)	if ((error = (val)) != 0) break
58 
59 #ifndef min
60 #define	min(a,b)	(((a)<(b)) ? (a) : (b))
61 #endif
62 
63 struct mod_info {
64 	char*	mi_name;
65 	int	mi_ver;
66 	SLIST_ENTRY(mod_info) mi_next;
67 };
68 
69 #ifdef notnow
70 struct kld_info {
71 	char*	k_filename;
72 	SLIST_HEAD(mod_list_head, mod_info) k_modules;
73 	SLIST_ENTRY(kld_info) k_next;
74 };
75 
76 SLIST_HEAD(kld_list_head, kld_info) kldlist;
77 #endif
78 
79 static int dflag, verbose;
80 
81 FILE *fxref;
82 
83 static const char *xref_file = "linker.hints";
84 
85 static char recbuf[MAXRECSIZE];
86 static int recpos, reccnt;
87 
88 void maketempfile(char *, const char *);
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_rel(&ef, start, sizeof(*p) * entries,
233 		    (void**)&p));
234 		orgp = p;
235 		while(entries--) {
236 			check(ef_seg_read_rel(&ef, (Elf_Off)*p, sizeof(md), &md));
237 			p++;
238 			check(ef_seg_read(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval));
239 			cval[MAXMODNAME] = '\0';
240 			parse_entry(&md, cval, &ef, kldname);
241 		}
242 		if (error)
243 			warnc(error, "error while reading %s", filename);
244 		free(orgp);
245 	} while(0);
246 	ef_close(&ef);
247 	return error;
248 }
249 
250 void
251 maketempfile(char *dest, const char *root)
252 {
253 	char *p;
254 
255 	strncpy(dest, root, MAXPATHLEN - 1);
256 	dest[MAXPATHLEN] = '\0';
257 
258 	if ((p = strrchr(dest, '/')) != 0)
259 		p++;
260 	else
261 		p = dest;
262 	strcpy(p, "lhint.XXXXXX");
263 	if (mkstemp(dest) == -1)
264 		err(1, "%s", dest);
265 }
266 
267 static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
268 
269 int
270 main(int argc, char *argv[])
271 {
272 	FTS *ftsp;
273 	FTSENT *p;
274 	int opt, fts_options, ival;
275 
276 	fts_options = FTS_PHYSICAL;
277 /*	SLIST_INIT(&kldlist);*/
278 
279 	while ((opt = getopt(argc, argv, "Rdf:v")) != -1) {
280 		switch (opt) {
281 		case 'd':
282 			dflag = 1;
283 			break;
284 		case 'f':
285 			xref_file = optarg;
286 			break;
287 		case 'v':
288 			verbose++;
289 			break;
290 		case 'R':
291 			fts_options |= FTS_COMFOLLOW;
292 			break;
293 		default:
294 			usage();
295 			/* NOTREACHED */
296 		}
297 	}
298 	if (argc - optind < 1)
299 		usage();
300 	argc -= optind;
301 	argv += optind;
302 
303 	ftsp = fts_open(argv, fts_options, 0);
304 	if (ftsp == NULL)
305 		exit(1);
306 
307 	for (;;) {
308 		p = fts_read(ftsp);
309 		if ((p == NULL || p->fts_info == FTS_D) && !dflag && fxref) {
310 			fclose(fxref);
311 			if (reccnt) {
312 				rename(tempname, xrefname);
313 			} else {
314 				unlink(tempname);
315 				unlink(xrefname);
316 			}
317 		}
318 		if (p == NULL)
319 			break;
320 		if (p && p->fts_info == FTS_D && !dflag) {
321 			snprintf(xrefname, sizeof(xrefname), "%s/%s",
322 			    ftsp->fts_path, xref_file);
323 			maketempfile(tempname, ftsp->fts_path);
324 			fxref = fopen(tempname, "w+t");
325 			if (fxref == NULL)
326 				err(1, "can't create %s", tempname);
327 			ival = 1;
328 			fwrite(&ival, sizeof(ival), 1, fxref);
329 			reccnt = 0;
330 		}
331 		if (p->fts_info != FTS_F)
332 			continue;
333 		read_kld(p->fts_path, p->fts_name);
334 	}
335 	fts_close(ftsp);
336 	return 0;
337 }
338 
339 static void
340 usage(void)
341 {
342 
343 	fprintf(stderr, "%s\n",
344 	    "usage: kldxref [-Rdv] [-f hintfile] path [path..]"
345 	);
346 	exit(1);
347 }
348