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