1192867Ssson/*
2192867Ssson * CDDL HEADER START
3192867Ssson *
4192867Ssson * The contents of this file are subject to the terms of the
5192867Ssson * Common Development and Distribution License, Version 1.0 only
6192867Ssson * (the "License").  You may not use this file except in compliance
7192867Ssson * with the License.
8192867Ssson *
9192867Ssson * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10192867Ssson * or http://www.opensolaris.org/os/licensing.
11192867Ssson * See the License for the specific language governing permissions
12192867Ssson * and limitations under the License.
13192867Ssson *
14192867Ssson * When distributing Covered Code, include this CDDL HEADER in each
15192867Ssson * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16192867Ssson * If applicable, add the following below this CDDL HEADER, with the
17192867Ssson * fields enclosed by brackets "[]" replaced with your own identifying
18192867Ssson * information: Portions Copyright [yyyy] [name of copyright owner]
19192867Ssson *
20192867Ssson * CDDL HEADER END
21192867Ssson */
22192867Ssson/*
23192867Ssson * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
24192867Ssson * All rights reserved.
25192867Ssson */
26192867Ssson
27192867Ssson#pragma ident	"%Z%%M%	%I%	%E% SMI"
28192867Ssson
29192867Ssson#include <stdio.h>
30192867Ssson#include <fcntl.h>
31192867Ssson#include <ctype.h>
32192867Ssson#include <string.h>
33192867Ssson#include <signal.h>
34192867Ssson#include <errno.h>
35192867Ssson#include <stdlib.h>
36192867Ssson#include <stdarg.h>
37192867Ssson#include <unistd.h>
38192867Ssson#include <limits.h>
39192867Ssson#include <sys/types.h>
40192867Ssson#include <sys/stat.h>
41192867Ssson
42192867Ssson#include <libelf.h>
43192867Ssson#include <link.h>
44192867Ssson#include <elf.h>
45277300Ssmh#ifdef illumos
46192867Ssson#include <sys/machelf.h>
47192867Ssson
48192867Ssson#include <kstat.h>
49192867Ssson#else
50192867Ssson#include <sys/elf.h>
51192867Ssson#include <sys/ksyms.h>
52270315Sdelphij#include <sys/param.h>
53270315Sdelphij#include <sys/module.h>
54270315Sdelphij#include <sys/linker.h>
55192867Ssson#endif
56192867Ssson#include <sys/cpuvar.h>
57192867Ssson
58192867Sssontypedef struct syment {
59192867Ssson	uintptr_t	addr;
60192867Ssson	char		*name;
61192867Ssson	size_t		size;
62192867Ssson} syment_t;
63192867Ssson
64192867Sssonstatic syment_t *symbol_table;
65192867Sssonstatic int nsyms, maxsyms;
66192867Sssonstatic char maxsymname[64];
67192867Ssson
68277300Ssmh#ifdef illumos
69192867Ssson#ifdef _ELF64
70192867Ssson#define	elf_getshdr elf64_getshdr
71192867Ssson#else
72192867Ssson#define	elf_getshdr elf32_getshdr
73192867Ssson#endif
74192867Ssson#endif
75192867Ssson
76192867Sssonstatic void
77192867Sssonadd_symbol(char *name, uintptr_t addr, size_t size)
78192867Ssson{
79192867Ssson	syment_t *sep;
80192867Ssson
81192867Ssson	if (nsyms >= maxsyms) {
82192867Ssson		maxsyms += 10000;
83192867Ssson		symbol_table = realloc(symbol_table, maxsyms * sizeof (*sep));
84192867Ssson		if (symbol_table == NULL) {
85192867Ssson			(void) fprintf(stderr, "can't allocate symbol table\n");
86192867Ssson			exit(3);
87192867Ssson		}
88192867Ssson	}
89192867Ssson	sep = &symbol_table[nsyms++];
90192867Ssson
91192867Ssson	sep->name = name;
92192867Ssson	sep->addr = addr;
93192867Ssson	sep->size = size;
94192867Ssson}
95192867Ssson
96192867Sssonstatic void
97192867Sssonremove_symbol(uintptr_t addr)
98192867Ssson{
99192867Ssson	int i;
100192867Ssson	syment_t *sep = symbol_table;
101192867Ssson
102192867Ssson	for (i = 0; i < nsyms; i++, sep++)
103192867Ssson		if (sep->addr == addr)
104192867Ssson			sep->addr = 0;
105192867Ssson}
106192867Ssson
107277300Ssmh#ifdef illumos
108192867Sssonstatic void
109192867Sssonfake_up_certain_popular_kernel_symbols(void)
110192867Ssson{
111192867Ssson	kstat_ctl_t *kc;
112192867Ssson	kstat_t *ksp;
113192867Ssson	char *name;
114192867Ssson
115192867Ssson	if ((kc = kstat_open()) == NULL)
116192867Ssson		return;
117192867Ssson
118192867Ssson	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
119192867Ssson		if (strcmp(ksp->ks_module, "cpu_info") == 0) {
120192867Ssson			if ((name = malloc(20)) == NULL)
121192867Ssson				break;
122192867Ssson			/*
123192867Ssson			 * For consistency, keep cpu[0] and toss cpu0
124192867Ssson			 * or any other such symbols.
125192867Ssson			 */
126192867Ssson			if (ksp->ks_instance == 0)
127192867Ssson				remove_symbol((uintptr_t)ksp->ks_private);
128192867Ssson			(void) sprintf(name, "cpu[%d]", ksp->ks_instance);
129192867Ssson			add_symbol(name, (uintptr_t)ksp->ks_private,
130192867Ssson			    sizeof (struct cpu));
131192867Ssson		}
132192867Ssson	}
133192867Ssson	(void) kstat_close(kc);
134192867Ssson}
135277300Ssmh#else /* !illumos */
136192867Sssonstatic void
137192867Sssonfake_up_certain_popular_kernel_symbols(void)
138192867Ssson{
139192867Ssson	char *name;
140192867Ssson	uintptr_t addr;
141192867Ssson	int i;
142192867Ssson
143192867Ssson	/* Good for up to 256 CPUs */
144192867Ssson	for(i=0; i < 256;  i++) {
145192867Ssson		if ((name = malloc(20)) == NULL)
146192867Ssson			break;
147192867Ssson		(void) sprintf(name, "cpu[%d]", i);
148192867Ssson		addr = 0x01000000 + (i << 16);
149192867Ssson		add_symbol(name, addr, sizeof (uintptr_t));
150192867Ssson	}
151192867Ssson}
152277300Ssmh#endif /* illumos */
153192867Ssson
154192867Sssonstatic int
155192867Sssonsymcmp(const void *p1, const void *p2)
156192867Ssson{
157192867Ssson	uintptr_t a1 = ((syment_t *)p1)->addr;
158192867Ssson	uintptr_t a2 = ((syment_t *)p2)->addr;
159192867Ssson
160192867Ssson	if (a1 < a2)
161192867Ssson		return (-1);
162192867Ssson	if (a1 > a2)
163192867Ssson		return (1);
164192867Ssson	return (0);
165192867Ssson}
166192867Ssson
167192867Sssonint
168192867Sssonsymtab_init(void)
169192867Ssson{
170192867Ssson	Elf		*elf;
171192867Ssson	Elf_Scn		*scn = NULL;
172192867Ssson	Sym		*symtab, *symp, *lastsym;
173192867Ssson	char		*strtab;
174192867Ssson	uint_t		cnt;
175192867Ssson	int		fd;
176192867Ssson	int		i;
177192867Ssson	int		strindex = -1;
178277300Ssmh#ifndef illumos
179192867Ssson	void		*ksyms;
180192867Ssson	size_t		sz;
181192867Ssson#endif
182192867Ssson
183277300Ssmh#ifndef illumos
184257213Smarkj	if ((fd = open("/dev/ksyms", O_RDONLY)) == -1) {
185257213Smarkj		if (errno == ENOENT && modfind("ksyms") == -1) {
186257213Smarkj			kldload("ksyms");
187257213Smarkj			fd = open("/dev/ksyms", O_RDONLY);
188257213Smarkj		}
189257213Smarkj		if (fd == -1)
190257213Smarkj			return (-1);
191257213Smarkj	}
192257213Smarkj#else
193192867Ssson	if ((fd = open("/dev/ksyms", O_RDONLY)) == -1)
194192867Ssson		return (-1);
195257213Smarkj#endif
196192867Ssson
197277300Ssmh#ifdef illumos
198192867Ssson	(void) elf_version(EV_CURRENT);
199192867Ssson
200192867Ssson	elf = elf_begin(fd, ELF_C_READ, NULL);
201192867Ssson#else
202192867Ssson	/*
203192867Ssson	 * XXX - libelf needs to be fixed so it will work with
204192867Ssson	 * non 'ordinary' files like /dev/ksyms.  The following
205192867Ssson	 * is a work around for now.
206192867Ssson	 */
207192867Ssson	if (elf_version(EV_CURRENT) == EV_NONE) {
208192867Ssson		close(fd);
209192867Ssson		return (-1);
210192867Ssson	}
211192867Ssson	if (ioctl(fd, KIOCGSIZE, &sz) < 0) {
212192867Ssson		close(fd);
213192867Ssson		return (-1);
214192867Ssson	}
215192867Ssson	if (ioctl(fd, KIOCGADDR, &ksyms) < 0) {
216192867Ssson		close(fd);
217192867Ssson		return (-1);
218192867Ssson	}
219192867Ssson	if ((elf = elf_memory(ksyms, sz)) == NULL) {
220192867Ssson		close(fd);
221192867Ssson		return (-1);
222192867Ssson	}
223192867Ssson#endif
224192867Ssson
225192867Ssson	for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) {
226192867Ssson		Shdr *shdr = elf_getshdr(scn);
227192867Ssson		if (shdr->sh_type == SHT_SYMTAB) {
228192867Ssson			symtab = (Sym *)elf_getdata(scn, NULL)->d_buf;
229192867Ssson			nsyms = shdr->sh_size / shdr->sh_entsize;
230192867Ssson			strindex = shdr->sh_link;
231192867Ssson		}
232192867Ssson	}
233192867Ssson
234192867Ssson	for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) {
235192867Ssson		if (cnt == strindex)
236192867Ssson			strtab = (char *)elf_getdata(scn, NULL)->d_buf;
237192867Ssson	}
238192867Ssson
239192867Ssson	lastsym = symtab + nsyms;
240192867Ssson	nsyms = 0;
241192867Ssson	for (symp = symtab; symp < lastsym; symp++)
242192867Ssson		if ((uint_t)ELF32_ST_TYPE(symp->st_info) <= STT_FUNC &&
243192867Ssson		    symp->st_size != 0)
244192867Ssson			add_symbol(symp->st_name + strtab,
245192867Ssson			    (uintptr_t)symp->st_value, (size_t)symp->st_size);
246192867Ssson
247192867Ssson	fake_up_certain_popular_kernel_symbols();
248192867Ssson	(void) sprintf(maxsymname, "0x%lx", ULONG_MAX);
249192867Ssson	add_symbol(maxsymname, ULONG_MAX, 1);
250192867Ssson
251192867Ssson	qsort(symbol_table, nsyms, sizeof (syment_t), symcmp);
252192867Ssson
253192867Ssson	/*
254192867Ssson	 * Destroy all duplicate symbols, then sort it again.
255192867Ssson	 */
256192867Ssson	for (i = 0; i < nsyms - 1; i++)
257192867Ssson		if (symbol_table[i].addr == symbol_table[i + 1].addr)
258192867Ssson			symbol_table[i].addr = 0;
259192867Ssson
260192867Ssson	qsort(symbol_table, nsyms, sizeof (syment_t), symcmp);
261192867Ssson
262192867Ssson	while (symbol_table[1].addr == 0) {
263192867Ssson		symbol_table++;
264192867Ssson		nsyms--;
265192867Ssson	}
266192867Ssson	symbol_table[0].name = "(usermode)";
267192867Ssson	symbol_table[0].addr = 0;
268192867Ssson	symbol_table[0].size = 1;
269192867Ssson
270192867Ssson	close(fd);
271192867Ssson	return (0);
272192867Ssson}
273192867Ssson
274192867Sssonchar *
275192867Sssonaddr_to_sym(uintptr_t addr, uintptr_t *offset, size_t *sizep)
276192867Ssson{
277192867Ssson	int lo = 0;
278192867Ssson	int hi = nsyms - 1;
279192867Ssson	int mid;
280192867Ssson	syment_t *sep;
281192867Ssson
282192867Ssson	while (hi - lo > 1) {
283192867Ssson		mid = (lo + hi) / 2;
284192867Ssson		if (addr >= symbol_table[mid].addr) {
285192867Ssson			lo = mid;
286192867Ssson		} else {
287192867Ssson			hi = mid;
288192867Ssson		}
289192867Ssson	}
290192867Ssson	sep = &symbol_table[lo];
291192867Ssson	*offset = addr - sep->addr;
292192867Ssson	*sizep = sep->size;
293192867Ssson	return (sep->name);
294192867Ssson}
295192867Ssson
296192867Sssonuintptr_t
297192867Sssonsym_to_addr(char *name)
298192867Ssson{
299192867Ssson	int i;
300192867Ssson	syment_t *sep = symbol_table;
301192867Ssson
302192867Ssson	for (i = 0; i < nsyms; i++) {
303192867Ssson		if (strcmp(name, sep->name) == 0)
304192867Ssson			return (sep->addr);
305192867Ssson		sep++;
306192867Ssson	}
307192867Ssson	return (0);
308192867Ssson}
309192867Ssson
310192867Sssonsize_t
311192867Sssonsym_size(char *name)
312192867Ssson{
313192867Ssson	int i;
314192867Ssson	syment_t *sep = symbol_table;
315192867Ssson
316192867Ssson	for (i = 0; i < nsyms; i++) {
317192867Ssson		if (strcmp(name, sep->name) == 0)
318192867Ssson			return (sep->size);
319192867Ssson		sep++;
320192867Ssson	}
321192867Ssson	return (0);
322192867Ssson}
323