19c6f9240SPeter Wemm /* 29c6f9240SPeter Wemm * Copyright (c) 2000, Boris Popov 39c6f9240SPeter Wemm * All rights reserved. 49c6f9240SPeter Wemm * 59c6f9240SPeter Wemm * Redistribution and use in source and binary forms, with or without 69c6f9240SPeter Wemm * modification, are permitted provided that the following conditions 79c6f9240SPeter Wemm * are met: 89c6f9240SPeter Wemm * 1. Redistributions of source code must retain the above copyright 99c6f9240SPeter Wemm * notice, this list of conditions and the following disclaimer. 109c6f9240SPeter Wemm * 2. Redistributions in binary form must reproduce the above copyright 119c6f9240SPeter Wemm * notice, this list of conditions and the following disclaimer in the 129c6f9240SPeter Wemm * documentation and/or other materials provided with the distribution. 139c6f9240SPeter Wemm * 3. All advertising materials mentioning features or use of this software 149c6f9240SPeter Wemm * must display the following acknowledgement: 159c6f9240SPeter Wemm * This product includes software developed by Boris Popov. 169c6f9240SPeter Wemm * 4. Neither the name of the author nor the names of any co-contributors 179c6f9240SPeter Wemm * may be used to endorse or promote products derived from this software 189c6f9240SPeter Wemm * without specific prior written permission. 199c6f9240SPeter Wemm * 209c6f9240SPeter Wemm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 219c6f9240SPeter Wemm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 229c6f9240SPeter Wemm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 239c6f9240SPeter Wemm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 249c6f9240SPeter Wemm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 259c6f9240SPeter Wemm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 269c6f9240SPeter Wemm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 279c6f9240SPeter Wemm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 289c6f9240SPeter Wemm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 299c6f9240SPeter Wemm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 309c6f9240SPeter Wemm * SUCH DAMAGE. 319c6f9240SPeter Wemm * 329c6f9240SPeter Wemm * $FreeBSD$ 339c6f9240SPeter Wemm */ 349c6f9240SPeter Wemm 35b7abc67eSMaxim Sobolev #include <sys/types.h> 369c6f9240SPeter Wemm #include <sys/param.h> 379c6f9240SPeter Wemm #include <sys/exec.h> 389c6f9240SPeter Wemm #include <sys/queue.h> 399c6f9240SPeter Wemm #include <sys/kernel.h> 409c6f9240SPeter Wemm #include <sys/reboot.h> 419c6f9240SPeter Wemm #include <sys/linker.h> 429c6f9240SPeter Wemm #include <sys/stat.h> 439c6f9240SPeter Wemm #include <sys/module.h> 449c6f9240SPeter Wemm #define FREEBSD_ELF 459c6f9240SPeter Wemm #include <err.h> 469c6f9240SPeter Wemm #include <fts.h> 479c6f9240SPeter Wemm #include <string.h> 489c6f9240SPeter Wemm #include <machine/elf.h> 499c6f9240SPeter Wemm #include <stdio.h> 509c6f9240SPeter Wemm #include <stdlib.h> 519c6f9240SPeter Wemm #include <unistd.h> 529c6f9240SPeter Wemm #include <errno.h> 539c6f9240SPeter Wemm 549c6f9240SPeter Wemm #include "ef.h" 559c6f9240SPeter Wemm 56e808299cSWarner Losh #define MAXRECSIZE 8192 579c6f9240SPeter Wemm #define check(val) if ((error = (val)) != 0) break 589c6f9240SPeter Wemm 599cb138bbSLuigi Rizzo static int dflag; /* do not create a hint file, only write on stdout */ 609cb138bbSLuigi Rizzo static int verbose; 619c6f9240SPeter Wemm 629cb138bbSLuigi Rizzo static FILE *fxref; /* current hints file */ 639c6f9240SPeter Wemm 6487e5cd7cSMike Heffner static const char *xref_file = "linker.hints"; 659c6f9240SPeter Wemm 669cb138bbSLuigi Rizzo /* 679cb138bbSLuigi Rizzo * A record is stored in the static buffer recbuf before going to disk. 689cb138bbSLuigi Rizzo */ 699c6f9240SPeter Wemm static char recbuf[MAXRECSIZE]; 709cb138bbSLuigi Rizzo static int recpos; /* current write position */ 719cb138bbSLuigi Rizzo static int reccnt; /* total record written to this file so far */ 729c6f9240SPeter Wemm 739c6f9240SPeter Wemm static void 749c6f9240SPeter Wemm intalign(void) 759c6f9240SPeter Wemm { 769c6f9240SPeter Wemm recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1); 779c6f9240SPeter Wemm } 789c6f9240SPeter Wemm 799c6f9240SPeter Wemm static void 809c6f9240SPeter Wemm record_start(void) 819c6f9240SPeter Wemm { 829c6f9240SPeter Wemm recpos = 0; 839c6f9240SPeter Wemm memset(recbuf, 0, MAXRECSIZE); 849c6f9240SPeter Wemm } 859c6f9240SPeter Wemm 869c6f9240SPeter Wemm static int 879c6f9240SPeter Wemm record_end(void) 889c6f9240SPeter Wemm { 899cb138bbSLuigi Rizzo if (recpos == 0) 909c6f9240SPeter Wemm return 0; 919c6f9240SPeter Wemm reccnt++; 929c6f9240SPeter Wemm intalign(); 939c6f9240SPeter Wemm fwrite(&recpos, sizeof(recpos), 1, fxref); 949c6f9240SPeter Wemm return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0; 959c6f9240SPeter Wemm } 969c6f9240SPeter Wemm 979c6f9240SPeter Wemm static int 989c6f9240SPeter Wemm record_buf(const void *buf, int size) 999c6f9240SPeter Wemm { 1009c6f9240SPeter Wemm if (MAXRECSIZE - recpos < size) 1019c6f9240SPeter Wemm errx(1, "record buffer overflow"); 1029c6f9240SPeter Wemm memcpy(recbuf + recpos, buf, size); 1039c6f9240SPeter Wemm recpos += size; 1049c6f9240SPeter Wemm return 0; 1059c6f9240SPeter Wemm } 1069c6f9240SPeter Wemm 1079cb138bbSLuigi Rizzo /* 1089cb138bbSLuigi Rizzo * An int is stored in host order and aligned 1099cb138bbSLuigi Rizzo */ 1109c6f9240SPeter Wemm static int 1119c6f9240SPeter Wemm record_int(int val) 1129c6f9240SPeter Wemm { 1139c6f9240SPeter Wemm intalign(); 1149c6f9240SPeter Wemm return record_buf(&val, sizeof(val)); 1159c6f9240SPeter Wemm } 1169c6f9240SPeter Wemm 1179cb138bbSLuigi Rizzo /* 1189cb138bbSLuigi Rizzo * A string is stored as 1-byte length plus data, no padding 1199cb138bbSLuigi Rizzo */ 1209c6f9240SPeter Wemm static int 1219c6f9240SPeter Wemm record_string(const char *str) 1229c6f9240SPeter Wemm { 1239cb138bbSLuigi Rizzo int len, error; 1249cb138bbSLuigi Rizzo u_char val; 1259c6f9240SPeter Wemm 1269c6f9240SPeter Wemm if (dflag) 1279c6f9240SPeter Wemm return 0; 1289cb138bbSLuigi Rizzo val = len = strlen(str); 1299cb138bbSLuigi Rizzo if (len > 255) 1309cb138bbSLuigi Rizzo errx(1, "string %s too long", str); 1319cb138bbSLuigi Rizzo error = record_buf(&val, sizeof(val)); 1329c6f9240SPeter Wemm if (error) 1339c6f9240SPeter Wemm return error; 1349c6f9240SPeter Wemm return record_buf(str, len); 1359c6f9240SPeter Wemm } 1369c6f9240SPeter Wemm 1379c6f9240SPeter Wemm static int 1389c6f9240SPeter Wemm parse_entry(struct mod_metadata *md, const char *cval, 1399c6f9240SPeter Wemm struct elf_file *ef, const char *kldname) 1409c6f9240SPeter Wemm { 1419c6f9240SPeter Wemm struct mod_depend mdp; 1429c6f9240SPeter Wemm struct mod_version mdv; 1439c6f9240SPeter Wemm Elf_Off data = (Elf_Off)md->md_data; 1449c6f9240SPeter Wemm int error = 0; 1459c6f9240SPeter Wemm 1469c6f9240SPeter Wemm record_start(); 1479c6f9240SPeter Wemm switch (md->md_type) { 1489c6f9240SPeter Wemm case MDT_DEPEND: 1499c6f9240SPeter Wemm if (!dflag) 1509c6f9240SPeter Wemm break; 1519772dc2aSIan Dowse check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); 1529c6f9240SPeter Wemm printf(" depends on %s.%d (%d,%d)\n", cval, 1539c6f9240SPeter Wemm mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); 1549c6f9240SPeter Wemm break; 1559c6f9240SPeter Wemm case MDT_VERSION: 1569772dc2aSIan Dowse check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); 1579cb138bbSLuigi Rizzo if (dflag) { 1589cb138bbSLuigi Rizzo printf(" interface %s.%d\n", cval, mdv.mv_version); 1599cb138bbSLuigi Rizzo } else { 1609c6f9240SPeter Wemm record_int(MDT_VERSION); 1619c6f9240SPeter Wemm record_string(cval); 1629c6f9240SPeter Wemm record_int(mdv.mv_version); 1639c6f9240SPeter Wemm record_string(kldname); 1649cb138bbSLuigi Rizzo } 1659c6f9240SPeter Wemm break; 1669c6f9240SPeter Wemm case MDT_MODULE: 1679cb138bbSLuigi Rizzo if (dflag) { 1689cb138bbSLuigi Rizzo printf(" module %s\n", cval); 1699cb138bbSLuigi Rizzo } else { 1709c6f9240SPeter Wemm record_int(MDT_MODULE); 1719c6f9240SPeter Wemm record_string(cval); 1729c6f9240SPeter Wemm record_string(kldname); 1739cb138bbSLuigi Rizzo } 1749c6f9240SPeter Wemm break; 175*b03747e9SWarner Losh case MDT_PNP_INFO: 176*b03747e9SWarner Losh if (dflag) { 177*b03747e9SWarner Losh printf(" pnp info for bus %s\n", cval); 178*b03747e9SWarner Losh } 1799c6f9240SPeter Wemm default: 1809f5529b4SRuslan Ermilov warnx("unknown metadata record %d in file %s", md->md_type, kldname); 1819c6f9240SPeter Wemm } 1829c6f9240SPeter Wemm if (!error) 1839c6f9240SPeter Wemm record_end(); 1849c6f9240SPeter Wemm return error; 1859c6f9240SPeter Wemm } 1869c6f9240SPeter Wemm 1879c6f9240SPeter Wemm static int 1889c6f9240SPeter Wemm read_kld(char *filename, char *kldname) 1899c6f9240SPeter Wemm { 1909c6f9240SPeter Wemm struct mod_metadata md; 1919c6f9240SPeter Wemm struct elf_file ef; 1929c6f9240SPeter Wemm void **p, **orgp; 1939772dc2aSIan Dowse int error, eftype, nmlen; 1949c6f9240SPeter Wemm long start, finish, entries; 1959c6f9240SPeter Wemm char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp; 1969c6f9240SPeter Wemm 1979c6f9240SPeter Wemm if (verbose || dflag) 1989c6f9240SPeter Wemm printf("%s\n", filename); 1999c6f9240SPeter Wemm error = ef_open(filename, &ef, verbose); 2009772dc2aSIan Dowse if (error) { 2014a8b7e33SIan Dowse error = ef_obj_open(filename, &ef, verbose); 2024a8b7e33SIan Dowse if (error) { 2039772dc2aSIan Dowse if (verbose) 2049772dc2aSIan Dowse warnc(error, "elf_open(%s)", filename); 2059c6f9240SPeter Wemm return error; 2069772dc2aSIan Dowse } 2074a8b7e33SIan Dowse } 2089772dc2aSIan Dowse eftype = EF_GET_TYPE(&ef); 2099772dc2aSIan Dowse if (eftype != EFT_KLD && eftype != EFT_KERNEL) { 2109772dc2aSIan Dowse EF_CLOSE(&ef); 2119c6f9240SPeter Wemm return 0; 2129c6f9240SPeter Wemm } 2139c6f9240SPeter Wemm if (!dflag) { 2149c6f9240SPeter Wemm cp = strrchr(kldname, '.'); 2159cb138bbSLuigi Rizzo nmlen = (cp != NULL) ? cp - kldname : (int)strlen(kldname); 2169cb138bbSLuigi Rizzo if (nmlen > MAXMODNAME) 2179cb138bbSLuigi Rizzo nmlen = MAXMODNAME; 2182fdfd0feSWarner Losh strlcpy(kldmodname, kldname, nmlen); 2199c6f9240SPeter Wemm /* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/ 2209c6f9240SPeter Wemm } 2219c6f9240SPeter Wemm do { 2229772dc2aSIan Dowse check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, 2239772dc2aSIan Dowse &entries)); 2249772dc2aSIan Dowse check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, 22576798703SDag-Erling Smørgrav (void *)&p)); 2269c6f9240SPeter Wemm orgp = p; 2279c6f9240SPeter Wemm while(entries--) { 2289772dc2aSIan Dowse check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md), 2299772dc2aSIan Dowse &md)); 2309c6f9240SPeter Wemm p++; 2319772dc2aSIan Dowse check(EF_SEG_READ(&ef, (Elf_Off)md.md_cval, 2329772dc2aSIan Dowse sizeof(cval), cval)); 2339c6f9240SPeter Wemm cval[MAXMODNAME] = '\0'; 2349c6f9240SPeter Wemm parse_entry(&md, cval, &ef, kldname); 2359c6f9240SPeter Wemm } 2369c6f9240SPeter Wemm if (error) 2379c6f9240SPeter Wemm warnc(error, "error while reading %s", filename); 2389c6f9240SPeter Wemm free(orgp); 2399c6f9240SPeter Wemm } while(0); 2409772dc2aSIan Dowse EF_CLOSE(&ef); 2419c6f9240SPeter Wemm return error; 2429c6f9240SPeter Wemm } 2439c6f9240SPeter Wemm 2449cb138bbSLuigi Rizzo /* 2459cb138bbSLuigi Rizzo * Create a temp file in directory root, make sure we don't 2469cb138bbSLuigi Rizzo * overflow the buffer for the destination name 2479cb138bbSLuigi Rizzo */ 2489cb138bbSLuigi Rizzo static FILE * 2499c6f9240SPeter Wemm maketempfile(char *dest, const char *root) 2509c6f9240SPeter Wemm { 2519c6f9240SPeter Wemm char *p; 2529cb138bbSLuigi Rizzo int n, fd; 2539c6f9240SPeter Wemm 2549cb138bbSLuigi Rizzo p = strrchr(root, '/'); 2559cb138bbSLuigi Rizzo n = p != NULL ? p - root + 1 : 0; 2569cb138bbSLuigi Rizzo if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= 2579cb138bbSLuigi Rizzo MAXPATHLEN) { 2589cb138bbSLuigi Rizzo errno = ENAMETOOLONG; 2599cb138bbSLuigi Rizzo return NULL; 2609cb138bbSLuigi Rizzo } 2619c6f9240SPeter Wemm 2629ceddbd5SMarcel Moolenaar fd = mkstemp(dest); 2639cb138bbSLuigi Rizzo if (fd < 0) 2649cb138bbSLuigi Rizzo return NULL; 265ddce5818SLuigi Rizzo fchmod(fd, 0644); /* nothing secret in the file */ 2669cb138bbSLuigi Rizzo return fdopen(fd, "w+"); 2679c6f9240SPeter Wemm } 2689c6f9240SPeter Wemm 2699c6f9240SPeter Wemm static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; 2709c6f9240SPeter Wemm 2719cb138bbSLuigi Rizzo static void 2729cb138bbSLuigi Rizzo usage(void) 2739cb138bbSLuigi Rizzo { 2749cb138bbSLuigi Rizzo 2759cb138bbSLuigi Rizzo fprintf(stderr, "%s\n", 2769cb138bbSLuigi Rizzo "usage: kldxref [-Rdv] [-f hintsfile] path ..." 2779cb138bbSLuigi Rizzo ); 2789cb138bbSLuigi Rizzo exit(1); 2799cb138bbSLuigi Rizzo } 2809cb138bbSLuigi Rizzo 2815d452ceaSJilles Tjoelker static int 2826d9cb20bSJilles Tjoelker compare(const FTSENT *const *a, const FTSENT *const *b) 2836d9cb20bSJilles Tjoelker { 2846d9cb20bSJilles Tjoelker if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D) 2856d9cb20bSJilles Tjoelker return 1; 2866d9cb20bSJilles Tjoelker if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D) 2876d9cb20bSJilles Tjoelker return -1; 2886d9cb20bSJilles Tjoelker return strcmp((*a)->fts_name, (*b)->fts_name); 2896d9cb20bSJilles Tjoelker } 2906d9cb20bSJilles Tjoelker 2916d9cb20bSJilles Tjoelker int 2929c6f9240SPeter Wemm main(int argc, char *argv[]) 2939c6f9240SPeter Wemm { 2949c6f9240SPeter Wemm FTS *ftsp; 2959c6f9240SPeter Wemm FTSENT *p; 2969c6f9240SPeter Wemm int opt, fts_options, ival; 297b7abc67eSMaxim Sobolev struct stat sb; 2989c6f9240SPeter Wemm 2999c6f9240SPeter Wemm fts_options = FTS_PHYSICAL; 3009c6f9240SPeter Wemm 3019c6f9240SPeter Wemm while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { 3029c6f9240SPeter Wemm switch (opt) { 3039cb138bbSLuigi Rizzo case 'd': /* no hint file, only print on stdout */ 3049c6f9240SPeter Wemm dflag = 1; 3059c6f9240SPeter Wemm break; 3069cb138bbSLuigi Rizzo case 'f': /* use this name instead of linker.hints */ 3079c6f9240SPeter Wemm xref_file = optarg; 3089c6f9240SPeter Wemm break; 3099c6f9240SPeter Wemm case 'v': 3109c6f9240SPeter Wemm verbose++; 3119c6f9240SPeter Wemm break; 3129cb138bbSLuigi Rizzo case 'R': /* recurse on directories */ 3139c6f9240SPeter Wemm fts_options |= FTS_COMFOLLOW; 3149c6f9240SPeter Wemm break; 3159c6f9240SPeter Wemm default: 3169c6f9240SPeter Wemm usage(); 3179c6f9240SPeter Wemm /* NOTREACHED */ 3189c6f9240SPeter Wemm } 3199c6f9240SPeter Wemm } 3209c6f9240SPeter Wemm if (argc - optind < 1) 3219c6f9240SPeter Wemm usage(); 3229c6f9240SPeter Wemm argc -= optind; 3239c6f9240SPeter Wemm argv += optind; 3249c6f9240SPeter Wemm 325b7abc67eSMaxim Sobolev if (stat(argv[0], &sb) != 0) 326b7abc67eSMaxim Sobolev err(1, "%s", argv[0]); 327b7abc67eSMaxim Sobolev if ((sb.st_mode & S_IFDIR) == 0) { 328b7abc67eSMaxim Sobolev errno = ENOTDIR; 329b7abc67eSMaxim Sobolev err(1, "%s", argv[0]); 330b7abc67eSMaxim Sobolev } 331b7abc67eSMaxim Sobolev 3326d9cb20bSJilles Tjoelker ftsp = fts_open(argv, fts_options, compare); 3339c6f9240SPeter Wemm if (ftsp == NULL) 3349c6f9240SPeter Wemm exit(1); 3359c6f9240SPeter Wemm 3369c6f9240SPeter Wemm for (;;) { 3379c6f9240SPeter Wemm p = fts_read(ftsp); 3389cb138bbSLuigi Rizzo if ((p == NULL || p->fts_info == FTS_D) && fxref) { 3399cb138bbSLuigi Rizzo /* close and rename the current hint file */ 3409c6f9240SPeter Wemm fclose(fxref); 3419ceddbd5SMarcel Moolenaar fxref = NULL; 3429c6f9240SPeter Wemm if (reccnt) { 3439c6f9240SPeter Wemm rename(tempname, xrefname); 3449c6f9240SPeter Wemm } else { 3459cb138bbSLuigi Rizzo /* didn't find any entry, ignore this file */ 3469c6f9240SPeter Wemm unlink(tempname); 3479c6f9240SPeter Wemm unlink(xrefname); 3489c6f9240SPeter Wemm } 3499c6f9240SPeter Wemm } 3509c6f9240SPeter Wemm if (p == NULL) 3519c6f9240SPeter Wemm break; 3529cb138bbSLuigi Rizzo if (p->fts_info == FTS_D && !dflag) { 3539cb138bbSLuigi Rizzo /* visiting a new directory, create a new hint file */ 3549c6f9240SPeter Wemm snprintf(xrefname, sizeof(xrefname), "%s/%s", 3559c6f9240SPeter Wemm ftsp->fts_path, xref_file); 3569ceddbd5SMarcel Moolenaar fxref = maketempfile(tempname, ftsp->fts_path); 3579c6f9240SPeter Wemm if (fxref == NULL) 3589c6f9240SPeter Wemm err(1, "can't create %s", tempname); 3599c6f9240SPeter Wemm ival = 1; 3609c6f9240SPeter Wemm fwrite(&ival, sizeof(ival), 1, fxref); 3619c6f9240SPeter Wemm reccnt = 0; 3629c6f9240SPeter Wemm } 3639cb138bbSLuigi Rizzo /* skip non-files or .symbols entries */ 3649c6f9240SPeter Wemm if (p->fts_info != FTS_F) 3659c6f9240SPeter Wemm continue; 3669f5529b4SRuslan Ermilov if (p->fts_namelen >= 8 && 3679f5529b4SRuslan Ermilov strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0) 3689f5529b4SRuslan Ermilov continue; 3699c6f9240SPeter Wemm read_kld(p->fts_path, p->fts_name); 3709c6f9240SPeter Wemm } 3719c6f9240SPeter Wemm fts_close(ftsp); 3729c6f9240SPeter Wemm return 0; 3739c6f9240SPeter Wemm } 374