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