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 #ifndef min 61 #define min(a,b) (((a)<(b)) ? (a) : (b)) 62 #endif 63 64 struct mod_info { 65 char* mi_name; 66 int mi_ver; 67 SLIST_ENTRY(mod_info) mi_next; 68 }; 69 70 #ifdef notnow 71 struct kld_info { 72 char* k_filename; 73 SLIST_HEAD(mod_list_head, mod_info) k_modules; 74 SLIST_ENTRY(kld_info) k_next; 75 }; 76 77 SLIST_HEAD(kld_list_head, kld_info) kldlist; 78 #endif 79 80 static int dflag, verbose; 81 82 FILE *fxref; 83 84 static const char *xref_file = "linker.hints"; 85 86 static char recbuf[MAXRECSIZE]; 87 static int recpos, reccnt; 88 89 FILE *maketempfile(char *, const char *); 90 static void usage(void); 91 92 static void 93 intalign(void) 94 { 95 recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1); 96 } 97 98 static void 99 record_start(void) 100 { 101 recpos = 0; 102 memset(recbuf, 0, MAXRECSIZE); 103 } 104 105 static int 106 record_end(void) 107 { 108 if (dflag || recpos == 0) 109 return 0; 110 reccnt++; 111 intalign(); 112 fwrite(&recpos, sizeof(recpos), 1, fxref); 113 return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0; 114 } 115 116 static int 117 record_buf(const void *buf, int size) 118 { 119 if (MAXRECSIZE - recpos < size) 120 errx(1, "record buffer overflow"); 121 memcpy(recbuf + recpos, buf, size); 122 recpos += size; 123 return 0; 124 } 125 126 static int 127 record_int(int val) 128 { 129 intalign(); 130 return record_buf(&val, sizeof(val)); 131 } 132 133 static int 134 record_byte(u_char val) 135 { 136 return record_buf(&val, sizeof(val)); 137 } 138 139 static int 140 record_string(const char *str) 141 { 142 int len = strlen(str); 143 int error; 144 145 if (dflag) 146 return 0; 147 error = record_byte(len); 148 if (error) 149 return error; 150 return record_buf(str, len); 151 } 152 153 static int 154 parse_entry(struct mod_metadata *md, const char *cval, 155 struct elf_file *ef, const char *kldname) 156 { 157 struct mod_depend mdp; 158 struct mod_version mdv; 159 Elf_Off data = (Elf_Off)md->md_data; 160 int error = 0; 161 162 record_start(); 163 switch (md->md_type) { 164 case MDT_DEPEND: 165 if (!dflag) 166 break; 167 check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); 168 printf(" depends on %s.%d (%d,%d)\n", cval, 169 mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); 170 break; 171 case MDT_VERSION: 172 check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); 173 record_int(MDT_VERSION); 174 record_string(cval); 175 record_int(mdv.mv_version); 176 record_string(kldname); 177 if (!dflag) 178 break; 179 printf(" interface %s.%d\n", cval, mdv.mv_version); 180 break; 181 case MDT_MODULE: 182 record_int(MDT_MODULE); 183 record_string(cval); 184 record_string(kldname); 185 if (!dflag) 186 break; 187 printf(" module %s\n", cval); 188 break; 189 default: 190 warnx("unknown metadata record %d in file %s", md->md_type, kldname); 191 } 192 if (!error) 193 record_end(); 194 return error; 195 } 196 197 static int 198 read_kld(char *filename, char *kldname) 199 { 200 struct mod_metadata md; 201 struct elf_file ef; 202 /* struct kld_info *kip; 203 struct mod_info *mip;*/ 204 void **p, **orgp; 205 int error, eftype, nmlen; 206 long start, finish, entries; 207 char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp; 208 209 if (verbose || dflag) 210 printf("%s\n", filename); 211 error = ef_open(filename, &ef, verbose); 212 if (error) { 213 error = ef_obj_open(filename, &ef, verbose); 214 if (error) { 215 if (verbose) 216 warnc(error, "elf_open(%s)", filename); 217 return error; 218 } 219 } 220 eftype = EF_GET_TYPE(&ef); 221 if (eftype != EFT_KLD && eftype != EFT_KERNEL) { 222 EF_CLOSE(&ef); 223 return 0; 224 } 225 if (!dflag) { 226 cp = strrchr(kldname, '.'); 227 nmlen = cp ? min(MAXMODNAME, cp - kldname) : 228 min(MAXMODNAME, strlen(kldname)); 229 strlcpy(kldmodname, kldname, nmlen); 230 /* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/ 231 } 232 do { 233 check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, 234 &entries)); 235 check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, 236 (void *)&p)); 237 orgp = p; 238 while(entries--) { 239 check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md), 240 &md)); 241 p++; 242 check(EF_SEG_READ(&ef, (Elf_Off)md.md_cval, 243 sizeof(cval), cval)); 244 cval[MAXMODNAME] = '\0'; 245 parse_entry(&md, cval, &ef, kldname); 246 } 247 if (error) 248 warnc(error, "error while reading %s", filename); 249 free(orgp); 250 } while(0); 251 EF_CLOSE(&ef); 252 return error; 253 } 254 255 FILE * 256 maketempfile(char *dest, const char *root) 257 { 258 char *p; 259 int fd; 260 261 strlcpy(dest, root, MAXPATHLEN); 262 263 if ((p = strrchr(dest, '/')) != 0) 264 p++; 265 else 266 p = dest; 267 strcpy(p, "lhint.XXXXXX"); 268 fd = mkstemp(dest); 269 return ((fd == -1) ? NULL : fdopen(fd, "w+")); 270 } 271 272 static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; 273 274 int 275 main(int argc, char *argv[]) 276 { 277 FTS *ftsp; 278 FTSENT *p; 279 int opt, fts_options, ival; 280 struct stat sb; 281 282 fts_options = FTS_PHYSICAL; 283 /* SLIST_INIT(&kldlist);*/ 284 285 while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { 286 switch (opt) { 287 case 'd': 288 dflag = 1; 289 break; 290 case 'f': 291 xref_file = optarg; 292 break; 293 case 'v': 294 verbose++; 295 break; 296 case 'R': 297 fts_options |= FTS_COMFOLLOW; 298 break; 299 default: 300 usage(); 301 /* NOTREACHED */ 302 } 303 } 304 if (argc - optind < 1) 305 usage(); 306 argc -= optind; 307 argv += optind; 308 309 if (stat(argv[0], &sb) != 0) 310 err(1, "%s", argv[0]); 311 if ((sb.st_mode & S_IFDIR) == 0) { 312 errno = ENOTDIR; 313 err(1, "%s", argv[0]); 314 } 315 316 ftsp = fts_open(argv, fts_options, 0); 317 if (ftsp == NULL) 318 exit(1); 319 320 for (;;) { 321 p = fts_read(ftsp); 322 if ((p == NULL || p->fts_info == FTS_D) && !dflag && fxref) { 323 fclose(fxref); 324 fxref = NULL; 325 if (reccnt) { 326 rename(tempname, xrefname); 327 } else { 328 unlink(tempname); 329 unlink(xrefname); 330 } 331 } 332 if (p == NULL) 333 break; 334 if (p && p->fts_info == FTS_D && !dflag) { 335 snprintf(xrefname, sizeof(xrefname), "%s/%s", 336 ftsp->fts_path, xref_file); 337 fxref = maketempfile(tempname, ftsp->fts_path); 338 if (fxref == NULL) 339 err(1, "can't create %s", tempname); 340 ival = 1; 341 fwrite(&ival, sizeof(ival), 1, fxref); 342 reccnt = 0; 343 } 344 if (p->fts_info != FTS_F) 345 continue; 346 if (p->fts_namelen >= 8 && 347 strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0) 348 continue; 349 read_kld(p->fts_path, p->fts_name); 350 } 351 fts_close(ftsp); 352 return 0; 353 } 354 355 static void 356 usage(void) 357 { 358 359 fprintf(stderr, "%s\n", 360 "usage: kldxref [-Rdv] [-f hintsfile] path ..." 361 ); 362 exit(1); 363 } 364