1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1997-1999 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <fcntl.h> 31 #include <ctype.h> 32 #include <string.h> 33 #include <signal.h> 34 #include <errno.h> 35 #include <stdlib.h> 36 #include <stdarg.h> 37 #include <unistd.h> 38 #include <limits.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 42 #include <libelf.h> 43 #include <link.h> 44 #include <elf.h> 45 #if defined(sun) 46 #include <sys/machelf.h> 47 48 #include <kstat.h> 49 #else 50 /* FreeBSD */ 51 #include <sys/elf.h> 52 #include <sys/ksyms.h> 53 #include <sys/param.h> 54 #include <sys/module.h> 55 #include <sys/linker.h> 56 #endif 57 #include <sys/cpuvar.h> 58 59 typedef struct syment { 60 uintptr_t addr; 61 char *name; 62 size_t size; 63 } syment_t; 64 65 static syment_t *symbol_table; 66 static int nsyms, maxsyms; 67 static char maxsymname[64]; 68 69 #if defined(sun) 70 #ifdef _ELF64 71 #define elf_getshdr elf64_getshdr 72 #else 73 #define elf_getshdr elf32_getshdr 74 #endif 75 #endif 76 77 static void 78 add_symbol(char *name, uintptr_t addr, size_t size) 79 { 80 syment_t *sep; 81 82 if (nsyms >= maxsyms) { 83 maxsyms += 10000; 84 symbol_table = realloc(symbol_table, maxsyms * sizeof (*sep)); 85 if (symbol_table == NULL) { 86 (void) fprintf(stderr, "can't allocate symbol table\n"); 87 exit(3); 88 } 89 } 90 sep = &symbol_table[nsyms++]; 91 92 sep->name = name; 93 sep->addr = addr; 94 sep->size = size; 95 } 96 97 static void 98 remove_symbol(uintptr_t addr) 99 { 100 int i; 101 syment_t *sep = symbol_table; 102 103 for (i = 0; i < nsyms; i++, sep++) 104 if (sep->addr == addr) 105 sep->addr = 0; 106 } 107 108 #if defined(sun) 109 static void 110 fake_up_certain_popular_kernel_symbols(void) 111 { 112 kstat_ctl_t *kc; 113 kstat_t *ksp; 114 char *name; 115 116 if ((kc = kstat_open()) == NULL) 117 return; 118 119 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 120 if (strcmp(ksp->ks_module, "cpu_info") == 0) { 121 if ((name = malloc(20)) == NULL) 122 break; 123 /* 124 * For consistency, keep cpu[0] and toss cpu0 125 * or any other such symbols. 126 */ 127 if (ksp->ks_instance == 0) 128 remove_symbol((uintptr_t)ksp->ks_private); 129 (void) sprintf(name, "cpu[%d]", ksp->ks_instance); 130 add_symbol(name, (uintptr_t)ksp->ks_private, 131 sizeof (struct cpu)); 132 } 133 } 134 (void) kstat_close(kc); 135 } 136 #else 137 /* FreeBSD */ 138 static void 139 fake_up_certain_popular_kernel_symbols(void) 140 { 141 char *name; 142 uintptr_t addr; 143 int i; 144 145 /* Good for up to 256 CPUs */ 146 for(i=0; i < 256; i++) { 147 if ((name = malloc(20)) == NULL) 148 break; 149 (void) sprintf(name, "cpu[%d]", i); 150 addr = 0x01000000 + (i << 16); 151 add_symbol(name, addr, sizeof (uintptr_t)); 152 } 153 } 154 #endif /* !defined(sun) */ 155 156 static int 157 symcmp(const void *p1, const void *p2) 158 { 159 uintptr_t a1 = ((syment_t *)p1)->addr; 160 uintptr_t a2 = ((syment_t *)p2)->addr; 161 162 if (a1 < a2) 163 return (-1); 164 if (a1 > a2) 165 return (1); 166 return (0); 167 } 168 169 int 170 symtab_init(void) 171 { 172 Elf *elf; 173 Elf_Scn *scn = NULL; 174 Sym *symtab, *symp, *lastsym; 175 char *strtab; 176 uint_t cnt; 177 int fd; 178 int i; 179 int strindex = -1; 180 #if !defined(sun) 181 void *ksyms; 182 size_t sz; 183 #endif 184 185 #if defined(__FreeBSD__) 186 if ((fd = open("/dev/ksyms", O_RDONLY)) == -1) { 187 if (errno == ENOENT && modfind("ksyms") == -1) { 188 kldload("ksyms"); 189 fd = open("/dev/ksyms", O_RDONLY); 190 } 191 if (fd == -1) 192 return (-1); 193 } 194 #else 195 if ((fd = open("/dev/ksyms", O_RDONLY)) == -1) 196 return (-1); 197 #endif 198 199 #if defined(sun) 200 (void) elf_version(EV_CURRENT); 201 202 elf = elf_begin(fd, ELF_C_READ, NULL); 203 #else 204 /* FreeBSD */ 205 /* 206 * XXX - libelf needs to be fixed so it will work with 207 * non 'ordinary' files like /dev/ksyms. The following 208 * is a work around for now. 209 */ 210 if (elf_version(EV_CURRENT) == EV_NONE) { 211 close(fd); 212 return (-1); 213 } 214 if (ioctl(fd, KIOCGSIZE, &sz) < 0) { 215 close(fd); 216 return (-1); 217 } 218 if (ioctl(fd, KIOCGADDR, &ksyms) < 0) { 219 close(fd); 220 return (-1); 221 } 222 if ((elf = elf_memory(ksyms, sz)) == NULL) { 223 close(fd); 224 return (-1); 225 } 226 #endif 227 228 for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) { 229 Shdr *shdr = elf_getshdr(scn); 230 if (shdr->sh_type == SHT_SYMTAB) { 231 symtab = (Sym *)elf_getdata(scn, NULL)->d_buf; 232 nsyms = shdr->sh_size / shdr->sh_entsize; 233 strindex = shdr->sh_link; 234 } 235 } 236 237 for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) { 238 if (cnt == strindex) 239 strtab = (char *)elf_getdata(scn, NULL)->d_buf; 240 } 241 242 lastsym = symtab + nsyms; 243 nsyms = 0; 244 for (symp = symtab; symp < lastsym; symp++) 245 if ((uint_t)ELF32_ST_TYPE(symp->st_info) <= STT_FUNC && 246 symp->st_size != 0) 247 add_symbol(symp->st_name + strtab, 248 (uintptr_t)symp->st_value, (size_t)symp->st_size); 249 250 fake_up_certain_popular_kernel_symbols(); 251 (void) sprintf(maxsymname, "0x%lx", ULONG_MAX); 252 add_symbol(maxsymname, ULONG_MAX, 1); 253 254 qsort(symbol_table, nsyms, sizeof (syment_t), symcmp); 255 256 /* 257 * Destroy all duplicate symbols, then sort it again. 258 */ 259 for (i = 0; i < nsyms - 1; i++) 260 if (symbol_table[i].addr == symbol_table[i + 1].addr) 261 symbol_table[i].addr = 0; 262 263 qsort(symbol_table, nsyms, sizeof (syment_t), symcmp); 264 265 while (symbol_table[1].addr == 0) { 266 symbol_table++; 267 nsyms--; 268 } 269 symbol_table[0].name = "(usermode)"; 270 symbol_table[0].addr = 0; 271 symbol_table[0].size = 1; 272 273 close(fd); 274 return (0); 275 } 276 277 char * 278 addr_to_sym(uintptr_t addr, uintptr_t *offset, size_t *sizep) 279 { 280 int lo = 0; 281 int hi = nsyms - 1; 282 int mid; 283 syment_t *sep; 284 285 while (hi - lo > 1) { 286 mid = (lo + hi) / 2; 287 if (addr >= symbol_table[mid].addr) { 288 lo = mid; 289 } else { 290 hi = mid; 291 } 292 } 293 sep = &symbol_table[lo]; 294 *offset = addr - sep->addr; 295 *sizep = sep->size; 296 return (sep->name); 297 } 298 299 uintptr_t 300 sym_to_addr(char *name) 301 { 302 int i; 303 syment_t *sep = symbol_table; 304 305 for (i = 0; i < nsyms; i++) { 306 if (strcmp(name, sep->name) == 0) 307 return (sep->addr); 308 sep++; 309 } 310 return (0); 311 } 312 313 size_t 314 sym_size(char *name) 315 { 316 int i; 317 syment_t *sep = symbol_table; 318 319 for (i = 0; i < nsyms; i++) { 320 if (strcmp(name, sep->name) == 0) 321 return (sep->size); 322 sep++; 323 } 324 return (0); 325 } 326