kldxref.c revision 109607
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: head/usr.sbin/kldxref/kldxref.c 109607 2003-01-21 03:51:53Z jake $
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
63struct mod_info {
64	char*	mi_name;
65	int	mi_ver;
66	SLIST_ENTRY(mod_info) mi_next;
67};
68
69#ifdef notnow
70struct 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
76SLIST_HEAD(kld_list_head, kld_info) kldlist;
77#endif
78
79static int dflag, verbose;
80
81FILE *fxref;
82
83static const char *xref_file = "linker.hints";
84
85static char recbuf[MAXRECSIZE];
86static int recpos, reccnt;
87
88void maketempfile(char *, const char *);
89static void usage(void);
90
91static void
92intalign(void)
93{
94	recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1);
95}
96
97static void
98record_start(void)
99{
100	recpos = 0;
101	memset(recbuf, 0, MAXRECSIZE);
102}
103
104static int
105record_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
115static int
116record_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
125static int
126record_int(int val)
127{
128	intalign();
129	return record_buf(&val, sizeof(val));
130}
131
132static int
133record_byte(u_char val)
134{
135	return record_buf(&val, sizeof(val));
136}
137
138static int
139record_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
152static int
153parse_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
196static int
197read_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
250void
251maketempfile(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
267static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
268
269int
270main(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
339static void
340usage(void)
341{
342
343	fprintf(stderr, "%s\n",
344	    "usage: kldxref [-Rdv] [-f hintfile] path [path..]"
345	);
346	exit(1);
347}
348