1 /* $OpenBSD: nlist.c,v 1.50 2016/09/10 05:48:18 jsg 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/types.h> 33 34 #include <db.h> 35 #include <err.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <kvm.h> 39 #include <limits.h> 40 #include <paths.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "extern.h" 47 48 #include <sys/mman.h> 49 #include <sys/stat.h> 50 #include <sys/file.h> 51 #include <sys/sysctl.h> 52 53 #include <elf_abi.h> 54 55 typedef struct nlist NLIST; 56 #define _strx n_un.n_strx 57 #define _name n_un.n_name 58 59 static char *kfile; 60 static char *fmterr; 61 62 int __elf_knlist(int fd, DB *db, int ksyms); 63 64 int 65 __elf_knlist(int fd, DB *db, int ksyms) 66 { 67 caddr_t strtab = NULL; 68 off_t symstroff, symoff; 69 u_long symsize, symstrsize; 70 u_long kernvma, kernoffs; 71 int i, error = 0; 72 Elf32_Word j; 73 Elf_Sym sbuf; 74 char buf[1024]; 75 Elf_Ehdr eh; 76 Elf_Shdr *sh = NULL; 77 DBT data, key; 78 NLIST nbuf; 79 FILE *fp; 80 int usemalloc = 0; 81 82 if ((fp = fdopen(fd, "r")) == NULL) 83 err(1, "%s", kfile); 84 85 if (fseek(fp, (off_t)0, SEEK_SET) == -1 || 86 fread(&eh, sizeof(eh), 1, fp) != 1 || 87 !IS_ELF(eh)) { 88 fclose(fp); 89 return (1); 90 } 91 92 sh = calloc(sizeof(Elf_Shdr), eh.e_shnum); 93 if (sh == NULL) 94 errx(1, "cannot allocate %zu bytes for symbol header", 95 sizeof(Elf_Shdr) * eh.e_shnum); 96 97 if (fseek (fp, eh.e_shoff, SEEK_SET) < 0) { 98 fmterr = "no exec header"; 99 error = -1; 100 goto done; 101 } 102 103 if (fread(sh, sizeof(Elf_Shdr) * eh.e_shnum, 1, fp) != 1) { 104 fmterr = "no exec header"; 105 error = -1; 106 goto done; 107 } 108 109 symstrsize = symsize = 0; 110 kernvma = (u_long)-1; /* 0 is a valid value (at least on hp300) */ 111 for (i = 0; i < eh.e_shnum; i++) { 112 if (sh[i].sh_type == SHT_STRTAB) { 113 for (j = 0; j < eh.e_shnum; j++) 114 if (sh[j].sh_type == SHT_SYMTAB && 115 sh[j].sh_link == (unsigned)i) { 116 symstroff = sh[i].sh_offset; 117 symstrsize = sh[i].sh_size; 118 } 119 } else if (sh[i].sh_type == SHT_SYMTAB) { 120 symoff = sh[i].sh_offset; 121 symsize = sh[i].sh_size; 122 } else if (sh[i].sh_type == SHT_PROGBITS && 123 (sh[i].sh_flags & SHF_EXECINSTR)) { 124 kernvma = sh[i].sh_addr; 125 kernoffs = sh[i].sh_offset; 126 } 127 } 128 129 if (symstrsize == 0 || symsize == 0 || kernvma == (u_long)-1) { 130 fmterr = "corrupt file"; 131 error = -1; 132 goto done; 133 } 134 135 /* 136 * Map string table into our address space. This gives us 137 * an easy way to randomly access all the strings, without 138 * making the memory allocation permanent as with malloc/free 139 * (i.e., munmap will return it to the system). 140 * 141 * XXX - we really want to check if this is a regular file. 142 * then we probably want a MAP_PRIVATE here. 143 */ 144 strtab = mmap(NULL, (size_t)symstrsize, PROT_READ, 145 MAP_SHARED|MAP_FILE, fileno(fp), symstroff); 146 if (strtab == MAP_FAILED) { 147 usemalloc = 1; 148 if ((strtab = malloc(symstrsize)) == NULL) { 149 fmterr = "out of memory"; 150 error = -1; 151 goto done; 152 } 153 if (fseek(fp, symstroff, SEEK_SET) == -1) { 154 fmterr = "corrupt file"; 155 error = -1; 156 goto done; 157 } 158 if (fread(strtab, symstrsize, 1, fp) != 1) { 159 fmterr = "corrupt file"; 160 error = -1; 161 goto done; 162 } 163 } 164 165 if (fseek(fp, symoff, SEEK_SET) == -1) { 166 fmterr = "corrupt file"; 167 error = -1; 168 goto done; 169 } 170 171 data.data = (u_char *)&nbuf; 172 data.size = sizeof(NLIST); 173 174 /* Read each symbol and enter it into the database. */ 175 while (symsize > 0) { 176 symsize -= sizeof(Elf_Sym); 177 if (fread((char *)&sbuf, sizeof(sbuf), 1, fp) != 1) { 178 if (feof(fp)) 179 fmterr = "corrupted symbol table"; 180 else 181 warn("%s", kfile); 182 error = -1; 183 goto done; 184 } 185 if (!sbuf.st_name) 186 continue; 187 188 nbuf.n_value = sbuf.st_value; 189 190 /* XXX type conversion is pretty rude... */ 191 switch(ELF_ST_TYPE(sbuf.st_info)) { 192 case STT_NOTYPE: 193 switch (sbuf.st_shndx) { 194 case SHN_UNDEF: 195 nbuf.n_type = N_UNDF; 196 break; 197 case SHN_ABS: 198 nbuf.n_type = N_ABS; 199 break; 200 case SHN_COMMON: 201 nbuf.n_type = N_COMM; 202 break; 203 default: 204 nbuf.n_type = N_COMM | N_EXT; 205 break; 206 } 207 break; 208 case STT_FUNC: 209 nbuf.n_type = N_TEXT; 210 break; 211 case STT_OBJECT: 212 nbuf.n_type = N_DATA; 213 break; 214 case STT_FILE: 215 nbuf.n_type = N_FN; 216 break; 217 } 218 if (ELF_ST_BIND(sbuf.st_info) == STB_LOCAL) 219 nbuf.n_type = N_EXT; 220 221 *buf = '_'; 222 strlcpy(buf + 1, strtab + sbuf.st_name, sizeof buf - 1); 223 key.data = (u_char *)buf; 224 key.size = strlen((char *)key.data); 225 if (db->put(db, &key, &data, 0)) 226 err(1, "record enter"); 227 228 if (strcmp((char *)key.data, VRS_SYM) == 0) { 229 long cur_off; 230 if (!ksyms) { 231 /* 232 * Calculate offset to the version string in 233 * the file. kernvma is where the kernel is 234 * really loaded; kernoffs is where in the 235 * file it starts. 236 */ 237 long voff; 238 voff = nbuf.n_value - kernvma + kernoffs; 239 cur_off = ftell(fp); 240 if (fseek(fp, voff, SEEK_SET) == -1) { 241 fmterr = "corrupted string table"; 242 error = -1; 243 goto done; 244 } 245 246 /* 247 * Read version string up to, and including 248 * newline. This code assumes that a newline 249 * terminates the version line. 250 */ 251 if (fgets(buf, sizeof(buf), fp) == NULL) { 252 fmterr = "corrupted string table"; 253 error = -1; 254 goto done; 255 } 256 } else { 257 /* 258 * This is /dev/ksyms or a look alike. 259 * Use sysctl() to get version since we 260 * don't have real text or data. 261 */ 262 int mib[2]; 263 size_t len; 264 char *p; 265 266 mib[0] = CTL_KERN; 267 mib[1] = KERN_VERSION; 268 len = sizeof(buf); 269 if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) { 270 err(1, "sysctl can't find kernel " 271 "version string"); 272 } 273 if ((p = strchr(buf, '\n')) != NULL) 274 *(p+1) = '\0'; 275 } 276 277 key.data = (u_char *)VRS_KEY; 278 key.size = sizeof(VRS_KEY) - 1; 279 data.data = (u_char *)buf; 280 data.size = strlen(buf); 281 if (db->put(db, &key, &data, 0)) 282 err(1, "record enter"); 283 284 /* Restore to original values. */ 285 data.data = (u_char *)&nbuf; 286 data.size = sizeof(NLIST); 287 if (!ksyms && fseek(fp, cur_off, SEEK_SET) == -1) { 288 fmterr = "corrupted string table"; 289 error = -1; 290 goto done; 291 } 292 } 293 } 294 done: 295 if (strtab) { 296 if (usemalloc) 297 free(strtab); 298 else 299 munmap(strtab, symstrsize); 300 } 301 (void)fclose(fp); 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