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