1 /* $NetBSD: nlist_elf32.c,v 1.6 1997/12/15 04:21:34 mrg Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Christopher G. Demetriou 17 * for the NetBSD Project. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 #ifndef lint 35 __RCSID("$NetBSD: nlist_elf32.c,v 1.6 1997/12/15 04:21:34 mrg Exp $"); 36 #endif /* not lint */ 37 38 /* If not included by nlist_elf64.c, ELFSIZE won't be defined. */ 39 #ifndef ELFSIZE 40 #define ELFSIZE 32 41 #endif 42 43 #include <sys/param.h> 44 #include <sys/mman.h> 45 #include <sys/stat.h> 46 47 #include <a.out.h> 48 #include <db.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <kvm.h> 53 #include <limits.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include "extern.h" 60 61 #if defined(NLIST_ELF32) || defined(NLIST_ELF64) 62 #include <sys/exec_elf.h> 63 #endif 64 65 #if (defined(NLIST_ELF32) && (ELFSIZE == 32)) || \ 66 (defined(NLIST_ELF64) && (ELFSIZE == 64)) 67 68 #define CONCAT(x,y) __CONCAT(x,y) 69 #define ELFNAME(x) CONCAT(elf,CONCAT(ELFSIZE,CONCAT(_,x))) 70 #define ELFNAME2(x,y) CONCAT(x,CONCAT(_elf,CONCAT(ELFSIZE,CONCAT(_,y)))) 71 #define ELFNAMEEND(x) CONCAT(x,CONCAT(_elf,ELFSIZE)) 72 #define ELFDEFNNAME(x) CONCAT(ELF,CONCAT(ELFSIZE,CONCAT(_,x))) 73 74 typedef struct nlist NLIST; 75 #define _strx n_un.n_strx 76 #define _name n_un.n_name 77 78 #define badfmt(str) \ 79 do { \ 80 warnx("%s: %s: %s", kfile, str, strerror(EFTYPE)); \ 81 punt(); \ 82 } while (0) 83 84 #define check(off, size) ((off < 0) || (off + size > mappedsize)) 85 #define BAD do { rv = -1; goto out; } while (0) 86 #define BADUNMAP do { rv = -1; goto unmap; } while (0) 87 88 static const char *kfile; 89 90 int 91 ELFNAMEEND(create_knlist)(name, db) 92 const char *name; 93 DB *db; 94 { 95 struct stat st; 96 struct nlist nbuf; 97 DBT key, data; 98 char *mappedfile, *symname, *fsymname, *tmpcp, *strtab; 99 size_t mappedsize, symnamesize, fsymnamesize; 100 Elf_Ehdr *ehdrp; 101 Elf_Shdr *shdrp, *symshdrp, *symstrshdrp; 102 Elf_Sym *symp; 103 Elf_Off shdr_off; 104 Elf_Word shdr_size; 105 #if (ELFSIZE == 32) 106 Elf32_Half nshdr; 107 #elif (ELFSIZE == 64) 108 Elf64_Half nshdr; 109 #endif 110 unsigned long i, nsyms; 111 int fd, rv; 112 113 rv = -1; 114 #ifdef __GNUC__ 115 /* fix compiler warnings */ 116 symshdrp = NULL; 117 symstrshdrp = NULL; 118 #endif 119 120 /* 121 * Open and map the whole file. If we can't open/stat it, 122 * something bad is going on so we punt. 123 */ 124 kfile = name; 125 if ((fd = open(name, O_RDONLY, 0)) < 0) { 126 warn("%s", kfile); 127 punt(); 128 } 129 if (fstat(fd, &st) < 0) { 130 warn("%s", kfile); 131 punt(); 132 } 133 if (st.st_size > SIZE_T_MAX) 134 BAD; 135 136 /* 137 * Map the file in its entirety. 138 */ 139 mappedsize = st.st_size; 140 mappedfile = mmap(NULL, mappedsize, PROT_READ, MAP_PRIVATE|MAP_FILE, 141 fd, 0); 142 if (mappedfile == (char *)-1) 143 BAD; 144 145 /* 146 * Make sure we can access the executable's header 147 * directly, and make sure the recognize the executable 148 * as an ELF binary. 149 */ 150 if (check(0, sizeof *ehdrp)) 151 BADUNMAP; 152 ehdrp = (Elf_Ehdr *)&mappedfile[0]; 153 154 if (memcmp(ehdrp->e_ident, Elf_e_ident, Elf_e_siz)) 155 BADUNMAP; 156 157 switch (ehdrp->e_machine) { 158 ELFDEFNNAME(MACHDEP_ID_CASES) 159 160 default: 161 BADUNMAP; 162 } 163 164 /* 165 * We've recognized it as an ELF binary. From here 166 * on out, all errors are fatal. 167 */ 168 169 /* 170 * Find the symbol list and string table. 171 */ 172 nshdr = ehdrp->e_shnum; 173 shdr_off = ehdrp->e_shoff; 174 shdr_size = ehdrp->e_shentsize * nshdr; 175 176 if (check(shdr_off, shdr_size) || 177 (sizeof *shdrp != ehdrp->e_shentsize)) 178 badfmt("bogus section header table"); 179 shdrp = (Elf_Shdr *)&mappedfile[shdr_off]; 180 181 for (i = 0; i < nshdr; i++) { 182 if (shdrp[i].sh_type == Elf_sht_symtab) { 183 symshdrp = &shdrp[i]; 184 symstrshdrp = &shdrp[shdrp[i].sh_link]; 185 } 186 } 187 188 if (symshdrp->sh_offset == 0) 189 badfmt("stripped"); 190 if (check(symshdrp->sh_offset, symshdrp->sh_size)) 191 badfmt("bogus symbol section header"); 192 if (check(symstrshdrp->sh_offset, symstrshdrp->sh_size)) 193 badfmt("bogus symbol string section header"); 194 195 symp = (Elf_Sym *)&mappedfile[symshdrp->sh_offset]; 196 nsyms = symshdrp->sh_size / sizeof(*symp); 197 strtab = &mappedfile[symstrshdrp->sh_offset]; 198 199 /* 200 * Set up the data item, pointing to a nlist structure. 201 * which we fill in for each symbol. 202 */ 203 data.data = (u_char *)&nbuf; 204 data.size = sizeof(nbuf); 205 206 /* 207 * Create a buffer (to be expanded later, if necessary) 208 * to hold symbol names after we've added underscores 209 * to them. 210 */ 211 symnamesize = 1024; 212 if ((symname = malloc(symnamesize)) == NULL) { 213 warn("malloc"); 214 punt(); 215 } 216 217 /* 218 * Read each symbol and enter it into the database. 219 */ 220 for (i = 0; i < nsyms; i++) { 221 222 /* 223 * No symbol name; ignore this symbol. 224 */ 225 if (symp[i].st_name == 0) 226 continue; 227 228 /* 229 * Find symbol name, copy it (with added underscore) to 230 * temporary buffer, and prepare the database key for 231 * insertion. 232 */ 233 fsymname = &strtab[symp[i].st_name]; 234 fsymnamesize = strlen(fsymname) + 1; 235 while (symnamesize < fsymnamesize + 1) { 236 symnamesize *= 2; 237 if ((symname = realloc(symname, symnamesize)) == NULL) { 238 warn("malloc"); 239 punt(); 240 } 241 } 242 strcpy(symname, "_"); 243 strcat(symname, fsymname); 244 245 key.data = symname; 246 key.size = strlen((char *)key.data); 247 248 /* 249 * Convert the symbol information into an nlist structure, 250 * as best we can. 251 */ 252 nbuf.n_value = symp[i].st_value; 253 switch (ELF_SYM_TYPE(symp[i].st_info)) { 254 default: 255 case Elf_estt_notype: 256 nbuf.n_type = N_UNDF; 257 break; 258 case Elf_estt_object: 259 nbuf.n_type = N_DATA; 260 break; 261 case Elf_estt_func: 262 nbuf.n_type = N_TEXT; 263 break; 264 case Elf_estt_file: 265 nbuf.n_type = N_FN; 266 break; 267 } 268 if (ELF_SYM_BIND(symp[i].st_info) != Elf_estb_local) 269 nbuf.n_type |= N_EXT; 270 nbuf.n_desc = 0; /* XXX */ 271 nbuf.n_other = 0; /* XXX */ 272 273 /* 274 * Enter the symbol into the database. 275 */ 276 if (db->put(db, &key, &data, 0)) { 277 warn("record enter"); 278 punt(); 279 } 280 281 /* 282 * If it's the kernel version string, we've gotta keep 283 * some extra data around. Under a seperate key, 284 * we enter the first line (i.e. up to the first newline, 285 * with the newline replaced by a NUL to terminate the 286 * entered string) of the version string. 287 */ 288 if (strcmp((char *)key.data, VRS_SYM) == 0) { 289 key.data = (u_char *)VRS_KEY; 290 key.size = sizeof(VRS_KEY) - 1; 291 /* Find the version string, relative to its section */ 292 data.data = strdup(&mappedfile[nbuf.n_value - 293 shdrp[symp[i].st_shndx].sh_addr + 294 shdrp[symp[i].st_shndx].sh_offset]); 295 /* assumes newline terminates version. */ 296 if ((tmpcp = strchr(data.data, '\n')) != NULL) 297 *tmpcp = '\0'; 298 data.size = strlen((char *)data.data); 299 300 if (db->put(db, &key, &data, 0)) { 301 warn("record enter"); 302 punt(); 303 } 304 305 /* free pointer created by strdup(). */ 306 free(data.data); 307 308 /* Restore to original values */ 309 data.data = (u_char *)&nbuf; 310 data.size = sizeof(nbuf); 311 } 312 } 313 314 rv = 0; 315 316 unmap: 317 munmap(mappedfile, mappedsize); 318 out: 319 return (rv); 320 } 321 322 #endif 323