1 /* $OpenBSD: nlist.c,v 1.45 2014/05/20 01:25:24 guenther Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 34 #include <a.out.h> 35 #include <db.h> 36 #include <err.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <kvm.h> 40 #include <limits.h> 41 #include <paths.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "extern.h" 48 49 #include <sys/mman.h> 50 #include <sys/stat.h> 51 #include <sys/file.h> 52 #include <sys/sysctl.h> 53 54 #include <elf_abi.h> 55 56 typedef struct nlist NLIST; 57 #define _strx n_un.n_strx 58 #define _name n_un.n_name 59 60 static char *kfile; 61 static char *fmterr; 62 63 int __elf_knlist(int fd, DB *db, int ksyms); 64 65 int 66 __elf_knlist(int fd, DB *db, int ksyms) 67 { 68 caddr_t strtab = NULL; 69 off_t symstroff, symoff; 70 u_long symsize, symstrsize; 71 u_long kernvma, kernoffs; 72 int i, error = 0; 73 Elf32_Word j; 74 Elf_Sym sbuf; 75 char buf[1024]; 76 Elf_Ehdr eh; 77 Elf_Shdr *sh = NULL; 78 DBT data, key; 79 NLIST nbuf; 80 FILE *fp; 81 int usemalloc = 0; 82 83 if ((fp = fdopen(fd, "r")) == NULL) 84 err(1, "%s", kfile); 85 86 if (fseek(fp, (off_t)0, SEEK_SET) == -1 || 87 fread(&eh, sizeof(eh), 1, fp) != 1 || 88 !IS_ELF(eh)) 89 return (1); 90 91 sh = (Elf_Shdr *)calloc(sizeof(Elf_Shdr), eh.e_shnum); 92 if (sh == NULL) 93 errx(1, "cannot allocate %zu bytes for symbol header", 94 sizeof(Elf_Shdr) * eh.e_shnum); 95 96 if (fseek (fp, eh.e_shoff, SEEK_SET) < 0) { 97 fmterr = "no exec header"; 98 error = -1; 99 goto done; 100 } 101 102 if (fread(sh, sizeof(Elf_Shdr) * eh.e_shnum, 1, fp) != 1) { 103 fmterr = "no exec header"; 104 error = -1; 105 goto done; 106 } 107 108 symstrsize = symsize = 0; 109 kernvma = (u_long)-1; /* 0 is a valid value (at least on hp300) */ 110 for (i = 0; i < eh.e_shnum; i++) { 111 if (sh[i].sh_type == SHT_STRTAB) { 112 for (j = 0; j < eh.e_shnum; j++) 113 if (sh[j].sh_type == SHT_SYMTAB && 114 sh[j].sh_link == (unsigned)i) { 115 symstroff = sh[i].sh_offset; 116 symstrsize = sh[i].sh_size; 117 } 118 } else if (sh[i].sh_type == SHT_SYMTAB) { 119 symoff = sh[i].sh_offset; 120 symsize = sh[i].sh_size; 121 } else if (sh[i].sh_type == SHT_PROGBITS && 122 (sh[i].sh_flags & SHF_EXECINSTR)) { 123 kernvma = sh[i].sh_addr; 124 kernoffs = sh[i].sh_offset; 125 } 126 } 127 128 if (symstrsize == 0 || symsize == 0 || kernvma == (u_long)-1) { 129 fmterr = "corrupt file"; 130 error = -1; 131 goto done; 132 } 133 134 /* 135 * Map string table into our address space. This gives us 136 * an easy way to randomly access all the strings, without 137 * making the memory allocation permanent as with malloc/free 138 * (i.e., munmap will return it to the system). 139 * 140 * XXX - we really want to check if this is a regular file. 141 * then we probably want a MAP_PRIVATE here. 142 */ 143 strtab = mmap(NULL, (size_t)symstrsize, PROT_READ, 144 MAP_SHARED|MAP_FILE, fileno(fp), symstroff); 145 if (strtab == MAP_FAILED) { 146 usemalloc = 1; 147 if ((strtab = malloc(symstrsize)) == NULL) { 148 fmterr = "out of memory"; 149 error = -1; 150 goto done; 151 } 152 if (fseek(fp, symstroff, SEEK_SET) == -1) { 153 fmterr = "corrupt file"; 154 error = -1; 155 goto done; 156 } 157 if (fread(strtab, symstrsize, 1, fp) != 1) { 158 fmterr = "corrupt file"; 159 error = -1; 160 goto done; 161 } 162 } 163 164 if (fseek(fp, symoff, SEEK_SET) == -1) { 165 fmterr = "corrupt file"; 166 error = -1; 167 goto done; 168 } 169 170 data.data = (u_char *)&nbuf; 171 data.size = sizeof(NLIST); 172 173 /* Read each symbol and enter it into the database. */ 174 while (symsize > 0) { 175 symsize -= sizeof(Elf_Sym); 176 if (fread((char *)&sbuf, sizeof(sbuf), 1, fp) != 1) { 177 if (feof(fp)) 178 fmterr = "corrupted symbol table"; 179 else 180 warn("%s", kfile); 181 error = -1; 182 goto done; 183 } 184 if (!sbuf.st_name) 185 continue; 186 187 nbuf.n_value = sbuf.st_value; 188 189 /* XXX type conversion is pretty rude... */ 190 switch(ELF_ST_TYPE(sbuf.st_info)) { 191 case STT_NOTYPE: 192 switch (sbuf.st_shndx) { 193 case SHN_UNDEF: 194 nbuf.n_type = N_UNDF; 195 break; 196 case SHN_ABS: 197 nbuf.n_type = N_ABS; 198 break; 199 case SHN_COMMON: 200 nbuf.n_type = N_COMM; 201 break; 202 default: 203 nbuf.n_type = N_COMM | N_EXT; 204 break; 205 } 206 break; 207 case STT_FUNC: 208 nbuf.n_type = N_TEXT; 209 break; 210 case STT_OBJECT: 211 nbuf.n_type = N_DATA; 212 break; 213 case STT_FILE: 214 nbuf.n_type = N_FN; 215 break; 216 } 217 if (ELF_ST_BIND(sbuf.st_info) == STB_LOCAL) 218 nbuf.n_type = N_EXT; 219 220 *buf = '_'; 221 strlcpy(buf + 1, strtab + sbuf.st_name, sizeof buf - 1); 222 key.data = (u_char *)buf; 223 key.size = strlen((char *)key.data); 224 if (db->put(db, &key, &data, 0)) 225 err(1, "record enter"); 226 227 if (strcmp((char *)key.data, VRS_SYM) == 0) { 228 long cur_off; 229 if (!ksyms) { 230 /* 231 * Calculate offset to the version string in 232 * the file. kernvma is where the kernel is 233 * really loaded; kernoffs is where in the 234 * file it starts. 235 */ 236 long voff; 237 voff = nbuf.n_value - kernvma + kernoffs; 238 cur_off = ftell(fp); 239 if (fseek(fp, voff, SEEK_SET) == -1) { 240 fmterr = "corrupted string table"; 241 error = -1; 242 goto done; 243 } 244 245 /* 246 * Read version string up to, and including 247 * newline. This code assumes that a newline 248 * terminates the version line. 249 */ 250 if (fgets(buf, sizeof(buf), fp) == NULL) { 251 fmterr = "corrupted string table"; 252 error = -1; 253 goto done; 254 } 255 } else { 256 /* 257 * This is /dev/ksyms or a look alike. 258 * Use sysctl() to get version since we 259 * don't have real text or data. 260 */ 261 int mib[2]; 262 size_t len; 263 char *p; 264 265 mib[0] = CTL_KERN; 266 mib[1] = KERN_VERSION; 267 len = sizeof(buf); 268 if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) { 269 err(1, "sysctl can't find kernel " 270 "version string"); 271 } 272 if ((p = strchr(buf, '\n')) != NULL) 273 *(p+1) = '\0'; 274 } 275 276 key.data = (u_char *)VRS_KEY; 277 key.size = sizeof(VRS_KEY) - 1; 278 data.data = (u_char *)buf; 279 data.size = strlen(buf); 280 if (db->put(db, &key, &data, 0)) 281 err(1, "record enter"); 282 283 /* Restore to original values. */ 284 data.data = (u_char *)&nbuf; 285 data.size = sizeof(NLIST); 286 if (!ksyms && fseek(fp, cur_off, SEEK_SET) == -1) { 287 fmterr = "corrupted string table"; 288 error = -1; 289 goto done; 290 } 291 } 292 } 293 done: 294 if (strtab) { 295 if (usemalloc) 296 free(strtab); 297 else 298 munmap(strtab, symstrsize); 299 } 300 (void)fclose(fp); 301 if (sh) 302 free(sh); 303 return (error); 304 } 305 306 int 307 create_knlist(char *name, int fd, DB *db) 308 { 309 int error, ksyms; 310 311 if (strcmp(name, _PATH_KSYMS) == 0) { 312 ksyms = 1; 313 } else { 314 ksyms = 0; 315 } 316 317 fmterr = NULL; 318 kfile = name; 319 /* rval of 1 means wrong executable type */ 320 error = __elf_knlist(fd, db, ksyms); 321 322 if (fmterr != NULL) 323 warnc(EFTYPE, "%s: %s", kfile, fmterr); 324 325 return(error); 326 } 327