xref: /freebsd/usr.sbin/kldxref/kldxref.c (revision 991666adc717e4f5fdf9854f67d240a00f6eb4ab)
1  /*-
2   * SPDX-License-Identifier: BSD-4-Clause
3   *
4   * Copyright (c) 2000, Boris Popov
5   * All rights reserved.
6   *
7   * Redistribution and use in source and binary forms, with or without
8   * modification, are permitted provided that the following conditions
9   * are met:
10   * 1. Redistributions of source code must retain the above copyright
11   *    notice, this list of conditions and the following disclaimer.
12   * 2. Redistributions in binary form must reproduce the above copyright
13   *    notice, this list of conditions and the following disclaimer in the
14   *    documentation and/or other materials provided with the distribution.
15   * 3. All advertising materials mentioning features or use of this software
16   *    must display the following acknowledgement:
17   *    This product includes software developed by Boris Popov.
18   * 4. Neither the name of the author nor the names of any co-contributors
19   *    may be used to endorse or promote products derived from this software
20   *    without specific prior written permission.
21   *
22   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32   * SUCH DAMAGE.
33   *
34   * $FreeBSD$
35   */
36  
37  #include <sys/types.h>
38  #include <sys/param.h>
39  #include <sys/endian.h>
40  #include <sys/exec.h>
41  #include <sys/queue.h>
42  #include <sys/kernel.h>
43  #include <sys/reboot.h>
44  #include <sys/linker.h>
45  #include <sys/stat.h>
46  #include <sys/module.h>
47  #define FREEBSD_ELF
48  
49  #include <ctype.h>
50  #include <err.h>
51  #include <errno.h>
52  #include <fts.h>
53  #include <stdbool.h>
54  #include <stdio.h>
55  #include <stdlib.h>
56  #include <string.h>
57  #include <unistd.h>
58  #include <machine/elf.h>
59  
60  #include "ef.h"
61  
62  #define	MAXRECSIZE	(64 << 10)	/* 64k */
63  #define check(val)	if ((error = (val)) != 0) break
64  
65  static bool dflag;	/* do not create a hint file, only write on stdout */
66  static int verbose;
67  
68  static FILE *fxref;	/* current hints file */
69  
70  static const char *xref_file = "linker.hints";
71  
72  /*
73   * A record is stored in the static buffer recbuf before going to disk.
74   */
75  static char recbuf[MAXRECSIZE];
76  static int recpos;	/* current write position */
77  static int reccnt;	/* total record written to this file so far */
78  
79  static void
80  intalign(void)
81  {
82  
83  	recpos = roundup2(recpos, sizeof(int));
84  }
85  
86  static void
87  record_start(void)
88  {
89  
90  	recpos = 0;
91  	memset(recbuf, 0, MAXRECSIZE);
92  }
93  
94  static int
95  record_end(void)
96  {
97  
98  	if (recpos == 0)
99  		return (0);
100  	reccnt++;
101  	intalign();
102  	fwrite(&recpos, sizeof(recpos), 1, fxref);
103  	return (fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0);
104  }
105  
106  static int
107  record_buf(const void *buf, size_t size)
108  {
109  
110  	if (MAXRECSIZE - recpos < size)
111  		errx(1, "record buffer overflow");
112  	memcpy(recbuf + recpos, buf, size);
113  	recpos += size;
114  	return (0);
115  }
116  
117  /*
118   * An int is stored in host order and aligned
119   */
120  static int
121  record_int(int val)
122  {
123  
124  	intalign();
125  	return (record_buf(&val, sizeof(val)));
126  }
127  
128  /*
129   * A string is stored as 1-byte length plus data, no padding
130   */
131  static int
132  record_string(const char *str)
133  {
134  	int error;
135  	size_t len;
136  	u_char val;
137  
138  	if (dflag)
139  		return (0);
140  	val = len = strlen(str);
141  	if (len > 255)
142  		errx(1, "string %s too long", str);
143  	error = record_buf(&val, sizeof(val));
144  	if (error != 0)
145  		return (error);
146  	return (record_buf(str, len));
147  }
148  
149  /* From sys/isa/pnp.c */
150  static char *
151  pnp_eisaformat(uint32_t id)
152  {
153  	uint8_t *data;
154  	static char idbuf[8];
155  	const char  hextoascii[] = "0123456789abcdef";
156  
157  	id = htole32(id);
158  	data = (uint8_t *)&id;
159  	idbuf[0] = '@' + ((data[0] & 0x7c) >> 2);
160  	idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5));
161  	idbuf[2] = '@' + (data[1] & 0x1f);
162  	idbuf[3] = hextoascii[(data[2] >> 4)];
163  	idbuf[4] = hextoascii[(data[2] & 0xf)];
164  	idbuf[5] = hextoascii[(data[3] >> 4)];
165  	idbuf[6] = hextoascii[(data[3] & 0xf)];
166  	idbuf[7] = 0;
167  	return (idbuf);
168  }
169  
170  struct pnp_elt
171  {
172  	int	pe_kind;	/* What kind of entry */
173  #define TYPE_SZ_MASK	0x0f
174  #define TYPE_FLAGGED	0x10	/* all f's is a wildcard */
175  #define	TYPE_INT	0x20	/* Is a number */
176  #define TYPE_PAIRED	0x40
177  #define TYPE_LE		0x80	/* Matches <= this value */
178  #define TYPE_GE		0x100	/* Matches >= this value */
179  #define TYPE_MASK	0x200	/* Specifies a mask to follow */
180  #define TYPE_U8		(1 | TYPE_INT)
181  #define TYPE_V8		(1 | TYPE_INT | TYPE_FLAGGED)
182  #define TYPE_G16	(2 | TYPE_INT | TYPE_GE)
183  #define TYPE_L16	(2 | TYPE_INT | TYPE_LE)
184  #define TYPE_M16	(2 | TYPE_INT | TYPE_MASK)
185  #define TYPE_U16	(2 | TYPE_INT)
186  #define TYPE_V16	(2 | TYPE_INT | TYPE_FLAGGED)
187  #define TYPE_U32	(4 | TYPE_INT)
188  #define TYPE_V32	(4 | TYPE_INT | TYPE_FLAGGED)
189  #define TYPE_W32	(4 | TYPE_INT | TYPE_PAIRED)
190  #define TYPE_D		7
191  #define TYPE_Z		8
192  #define TYPE_P		9
193  #define TYPE_E		10
194  #define TYPE_T		11
195  	int	pe_offset;	/* Offset within the element */
196  	char *	pe_key;		/* pnp key name */
197  	TAILQ_ENTRY(pnp_elt) next; /* Link */
198  };
199  typedef TAILQ_HEAD(pnp_head, pnp_elt) pnp_list;
200  
201  /*
202   * this function finds the data from the pnp table, as described by the
203   * the description and creates a new output (new_desc). This output table
204   * is a form that's easier for the agent that's automatically loading the
205   * modules.
206   *
207   * The format output is the simplified string from this routine in the
208   * same basic format as the pnp string, as documented in sys/module.h.
209   * First a string describing the format is output, the a count of the
210   * number of records, then each record. The format string also describes
211   * the length of each entry (though it isn't a fixed length when strings
212   * are present).
213   *
214   *	type	Output		Meaning
215   *	I	uint32_t	Integer equality comparison
216   *	J	uint32_t	Pair of uint16_t fields converted to native
217   *				byte order. The two fields both must match.
218   *	G	uint32_t	Greater than or equal to
219   *	L	uint32_t	Less than or equal to
220   *	M	uint32_t	Mask of which fields to test. Fields that
221   *				take up space increment the count. This
222   *				field must be first, and resets the count.
223   *	D	string		Description of the device this pnp info is for
224   *	Z	string		pnp string must match this
225   *	T	nothing		T fields set pnp values that must be true for
226   *				the entire table.
227   * Values are packed the same way that other values are packed in this file.
228   * Strings and int32_t's start on a 32-bit boundary and are padded with 0
229   * bytes. Objects that are smaller than uint32_t are converted, without
230   * sign extension to uint32_t to simplify parsing downstream.
231   */
232  static int
233  parse_pnp_list(const char *desc, char **new_desc, pnp_list *list)
234  {
235  	const char *walker, *ep;
236  	const char *colon, *semi;
237  	struct pnp_elt *elt;
238  	char *nd;
239  	char type[8], key[32];
240  	int off;
241  
242  	walker = desc;
243  	ep = desc + strlen(desc);
244  	off = 0;
245  	nd = *new_desc = malloc(strlen(desc) + 1);
246  	if (verbose > 1)
247  		printf("Converting %s into a list\n", desc);
248  	while (walker < ep) {
249  		colon = strchr(walker, ':');
250  		semi = strchr(walker, ';');
251  		if (semi != NULL && semi < colon)
252  			goto err;
253  		if (colon - walker > sizeof(type))
254  			goto err;
255  		strncpy(type, walker, colon - walker);
256  		type[colon - walker] = '\0';
257  		if (semi != NULL) {
258  			if (semi - colon >= sizeof(key))
259  				goto err;
260  			strncpy(key, colon + 1, semi - colon - 1);
261  			key[semi - colon - 1] = '\0';
262  			walker = semi + 1;
263  			/* Fail safe if we have spaces after ; */
264  			while (walker < ep && isspace(*walker))
265  				walker++;
266  		} else {
267  			if (strlen(colon + 1) >= sizeof(key))
268  				goto err;
269  			strcpy(key, colon + 1);
270  			walker = ep;
271  		}
272  		if (verbose > 1)
273  			printf("Found type %s for name %s\n", type, key);
274  		/* Skip pointer place holders */
275  		if (strcmp(type, "P") == 0) {
276  			off += sizeof(void *);
277  			continue;
278  		}
279  
280  		/*
281  		 * Add a node of the appropriate type
282  		 */
283  		elt = malloc(sizeof(struct pnp_elt) + strlen(key) + 1);
284  		TAILQ_INSERT_TAIL(list, elt, next);
285  		elt->pe_key = (char *)(elt + 1);
286  		elt->pe_offset = off;
287  		if (strcmp(type, "U8") == 0)
288  			elt->pe_kind = TYPE_U8;
289  		else if (strcmp(type, "V8") == 0)
290  			elt->pe_kind = TYPE_V8;
291  		else if (strcmp(type, "G16") == 0)
292  			elt->pe_kind = TYPE_G16;
293  		else if (strcmp(type, "L16") == 0)
294  			elt->pe_kind = TYPE_L16;
295  		else if (strcmp(type, "M16") == 0)
296  			elt->pe_kind = TYPE_M16;
297  		else if (strcmp(type, "U16") == 0)
298  			elt->pe_kind = TYPE_U16;
299  		else if (strcmp(type, "V16") == 0)
300  			elt->pe_kind = TYPE_V16;
301  		else if (strcmp(type, "U32") == 0)
302  			elt->pe_kind = TYPE_U32;
303  		else if (strcmp(type, "V32") == 0)
304  			elt->pe_kind = TYPE_V32;
305  		else if (strcmp(type, "W32") == 0)
306  			elt->pe_kind = TYPE_W32;
307  		else if (strcmp(type, "D") == 0)	/* description char * */
308  			elt->pe_kind = TYPE_D;
309  		else if (strcmp(type, "Z") == 0)	/* char * to match */
310  			elt->pe_kind = TYPE_Z;
311  		else if (strcmp(type, "P") == 0)	/* Pointer -- ignored */
312  			elt->pe_kind = TYPE_P;
313  		else if (strcmp(type, "E") == 0)	/* EISA PNP ID, as uint32_t */
314  			elt->pe_kind = TYPE_E;
315  		else if (strcmp(type, "T") == 0)
316  			elt->pe_kind = TYPE_T;
317  		else
318  			goto err;
319  		/*
320  		 * Maybe the rounding here needs to be more nuanced and/or somehow
321  		 * architecture specific. Fortunately, most tables in the system
322  		 * have sane ordering of types.
323  		 */
324  		if (elt->pe_kind & TYPE_INT) {
325  			elt->pe_offset = roundup2(elt->pe_offset, elt->pe_kind & TYPE_SZ_MASK);
326  			off = elt->pe_offset + (elt->pe_kind & TYPE_SZ_MASK);
327  		} else if (elt->pe_kind == TYPE_E) {
328  			/* Type E stored as Int, displays as string */
329  			elt->pe_offset = roundup2(elt->pe_offset, sizeof(uint32_t));
330  			off = elt->pe_offset + sizeof(uint32_t);
331  		} else if (elt->pe_kind == TYPE_T) {
332  			/* doesn't actually consume space in the table */
333  			off = elt->pe_offset;
334  		} else {
335  			elt->pe_offset = roundup2(elt->pe_offset, sizeof(void *));
336  			off = elt->pe_offset + sizeof(void *);
337  		}
338  		if (elt->pe_kind & TYPE_PAIRED) {
339  			char *word, *ctx;
340  
341  			for (word = strtok_r(key, "/", &ctx);
342  			     word; word = strtok_r(NULL, "/", &ctx)) {
343  				sprintf(nd, "%c:%s;", elt->pe_kind & TYPE_FLAGGED ? 'J' : 'I',
344  				    word);
345  				nd += strlen(nd);
346  			}
347  
348  		}
349  		else {
350  			if (elt->pe_kind & TYPE_FLAGGED)
351  				*nd++ = 'J';
352  			else if (elt->pe_kind & TYPE_GE)
353  				*nd++ = 'G';
354  			else if (elt->pe_kind & TYPE_LE)
355  				*nd++ = 'L';
356  			else if (elt->pe_kind & TYPE_MASK)
357  				*nd++ = 'M';
358  			else if (elt->pe_kind & TYPE_INT)
359  				*nd++ = 'I';
360  			else if (elt->pe_kind == TYPE_D)
361  				*nd++ = 'D';
362  			else if (elt->pe_kind == TYPE_Z || elt->pe_kind == TYPE_E)
363  				*nd++ = 'Z';
364  			else if (elt->pe_kind == TYPE_T)
365  				*nd++ = 'T';
366  			else
367  				errx(1, "Impossible type %x\n", elt->pe_kind);
368  			*nd++ = ':';
369  			strcpy(nd, key);
370  			nd += strlen(nd);
371  			*nd++ = ';';
372  		}
373  	}
374  	*nd++ = '\0';
375  	return (0);
376  err:
377  	errx(1, "Parse error of description string %s", desc);
378  }
379  
380  static int
381  parse_entry(struct mod_metadata *md, const char *cval,
382      struct elf_file *ef, const char *kldname)
383  {
384  	struct mod_depend mdp;
385  	struct mod_version mdv;
386  	struct mod_pnp_match_info pnp;
387  	char descr[1024];
388  	Elf_Off data;
389  	int error, i;
390  	size_t len;
391  	char *walker;
392  	void *table;
393  
394  	data = (Elf_Off)md->md_data;
395  	error = 0;
396  	record_start();
397  	switch (md->md_type) {
398  	case MDT_DEPEND:
399  		if (!dflag)
400  			break;
401  		check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp));
402  		printf("  depends on %s.%d (%d,%d)\n", cval,
403  		    mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum);
404  		break;
405  	case MDT_VERSION:
406  		check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv));
407  		if (dflag) {
408  			printf("  interface %s.%d\n", cval, mdv.mv_version);
409  		} else {
410  			record_int(MDT_VERSION);
411  			record_string(cval);
412  			record_int(mdv.mv_version);
413  			record_string(kldname);
414  		}
415  		break;
416  	case MDT_MODULE:
417  		if (dflag) {
418  			printf("  module %s\n", cval);
419  		} else {
420  			record_int(MDT_MODULE);
421  			record_string(cval);
422  			record_string(kldname);
423  		}
424  		break;
425  	case MDT_PNP_INFO:
426  		check(EF_SEG_READ_REL(ef, data, sizeof(pnp), &pnp));
427  		check(EF_SEG_READ_STRING(ef, (Elf_Off)pnp.descr, sizeof(descr), descr));
428  		descr[sizeof(descr) - 1] = '\0';
429  		if (dflag) {
430  			printf("  pnp info for bus %s format %s %d entries of %d bytes\n",
431  			    cval, descr, pnp.num_entry, pnp.entry_len);
432  		} else {
433  			pnp_list list;
434  			struct pnp_elt *elt, *elt_tmp;
435  			char *new_descr;
436  
437  			if (verbose > 1)
438  				printf("  pnp info for bus %s format %s %d entries of %d bytes\n",
439  				    cval, descr, pnp.num_entry, pnp.entry_len);
440  			/*
441  			 * Parse descr to weed out the chaff and to create a list
442  			 * of offsets to output.
443  			 */
444  			TAILQ_INIT(&list);
445  			parse_pnp_list(descr, &new_descr, &list);
446  			record_int(MDT_PNP_INFO);
447  			record_string(cval);
448  			record_string(new_descr);
449  			record_int(pnp.num_entry);
450  			len = pnp.num_entry * pnp.entry_len;
451  			walker = table = malloc(len);
452  			check(EF_SEG_READ_REL(ef, (Elf_Off)pnp.table, len, table));
453  
454  			/*
455  			 * Walk the list and output things. We've collapsed all the
456  			 * variant forms of the table down to just ints and strings.
457  			 */
458  			for (i = 0; i < pnp.num_entry; i++) {
459  				TAILQ_FOREACH(elt, &list, next) {
460  					uint8_t v1;
461  					uint16_t v2;
462  					uint32_t v4;
463  					int	value;
464  					char buffer[1024];
465  
466  					if (elt->pe_kind == TYPE_W32) {
467  						memcpy(&v4, walker + elt->pe_offset, sizeof(v4));
468  						value = v4 & 0xffff;
469  						record_int(value);
470  						if (verbose > 1)
471  							printf("W32:%#x", value);
472  						value = (v4 >> 16) & 0xffff;
473  						record_int(value);
474  						if (verbose > 1)
475  							printf(":%#x;", value);
476  					} else if (elt->pe_kind & TYPE_INT) {
477  						switch (elt->pe_kind & TYPE_SZ_MASK) {
478  						case 1:
479  							memcpy(&v1, walker + elt->pe_offset, sizeof(v1));
480  							if ((elt->pe_kind & TYPE_FLAGGED) && v1 == 0xff)
481  								value = -1;
482  							else
483  								value = v1;
484  							break;
485  						case 2:
486  							memcpy(&v2, walker + elt->pe_offset, sizeof(v2));
487  							if ((elt->pe_kind & TYPE_FLAGGED) && v2 == 0xffff)
488  								value = -1;
489  							else
490  								value = v2;
491  							break;
492  						case 4:
493  							memcpy(&v4, walker + elt->pe_offset, sizeof(v4));
494  							if ((elt->pe_kind & TYPE_FLAGGED) && v4 == 0xffffffff)
495  								value = -1;
496  							else
497  								value = v4;
498  							break;
499  						default:
500  							errx(1, "Invalid size somehow %#x", elt->pe_kind);
501  						}
502  						if (verbose > 1)
503  							printf("I:%#x;", value);
504  						record_int(value);
505  					} else if (elt->pe_kind == TYPE_T) {
506  						/* Do nothing */
507  					} else { /* E, Z or D -- P already filtered */
508  						if (elt->pe_kind == TYPE_E) {
509  							memcpy(&v4, walker + elt->pe_offset, sizeof(v4));
510  							strcpy(buffer, pnp_eisaformat(v4));
511  						} else {
512  							char *ptr;
513  
514  							ptr = *(char **)(walker + elt->pe_offset);
515  							buffer[0] = '\0';
516  							if (ptr != NULL) {
517  								EF_SEG_READ_STRING(ef, (Elf_Off)ptr,
518  								    sizeof(buffer), buffer);
519  								buffer[sizeof(buffer) - 1] = '\0';
520  							}
521  						}
522  						if (verbose > 1)
523  							printf("%c:%s;", elt->pe_kind == TYPE_E ? 'E' : (elt->pe_kind == TYPE_Z ? 'Z' : 'D'), buffer);
524  						record_string(buffer);
525  					}
526  				}
527  				if (verbose > 1)
528  					printf("\n");
529  				walker += pnp.entry_len;
530  			}
531  			/* Now free it */
532  			TAILQ_FOREACH_SAFE(elt, &list, next, elt_tmp) {
533  				TAILQ_REMOVE(&list, elt, next);
534  				free(elt);
535  			}
536  			free(table);
537  		}
538  		break;
539  	default:
540  		warnx("unknown metadata record %d in file %s", md->md_type, kldname);
541  	}
542  	if (!error)
543  		record_end();
544  	return (error);
545  }
546  
547  static int
548  read_kld(char *filename, char *kldname)
549  {
550  	struct mod_metadata md;
551  	struct elf_file ef;
552  	void **p, **orgp;
553  	int error, eftype;
554  	long start, finish, entries;
555  	char cval[MAXMODNAME + 1];
556  
557  	if (verbose || dflag)
558  		printf("%s\n", filename);
559  	error = ef_open(filename, &ef, verbose);
560  	if (error != 0) {
561  		error = ef_obj_open(filename, &ef, verbose);
562  		if (error != 0) {
563  			if (verbose)
564  				warnc(error, "elf_open(%s)", filename);
565  			return (error);
566  		}
567  	}
568  	eftype = EF_GET_TYPE(&ef);
569  	if (eftype != EFT_KLD && eftype != EFT_KERNEL)  {
570  		EF_CLOSE(&ef);
571  		return (0);
572  	}
573  	do {
574  		check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish,
575  		    &entries));
576  		check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries,
577  		    (void *)&p));
578  		orgp = p;
579  		while(entries--) {
580  			check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md),
581  			    &md));
582  			p++;
583  			check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval,
584  			    sizeof(cval), cval));
585  			parse_entry(&md, cval, &ef, kldname);
586  		}
587  		if (error != 0)
588  			warnc(error, "error while reading %s", filename);
589  		free(orgp);
590  	} while(0);
591  	EF_CLOSE(&ef);
592  	return (error);
593  }
594  
595  /*
596   * Create a temp file in directory root, make sure we don't
597   * overflow the buffer for the destination name
598   */
599  static FILE *
600  maketempfile(char *dest, const char *root)
601  {
602  	char *p;
603  	int n, fd;
604  
605  	p = strrchr(root, '/');
606  	n = p != NULL ? p - root + 1 : 0;
607  	if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >=
608  	    MAXPATHLEN) {
609  		errno = ENAMETOOLONG;
610  		return (NULL);
611  	}
612  
613  	fd = mkstemp(dest);
614  	if (fd < 0)
615  		return (NULL);
616  	fchmod(fd, 0644);	/* nothing secret in the file */
617  	return (fdopen(fd, "w+"));
618  }
619  
620  static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
621  
622  static void
623  usage(void)
624  {
625  
626  	fprintf(stderr, "%s\n",
627  	    "usage: kldxref [-Rdv] [-f hintsfile] path ..."
628  	);
629  	exit(1);
630  }
631  
632  static int
633  compare(const FTSENT *const *a, const FTSENT *const *b)
634  {
635  
636  	if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D)
637  		return (1);
638  	if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D)
639  		return (-1);
640  	return (strcmp((*a)->fts_name, (*b)->fts_name));
641  }
642  
643  int
644  main(int argc, char *argv[])
645  {
646  	FTS *ftsp;
647  	FTSENT *p;
648  	int opt, fts_options, ival;
649  	struct stat sb;
650  
651  	fts_options = FTS_PHYSICAL;
652  
653  	while ((opt = getopt(argc, argv, "Rdf:v")) != -1) {
654  		switch (opt) {
655  		case 'd':	/* no hint file, only print on stdout */
656  			dflag = true;
657  			break;
658  		case 'f':	/* use this name instead of linker.hints */
659  			xref_file = optarg;
660  			break;
661  		case 'v':
662  			verbose++;
663  			break;
664  		case 'R':	/* recurse on directories */
665  			fts_options |= FTS_COMFOLLOW;
666  			break;
667  		default:
668  			usage();
669  			/* NOTREACHED */
670  		}
671  	}
672  	if (argc - optind < 1)
673  		usage();
674  	argc -= optind;
675  	argv += optind;
676  
677  	if (stat(argv[0], &sb) != 0)
678  		err(1, "%s", argv[0]);
679  	if ((sb.st_mode & S_IFDIR) == 0) {
680  		errno = ENOTDIR;
681  		err(1, "%s", argv[0]);
682  	}
683  
684  	ftsp = fts_open(argv, fts_options, compare);
685  	if (ftsp == NULL)
686  		exit(1);
687  
688  	for (;;) {
689  		p = fts_read(ftsp);
690  		if ((p == NULL || p->fts_info == FTS_D) && fxref) {
691  			/* close and rename the current hint file */
692  			fclose(fxref);
693  			fxref = NULL;
694  			if (reccnt != 0) {
695  				rename(tempname, xrefname);
696  			} else {
697  				/* didn't find any entry, ignore this file */
698  				unlink(tempname);
699  				unlink(xrefname);
700  			}
701  		}
702  		if (p == NULL)
703  			break;
704  		if (p->fts_info == FTS_D && !dflag) {
705  			/* visiting a new directory, create a new hint file */
706  			snprintf(xrefname, sizeof(xrefname), "%s/%s",
707  			    ftsp->fts_path, xref_file);
708  			fxref = maketempfile(tempname, ftsp->fts_path);
709  			if (fxref == NULL)
710  				err(1, "can't create %s", tempname);
711  			ival = 1;
712  			fwrite(&ival, sizeof(ival), 1, fxref);
713  			reccnt = 0;
714  		}
715  		/* skip non-files and separate debug files */
716  		if (p->fts_info != FTS_F)
717  			continue;
718  		if (p->fts_namelen >= 6 &&
719  		    strcmp(p->fts_name + p->fts_namelen - 6, ".debug") == 0)
720  			continue;
721  		if (p->fts_namelen >= 8 &&
722  		    strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0)
723  			continue;
724  		read_kld(p->fts_path, p->fts_name);
725  	}
726  	fts_close(ftsp);
727  	return (0);
728  }
729