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 <link.h> 469c6f9240SPeter Wemm #include <err.h> 479c6f9240SPeter Wemm #include <fts.h> 489c6f9240SPeter Wemm #include <string.h> 499c6f9240SPeter Wemm #include <machine/elf.h> 509c6f9240SPeter Wemm #include <stdio.h> 519c6f9240SPeter Wemm #include <stdlib.h> 529c6f9240SPeter Wemm #include <unistd.h> 539c6f9240SPeter Wemm #include <errno.h> 549c6f9240SPeter Wemm 559c6f9240SPeter Wemm #include "ef.h" 569c6f9240SPeter Wemm 579c6f9240SPeter Wemm #define MAXRECSIZE 1024 589c6f9240SPeter Wemm #define check(val) if ((error = (val)) != 0) break 599c6f9240SPeter Wemm 60*9cb138bbSLuigi Rizzo static int dflag; /* do not create a hint file, only write on stdout */ 61*9cb138bbSLuigi Rizzo static int verbose; 629c6f9240SPeter Wemm 63*9cb138bbSLuigi Rizzo static FILE *fxref; /* current hints file */ 649c6f9240SPeter Wemm 6587e5cd7cSMike Heffner static const char *xref_file = "linker.hints"; 669c6f9240SPeter Wemm 67*9cb138bbSLuigi Rizzo /* 68*9cb138bbSLuigi Rizzo * A record is stored in the static buffer recbuf before going to disk. 69*9cb138bbSLuigi Rizzo */ 709c6f9240SPeter Wemm static char recbuf[MAXRECSIZE]; 71*9cb138bbSLuigi Rizzo static int recpos; /* current write position */ 72*9cb138bbSLuigi Rizzo static int reccnt; /* total record written to this file so far */ 739c6f9240SPeter Wemm 749c6f9240SPeter Wemm static void 759c6f9240SPeter Wemm intalign(void) 769c6f9240SPeter Wemm { 779c6f9240SPeter Wemm recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1); 789c6f9240SPeter Wemm } 799c6f9240SPeter Wemm 809c6f9240SPeter Wemm static void 819c6f9240SPeter Wemm record_start(void) 829c6f9240SPeter Wemm { 839c6f9240SPeter Wemm recpos = 0; 849c6f9240SPeter Wemm memset(recbuf, 0, MAXRECSIZE); 859c6f9240SPeter Wemm } 869c6f9240SPeter Wemm 879c6f9240SPeter Wemm static int 889c6f9240SPeter Wemm record_end(void) 899c6f9240SPeter Wemm { 90*9cb138bbSLuigi Rizzo if (recpos == 0) 919c6f9240SPeter Wemm return 0; 929c6f9240SPeter Wemm reccnt++; 939c6f9240SPeter Wemm intalign(); 949c6f9240SPeter Wemm fwrite(&recpos, sizeof(recpos), 1, fxref); 959c6f9240SPeter Wemm return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0; 969c6f9240SPeter Wemm } 979c6f9240SPeter Wemm 989c6f9240SPeter Wemm static int 999c6f9240SPeter Wemm record_buf(const void *buf, int size) 1009c6f9240SPeter Wemm { 1019c6f9240SPeter Wemm if (MAXRECSIZE - recpos < size) 1029c6f9240SPeter Wemm errx(1, "record buffer overflow"); 1039c6f9240SPeter Wemm memcpy(recbuf + recpos, buf, size); 1049c6f9240SPeter Wemm recpos += size; 1059c6f9240SPeter Wemm return 0; 1069c6f9240SPeter Wemm } 1079c6f9240SPeter Wemm 108*9cb138bbSLuigi Rizzo /* 109*9cb138bbSLuigi Rizzo * An int is stored in host order and aligned 110*9cb138bbSLuigi Rizzo */ 1119c6f9240SPeter Wemm static int 1129c6f9240SPeter Wemm record_int(int val) 1139c6f9240SPeter Wemm { 1149c6f9240SPeter Wemm intalign(); 1159c6f9240SPeter Wemm return record_buf(&val, sizeof(val)); 1169c6f9240SPeter Wemm } 1179c6f9240SPeter Wemm 118*9cb138bbSLuigi Rizzo /* 119*9cb138bbSLuigi Rizzo * A string is stored as 1-byte length plus data, no padding 120*9cb138bbSLuigi Rizzo */ 1219c6f9240SPeter Wemm static int 1229c6f9240SPeter Wemm record_string(const char *str) 1239c6f9240SPeter Wemm { 124*9cb138bbSLuigi Rizzo int len, error; 125*9cb138bbSLuigi Rizzo u_char val; 1269c6f9240SPeter Wemm 1279c6f9240SPeter Wemm if (dflag) 1289c6f9240SPeter Wemm return 0; 129*9cb138bbSLuigi Rizzo val = len = strlen(str); 130*9cb138bbSLuigi Rizzo if (len > 255) 131*9cb138bbSLuigi Rizzo errx(1, "string %s too long", str); 132*9cb138bbSLuigi Rizzo error = record_buf(&val, sizeof(val)); 1339c6f9240SPeter Wemm if (error) 1349c6f9240SPeter Wemm return error; 1359c6f9240SPeter Wemm return record_buf(str, len); 1369c6f9240SPeter Wemm } 1379c6f9240SPeter Wemm 1389c6f9240SPeter Wemm static int 1399c6f9240SPeter Wemm parse_entry(struct mod_metadata *md, const char *cval, 1409c6f9240SPeter Wemm struct elf_file *ef, const char *kldname) 1419c6f9240SPeter Wemm { 1429c6f9240SPeter Wemm struct mod_depend mdp; 1439c6f9240SPeter Wemm struct mod_version mdv; 1449c6f9240SPeter Wemm Elf_Off data = (Elf_Off)md->md_data; 1459c6f9240SPeter Wemm int error = 0; 1469c6f9240SPeter Wemm 1479c6f9240SPeter Wemm record_start(); 1489c6f9240SPeter Wemm switch (md->md_type) { 1499c6f9240SPeter Wemm case MDT_DEPEND: 1509c6f9240SPeter Wemm if (!dflag) 1519c6f9240SPeter Wemm break; 1529772dc2aSIan Dowse check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); 1539c6f9240SPeter Wemm printf(" depends on %s.%d (%d,%d)\n", cval, 1549c6f9240SPeter Wemm mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); 1559c6f9240SPeter Wemm break; 1569c6f9240SPeter Wemm case MDT_VERSION: 1579772dc2aSIan Dowse check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); 158*9cb138bbSLuigi Rizzo if (dflag) { 159*9cb138bbSLuigi Rizzo printf(" interface %s.%d\n", cval, mdv.mv_version); 160*9cb138bbSLuigi Rizzo } else { 1619c6f9240SPeter Wemm record_int(MDT_VERSION); 1629c6f9240SPeter Wemm record_string(cval); 1639c6f9240SPeter Wemm record_int(mdv.mv_version); 1649c6f9240SPeter Wemm record_string(kldname); 165*9cb138bbSLuigi Rizzo } 1669c6f9240SPeter Wemm break; 1679c6f9240SPeter Wemm case MDT_MODULE: 168*9cb138bbSLuigi Rizzo if (dflag) { 169*9cb138bbSLuigi Rizzo printf(" module %s\n", cval); 170*9cb138bbSLuigi Rizzo } else { 1719c6f9240SPeter Wemm record_int(MDT_MODULE); 1729c6f9240SPeter Wemm record_string(cval); 1739c6f9240SPeter Wemm record_string(kldname); 174*9cb138bbSLuigi Rizzo } 1759c6f9240SPeter Wemm break; 1769c6f9240SPeter Wemm default: 1779f5529b4SRuslan Ermilov warnx("unknown metadata record %d in file %s", md->md_type, kldname); 1789c6f9240SPeter Wemm } 1799c6f9240SPeter Wemm if (!error) 1809c6f9240SPeter Wemm record_end(); 1819c6f9240SPeter Wemm return error; 1829c6f9240SPeter Wemm } 1839c6f9240SPeter Wemm 1849c6f9240SPeter Wemm static int 1859c6f9240SPeter Wemm read_kld(char *filename, char *kldname) 1869c6f9240SPeter Wemm { 1879c6f9240SPeter Wemm struct mod_metadata md; 1889c6f9240SPeter Wemm struct elf_file ef; 1899c6f9240SPeter Wemm void **p, **orgp; 1909772dc2aSIan Dowse int error, eftype, nmlen; 1919c6f9240SPeter Wemm long start, finish, entries; 1929c6f9240SPeter Wemm char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp; 1939c6f9240SPeter Wemm 1949c6f9240SPeter Wemm if (verbose || dflag) 1959c6f9240SPeter Wemm printf("%s\n", filename); 1969c6f9240SPeter Wemm error = ef_open(filename, &ef, verbose); 1979772dc2aSIan Dowse if (error) { 1984a8b7e33SIan Dowse error = ef_obj_open(filename, &ef, verbose); 1994a8b7e33SIan Dowse if (error) { 2009772dc2aSIan Dowse if (verbose) 2019772dc2aSIan Dowse warnc(error, "elf_open(%s)", filename); 2029c6f9240SPeter Wemm return error; 2039772dc2aSIan Dowse } 2044a8b7e33SIan Dowse } 2059772dc2aSIan Dowse eftype = EF_GET_TYPE(&ef); 2069772dc2aSIan Dowse if (eftype != EFT_KLD && eftype != EFT_KERNEL) { 2079772dc2aSIan Dowse EF_CLOSE(&ef); 2089c6f9240SPeter Wemm return 0; 2099c6f9240SPeter Wemm } 2109c6f9240SPeter Wemm if (!dflag) { 2119c6f9240SPeter Wemm cp = strrchr(kldname, '.'); 212*9cb138bbSLuigi Rizzo nmlen = (cp != NULL) ? cp - kldname : (int)strlen(kldname); 213*9cb138bbSLuigi Rizzo if (nmlen > MAXMODNAME) 214*9cb138bbSLuigi Rizzo nmlen = MAXMODNAME; 2152fdfd0feSWarner Losh strlcpy(kldmodname, kldname, nmlen); 2169c6f9240SPeter Wemm /* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/ 2179c6f9240SPeter Wemm } 2189c6f9240SPeter Wemm do { 2199772dc2aSIan Dowse check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, 2209772dc2aSIan Dowse &entries)); 2219772dc2aSIan Dowse check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, 22276798703SDag-Erling Smørgrav (void *)&p)); 2239c6f9240SPeter Wemm orgp = p; 2249c6f9240SPeter Wemm while(entries--) { 2259772dc2aSIan Dowse check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md), 2269772dc2aSIan Dowse &md)); 2279c6f9240SPeter Wemm p++; 2289772dc2aSIan Dowse check(EF_SEG_READ(&ef, (Elf_Off)md.md_cval, 2299772dc2aSIan Dowse sizeof(cval), cval)); 2309c6f9240SPeter Wemm cval[MAXMODNAME] = '\0'; 2319c6f9240SPeter Wemm parse_entry(&md, cval, &ef, kldname); 2329c6f9240SPeter Wemm } 2339c6f9240SPeter Wemm if (error) 2349c6f9240SPeter Wemm warnc(error, "error while reading %s", filename); 2359c6f9240SPeter Wemm free(orgp); 2369c6f9240SPeter Wemm } while(0); 2379772dc2aSIan Dowse EF_CLOSE(&ef); 2389c6f9240SPeter Wemm return error; 2399c6f9240SPeter Wemm } 2409c6f9240SPeter Wemm 241*9cb138bbSLuigi Rizzo /* 242*9cb138bbSLuigi Rizzo * Create a temp file in directory root, make sure we don't 243*9cb138bbSLuigi Rizzo * overflow the buffer for the destination name 244*9cb138bbSLuigi Rizzo */ 245*9cb138bbSLuigi Rizzo static FILE * 2469c6f9240SPeter Wemm maketempfile(char *dest, const char *root) 2479c6f9240SPeter Wemm { 2489c6f9240SPeter Wemm char *p; 249*9cb138bbSLuigi Rizzo int n, fd; 2509c6f9240SPeter Wemm 251*9cb138bbSLuigi Rizzo p = strrchr(root, '/'); 252*9cb138bbSLuigi Rizzo n = p != NULL ? p - root + 1 : 0; 253*9cb138bbSLuigi Rizzo if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= 254*9cb138bbSLuigi Rizzo MAXPATHLEN) { 255*9cb138bbSLuigi Rizzo errno = ENAMETOOLONG; 256*9cb138bbSLuigi Rizzo return NULL; 257*9cb138bbSLuigi Rizzo } 2589c6f9240SPeter Wemm 2599ceddbd5SMarcel Moolenaar fd = mkstemp(dest); 260*9cb138bbSLuigi Rizzo if (fd < 0) 261*9cb138bbSLuigi Rizzo return NULL; 262ddce5818SLuigi Rizzo fchmod(fd, 0644); /* nothing secret in the file */ 263*9cb138bbSLuigi Rizzo return fdopen(fd, "w+"); 2649c6f9240SPeter Wemm } 2659c6f9240SPeter Wemm 2669c6f9240SPeter Wemm static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; 2679c6f9240SPeter Wemm 268*9cb138bbSLuigi Rizzo static void 269*9cb138bbSLuigi Rizzo usage(void) 270*9cb138bbSLuigi Rizzo { 271*9cb138bbSLuigi Rizzo 272*9cb138bbSLuigi Rizzo fprintf(stderr, "%s\n", 273*9cb138bbSLuigi Rizzo "usage: kldxref [-Rdv] [-f hintsfile] path ..." 274*9cb138bbSLuigi Rizzo ); 275*9cb138bbSLuigi Rizzo exit(1); 276*9cb138bbSLuigi Rizzo } 277*9cb138bbSLuigi Rizzo 2789c6f9240SPeter Wemm int 2799c6f9240SPeter Wemm main(int argc, char *argv[]) 2809c6f9240SPeter Wemm { 2819c6f9240SPeter Wemm FTS *ftsp; 2829c6f9240SPeter Wemm FTSENT *p; 2839c6f9240SPeter Wemm int opt, fts_options, ival; 284b7abc67eSMaxim Sobolev struct stat sb; 2859c6f9240SPeter Wemm 2869c6f9240SPeter Wemm fts_options = FTS_PHYSICAL; 2879c6f9240SPeter Wemm 2889c6f9240SPeter Wemm while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { 2899c6f9240SPeter Wemm switch (opt) { 290*9cb138bbSLuigi Rizzo case 'd': /* no hint file, only print on stdout */ 2919c6f9240SPeter Wemm dflag = 1; 2929c6f9240SPeter Wemm break; 293*9cb138bbSLuigi Rizzo case 'f': /* use this name instead of linker.hints */ 2949c6f9240SPeter Wemm xref_file = optarg; 2959c6f9240SPeter Wemm break; 2969c6f9240SPeter Wemm case 'v': 2979c6f9240SPeter Wemm verbose++; 2989c6f9240SPeter Wemm break; 299*9cb138bbSLuigi Rizzo case 'R': /* recurse on directories */ 3009c6f9240SPeter Wemm fts_options |= FTS_COMFOLLOW; 3019c6f9240SPeter Wemm break; 3029c6f9240SPeter Wemm default: 3039c6f9240SPeter Wemm usage(); 3049c6f9240SPeter Wemm /* NOTREACHED */ 3059c6f9240SPeter Wemm } 3069c6f9240SPeter Wemm } 3079c6f9240SPeter Wemm if (argc - optind < 1) 3089c6f9240SPeter Wemm usage(); 3099c6f9240SPeter Wemm argc -= optind; 3109c6f9240SPeter Wemm argv += optind; 3119c6f9240SPeter Wemm 312b7abc67eSMaxim Sobolev if (stat(argv[0], &sb) != 0) 313b7abc67eSMaxim Sobolev err(1, "%s", argv[0]); 314b7abc67eSMaxim Sobolev if ((sb.st_mode & S_IFDIR) == 0) { 315b7abc67eSMaxim Sobolev errno = ENOTDIR; 316b7abc67eSMaxim Sobolev err(1, "%s", argv[0]); 317b7abc67eSMaxim Sobolev } 318b7abc67eSMaxim Sobolev 3199c6f9240SPeter Wemm ftsp = fts_open(argv, fts_options, 0); 3209c6f9240SPeter Wemm if (ftsp == NULL) 3219c6f9240SPeter Wemm exit(1); 3229c6f9240SPeter Wemm 3239c6f9240SPeter Wemm for (;;) { 3249c6f9240SPeter Wemm p = fts_read(ftsp); 325*9cb138bbSLuigi Rizzo if ((p == NULL || p->fts_info == FTS_D) && fxref) { 326*9cb138bbSLuigi Rizzo /* close and rename the current hint file */ 3279c6f9240SPeter Wemm fclose(fxref); 3289ceddbd5SMarcel Moolenaar fxref = NULL; 3299c6f9240SPeter Wemm if (reccnt) { 3309c6f9240SPeter Wemm rename(tempname, xrefname); 3319c6f9240SPeter Wemm } else { 332*9cb138bbSLuigi Rizzo /* didn't find any entry, ignore this file */ 3339c6f9240SPeter Wemm unlink(tempname); 3349c6f9240SPeter Wemm unlink(xrefname); 3359c6f9240SPeter Wemm } 3369c6f9240SPeter Wemm } 3379c6f9240SPeter Wemm if (p == NULL) 3389c6f9240SPeter Wemm break; 339*9cb138bbSLuigi Rizzo if (p->fts_info == FTS_D && !dflag) { 340*9cb138bbSLuigi Rizzo /* visiting a new directory, create a new hint file */ 3419c6f9240SPeter Wemm snprintf(xrefname, sizeof(xrefname), "%s/%s", 3429c6f9240SPeter Wemm ftsp->fts_path, xref_file); 3439ceddbd5SMarcel Moolenaar fxref = maketempfile(tempname, ftsp->fts_path); 3449c6f9240SPeter Wemm if (fxref == NULL) 3459c6f9240SPeter Wemm err(1, "can't create %s", tempname); 3469c6f9240SPeter Wemm ival = 1; 3479c6f9240SPeter Wemm fwrite(&ival, sizeof(ival), 1, fxref); 3489c6f9240SPeter Wemm reccnt = 0; 3499c6f9240SPeter Wemm } 350*9cb138bbSLuigi Rizzo /* skip non-files or .symbols entries */ 3519c6f9240SPeter Wemm if (p->fts_info != FTS_F) 3529c6f9240SPeter Wemm continue; 3539f5529b4SRuslan Ermilov if (p->fts_namelen >= 8 && 3549f5529b4SRuslan Ermilov strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0) 3559f5529b4SRuslan Ermilov continue; 3569c6f9240SPeter Wemm read_kld(p->fts_path, p->fts_name); 3579c6f9240SPeter Wemm } 3589c6f9240SPeter Wemm fts_close(ftsp); 3599c6f9240SPeter Wemm return 0; 3609c6f9240SPeter Wemm } 361