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