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