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 #endif 54 #include <sys/cpuvar.h> 55 56 typedef struct syment { 57 uintptr_t addr; 58 char *name; 59 size_t size; 60 } syment_t; 61 62 static syment_t *symbol_table; 63 static int nsyms, maxsyms; 64 static char maxsymname[64]; 65 66 #if defined(sun) 67 #ifdef _ELF64 68 #define elf_getshdr elf64_getshdr 69 #else 70 #define elf_getshdr elf32_getshdr 71 #endif 72 #endif 73 74 static void 75 add_symbol(char *name, uintptr_t addr, size_t size) 76 { 77 syment_t *sep; 78 79 if (nsyms >= maxsyms) { 80 maxsyms += 10000; 81 symbol_table = realloc(symbol_table, maxsyms * sizeof (*sep)); 82 if (symbol_table == NULL) { 83 (void) fprintf(stderr, "can't allocate symbol table\n"); 84 exit(3); 85 } 86 } 87 sep = &symbol_table[nsyms++]; 88 89 sep->name = name; 90 sep->addr = addr; 91 sep->size = size; 92 } 93 94 static void 95 remove_symbol(uintptr_t addr) 96 { 97 int i; 98 syment_t *sep = symbol_table; 99 100 for (i = 0; i < nsyms; i++, sep++) 101 if (sep->addr == addr) 102 sep->addr = 0; 103 } 104 105 #if defined(sun) 106 static void 107 fake_up_certain_popular_kernel_symbols(void) 108 { 109 kstat_ctl_t *kc; 110 kstat_t *ksp; 111 char *name; 112 113 if ((kc = kstat_open()) == NULL) 114 return; 115 116 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 117 if (strcmp(ksp->ks_module, "cpu_info") == 0) { 118 if ((name = malloc(20)) == NULL) 119 break; 120 /* 121 * For consistency, keep cpu[0] and toss cpu0 122 * or any other such symbols. 123 */ 124 if (ksp->ks_instance == 0) 125 remove_symbol((uintptr_t)ksp->ks_private); 126 (void) sprintf(name, "cpu[%d]", ksp->ks_instance); 127 add_symbol(name, (uintptr_t)ksp->ks_private, 128 sizeof (struct cpu)); 129 } 130 } 131 (void) kstat_close(kc); 132 } 133 #else 134 /* FreeBSD */ 135 static void 136 fake_up_certain_popular_kernel_symbols(void) 137 { 138 char *name; 139 uintptr_t addr; 140 int i; 141 142 /* Good for up to 256 CPUs */ 143 for(i=0; i < 256; i++) { 144 if ((name = malloc(20)) == NULL) 145 break; 146 (void) sprintf(name, "cpu[%d]", i); 147 addr = 0x01000000 + (i << 16); 148 add_symbol(name, addr, sizeof (uintptr_t)); 149 } 150 } 151 #endif /* !defined(sun) */ 152 153 static int 154 symcmp(const void *p1, const void *p2) 155 { 156 uintptr_t a1 = ((syment_t *)p1)->addr; 157 uintptr_t a2 = ((syment_t *)p2)->addr; 158 159 if (a1 < a2) 160 return (-1); 161 if (a1 > a2) 162 return (1); 163 return (0); 164 } 165 166 int 167 symtab_init(void) 168 { 169 Elf *elf; 170 Elf_Scn *scn = NULL; 171 Sym *symtab, *symp, *lastsym; 172 char *strtab; 173 uint_t cnt; 174 int fd; 175 int i; 176 int strindex = -1; 177 #if !defined(sun) 178 void *ksyms; 179 size_t sz; 180 #endif 181 182 if ((fd = open("/dev/ksyms", O_RDONLY)) == -1) 183 return (-1); 184 185 #if defined(sun) 186 (void) elf_version(EV_CURRENT); 187 188 elf = elf_begin(fd, ELF_C_READ, NULL); 189 #else 190 /* FreeBSD */ 191 /* 192 * XXX - libelf needs to be fixed so it will work with 193 * non 'ordinary' files like /dev/ksyms. The following 194 * is a work around for now. 195 */ 196 if (elf_version(EV_CURRENT) == EV_NONE) { 197 close(fd); 198 return (-1); 199 } 200 if (ioctl(fd, KIOCGSIZE, &sz) < 0) { 201 close(fd); 202 return (-1); 203 } 204 if (ioctl(fd, KIOCGADDR, &ksyms) < 0) { 205 close(fd); 206 return (-1); 207 } 208 if ((elf = elf_memory(ksyms, sz)) == NULL) { 209 close(fd); 210 return (-1); 211 } 212 #endif 213 214 for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) { 215 Shdr *shdr = elf_getshdr(scn); 216 if (shdr->sh_type == SHT_SYMTAB) { 217 symtab = (Sym *)elf_getdata(scn, NULL)->d_buf; 218 nsyms = shdr->sh_size / shdr->sh_entsize; 219 strindex = shdr->sh_link; 220 } 221 } 222 223 for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) { 224 if (cnt == strindex) 225 strtab = (char *)elf_getdata(scn, NULL)->d_buf; 226 } 227 228 lastsym = symtab + nsyms; 229 nsyms = 0; 230 for (symp = symtab; symp < lastsym; symp++) 231 if ((uint_t)ELF32_ST_TYPE(symp->st_info) <= STT_FUNC && 232 symp->st_size != 0) 233 add_symbol(symp->st_name + strtab, 234 (uintptr_t)symp->st_value, (size_t)symp->st_size); 235 236 fake_up_certain_popular_kernel_symbols(); 237 (void) sprintf(maxsymname, "0x%lx", ULONG_MAX); 238 add_symbol(maxsymname, ULONG_MAX, 1); 239 240 qsort(symbol_table, nsyms, sizeof (syment_t), symcmp); 241 242 /* 243 * Destroy all duplicate symbols, then sort it again. 244 */ 245 for (i = 0; i < nsyms - 1; i++) 246 if (symbol_table[i].addr == symbol_table[i + 1].addr) 247 symbol_table[i].addr = 0; 248 249 qsort(symbol_table, nsyms, sizeof (syment_t), symcmp); 250 251 while (symbol_table[1].addr == 0) { 252 symbol_table++; 253 nsyms--; 254 } 255 symbol_table[0].name = "(usermode)"; 256 symbol_table[0].addr = 0; 257 symbol_table[0].size = 1; 258 259 close(fd); 260 return (0); 261 } 262 263 char * 264 addr_to_sym(uintptr_t addr, uintptr_t *offset, size_t *sizep) 265 { 266 int lo = 0; 267 int hi = nsyms - 1; 268 int mid; 269 syment_t *sep; 270 271 while (hi - lo > 1) { 272 mid = (lo + hi) / 2; 273 if (addr >= symbol_table[mid].addr) { 274 lo = mid; 275 } else { 276 hi = mid; 277 } 278 } 279 sep = &symbol_table[lo]; 280 *offset = addr - sep->addr; 281 *sizep = sep->size; 282 return (sep->name); 283 } 284 285 uintptr_t 286 sym_to_addr(char *name) 287 { 288 int i; 289 syment_t *sep = symbol_table; 290 291 for (i = 0; i < nsyms; i++) { 292 if (strcmp(name, sep->name) == 0) 293 return (sep->addr); 294 sep++; 295 } 296 return (0); 297 } 298 299 size_t 300 sym_size(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->size); 308 sep++; 309 } 310 return (0); 311 } 312