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$ 33 */ 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/exec.h> 38 #include <sys/queue.h> 39 #include <sys/kernel.h> 40 #include <sys/reboot.h> 41 #include <sys/linker.h> 42 #include <sys/stat.h> 43 #include <sys/module.h> 44 #define FREEBSD_ELF 45 #include <link.h> 46 #include <err.h> 47 #include <fts.h> 48 #include <string.h> 49 #include <machine/elf.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <unistd.h> 53 #include <errno.h> 54 55 #include "ef.h" 56 57 #define MAXRECSIZE 1024 58 #define check(val) if ((error = (val)) != 0) break 59 60 static int dflag; /* do not create a hint file, only write on stdout */ 61 static int verbose; 62 63 static FILE *fxref; /* current hints file */ 64 65 static const char *xref_file = "linker.hints"; 66 67 /* 68 * A record is stored in the static buffer recbuf before going to disk. 69 */ 70 static char recbuf[MAXRECSIZE]; 71 static int recpos; /* current write position */ 72 static int reccnt; /* total record written to this file so far */ 73 74 static void 75 intalign(void) 76 { 77 recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1); 78 } 79 80 static void 81 record_start(void) 82 { 83 recpos = 0; 84 memset(recbuf, 0, MAXRECSIZE); 85 } 86 87 static int 88 record_end(void) 89 { 90 if (recpos == 0) 91 return 0; 92 reccnt++; 93 intalign(); 94 fwrite(&recpos, sizeof(recpos), 1, fxref); 95 return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0; 96 } 97 98 static int 99 record_buf(const void *buf, int size) 100 { 101 if (MAXRECSIZE - recpos < size) 102 errx(1, "record buffer overflow"); 103 memcpy(recbuf + recpos, buf, size); 104 recpos += size; 105 return 0; 106 } 107 108 /* 109 * An int is stored in host order and aligned 110 */ 111 static int 112 record_int(int val) 113 { 114 intalign(); 115 return record_buf(&val, sizeof(val)); 116 } 117 118 /* 119 * A string is stored as 1-byte length plus data, no padding 120 */ 121 static int 122 record_string(const char *str) 123 { 124 int len, error; 125 u_char val; 126 127 if (dflag) 128 return 0; 129 val = len = strlen(str); 130 if (len > 255) 131 errx(1, "string %s too long", str); 132 error = record_buf(&val, sizeof(val)); 133 if (error) 134 return error; 135 return record_buf(str, len); 136 } 137 138 static int 139 parse_entry(struct mod_metadata *md, const char *cval, 140 struct elf_file *ef, const char *kldname) 141 { 142 struct mod_depend mdp; 143 struct mod_version mdv; 144 Elf_Off data = (Elf_Off)md->md_data; 145 int error = 0; 146 147 record_start(); 148 switch (md->md_type) { 149 case MDT_DEPEND: 150 if (!dflag) 151 break; 152 check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); 153 printf(" depends on %s.%d (%d,%d)\n", cval, 154 mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); 155 break; 156 case MDT_VERSION: 157 check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); 158 if (dflag) { 159 printf(" interface %s.%d\n", cval, mdv.mv_version); 160 } else { 161 record_int(MDT_VERSION); 162 record_string(cval); 163 record_int(mdv.mv_version); 164 record_string(kldname); 165 } 166 break; 167 case MDT_MODULE: 168 if (dflag) { 169 printf(" module %s\n", cval); 170 } else { 171 record_int(MDT_MODULE); 172 record_string(cval); 173 record_string(kldname); 174 } 175 break; 176 default: 177 warnx("unknown metadata record %d in file %s", md->md_type, kldname); 178 } 179 if (!error) 180 record_end(); 181 return error; 182 } 183 184 static int 185 read_kld(char *filename, char *kldname) 186 { 187 struct mod_metadata md; 188 struct elf_file ef; 189 void **p, **orgp; 190 int error, eftype, nmlen; 191 long start, finish, entries; 192 char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp; 193 194 if (verbose || dflag) 195 printf("%s\n", filename); 196 error = ef_open(filename, &ef, verbose); 197 if (error) { 198 error = ef_obj_open(filename, &ef, verbose); 199 if (error) { 200 if (verbose) 201 warnc(error, "elf_open(%s)", filename); 202 return error; 203 } 204 } 205 eftype = EF_GET_TYPE(&ef); 206 if (eftype != EFT_KLD && eftype != EFT_KERNEL) { 207 EF_CLOSE(&ef); 208 return 0; 209 } 210 if (!dflag) { 211 cp = strrchr(kldname, '.'); 212 nmlen = (cp != NULL) ? cp - kldname : (int)strlen(kldname); 213 if (nmlen > MAXMODNAME) 214 nmlen = MAXMODNAME; 215 strlcpy(kldmodname, kldname, nmlen); 216 /* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/ 217 } 218 do { 219 check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, 220 &entries)); 221 check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, 222 (void *)&p)); 223 orgp = p; 224 while(entries--) { 225 check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md), 226 &md)); 227 p++; 228 check(EF_SEG_READ(&ef, (Elf_Off)md.md_cval, 229 sizeof(cval), cval)); 230 cval[MAXMODNAME] = '\0'; 231 parse_entry(&md, cval, &ef, kldname); 232 } 233 if (error) 234 warnc(error, "error while reading %s", filename); 235 free(orgp); 236 } while(0); 237 EF_CLOSE(&ef); 238 return error; 239 } 240 241 /* 242 * Create a temp file in directory root, make sure we don't 243 * overflow the buffer for the destination name 244 */ 245 static FILE * 246 maketempfile(char *dest, const char *root) 247 { 248 char *p; 249 int n, fd; 250 251 p = strrchr(root, '/'); 252 n = p != NULL ? p - root + 1 : 0; 253 if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= 254 MAXPATHLEN) { 255 errno = ENAMETOOLONG; 256 return NULL; 257 } 258 259 fd = mkstemp(dest); 260 if (fd < 0) 261 return NULL; 262 fchmod(fd, 0644); /* nothing secret in the file */ 263 return fdopen(fd, "w+"); 264 } 265 266 static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; 267 268 static void 269 usage(void) 270 { 271 272 fprintf(stderr, "%s\n", 273 "usage: kldxref [-Rdv] [-f hintsfile] path ..." 274 ); 275 exit(1); 276 } 277 278 int 279 main(int argc, char *argv[]) 280 { 281 FTS *ftsp; 282 FTSENT *p; 283 int opt, fts_options, ival; 284 struct stat sb; 285 286 fts_options = FTS_PHYSICAL; 287 288 while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { 289 switch (opt) { 290 case 'd': /* no hint file, only print on stdout */ 291 dflag = 1; 292 break; 293 case 'f': /* use this name instead of linker.hints */ 294 xref_file = optarg; 295 break; 296 case 'v': 297 verbose++; 298 break; 299 case 'R': /* recurse on directories */ 300 fts_options |= FTS_COMFOLLOW; 301 break; 302 default: 303 usage(); 304 /* NOTREACHED */ 305 } 306 } 307 if (argc - optind < 1) 308 usage(); 309 argc -= optind; 310 argv += optind; 311 312 if (stat(argv[0], &sb) != 0) 313 err(1, "%s", argv[0]); 314 if ((sb.st_mode & S_IFDIR) == 0) { 315 errno = ENOTDIR; 316 err(1, "%s", argv[0]); 317 } 318 319 ftsp = fts_open(argv, fts_options, 0); 320 if (ftsp == NULL) 321 exit(1); 322 323 for (;;) { 324 p = fts_read(ftsp); 325 if ((p == NULL || p->fts_info == FTS_D) && fxref) { 326 /* close and rename the current hint file */ 327 fclose(fxref); 328 fxref = NULL; 329 if (reccnt) { 330 rename(tempname, xrefname); 331 } else { 332 /* didn't find any entry, ignore this file */ 333 unlink(tempname); 334 unlink(xrefname); 335 } 336 } 337 if (p == NULL) 338 break; 339 if (p->fts_info == FTS_D && !dflag) { 340 /* visiting a new directory, create a new hint file */ 341 snprintf(xrefname, sizeof(xrefname), "%s/%s", 342 ftsp->fts_path, xref_file); 343 fxref = maketempfile(tempname, ftsp->fts_path); 344 if (fxref == NULL) 345 err(1, "can't create %s", tempname); 346 ival = 1; 347 fwrite(&ival, sizeof(ival), 1, fxref); 348 reccnt = 0; 349 } 350 /* skip non-files or .symbols entries */ 351 if (p->fts_info != FTS_F) 352 continue; 353 if (p->fts_namelen >= 8 && 354 strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0) 355 continue; 356 read_kld(p->fts_path, p->fts_name); 357 } 358 fts_close(ftsp); 359 return 0; 360 } 361