sym.c revision 323180
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/param.h> 52#include <sys/module.h> 53#include <sys/linker.h> 54#endif 55#include <sys/cpuvar.h> 56 57typedef struct syment { 58 uintptr_t addr; 59 char *name; 60 size_t size; 61} syment_t; 62 63static syment_t *symbol_table; 64static int nsyms, maxsyms; 65static char maxsymname[64]; 66 67#ifdef illumos 68#ifdef _ELF64 69#define elf_getshdr elf64_getshdr 70#else 71#define elf_getshdr elf32_getshdr 72#endif 73#endif 74 75static void 76add_symbol(char *name, uintptr_t addr, size_t size) 77{ 78 syment_t *sep; 79 80 if (nsyms >= maxsyms) { 81 maxsyms += 10000; 82 symbol_table = realloc(symbol_table, maxsyms * sizeof (*sep)); 83 if (symbol_table == NULL) { 84 (void) fprintf(stderr, "can't allocate symbol table\n"); 85 exit(3); 86 } 87 } 88 sep = &symbol_table[nsyms++]; 89 90 sep->name = name; 91 sep->addr = addr; 92 sep->size = size; 93} 94 95static void 96remove_symbol(uintptr_t addr) 97{ 98 int i; 99 syment_t *sep = symbol_table; 100 101 for (i = 0; i < nsyms; i++, sep++) 102 if (sep->addr == addr) 103 sep->addr = 0; 104} 105 106#ifdef illumos 107static void 108fake_up_certain_popular_kernel_symbols(void) 109{ 110 kstat_ctl_t *kc; 111 kstat_t *ksp; 112 char *name; 113 114 if ((kc = kstat_open()) == NULL) 115 return; 116 117 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 118 if (strcmp(ksp->ks_module, "cpu_info") == 0) { 119 if ((name = malloc(20)) == NULL) 120 break; 121 /* 122 * For consistency, keep cpu[0] and toss cpu0 123 * or any other such symbols. 124 */ 125 if (ksp->ks_instance == 0) 126 remove_symbol((uintptr_t)ksp->ks_private); 127 (void) sprintf(name, "cpu[%d]", ksp->ks_instance); 128 add_symbol(name, (uintptr_t)ksp->ks_private, 129 sizeof (struct cpu)); 130 } 131 } 132 (void) kstat_close(kc); 133} 134#else /* !illumos */ 135static void 136fake_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 /* illumos */ 152 153static int 154symcmp(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 166int 167symtab_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 178#ifndef illumos 179 if ((fd = open("/dev/ksyms", O_RDONLY)) == -1) { 180 if (errno == ENOENT && modfind("ksyms") == -1) { 181 kldload("ksyms"); 182 fd = open("/dev/ksyms", O_RDONLY); 183 } 184 if (fd == -1) 185 return (-1); 186 } 187#else 188 if ((fd = open("/dev/ksyms", O_RDONLY)) == -1) 189 return (-1); 190#endif 191 192 (void) elf_version(EV_CURRENT); 193 194 elf = elf_begin(fd, ELF_C_READ, NULL); 195 196 for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) { 197 Shdr *shdr = elf_getshdr(scn); 198 if (shdr->sh_type == SHT_SYMTAB) { 199 symtab = (Sym *)elf_getdata(scn, NULL)->d_buf; 200 nsyms = shdr->sh_size / shdr->sh_entsize; 201 strindex = shdr->sh_link; 202 } 203 } 204 205 for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) { 206 if (cnt == strindex) 207 strtab = (char *)elf_getdata(scn, NULL)->d_buf; 208 } 209 210 lastsym = symtab + nsyms; 211 nsyms = 0; 212 for (symp = symtab; symp < lastsym; symp++) 213 if ((uint_t)ELF32_ST_TYPE(symp->st_info) <= STT_FUNC && 214 symp->st_size != 0) 215 add_symbol(symp->st_name + strtab, 216 (uintptr_t)symp->st_value, (size_t)symp->st_size); 217 218 fake_up_certain_popular_kernel_symbols(); 219 (void) sprintf(maxsymname, "0x%lx", ULONG_MAX); 220 add_symbol(maxsymname, ULONG_MAX, 1); 221 222 qsort(symbol_table, nsyms, sizeof (syment_t), symcmp); 223 224 /* 225 * Destroy all duplicate symbols, then sort it again. 226 */ 227 for (i = 0; i < nsyms - 1; i++) 228 if (symbol_table[i].addr == symbol_table[i + 1].addr) 229 symbol_table[i].addr = 0; 230 231 qsort(symbol_table, nsyms, sizeof (syment_t), symcmp); 232 233 while (symbol_table[1].addr == 0) { 234 symbol_table++; 235 nsyms--; 236 } 237 symbol_table[0].name = "(usermode)"; 238 symbol_table[0].addr = 0; 239 symbol_table[0].size = 1; 240 241 close(fd); 242 return (0); 243} 244 245char * 246addr_to_sym(uintptr_t addr, uintptr_t *offset, size_t *sizep) 247{ 248 int lo = 0; 249 int hi = nsyms - 1; 250 int mid; 251 syment_t *sep; 252 253 while (hi - lo > 1) { 254 mid = (lo + hi) / 2; 255 if (addr >= symbol_table[mid].addr) { 256 lo = mid; 257 } else { 258 hi = mid; 259 } 260 } 261 sep = &symbol_table[lo]; 262 *offset = addr - sep->addr; 263 *sizep = sep->size; 264 return (sep->name); 265} 266 267uintptr_t 268sym_to_addr(char *name) 269{ 270 int i; 271 syment_t *sep = symbol_table; 272 273 for (i = 0; i < nsyms; i++) { 274 if (strcmp(name, sep->name) == 0) 275 return (sep->addr); 276 sep++; 277 } 278 return (0); 279} 280 281size_t 282sym_size(char *name) 283{ 284 int i; 285 syment_t *sep = symbol_table; 286 287 for (i = 0; i < nsyms; i++) { 288 if (strcmp(name, sep->name) == 0) 289 return (sep->size); 290 sep++; 291 } 292 return (0); 293} 294