who.c revision 12927:a27c46eb192b
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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25#include <stdio.h>
26#include <fcntl.h>
27#include <link.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <strings.h>
31#include <sys/regset.h>
32#include <sys/frame.h>
33#include <sys/stack.h>
34#include <signal.h>
35
36#include "env.h"
37#include "mach.h"
38#include "who.h"
39
40
41static int	detail_syms = 0;	/* display detail symbol information */
42static Objinfo	*objhead = NULL;	/* head of object list */
43static Elist	*funclist = NULL;
44static sigset_t	iset;
45
46static void
47add_object(Objinfo **objlist, Link_map *lmp)
48{
49	Objinfo		*op, *cur, *prev;
50	Elf_Ehdr	*ehdr;
51	Elf_Phdr	*phdr;
52	caddr_t		lpc, hpc;
53	int		i;
54
55	if ((op = calloc(1, sizeof (Objinfo))) == NULL) {
56		(void) fprintf(stderr, "who.so.1: calloc failed\n");
57		exit(1);
58	}
59
60	lpc = hpc = (caddr_t)lmp->l_addr;
61	/* LINTED */
62	ehdr = (Elf_Ehdr *)lpc;
63
64	/* LINTED */
65	for (i = 0, phdr = (Elf_Phdr *)(ehdr->e_phoff + lpc);
66	    i < ehdr->e_phnum; i++, phdr++) {
67		caddr_t		_hpc;
68		if ((phdr->p_type == PT_LOAD) &&
69		    ((_hpc = phdr->p_vaddr + phdr->p_memsz + lpc) > hpc))
70			hpc = _hpc;
71	}
72	op->o_lpc = lpc;
73	op->o_hpc = hpc;
74	op->o_lmp = lmp;
75
76	if (ehdr->e_type == ET_EXEC)
77		op->o_flags |= FLG_OB_FIXED;
78
79	if (*objlist == NULL) {
80		*objlist = op;
81		return;
82	}
83	/*
84	 * Do an insertion sort to maintain the list
85	 * in order.
86	 */
87	if ((*objlist)->o_lmp->l_addr > lmp->l_addr) {
88		op->o_next = *objlist;
89		*objlist = op;
90		return;
91	}
92
93	for (prev = NULL, cur = *objlist; cur; prev = cur, cur = cur->o_next) {
94		if (lpc < cur->o_lpc)
95			break;
96	}
97	if (prev == NULL) {
98		op->o_next = *objlist;
99		*objlist = op;
100		return;
101	}
102	prev->o_next = op;
103	op->o_next = cur;
104}
105
106static void
107remove_object(Objinfo **objlist, Link_map *lmp)
108{
109	Objinfo	*cur, *prev;
110
111	for (prev = NULL, cur = *objlist; cur; prev = cur, cur = cur->o_next) {
112		if (cur->o_lmp == lmp)
113			break;
114	}
115
116	/*
117	 * Did we find it?
118	 */
119	if (!cur)
120		return;
121
122	if (!prev)
123		*objlist = cur->o_next;
124	else
125		prev->o_next = cur->o_next;
126
127	if (cur->o_elf) {
128		(void) elf_end(cur->o_elf);
129		(void) close(cur->o_fd);
130	}
131	free(cur);
132}
133
134static void
135print_simple_address(void *pc)
136{
137	Dl_info		info;
138
139	if (dladdr(pc, &info) == 0) {
140		(void) fprintf(stderr,
141		    "\t<unknown>: 0x%lx\n", (unsigned long)pc);
142		return;
143	}
144
145	(void) fprintf(stderr, "\t%s:%s+0x%lx\n", info.dli_fname,
146	    info.dli_sname,
147	    (ulong_t)((uintptr_t)pc - (uintptr_t)info.dli_saddr));
148}
149
150static void
151load_syms(Objinfo *op)
152{
153	int	fd;
154	Elf	*elf;
155	Elf_Scn	*scn;
156
157	if (elf_version(EV_CURRENT) == EV_NONE) {
158		op->o_flags |= FLG_OB_NOSYMS;
159		return;
160	}
161
162	if ((fd = open(op->o_lmp->l_name, O_RDONLY)) == -1) {
163		op->o_flags |= FLG_OB_NOSYMS;
164		return;
165	}
166
167	if ((elf = elf_begin(fd, ELF_C_READ, 0)) == NULL) {
168		op->o_flags |= FLG_OB_NOSYMS;
169		(void) close(fd);
170		return;
171	}
172	scn = NULL;
173	while ((scn = elf_nextscn(elf, scn)) != NULL) {
174		Elf_Shdr	*shdr;
175		Elf_Data	*data;
176
177		shdr = elf_getshdr(scn);
178		if (shdr->sh_type != SHT_SYMTAB)
179			continue;
180		data = elf_getdata(scn, 0);
181		op->o_syms = (Elf_Sym *)data->d_buf;
182		/* LINTED */
183		op->o_symcnt = (uint_t)(shdr->sh_size / shdr->sh_entsize);
184		scn = elf_getscn(elf, shdr->sh_link);
185		data = elf_getdata(scn, 0);
186		op->o_strs = (const char *)data->d_buf;
187	}
188	if (!op->o_syms) {
189		(void) elf_end(elf);
190		(void) close(fd);
191		op->o_flags |= FLG_OB_NOSYMS;
192	}
193}
194
195
196static void
197print_address(caddr_t pc)
198{
199	Elf_Sym	*sym, *_sym;
200	Objinfo	*op;
201	int	i;
202
203	if (!detail_syms) {
204		print_simple_address(pc);
205		return;
206	}
207	for (op = objhead; op; op = op->o_next) {
208		if ((pc >= op->o_lpc) && (pc <= op->o_hpc))
209			break;
210	}
211	if (op && (op->o_syms == NULL))
212		load_syms(op);
213
214	if (!op || (op->o_flags & FLG_OB_NOSYMS)) {
215		print_simple_address(pc);
216		return;
217	}
218
219	sym = op->o_syms;
220	if ((op->o_flags & FLG_OB_FIXED) == 0)
221		pc = (caddr_t)((uintptr_t)pc - (uintptr_t)op->o_lpc);
222	for (i = 0, _sym = op->o_syms; i < op->o_symcnt; i++, _sym++) {
223		if (((uintptr_t)_sym->st_value < (uintptr_t)pc) &&
224		    (_sym->st_value > sym->st_value))
225			sym = _sym;
226	}
227	(void) fprintf(stderr, "\t%s:%s+0x%lx\n", op->o_lmp->l_name,
228	    sym->st_name + op->o_strs,
229	    (ulong_t)((uintptr_t)pc - (uintptr_t)sym->st_value));
230}
231
232static void
233print_stack(struct frame *sp)
234{
235	FLUSHWIN();
236
237	while (sp && sp->fr_savpc) {
238		print_address((caddr_t)sp->fr_savpc);
239		sp = (struct frame *)((ulong_t)sp->fr_savfp + STACK_BIAS);
240	}
241}
242
243uint_t
244la_version(uint_t version)
245{
246	if (version > LAV_CURRENT)
247		(void) fprintf(stderr, "who.so: unexpected version: %d\n",
248		    version);
249
250	if (checkenv((const char *)"WHO_DETAIL"))
251		detail_syms++;
252
253	build_env_list(&funclist, (const char *)"WHOCALLS");
254
255	/*
256	 * Initalize iset to the full set of signals to be masked durring
257	 * pltenter/pltexit
258	 */
259	(void) sigfillset(&iset);
260
261	return (LAV_CURRENT);
262}
263
264/* ARGSUSED1 */
265uint_t
266la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie)
267{
268	add_object(&objhead, lmp);
269	return (LA_FLG_BINDTO | LA_FLG_BINDFROM);
270}
271
272uint_t
273la_objclose(uintptr_t *cookie)
274{
275	remove_object(&objhead, (Link_map *)(*cookie));
276	return (1);
277}
278
279/* ARGSUSED1 */
280#if	defined(__sparcv9)
281uintptr_t
282la_sparcv9_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
283	uintptr_t *defcookie, La_sparcv9_regs *regset, uint_t *sb_flags,
284	const char *sym_name)
285#elif	defined(__sparc)
286uintptr_t
287la_sparcv8_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie,
288	uintptr_t *defcookie, La_sparcv8_regs *regset, uint_t *sb_flags)
289#elif   defined(__amd64)
290uintptr_t
291la_amd64_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
292	uintptr_t *defcookie, La_amd64_regs *regset, uint_t *sb_flags,
293	const char *sym_name)
294#elif   defined(__i386)
295uintptr_t
296la_i86_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcooke,
297	uintptr_t *defcook, La_i86_regs *regset, uint_t *sb_flags)
298#endif
299{
300	sigset_t	oset;
301#if	!defined(_LP64)
302	const char	*sym_name = (const char *)symp->st_name;
303#endif
304
305	(void) sigprocmask(SIG_BLOCK, &iset, &oset);
306	if (check_list(funclist, sym_name)) {
307		struct frame	*frame_p;
308
309		(void) fprintf(stderr, "%s(0x%lx, 0x%lx, 0x%lx)\n", sym_name,
310		    (long)GETARG0(regset), (long)GETARG1(regset),
311		    (long)GETARG2(regset));
312
313		print_address((caddr_t)GETPREVPC(regset));
314
315		frame_p = (struct frame *)((ulong_t)GETFRAME(regset)
316		    + STACK_BIAS);
317
318		print_stack(frame_p);
319		(void) fflush(stdout);
320	}
321	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
322	return (symp->st_value);
323}
324