1 /*- 2 * Copyright (c) 1990, 1993 3 * The Regents of the University of California. 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 the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 /*static char sccsid[] = "from: @(#)nlist.c 8.1 (Berkeley) 6/6/93";*/ 36 static char *rcsid = "$Id: nlist.c,v 1.5 1996/07/31 17:21:47 deraadt Exp $"; 37 #endif /* not lint */ 38 39 #include <sys/param.h> 40 41 #include <a.out.h> 42 #include <db.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <kvm.h> 47 #include <limits.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 #include "extern.h" 54 55 #include <sys/mman.h> 56 #include <sys/stat.h> 57 #include <sys/file.h> 58 59 #ifdef DO_ELF 60 #include <elf_abi.h> 61 #endif 62 63 typedef struct nlist NLIST; 64 #define _strx n_un.n_strx 65 #define _name n_un.n_name 66 67 #define badfmt(str) errx(1, "%s: %s: %s", kfile, str, strerror(EFTYPE)) 68 static char *kfile; 69 70 #if defined(DO_AOUT) 71 72 static void badread __P((int, char *)); 73 static u_long get_kerntext __P((char *kfn, u_int magic)); 74 75 int 76 __aout_knlist(name, db) 77 char *name; 78 DB *db; 79 { 80 register int nsyms; 81 struct exec ebuf; 82 FILE *fp; 83 NLIST nbuf; 84 DBT data, key; 85 int fd, nr, strsize; 86 u_long kerntextoff; 87 char *strtab, buf[1024]; 88 89 kfile = name; 90 if ((fd = open(name, O_RDONLY, 0)) < 0) 91 err(1, "%s", name); 92 93 /* Read in exec structure. */ 94 nr = read(fd, &ebuf, sizeof(struct exec)); 95 if (nr != sizeof(struct exec)) 96 return(-1); 97 98 /* Check magic number and symbol count. */ 99 if (N_BADMAG(ebuf)) 100 return(-1); 101 102 if (!ebuf.a_syms) 103 badfmt("stripped"); 104 105 /* Seek to string table. */ 106 if (lseek(fd, N_STROFF(ebuf), SEEK_SET) == -1) 107 badfmt("corrupted string table"); 108 109 /* Read in the size of the symbol table. */ 110 nr = read(fd, (char *)&strsize, sizeof(strsize)); 111 if (nr != sizeof(strsize)) 112 badread(nr, "no symbol table"); 113 114 /* Read in the string table. */ 115 strsize -= sizeof(strsize); 116 if (!(strtab = malloc(strsize))) 117 err(1, NULL); 118 if ((nr = read(fd, strtab, strsize)) != strsize) 119 badread(nr, "corrupted symbol table"); 120 121 /* Seek to symbol table. */ 122 if (!(fp = fdopen(fd, "r"))) 123 err(1, "%s", name); 124 if (fseek(fp, N_SYMOFF(ebuf), SEEK_SET) == -1) 125 err(1, "%s", name); 126 127 data.data = (u_char *)&nbuf; 128 data.size = sizeof(NLIST); 129 130 kerntextoff = get_kerntext(name, N_GETMAGIC(ebuf)); 131 132 /* Read each symbol and enter it into the database. */ 133 nsyms = ebuf.a_syms / sizeof(struct nlist); 134 while (nsyms--) { 135 if (fread((char *)&nbuf, sizeof (NLIST), 1, fp) != 1) { 136 if (feof(fp)) 137 badfmt("corrupted symbol table"); 138 err(1, "%s", name); 139 } 140 if (!nbuf._strx || nbuf.n_type&N_STAB) 141 continue; 142 143 key.data = (u_char *)strtab + nbuf._strx - sizeof(long); 144 key.size = strlen((char *)key.data); 145 if (db->put(db, &key, &data, 0)) 146 err(1, "record enter"); 147 148 if (strcmp((char *)key.data, VRS_SYM) == 0) { 149 long cur_off, voff; 150 /* 151 * Calculate offset relative to a normal (non-kernel) 152 * a.out. Kerntextoff is where the kernel is really 153 * loaded; N_TXTADDR is where a normal file is loaded. 154 * From there, locate file offset in text or data. 155 */ 156 voff = nbuf.n_value - kerntextoff + N_TXTADDR(ebuf); 157 if ((nbuf.n_type & N_TYPE) == N_TEXT) 158 voff += N_TXTOFF(ebuf) - N_TXTADDR(ebuf); 159 else 160 voff += N_DATOFF(ebuf) - N_DATADDR(ebuf); 161 cur_off = ftell(fp); 162 if (fseek(fp, voff, SEEK_SET) == -1) 163 badfmt("corrupted string table"); 164 165 /* 166 * Read version string up to, and including newline. 167 * This code assumes that a newline terminates the 168 * version line. 169 */ 170 if (fgets(buf, sizeof(buf), fp) == NULL) 171 badfmt("corrupted string table"); 172 173 key.data = (u_char *)VRS_KEY; 174 key.size = sizeof(VRS_KEY) - 1; 175 data.data = (u_char *)buf; 176 data.size = strlen(buf); 177 if (db->put(db, &key, &data, 0)) 178 err(1, "record enter"); 179 180 /* Restore to original values. */ 181 data.data = (u_char *)&nbuf; 182 data.size = sizeof(NLIST); 183 if (fseek(fp, cur_off, SEEK_SET) == -1) 184 badfmt("corrupted string table"); 185 } 186 } 187 (void)fclose(fp); 188 return(0); 189 } 190 191 /* 192 * XXX: Using this value from machine/param.h introduces a 193 * XXX: machine dependency on this program, so /usr can not 194 * XXX: be shared between (i.e.) several m68k machines. 195 * Instead of compiling in KERNTEXTOFF or KERNBASE, try to 196 * determine the text start address from a standard symbol. 197 * For backward compatibility, use the old compiled-in way 198 * when the standard symbol name is not found. 199 */ 200 #ifndef KERNTEXTOFF 201 #define KERNTEXTOFF KERNBASE 202 #endif 203 204 static u_long 205 get_kerntext(name, magic) 206 char *name; 207 u_int magic; 208 { 209 NLIST nl[2]; 210 211 bzero((caddr_t)nl, sizeof(nl)); 212 nl[0]._name = "_kernel_text"; 213 214 if (nlist(name, nl) != 0) 215 return (KERNTEXTOFF); 216 217 if (magic == ZMAGIC || magic == QMAGIC) 218 return (nl[0].n_value - sizeof(struct exec)); 219 return (nl[0].n_value); 220 } 221 222 static void 223 badread(nr, p) 224 int nr; 225 char *p; 226 { 227 if (nr < 0) 228 err(1, "%s", kfile); 229 badfmt(p); 230 } 231 232 #endif /* DO_AOUT */ 233 234 #ifdef DO_ELF 235 int 236 __elf_knlist(name, db) 237 char *name; 238 DB *db; 239 { 240 register struct nlist *p; 241 register caddr_t strtab; 242 register off_t symstroff, symoff; 243 register u_long symsize; 244 register u_long kernvma, kernoffs; 245 register int cc, i; 246 Elf32_Sym sbuf; 247 Elf32_Sym *s; 248 size_t symstrsize; 249 char *shstr, buf[1024]; 250 Elf32_Ehdr eh; 251 Elf32_Shdr *sh = NULL; 252 struct stat st; 253 DBT data, key; 254 NLIST nbuf; 255 FILE *fp; 256 257 kfile = name; 258 if ((fp = fopen(name, "r")) < 0) 259 err(1, "%s", name); 260 261 if (fseek(fp, (off_t)0, SEEK_SET) == -1 || 262 fread(&eh, sizeof(eh), 1, fp) != 1 || 263 !IS_ELF(eh)) 264 return(-1); 265 266 sh = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr) * eh.e_shnum); 267 268 if (fseek (fp, eh.e_shoff, SEEK_SET) < 0) 269 badfmt("no exec header"); 270 271 if (fread(sh, sizeof(Elf32_Shdr) * eh.e_shnum, 1, fp) != 1) 272 badfmt("no exec header"); 273 274 shstr = (char *)malloc(sh[eh.e_shstrndx].sh_size); 275 if (fseek (fp, sh[eh.e_shstrndx].sh_offset, SEEK_SET) < 0) 276 badfmt("corrupt file"); 277 if (fread(shstr, sh[eh.e_shstrndx].sh_size, 1, fp) != 1) 278 badfmt("corrupt file"); 279 280 for (i = 0; i < eh.e_shnum; i++) { 281 if (strcmp (shstr + sh[i].sh_name, ".strtab") == 0) { 282 symstroff = sh[i].sh_offset; 283 symstrsize = sh[i].sh_size; 284 } 285 else if (strcmp (shstr + sh[i].sh_name, ".symtab") == 0) { 286 symoff = sh[i].sh_offset; 287 symsize = sh[i].sh_size; 288 } 289 else if (strcmp (shstr + sh[i].sh_name, ".text") == 0) { 290 kernvma = sh[i].sh_addr; 291 kernoffs = sh[i].sh_offset; 292 } 293 } 294 295 296 /* Check for files too large to mmap. */ 297 /* XXX is this really possible? */ 298 if (symstrsize > SIZE_T_MAX) { 299 badfmt("corrupt file"); 300 } 301 /* 302 * Map string table into our address space. This gives us 303 * an easy way to randomly access all the strings, without 304 * making the memory allocation permanent as with malloc/free 305 * (i.e., munmap will return it to the system). 306 */ 307 strtab = mmap(NULL, (size_t)symstrsize, PROT_READ, 0, fileno(fp), symstroff); 308 if (strtab == (char *)-1) 309 badfmt("corrupt file"); 310 311 if (fseek(fp, symoff, SEEK_SET) == -1) 312 badfmt("corrupt file"); 313 314 data.data = (u_char *)&nbuf; 315 data.size = sizeof(NLIST); 316 317 /* Read each symbol and enter it into the database. */ 318 while (symsize > 0) { 319 symsize -= sizeof(Elf32_Sym); 320 if (fread((char *)&sbuf, sizeof(sbuf), 1, fp) != 1) { 321 if (feof(fp)) 322 badfmt("corrupted symbol table"); 323 err(1, "%s", name); 324 } 325 if (!sbuf.st_name) 326 continue; 327 328 nbuf.n_value = sbuf.st_value; 329 330 /*XXX type conversion is pretty rude... */ 331 switch(ELF32_ST_TYPE(sbuf.st_info)) { 332 case STT_NOTYPE: 333 nbuf.n_type = N_UNDF; 334 break; 335 case STT_FUNC: 336 nbuf.n_type = N_TEXT; 337 break; 338 case STT_OBJECT: 339 nbuf.n_type = N_DATA; 340 break; 341 } 342 if(ELF32_ST_BIND(sbuf.st_info) == STB_LOCAL) 343 nbuf.n_type = N_EXT; 344 345 if(eh.e_machine == EM_MIPS) { 346 *buf = '_'; 347 strcpy(buf+1,strtab + sbuf.st_name); 348 key.data = (u_char *)buf; 349 } 350 else { 351 key.data = (u_char *)(strtab + sbuf.st_name); 352 } 353 key.size = strlen((char *)key.data); 354 if (db->put(db, &key, &data, 0)) 355 err(1, "record enter"); 356 357 if (strcmp((char *)key.data, VRS_SYM) == 0) { 358 long cur_off, voff; 359 /* 360 * Calculate offset to the version string in the 361 * file. kernvma is where the kernel is really 362 * loaded; kernoffs is where in the file it starts. 363 */ 364 voff = nbuf.n_value - kernvma + kernoffs; 365 cur_off = ftell(fp); 366 if (fseek(fp, voff, SEEK_SET) == -1) 367 badfmt("corrupted string table"); 368 369 /* 370 * Read version string up to, and including newline. 371 * This code assumes that a newline terminates the 372 * version line. 373 */ 374 if (fgets(buf, sizeof(buf), fp) == NULL) 375 badfmt("corrupted string table"); 376 377 key.data = (u_char *)VRS_KEY; 378 key.size = sizeof(VRS_KEY) - 1; 379 data.data = (u_char *)buf; 380 data.size = strlen(buf); 381 if (db->put(db, &key, &data, 0)) 382 err(1, "record enter"); 383 384 /* Restore to original values. */ 385 data.data = (u_char *)&nbuf; 386 data.size = sizeof(NLIST); 387 if (fseek(fp, cur_off, SEEK_SET) == -1) 388 badfmt("corrupted string table"); 389 } 390 } 391 munmap(strtab, symstrsize); 392 (void)fclose(fp); 393 return(0); 394 } 395 #endif /* DO_ELF */ 396 397 #ifdef DO_ECOFF 398 int 399 __ecoff_knlist(name, db) 400 char *name; 401 DB *db; 402 { 403 return (-1); 404 } 405 #endif /* DO_ECOFF */ 406 407 static struct knlist_handlers { 408 int (*fn) __P((char *name, DB *db)); 409 } nlist_fn[] = { 410 #ifdef DO_AOUT 411 { __aout_knlist }, 412 #endif 413 #ifdef DO_ELF 414 { __elf_knlist }, 415 #endif 416 #ifdef DO_ECOFF 417 { __ecoff_knlist }, 418 #endif 419 }; 420 421 void 422 create_knlist(name, db) 423 char *name; 424 DB *db; 425 { 426 int n, i; 427 428 for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) { 429 n = (nlist_fn[i].fn)(name, db); 430 if (n != -1) 431 break; 432 } 433 } 434