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>
45192867Ssson#if defined(sun)
46192867Ssson#include <sys/machelf.h>
47192867Ssson
48192867Ssson#include <kstat.h>
49192867Ssson#else
50192867Ssson/* FreeBSD */
51192867Ssson#include <sys/elf.h>
52192867Ssson#include <sys/ksyms.h>
53192867Ssson#endif
54192867Ssson#include <sys/cpuvar.h>
55192867Ssson
56192867Sssontypedef struct syment {
57192867Ssson	uintptr_t	addr;
58192867Ssson	char		*name;
59192867Ssson	size_t		size;
60192867Ssson} syment_t;
61192867Ssson
62192867Sssonstatic syment_t *symbol_table;
63192867Sssonstatic int nsyms, maxsyms;
64192867Sssonstatic char maxsymname[64];
65192867Ssson
66192867Ssson#if defined(sun)
67192867Ssson#ifdef _ELF64
68192867Ssson#define	elf_getshdr elf64_getshdr
69192867Ssson#else
70192867Ssson#define	elf_getshdr elf32_getshdr
71192867Ssson#endif
72192867Ssson#endif
73192867Ssson
74192867Sssonstatic void
75192867Sssonadd_symbol(char *name, uintptr_t addr, size_t size)
76192867Ssson{
77192867Ssson	syment_t *sep;
78192867Ssson
79192867Ssson	if (nsyms >= maxsyms) {
80192867Ssson		maxsyms += 10000;
81192867Ssson		symbol_table = realloc(symbol_table, maxsyms * sizeof (*sep));
82192867Ssson		if (symbol_table == NULL) {
83192867Ssson			(void) fprintf(stderr, "can't allocate symbol table\n");
84192867Ssson			exit(3);
85192867Ssson		}
86192867Ssson	}
87192867Ssson	sep = &symbol_table[nsyms++];
88192867Ssson
89192867Ssson	sep->name = name;
90192867Ssson	sep->addr = addr;
91192867Ssson	sep->size = size;
92192867Ssson}
93192867Ssson
94192867Sssonstatic void
95192867Sssonremove_symbol(uintptr_t addr)
96192867Ssson{
97192867Ssson	int i;
98192867Ssson	syment_t *sep = symbol_table;
99192867Ssson
100192867Ssson	for (i = 0; i < nsyms; i++, sep++)
101192867Ssson		if (sep->addr == addr)
102192867Ssson			sep->addr = 0;
103192867Ssson}
104192867Ssson
105192867Ssson#if defined(sun)
106192867Sssonstatic void
107192867Sssonfake_up_certain_popular_kernel_symbols(void)
108192867Ssson{
109192867Ssson	kstat_ctl_t *kc;
110192867Ssson	kstat_t *ksp;
111192867Ssson	char *name;
112192867Ssson
113192867Ssson	if ((kc = kstat_open()) == NULL)
114192867Ssson		return;
115192867Ssson
116192867Ssson	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
117192867Ssson		if (strcmp(ksp->ks_module, "cpu_info") == 0) {
118192867Ssson			if ((name = malloc(20)) == NULL)
119192867Ssson				break;
120192867Ssson			/*
121192867Ssson			 * For consistency, keep cpu[0] and toss cpu0
122192867Ssson			 * or any other such symbols.
123192867Ssson			 */
124192867Ssson			if (ksp->ks_instance == 0)
125192867Ssson				remove_symbol((uintptr_t)ksp->ks_private);
126192867Ssson			(void) sprintf(name, "cpu[%d]", ksp->ks_instance);
127192867Ssson			add_symbol(name, (uintptr_t)ksp->ks_private,
128192867Ssson			    sizeof (struct cpu));
129192867Ssson		}
130192867Ssson	}
131192867Ssson	(void) kstat_close(kc);
132192867Ssson}
133192867Ssson#else
134192867Ssson/* FreeBSD */
135192867Sssonstatic void
136192867Sssonfake_up_certain_popular_kernel_symbols(void)
137192867Ssson{
138192867Ssson	char *name;
139192867Ssson	uintptr_t addr;
140192867Ssson	int i;
141192867Ssson
142192867Ssson	/* Good for up to 256 CPUs */
143192867Ssson	for(i=0; i < 256;  i++) {
144192867Ssson		if ((name = malloc(20)) == NULL)
145192867Ssson			break;
146192867Ssson		(void) sprintf(name, "cpu[%d]", i);
147192867Ssson		addr = 0x01000000 + (i << 16);
148192867Ssson		add_symbol(name, addr, sizeof (uintptr_t));
149192867Ssson	}
150192867Ssson}
151192867Ssson#endif /* !defined(sun) */
152192867Ssson
153192867Sssonstatic int
154192867Sssonsymcmp(const void *p1, const void *p2)
155192867Ssson{
156192867Ssson	uintptr_t a1 = ((syment_t *)p1)->addr;
157192867Ssson	uintptr_t a2 = ((syment_t *)p2)->addr;
158192867Ssson
159192867Ssson	if (a1 < a2)
160192867Ssson		return (-1);
161192867Ssson	if (a1 > a2)
162192867Ssson		return (1);
163192867Ssson	return (0);
164192867Ssson}
165192867Ssson
166192867Sssonint
167192867Sssonsymtab_init(void)
168192867Ssson{
169192867Ssson	Elf		*elf;
170192867Ssson	Elf_Scn		*scn = NULL;
171192867Ssson	Sym		*symtab, *symp, *lastsym;
172192867Ssson	char		*strtab;
173192867Ssson	uint_t		cnt;
174192867Ssson	int		fd;
175192867Ssson	int		i;
176192867Ssson	int		strindex = -1;
177192867Ssson#if !defined(sun)
178192867Ssson	void		*ksyms;
179192867Ssson	size_t		sz;
180192867Ssson#endif
181192867Ssson
182192867Ssson	if ((fd = open("/dev/ksyms", O_RDONLY)) == -1)
183192867Ssson		return (-1);
184192867Ssson
185192867Ssson#if defined(sun)
186192867Ssson	(void) elf_version(EV_CURRENT);
187192867Ssson
188192867Ssson	elf = elf_begin(fd, ELF_C_READ, NULL);
189192867Ssson#else
190192867Ssson	/* FreeBSD */
191192867Ssson	/*
192192867Ssson	 * XXX - libelf needs to be fixed so it will work with
193192867Ssson	 * non 'ordinary' files like /dev/ksyms.  The following
194192867Ssson	 * is a work around for now.
195192867Ssson	 */
196192867Ssson	if (elf_version(EV_CURRENT) == EV_NONE) {
197192867Ssson		close(fd);
198192867Ssson		return (-1);
199192867Ssson	}
200192867Ssson	if (ioctl(fd, KIOCGSIZE, &sz) < 0) {
201192867Ssson		close(fd);
202192867Ssson		return (-1);
203192867Ssson	}
204192867Ssson	if (ioctl(fd, KIOCGADDR, &ksyms) < 0) {
205192867Ssson		close(fd);
206192867Ssson		return (-1);
207192867Ssson	}
208192867Ssson	if ((elf = elf_memory(ksyms, sz)) == NULL) {
209192867Ssson		close(fd);
210192867Ssson		return (-1);
211192867Ssson	}
212192867Ssson#endif
213192867Ssson
214192867Ssson	for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) {
215192867Ssson		Shdr *shdr = elf_getshdr(scn);
216192867Ssson		if (shdr->sh_type == SHT_SYMTAB) {
217192867Ssson			symtab = (Sym *)elf_getdata(scn, NULL)->d_buf;
218192867Ssson			nsyms = shdr->sh_size / shdr->sh_entsize;
219192867Ssson			strindex = shdr->sh_link;
220192867Ssson		}
221192867Ssson	}
222192867Ssson
223192867Ssson	for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) {
224192867Ssson		if (cnt == strindex)
225192867Ssson			strtab = (char *)elf_getdata(scn, NULL)->d_buf;
226192867Ssson	}
227192867Ssson
228192867Ssson	lastsym = symtab + nsyms;
229192867Ssson	nsyms = 0;
230192867Ssson	for (symp = symtab; symp < lastsym; symp++)
231192867Ssson		if ((uint_t)ELF32_ST_TYPE(symp->st_info) <= STT_FUNC &&
232192867Ssson		    symp->st_size != 0)
233192867Ssson			add_symbol(symp->st_name + strtab,
234192867Ssson			    (uintptr_t)symp->st_value, (size_t)symp->st_size);
235192867Ssson
236192867Ssson	fake_up_certain_popular_kernel_symbols();
237192867Ssson	(void) sprintf(maxsymname, "0x%lx", ULONG_MAX);
238192867Ssson	add_symbol(maxsymname, ULONG_MAX, 1);
239192867Ssson
240192867Ssson	qsort(symbol_table, nsyms, sizeof (syment_t), symcmp);
241192867Ssson
242192867Ssson	/*
243192867Ssson	 * Destroy all duplicate symbols, then sort it again.
244192867Ssson	 */
245192867Ssson	for (i = 0; i < nsyms - 1; i++)
246192867Ssson		if (symbol_table[i].addr == symbol_table[i + 1].addr)
247192867Ssson			symbol_table[i].addr = 0;
248192867Ssson
249192867Ssson	qsort(symbol_table, nsyms, sizeof (syment_t), symcmp);
250192867Ssson
251192867Ssson	while (symbol_table[1].addr == 0) {
252192867Ssson		symbol_table++;
253192867Ssson		nsyms--;
254192867Ssson	}
255192867Ssson	symbol_table[0].name = "(usermode)";
256192867Ssson	symbol_table[0].addr = 0;
257192867Ssson	symbol_table[0].size = 1;
258192867Ssson
259192867Ssson	close(fd);
260192867Ssson	return (0);
261192867Ssson}
262192867Ssson
263192867Sssonchar *
264192867Sssonaddr_to_sym(uintptr_t addr, uintptr_t *offset, size_t *sizep)
265192867Ssson{
266192867Ssson	int lo = 0;
267192867Ssson	int hi = nsyms - 1;
268192867Ssson	int mid;
269192867Ssson	syment_t *sep;
270192867Ssson
271192867Ssson	while (hi - lo > 1) {
272192867Ssson		mid = (lo + hi) / 2;
273192867Ssson		if (addr >= symbol_table[mid].addr) {
274192867Ssson			lo = mid;
275192867Ssson		} else {
276192867Ssson			hi = mid;
277192867Ssson		}
278192867Ssson	}
279192867Ssson	sep = &symbol_table[lo];
280192867Ssson	*offset = addr - sep->addr;
281192867Ssson	*sizep = sep->size;
282192867Ssson	return (sep->name);
283192867Ssson}
284192867Ssson
285192867Sssonuintptr_t
286192867Sssonsym_to_addr(char *name)
287192867Ssson{
288192867Ssson	int i;
289192867Ssson	syment_t *sep = symbol_table;
290192867Ssson
291192867Ssson	for (i = 0; i < nsyms; i++) {
292192867Ssson		if (strcmp(name, sep->name) == 0)
293192867Ssson			return (sep->addr);
294192867Ssson		sep++;
295192867Ssson	}
296192867Ssson	return (0);
297192867Ssson}
298192867Ssson
299192867Sssonsize_t
300192867Sssonsym_size(char *name)
301192867Ssson{
302192867Ssson	int i;
303192867Ssson	syment_t *sep = symbol_table;
304192867Ssson
305192867Ssson	for (i = 0; i < nsyms; i++) {
306192867Ssson		if (strcmp(name, sep->name) == 0)
307192867Ssson			return (sep->size);
308192867Ssson		sep++;
309192867Ssson	}
310192867Ssson	return (0);
311192867Ssson}
312