183322Speter/* 283322Speter * Copyright (c) 2000, Boris Popov 383322Speter * All rights reserved. 483322Speter * 583322Speter * Redistribution and use in source and binary forms, with or without 683322Speter * modification, are permitted provided that the following conditions 783322Speter * are met: 883322Speter * 1. Redistributions of source code must retain the above copyright 983322Speter * notice, this list of conditions and the following disclaimer. 1083322Speter * 2. Redistributions in binary form must reproduce the above copyright 1183322Speter * notice, this list of conditions and the following disclaimer in the 1283322Speter * documentation and/or other materials provided with the distribution. 1383322Speter * 3. All advertising materials mentioning features or use of this software 1483322Speter * must display the following acknowledgement: 1583322Speter * This product includes software developed by Boris Popov. 1683322Speter * 4. Neither the name of the author nor the names of any co-contributors 1783322Speter * may be used to endorse or promote products derived from this software 1883322Speter * without specific prior written permission. 1983322Speter * 2083322Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2183322Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2283322Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2383322Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2483322Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2583322Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2683322Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2783322Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2883322Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2983322Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3083322Speter * SUCH DAMAGE. 3183322Speter * 3283322Speter * $FreeBSD$ 3383322Speter */ 3483322Speter 35128742Ssobomax#include <sys/types.h> 3683322Speter#include <sys/param.h> 3783322Speter#include <sys/exec.h> 3883322Speter#include <sys/queue.h> 3983322Speter#include <sys/kernel.h> 4083322Speter#include <sys/reboot.h> 4183322Speter#include <sys/linker.h> 4283322Speter#include <sys/stat.h> 4383322Speter#include <sys/module.h> 4483322Speter#define FREEBSD_ELF 4583322Speter#include <err.h> 4683322Speter#include <fts.h> 4783322Speter#include <string.h> 4883322Speter#include <machine/elf.h> 4983322Speter#include <stdio.h> 5083322Speter#include <stdlib.h> 5183322Speter#include <unistd.h> 5283322Speter#include <errno.h> 5383322Speter 5483322Speter#include "ef.h" 5583322Speter 56298492Semaste#define MAXRECSIZE 8192 5783322Speter#define check(val) if ((error = (val)) != 0) break 5883322Speter 59186827Sluigistatic int dflag; /* do not create a hint file, only write on stdout */ 60186827Sluigistatic int verbose; 6183322Speter 62186827Sluigistatic FILE *fxref; /* current hints file */ 6383322Speter 6487551Smikehstatic const char *xref_file = "linker.hints"; 6583322Speter 66186827Sluigi/* 67186827Sluigi * A record is stored in the static buffer recbuf before going to disk. 68186827Sluigi */ 6983322Speterstatic char recbuf[MAXRECSIZE]; 70186827Sluigistatic int recpos; /* current write position */ 71186827Sluigistatic int reccnt; /* total record written to this file so far */ 7283322Speter 7383322Speterstatic void 7483322Speterintalign(void) 7583322Speter{ 7683322Speter recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1); 7783322Speter} 7883322Speter 7983322Speterstatic void 8083322Speterrecord_start(void) 8183322Speter{ 8283322Speter recpos = 0; 8383322Speter memset(recbuf, 0, MAXRECSIZE); 8483322Speter} 8583322Speter 8683322Speterstatic int 8783322Speterrecord_end(void) 8883322Speter{ 89186827Sluigi if (recpos == 0) 9083322Speter return 0; 9183322Speter reccnt++; 9283322Speter intalign(); 9383322Speter fwrite(&recpos, sizeof(recpos), 1, fxref); 9483322Speter return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0; 9583322Speter} 9683322Speter 9783322Speterstatic int 9883322Speterrecord_buf(const void *buf, int size) 9983322Speter{ 10083322Speter if (MAXRECSIZE - recpos < size) 10183322Speter errx(1, "record buffer overflow"); 10283322Speter memcpy(recbuf + recpos, buf, size); 10383322Speter recpos += size; 10483322Speter return 0; 10583322Speter} 10683322Speter 107186827Sluigi/* 108186827Sluigi * An int is stored in host order and aligned 109186827Sluigi */ 11083322Speterstatic int 11183322Speterrecord_int(int val) 11283322Speter{ 11383322Speter intalign(); 11483322Speter return record_buf(&val, sizeof(val)); 11583322Speter} 11683322Speter 117186827Sluigi/* 118186827Sluigi * A string is stored as 1-byte length plus data, no padding 119186827Sluigi */ 12083322Speterstatic int 12183322Speterrecord_string(const char *str) 12283322Speter{ 123186827Sluigi int len, error; 124186827Sluigi u_char val; 125186827Sluigi 12683322Speter if (dflag) 12783322Speter return 0; 128186827Sluigi val = len = strlen(str); 129186827Sluigi if (len > 255) 130186827Sluigi errx(1, "string %s too long", str); 131186827Sluigi error = record_buf(&val, sizeof(val)); 13283322Speter if (error) 13383322Speter return error; 13483322Speter return record_buf(str, len); 13583322Speter} 13683322Speter 13783322Speterstatic int 13883322Speterparse_entry(struct mod_metadata *md, const char *cval, 13983322Speter struct elf_file *ef, const char *kldname) 14083322Speter{ 14183322Speter struct mod_depend mdp; 14283322Speter struct mod_version mdv; 14383322Speter Elf_Off data = (Elf_Off)md->md_data; 14483322Speter int error = 0; 14583322Speter 14683322Speter record_start(); 14783322Speter switch (md->md_type) { 14883322Speter case MDT_DEPEND: 14983322Speter if (!dflag) 15083322Speter break; 151134358Siedowse check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); 15283322Speter printf(" depends on %s.%d (%d,%d)\n", cval, 15383322Speter mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); 15483322Speter break; 15583322Speter case MDT_VERSION: 156134358Siedowse check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); 157186827Sluigi if (dflag) { 158186827Sluigi printf(" interface %s.%d\n", cval, mdv.mv_version); 159186827Sluigi } else { 160186827Sluigi record_int(MDT_VERSION); 161186827Sluigi record_string(cval); 162186827Sluigi record_int(mdv.mv_version); 163186827Sluigi record_string(kldname); 164186827Sluigi } 16583322Speter break; 16683322Speter case MDT_MODULE: 167186827Sluigi if (dflag) { 168186827Sluigi printf(" module %s\n", cval); 169186827Sluigi } else { 170186827Sluigi record_int(MDT_MODULE); 171186827Sluigi record_string(cval); 172186827Sluigi record_string(kldname); 173186827Sluigi } 17483322Speter break; 175298493Semaste case MDT_PNP_INFO: 176298493Semaste if (dflag) { 177298493Semaste printf(" pnp info for bus %s\n", cval); 178298493Semaste } 17983322Speter default: 180152302Sru warnx("unknown metadata record %d in file %s", md->md_type, kldname); 18183322Speter } 18283322Speter if (!error) 18383322Speter record_end(); 18483322Speter return error; 18583322Speter} 18683322Speter 18783322Speterstatic int 18883322Speterread_kld(char *filename, char *kldname) 18983322Speter{ 19083322Speter struct mod_metadata md; 19183322Speter struct elf_file ef; 19283322Speter void **p, **orgp; 193134358Siedowse int error, eftype, nmlen; 19483322Speter long start, finish, entries; 19583322Speter char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp; 19683322Speter 19783322Speter if (verbose || dflag) 19883322Speter printf("%s\n", filename); 19983322Speter error = ef_open(filename, &ef, verbose); 200134358Siedowse if (error) { 201134361Siedowse error = ef_obj_open(filename, &ef, verbose); 202134361Siedowse if (error) { 203134361Siedowse if (verbose) 204134361Siedowse warnc(error, "elf_open(%s)", filename); 205134361Siedowse return error; 206134361Siedowse } 207134358Siedowse } 208134358Siedowse eftype = EF_GET_TYPE(&ef); 209134358Siedowse if (eftype != EFT_KLD && eftype != EFT_KERNEL) { 210134358Siedowse EF_CLOSE(&ef); 21183322Speter return 0; 21283322Speter } 21383322Speter if (!dflag) { 21483322Speter cp = strrchr(kldname, '.'); 215186827Sluigi nmlen = (cp != NULL) ? cp - kldname : (int)strlen(kldname); 216186827Sluigi if (nmlen > MAXMODNAME) 217186827Sluigi nmlen = MAXMODNAME; 218161004Simp strlcpy(kldmodname, kldname, nmlen); 21983322Speter/* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/ 22083322Speter } 22183322Speter do { 222134358Siedowse check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, 223134358Siedowse &entries)); 224134358Siedowse check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, 225127029Sdes (void *)&p)); 22683322Speter orgp = p; 22783322Speter while(entries--) { 228134358Siedowse check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md), 229134358Siedowse &md)); 23083322Speter p++; 231134358Siedowse check(EF_SEG_READ(&ef, (Elf_Off)md.md_cval, 232134358Siedowse sizeof(cval), cval)); 23383322Speter cval[MAXMODNAME] = '\0'; 23483322Speter parse_entry(&md, cval, &ef, kldname); 23583322Speter } 23683322Speter if (error) 23783322Speter warnc(error, "error while reading %s", filename); 23883322Speter free(orgp); 23983322Speter } while(0); 240134358Siedowse EF_CLOSE(&ef); 24183322Speter return error; 24283322Speter} 24383322Speter 244186827Sluigi/* 245186827Sluigi * Create a temp file in directory root, make sure we don't 246186827Sluigi * overflow the buffer for the destination name 247186827Sluigi */ 248186827Sluigistatic FILE * 24983322Spetermaketempfile(char *dest, const char *root) 25083322Speter{ 25183322Speter char *p; 252186827Sluigi int n, fd; 25383322Speter 254186827Sluigi p = strrchr(root, '/'); 255186827Sluigi n = p != NULL ? p - root + 1 : 0; 256186827Sluigi if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= 257186827Sluigi MAXPATHLEN) { 258186827Sluigi errno = ENAMETOOLONG; 259186827Sluigi return NULL; 260186827Sluigi } 26183322Speter 262160818Smarcel fd = mkstemp(dest); 263186827Sluigi if (fd < 0) 264186827Sluigi return NULL; 265186827Sluigi fchmod(fd, 0644); /* nothing secret in the file */ 266186827Sluigi return fdopen(fd, "w+"); 26783322Speter} 26883322Speter 26983322Speterstatic char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; 27083322Speter 271186827Sluigistatic void 272186827Sluigiusage(void) 273186827Sluigi{ 274186827Sluigi 275186827Sluigi fprintf(stderr, "%s\n", 276186827Sluigi "usage: kldxref [-Rdv] [-f hintsfile] path ..." 277186827Sluigi ); 278186827Sluigi exit(1); 279186827Sluigi} 280186827Sluigi 281259751Sjillesstatic int 282256060Sjillescompare(const FTSENT *const *a, const FTSENT *const *b) 283256060Sjilles{ 284256060Sjilles if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D) 285256060Sjilles return 1; 286256060Sjilles if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D) 287256060Sjilles return -1; 288256060Sjilles return strcmp((*a)->fts_name, (*b)->fts_name); 289256060Sjilles} 290256060Sjilles 29183322Speterint 29283322Spetermain(int argc, char *argv[]) 29383322Speter{ 29483322Speter FTS *ftsp; 29583322Speter FTSENT *p; 29683322Speter int opt, fts_options, ival; 297128742Ssobomax struct stat sb; 29883322Speter 29983322Speter fts_options = FTS_PHYSICAL; 30083322Speter 30183322Speter while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { 30283322Speter switch (opt) { 303186827Sluigi case 'd': /* no hint file, only print on stdout */ 30483322Speter dflag = 1; 30583322Speter break; 306186827Sluigi case 'f': /* use this name instead of linker.hints */ 30783322Speter xref_file = optarg; 30883322Speter break; 30983322Speter case 'v': 31083322Speter verbose++; 31183322Speter break; 312186827Sluigi case 'R': /* recurse on directories */ 31383322Speter fts_options |= FTS_COMFOLLOW; 31483322Speter break; 31583322Speter default: 31683322Speter usage(); 31783322Speter /* NOTREACHED */ 31883322Speter } 31983322Speter } 32083322Speter if (argc - optind < 1) 32183322Speter usage(); 32283322Speter argc -= optind; 32383322Speter argv += optind; 32483322Speter 325128742Ssobomax if (stat(argv[0], &sb) != 0) 326128742Ssobomax err(1, "%s", argv[0]); 327128742Ssobomax if ((sb.st_mode & S_IFDIR) == 0) { 328128742Ssobomax errno = ENOTDIR; 329128742Ssobomax err(1, "%s", argv[0]); 330128742Ssobomax } 331128742Ssobomax 332256060Sjilles ftsp = fts_open(argv, fts_options, compare); 33383322Speter if (ftsp == NULL) 33483322Speter exit(1); 33583322Speter 33683322Speter for (;;) { 33783322Speter p = fts_read(ftsp); 338186827Sluigi if ((p == NULL || p->fts_info == FTS_D) && fxref) { 339186827Sluigi /* close and rename the current hint file */ 34083322Speter fclose(fxref); 341160818Smarcel fxref = NULL; 34283322Speter if (reccnt) { 34383322Speter rename(tempname, xrefname); 34483322Speter } else { 345186827Sluigi /* didn't find any entry, ignore this file */ 34683322Speter unlink(tempname); 34783322Speter unlink(xrefname); 34883322Speter } 34983322Speter } 35083322Speter if (p == NULL) 35183322Speter break; 352186827Sluigi if (p->fts_info == FTS_D && !dflag) { 353186827Sluigi /* visiting a new directory, create a new hint file */ 35483322Speter snprintf(xrefname, sizeof(xrefname), "%s/%s", 35583322Speter ftsp->fts_path, xref_file); 356160818Smarcel fxref = maketempfile(tempname, ftsp->fts_path); 35783322Speter if (fxref == NULL) 35883322Speter err(1, "can't create %s", tempname); 35983322Speter ival = 1; 36083322Speter fwrite(&ival, sizeof(ival), 1, fxref); 36183322Speter reccnt = 0; 36283322Speter } 363298494Semaste /* skip non-files and separate debug files */ 36483322Speter if (p->fts_info != FTS_F) 36583322Speter continue; 366298494Semaste if (p->fts_namelen >= 6 && 367298494Semaste strcmp(p->fts_name + p->fts_namelen - 6, ".debug") == 0) 368298494Semaste continue; 369152302Sru if (p->fts_namelen >= 8 && 370152302Sru strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0) 371152302Sru continue; 37283322Speter read_kld(p->fts_path, p->fts_name); 37383322Speter } 37483322Speter fts_close(ftsp); 37583322Speter return 0; 37683322Speter} 377