xref: /illumos-gate/usr/src/lib/abi/apptrace/common/apptrace.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <link.h>
30 #include <dlfcn.h>
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/resource.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <regex.h>
40 #include <signal.h>
41 #include <synch.h>
42 #include <fcntl.h>
43 #include <sys/stat.h>
44 #include <apptrace.h>
45 #include <libintl.h>
46 #include <locale.h>
47 #include <limits.h>
48 #include <sys/sysmacros.h>
49 #include "abienv.h"
50 #include "mach.h"
51 
52 #include <libproc.h>
53 #include <libctf.h>
54 
55 #define	NUM_ARGS 40
56 
57 extern const char	*type_name(ctf_file_t *, ctf_id_t, char *, size_t);
58 extern void		print_value(ctf_file_t *, ctf_id_t, ulong_t);
59 
60 static struct ps_prochandle	*proc_hdl = NULL;
61 
62 static Liblist	*bindto_list;
63 static Liblist	*bindto_excl;
64 static Liblist	*bindfrom_list;
65 static Liblist	*bindfrom_excl;
66 static Liblist	*intlib_list;
67 static uint_t	pidout;
68 static Intlist	*trace_list;
69 static Intlist	*trace_excl;
70 static Intlist	*verbose_list;
71 static Intlist	*verbose_excl;
72 
73 /*
74  * Required for calls to build_env_list1 where
75  * things are added to the end of the list (preserving
76  * search order implied by the setting of env variables
77  * in apptracecmd.c)
78  */
79 static Liblist	*intlib_listend;
80 
81 /*
82  * These globals are sought and used by interceptlib.c
83  * which goes into all interceptor objects.
84  */
85 FILE		*ABISTREAM = stderr;
86 sigset_t	abisigset;
87 
88 /*
89  * Strings are printed with "%.*s", abi_strpsz, string
90  */
91 int		abi_strpsz = 20;
92 
93 /*
94  * Special function pointers that'll be set up to point at the
95  * libc/libthread versions in the _application's_ link map (as opposed
96  * to our own).
97  *
98  * Additionally, it is impossible to generalize the programmatic
99  * creation of interceptor functions for variable argument list
100  * functions.  However, in the case of the printf family, there is a
101  * vprintf equivalent.  The interceptors for the printf family live in
102  * interceptor.c and they call the appropriate vprintf interface
103  * instead of the printf interface that they're intercepting.  The
104  * link map issue remains, however, so function pointers for the
105  * vprintf family in the application's link map are set up here.
106  *
107  * The interceptors also need to examine errno which also needs to be
108  * extracted from the base link map.
109  *
110  * All of these pointers are initialized in la_preinit().
111  */
112 
113 thread_t (*abi_thr_self)(void);
114 int (*abi_thr_main)(void);
115 
116 int (*ABI_VFPRINTF)(FILE *, char const *, va_list);
117 int (*ABI_VFWPRINTF)(FILE *, const wchar_t *, va_list);
118 int (*ABI_VPRINTF)(char const *, va_list);
119 int (*ABI_VSNPRINTF)(char *, size_t, char const *, va_list);
120 int (*ABI_VSPRINTF)(char *, char const *, va_list);
121 int (*ABI_VSWPRINTF)(wchar_t *, size_t, const wchar_t *, va_list);
122 int (*ABI_VWPRINTF)(const wchar_t *, va_list);
123 int *(*__abi_real_errno)(void);
124 
125 #if defined(__sparcv9)
126 static char const *libcpath		= "/lib/sparcv9/libc.so.1";
127 #elif defined(__amd64)
128 static char const *libcpath		= "/lib/amd64/libc.so.1";
129 #else
130 static char const *libcpath		= "/lib/libc.so.1";
131 #endif
132 
133 /* Used as arguments later to dlsym */
134 static char const *thr_main_sym		= "thr_main";
135 static char const *thr_self_sym		= "thr_self";
136 static char const *vfprintf_sym		= "vfprintf";
137 static char const *vfwprintf_sym	= "vfwprintf";
138 static char const *vprintf_sym		= "vprintf";
139 static char const *vsnprintf_sym	= "vsnprintf";
140 static char const *vsprintf_sym		= "vsprintf";
141 static char const *vswprintf_sym	= "vswprintf";
142 static char const *vwprintf_sym		= "vwprintf";
143 static char const *errno_sym		= "___errno";
144 
145 /*
146  * The list of functions below are functions for which
147  * apptrace.so will not perform any tracing.
148  *
149  * The user visible failure of tracing these functions
150  * is a core dump of the application under observation.
151  *
152  * This list was originally discovered during sotruss
153  * development.  Attempts lacking sufficient determination
154  * to shrink this list have failed.
155  *
156  * There are a number of different kinds of issues here.
157  *
158  * The .stretX functions have to do with the relationship
159  * that the caller and callee has with functions that
160  * return structures and the altered calling convention
161  * that results.
162  *
163  * We cannot trace *setjmp because the caller of these routines
164  * is not allow to return which is exactly what an interceptor
165  * function is going to do.
166  *
167  * The *context functions are on the list because we cannot trace
168  * netscape without them on the list, but the exact mechanics of the
169  * failure are not known at this time.
170  *
171  * The leaf functions *getsp can probably be removed given the
172  * presence of an interceptor but that experiment has not been
173  * conducted.
174  *
175  * NOTE: this list *must* be maintained in alphabetical order.
176  *	 if this list ever became too long a faster search mechanism
177  *	 should be considered.
178  */
179 static char *spec_sym[] = {
180 #if defined(sparc)
181 	".stret1",
182 	".stret2",
183 	".stret4",
184 	".stret8",
185 #endif
186 	"__getcontext",
187 	"_getcontext",
188 	"_getsp",
189 	"_longjmp",
190 	"_setcontext",
191 	"_setjmp",
192 	"_siglongjmp",
193 	"_sigsetjmp",
194 	"_vfork",
195 	"getcontext",
196 	"getsp",
197 	"longjmp",
198 	"setcontext",
199 	"setjmp",
200 	"siglongjmp",
201 	"sigsetjmp",
202 	"vfork",
203 	NULL
204 };
205 
206 uint_t
207 la_version(uint_t version)
208 {
209 	char		*str;
210 	FILE		*fp;
211 
212 	if (version > LAV_CURRENT)
213 		(void) fprintf(stderr,
214 				dgettext(TEXT_DOMAIN,
215 					"apptrace: unexpected version: %u\n"),
216 				version);
217 
218 	build_env_list(&bindto_list, "APPTRACE_BINDTO");
219 	build_env_list(&bindto_excl, "APPTRACE_BINDTO_EXCLUDE");
220 
221 	build_env_list(&bindfrom_list, "APPTRACE_BINDFROM");
222 	build_env_list(&bindfrom_excl, "APPTRACE_BINDFROM_EXCLUDE");
223 
224 	if (checkenv("APPTRACE_PID") != NULL) {
225 		pidout = 1;
226 	} else {
227 		char *str = "LD_AUDIT=";
228 		char *str2 = "LD_AUDIT64=";
229 		/*
230 		 * This disables apptrace output in subsequent exec'ed
231 		 * processes.
232 		 */
233 		(void) putenv(str);
234 		(void) putenv(str2);
235 	}
236 
237 	if ((str = checkenv("APPTRACE_OUTPUT")) != NULL) {
238 		int fd, newfd, targetfd, lowerlimit;
239 		struct rlimit rl;
240 
241 		if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
242 			(void) fprintf(stderr,
243 					dgettext(TEXT_DOMAIN,
244 						"apptrace: getrlimit: %s\n"),
245 					strerror(errno));
246 			exit(EXIT_FAILURE);
247 		}
248 
249 		fd = open(str, O_WRONLY|O_CREAT|O_TRUNC, 0666);
250 		if (fd == -1) {
251 			(void) fprintf(stderr,
252 					dgettext(TEXT_DOMAIN,
253 						"apptrace: %s: %s\n"),
254 					str,
255 					strerror(errno));
256 			exit(EXIT_FAILURE);
257 		}
258 
259 		/*
260 		 * Those fans of dup2 should note that dup2 cannot
261 		 * be used below because dup2 closes the target file
262 		 * descriptor.  Thus, if we're apptracing say, ksh
263 		 * we'd have closed the fd it uses for the history
264 		 * file (63 on my box).
265 		 *
266 		 * fcntl with F_DUPFD returns first available >= arg3
267 		 * so we iterate from the top until we find a available
268 		 * fd.
269 		 *
270 		 * Not finding an fd after 10 tries is a failure.
271 		 *
272 		 * Since the _file member of the FILE structure is an
273 		 * unsigned char, we must clamp our fd request to
274 		 * UCHAR_MAX
275 		 */
276 		lowerlimit = ((rl.rlim_cur >
277 		    UCHAR_MAX) ? UCHAR_MAX : rl.rlim_cur) - 10;
278 
279 		for (targetfd = lowerlimit + 10;
280 		    targetfd > lowerlimit; targetfd--) {
281 			if ((newfd = fcntl(fd, F_DUPFD, targetfd)) != -1)
282 				break;
283 		}
284 
285 		if (newfd == -1) {
286 			(void) fprintf(stderr,
287 					dgettext(TEXT_DOMAIN,
288 						"apptrace: F_DUPFD: %s\n"),
289 					strerror(errno));
290 			exit(EXIT_FAILURE);
291 		}
292 		(void) close(fd);
293 
294 		if (fcntl(newfd, F_SETFD, FD_CLOEXEC) == -1) {
295 			(void) fprintf(stderr,
296 					dgettext(TEXT_DOMAIN,
297 					"apptrace: fcntl FD_CLOEXEC: %s\n"),
298 					strerror(errno));
299 			exit(EXIT_FAILURE);
300 		}
301 
302 		if ((fp = fdopen(newfd, "w")) != NULL) {
303 			ABISTREAM = fp;
304 		} else {
305 			(void) fprintf(stderr,
306 					dgettext(TEXT_DOMAIN,
307 						"apptrace: fdopen: %s\n"),
308 					strerror(errno));
309 			exit(EXIT_FAILURE);
310 		}
311 	}
312 
313 #if defined(_LP64)
314 	build_env_list1(&intlib_list, &intlib_listend,
315 	    "APPTRACE_INTERCEPTORS64");
316 #else
317 	build_env_list1(&intlib_list, &intlib_listend,
318 	    "APPTRACE_INTERCEPTORS");
319 #endif
320 
321 	/* Set up lists interfaces to trace or ignore */
322 	env_to_intlist(&trace_list, "APPTRACE_INTERFACES");
323 	env_to_intlist(&trace_excl, "APPTRACE_INTERFACES_EXCLUDE");
324 	env_to_intlist(&verbose_list, "APPTRACE_VERBOSE");
325 	env_to_intlist(&verbose_excl, "APPTRACE_VERBOSE_EXCLUDE");
326 
327 	return (LAV_CURRENT);
328 }
329 
330 /* ARGSUSED1 */
331 uint_t
332 la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie)
333 {
334 	uint_t		flags;
335 	static int	first = 1;
336 	int		perr;
337 
338 	/*
339 	 * If this is the first time in, then l_name is the app
340 	 * and unless the user gave an explict from list
341 	 * we will trace calls from it.
342 	 */
343 	if (first && bindfrom_list == NULL) {
344 		flags = LA_FLG_BINDFROM | LA_FLG_BINDTO;
345 		first = 0;
346 		goto work;
347 	}
348 
349 	/*
350 	 * If we have no bindto_list, then we assume that we
351 	 * bindto everything (apptrace -T \*)
352 	 *
353 	 * Otherwise we make sure that l_name is on the list.
354 	 */
355 	flags = 0;
356 	if (bindto_list == NULL) {
357 		flags = LA_FLG_BINDTO;
358 	} else if (check_list(bindto_list, lmp->l_name) != NULL) {
359 		flags |= LA_FLG_BINDTO;
360 	}
361 
362 	/*
363 	 * If l_name is on the exclusion list, zero the bit.
364 	 */
365 	if ((bindto_excl != NULL) &&
366 	    check_list(bindto_excl, lmp->l_name) != NULL) {
367 		flags &= ~LA_FLG_BINDTO;
368 	}
369 
370 	/*
371 	 * If l_name is on the bindfrom list then trace
372 	 */
373 	if (check_list(bindfrom_list, lmp->l_name) != NULL) {
374 		flags |= LA_FLG_BINDFROM;
375 	}
376 
377 	/*
378 	 * If l_name is on the exclusion list, zero the bit
379 	 * else trace, (this allows "-F !foo" to imply
380 	 * "-F '*' -F !foo")
381 	 */
382 	if (check_list(bindfrom_excl, lmp->l_name) != NULL) {
383 		flags &= ~LA_FLG_BINDFROM;
384 	} else if (bindfrom_excl != NULL && bindfrom_list == NULL) {
385 		flags |= LA_FLG_BINDFROM;
386 	}
387 
388 work:
389 	if (flags) {
390 		*cookie = (uintptr_t)abibasename(lmp->l_name);
391 
392 		/*
393 		 * only call Pgrab() once to get the ps_prochandle
394 		 */
395 		if (proc_hdl == NULL)
396 			proc_hdl = Pgrab(getpid(), PGRAB_RDONLY, &perr);
397 	}
398 
399 	return (flags);
400 }
401 
402 static void
403 apptrace_preinit_fail(void)
404 {
405 	(void) fprintf(stderr,
406 			dgettext(TEXT_DOMAIN, "apptrace: la_preinit: %s\n"),
407 			dlerror());
408 	exit(EXIT_FAILURE);
409 }
410 
411 /* ARGSUSED */
412 void
413 la_preinit(uintptr_t *cookie)
414 {
415 	void	*h = NULL;
416 
417 	(void) sigfillset(&abisigset);
418 
419 	h = dlmopen(LM_ID_BASE, libcpath, RTLD_LAZY | RTLD_NOLOAD);
420 	if (h == NULL)
421 		apptrace_preinit_fail();
422 
423 	if ((abi_thr_self =
424 	    (thread_t (*)(void)) dlsym(h, thr_self_sym)) == NULL)
425 		apptrace_preinit_fail();
426 	if ((abi_thr_main =
427 	    (int (*)(void)) dlsym(h, thr_main_sym)) == NULL)
428 		apptrace_preinit_fail();
429 
430 	/* Do printf style pointers */
431 	if ((ABI_VFPRINTF =
432 	    (int (*)(FILE *, char const *, va_list))
433 	    dlsym(h, vfprintf_sym)) == NULL)
434 		apptrace_preinit_fail();
435 
436 	if ((ABI_VFWPRINTF =
437 	    (int (*)(FILE *, const wchar_t *, va_list))
438 	    dlsym(h, vfwprintf_sym)) == NULL)
439 		apptrace_preinit_fail();
440 
441 	if ((ABI_VPRINTF =
442 	    (int (*)(char const *, va_list))
443 	    dlsym(h, vprintf_sym)) == NULL)
444 		apptrace_preinit_fail();
445 
446 	if ((ABI_VSNPRINTF =
447 	    (int (*)(char *, size_t, char const *, va_list))
448 	    dlsym(h, vsnprintf_sym)) == NULL)
449 		apptrace_preinit_fail();
450 
451 	if ((ABI_VSPRINTF =
452 	    (int (*)(char *, char const *, va_list))
453 	    dlsym(h, vsprintf_sym)) == NULL)
454 		apptrace_preinit_fail();
455 
456 	if ((ABI_VSWPRINTF =
457 	    (int (*)(wchar_t *, size_t, const wchar_t *, va_list))
458 	    dlsym(h, vswprintf_sym)) == NULL)
459 		apptrace_preinit_fail();
460 
461 	if ((ABI_VWPRINTF =
462 	    (int (*)(const wchar_t *, va_list))
463 	    dlsym(h, vwprintf_sym)) == NULL)
464 		apptrace_preinit_fail();
465 
466 	if ((__abi_real_errno =
467 	    (int *(*)(void))
468 	    dlsym(h, errno_sym)) == NULL)
469 		apptrace_preinit_fail();
470 
471 	(void) dlclose(h);
472 }
473 
474 /* ARGSUSED1 */
475 #if defined(_LP64)
476 uintptr_t
477 la_symbind64(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcook,
478     uintptr_t *defcook, uint_t *sb_flags, char const *sym_name)
479 #else
480 uintptr_t
481 la_symbind32(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcook,
482     uintptr_t *defcook, uint_t *sb_flags)
483 #endif
484 {
485 #if !defined(_LP64)
486 	char const *sym_name = (char const *) symp->st_name;
487 #endif
488 	int intercept = 0, verbose = 0;
489 	uintptr_t ret = symp->st_value;
490 	uint_t ndx;
491 	char *str;
492 
493 #if defined(_LP64)
494 	if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
495 		goto end;
496 #else
497 	/* If we're not looking at a function, bug out */
498 	if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
499 		goto end;
500 #endif
501 
502 	if (verbose_list != NULL) {
503 		/* apptrace ... -v verbose_list ... cmd */
504 		if (check_intlist(verbose_list, sym_name))
505 			verbose = 1;
506 	}
507 	if (verbose_excl != NULL) {
508 		/* apptrace ... -v !verbose_excl ... cmd */
509 		if (check_intlist(verbose_excl, sym_name))
510 			verbose = 0;
511 		else if (verbose_list == NULL && trace_list == NULL &&
512 		    trace_excl == NULL)
513 			/* apptrace -v !verbose_excl cmd */
514 			intercept = 1;
515 	}
516 	if (trace_list != NULL) {
517 		/* apptrace ... -t trace_list ... cmd */
518 		if (check_intlist(trace_list, sym_name))
519 			intercept = 1;
520 	} else if (verbose_list == NULL && verbose_excl == NULL)
521 		/* default (implies -t '*'):  apptrace cmd */
522 		intercept = 1;
523 
524 	if (trace_excl != NULL) {
525 		/* apptrace ... -t !trace_excl ... cmd */
526 		if (check_intlist(trace_excl, sym_name))
527 			intercept = 0;
528 	}
529 
530 	if (verbose == 0 && intercept == 0) {
531 		*sb_flags |= (LA_SYMB_NOPLTEXIT | LA_SYMB_NOPLTENTER);
532 		goto end;
533 	}
534 
535 	/*
536 	 * Check to see if this symbol is one of the 'special' symbols.
537 	 * If so we disable calls for that symbol.
538 	 */
539 	for (ndx = 0; (str = spec_sym[ndx]) != NULL; ndx++) {
540 		int	cmpval;
541 		cmpval = strcmp(sym_name, str);
542 		if (cmpval < 0)
543 			break;
544 		if (cmpval == 0) {
545 			intercept = verbose = 0;
546 			*sb_flags |= (LA_SYMB_NOPLTEXIT | LA_SYMB_NOPLTENTER);
547 			break;
548 		}
549 	}
550 
551 end:
552 	return (ret);
553 }
554 
555 /* ARGSUSED1 */
556 #if	defined(__sparcv9)
557 uintptr_t
558 la_sparcv9_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
559 	uintptr_t *defcookie, La_sparcv9_regs *regset, uint_t *sb_flags,
560 	char const *sym_name)
561 #elif	defined(__sparc)
562 uintptr_t
563 la_sparcv8_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie,
564 	uintptr_t *defcookie, La_sparcv8_regs *regset, uint_t *sb_flags)
565 #elif   defined(__amd64)
566 uintptr_t
567 la_amd64_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
568 	uintptr_t *defcookie, La_amd64_regs *regset, uint_t *sb_flags,
569 	char const *sym_name)
570 #elif   defined(__i386)
571 uintptr_t
572 la_i86_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie,
573 	uintptr_t *defcookie, La_i86_regs *regset, uint_t *sb_flags)
574 #endif
575 {
576 	char		*defname = (char *)(*defcookie);
577 	char		*refname = (char *)(*refcookie);
578 	sigset_t	omask;
579 #if	!defined(_LP64)
580 	char const	*sym_name = (char const *)symp->st_name;
581 #endif
582 
583 	char		buf[256];
584 	GElf_Sym	sym;
585 	prsyminfo_t	si;
586 	ctf_file_t	*ctfp;
587 	ctf_funcinfo_t	finfo;
588 	int		argc;
589 	ctf_id_t	argt[NUM_ARGS];
590 	ulong_t		argv[NUM_ARGS];
591 	int		i;
592 	char		*sep = "";
593 	ctf_id_t	type, rtype;
594 	int		kind;
595 
596 	abilock(&omask);
597 
598 	if (pidout)
599 		(void) fprintf(ABISTREAM, "%7u:", (unsigned int)getpid());
600 
601 	if ((ctfp = Pname_to_ctf(proc_hdl, defname)) == NULL)
602 		goto fail;
603 
604 	if (Pxlookup_by_name(proc_hdl, PR_LMID_EVERY, defname, sym_name,
605 	    &sym, &si) != 0)
606 		goto fail;
607 
608 	if (ctf_func_info(ctfp, si.prs_id, &finfo) == CTF_ERR)
609 		goto fail;
610 
611 	(void) type_name(ctfp, finfo.ctc_return, buf, sizeof (buf));
612 	(void) fprintf(ABISTREAM, "-> %-8s -> %8s:%s %s(",
613 	    refname, defname, buf, sym_name);
614 
615 	/*
616 	 * According to bug in la_pltexit(), it can't return
617 	 * if the type is just a struct/union.  So, if the return
618 	 * type is a struct/union, la_pltexit() should be off.
619 	 */
620 	rtype = ctf_type_resolve(ctfp, finfo.ctc_return);
621 	type = ctf_type_reference(ctfp, rtype);
622 	rtype = ctf_type_resolve(ctfp, type);
623 	kind = ctf_type_kind(ctfp, rtype);
624 	if ((kind == CTF_K_STRUCT || kind == CTF_K_UNION) &&
625 	    strpbrk(buf, "*") == NULL)
626 		*sb_flags |= LA_SYMB_NOPLTEXIT;
627 
628 	argc = MIN(sizeof (argt) / sizeof (argt[0]), finfo.ctc_argc);
629 	(void) ctf_func_args(ctfp, si.prs_id, argc, argt);
630 
631 	argv[0] = GETARG0(regset);
632 	if (argc > 1)
633 		argv[1] = GETARG1(regset);
634 	if (argc > 2)
635 		argv[2] = GETARG2(regset);
636 	if (argc > 3)
637 		argv[3] = GETARG3(regset);
638 	if (argc > 4)
639 		argv[4] = GETARG4(regset);
640 	if (argc > 5)
641 		argv[5] = GETARG5(regset);
642 	if (argc > 6) {
643 		for (i = 6; i < argc; i++)
644 			argv[i] = GETARG_6NUP(i, regset);
645 	}
646 
647 	for (i = 0; i < argc; i++) {
648 		(void) type_name(ctfp, argt[i], buf, sizeof (buf));
649 		(void) fprintf(ABISTREAM, "%s%s = ", sep, buf);
650 		rtype = ctf_type_resolve(ctfp, argt[i]);
651 		type = ctf_type_reference(ctfp, rtype);
652 		rtype = ctf_type_resolve(ctfp, type);
653 		kind = ctf_type_kind(ctfp, rtype);
654 		if (kind == CTF_K_STRUCT || kind == CTF_K_UNION)
655 			(void) fprintf(ABISTREAM, "0x%p", (void *)argv[i]);
656 		else
657 			print_value(ctfp, argt[i], argv[i]);
658 		sep = ", ";
659 	}
660 
661 	if (finfo.ctc_flags & CTF_FUNC_VARARG)
662 		(void) fprintf(ABISTREAM, "%s...", sep);
663 	else if (argc == 0)
664 		(void) fprintf(ABISTREAM, "void");
665 
666 	if ((*sb_flags & LA_SYMB_NOPLTEXIT) != 0)
667 		(void) fprintf(ABISTREAM, ") ** ST\n");
668 	else
669 		(void) fprintf(ABISTREAM, ")\n");
670 
671 	if (verbose_list != NULL &&
672 	    check_intlist(verbose_list, sym_name) != 0) {
673 		for (i = 0; i < argc; i++) {
674 			(void) type_name(ctfp, argt[i], buf, sizeof (buf));
675 			(void) fprintf(ABISTREAM, "\targ%d = (%s) ", i, buf);
676 			print_value(ctfp, argt[i], argv[i]);
677 			(void) fprintf(ABISTREAM, "\n");
678 		}
679 		if ((*sb_flags & LA_SYMB_NOPLTEXIT) != 0) {
680 			if (kind == CTF_K_STRUCT)
681 				(void) fprintf(ABISTREAM,
682 				    "\treturn = (struct), apptrace "
683 				    "will not trace the return\n");
684 			else
685 				(void) fprintf(ABISTREAM,
686 				    "\treturn = (union), apptrace "
687 				    "will not trace the return\n");
688 		}
689 	}
690 
691 	(void) fflush(ABISTREAM);
692 	abiunlock(&omask);
693 	return (symp->st_value);
694 
695 fail:
696 	(void) fprintf(ABISTREAM,
697 	    "-> %-8s -> %8s:%s(0x%lx, 0x%lx, 0x%lx) ** NR\n",
698 	    refname, defname, sym_name,
699 	    (ulong_t)GETARG0(regset),
700 	    (ulong_t)GETARG1(regset),
701 	    (ulong_t)GETARG2(regset));
702 
703 	*sb_flags |= LA_SYMB_NOPLTEXIT;
704 	(void) fflush(ABISTREAM);
705 	abiunlock(&omask);
706 	return (symp->st_value);
707 }
708 
709 /* ARGSUSED */
710 #if	defined(_LP64)
711 uintptr_t
712 la_pltexit64(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie,
713 	uintptr_t *defcookie, uintptr_t retval, const char *sym_name)
714 #else
715 uintptr_t
716 la_pltexit(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie,
717 	uintptr_t *defcookie, uintptr_t retval)
718 #endif
719 {
720 #if	!defined(_LP64)
721 	const char	*sym_name = (const char *)symp->st_name;
722 #endif
723 	sigset_t	omask;
724 	char		buf[256];
725 	GElf_Sym	sym;
726 	prsyminfo_t	si;
727 	ctf_file_t	*ctfp;
728 	ctf_funcinfo_t	finfo;
729 	char		*defname = (char *)(*defcookie);
730 	char		*refname = (char *)(*refcookie);
731 
732 	abilock(&omask);
733 
734 	if (pidout)
735 		(void) fprintf(ABISTREAM, "%7u:", (unsigned int)getpid());
736 
737 	if (retval == 0) {
738 		if (verbose_list == NULL) {
739 			(void) fprintf(ABISTREAM, "<- %-8s -> %8s:%s()\n",
740 			    refname, defname, sym_name);
741 			(void) fflush(ABISTREAM);
742 		}
743 		abiunlock(&omask);
744 		return (retval);
745 	}
746 
747 	if ((ctfp = Pname_to_ctf(proc_hdl, defname)) == NULL)
748 		goto fail;
749 
750 	if (Pxlookup_by_name(proc_hdl, PR_LMID_EVERY, defname,
751 	    sym_name, &sym, &si) != 0)
752 		goto fail;
753 
754 	if (ctf_func_info(ctfp, si.prs_id, &finfo) == CTF_ERR)
755 		goto fail;
756 
757 	if (verbose_list != NULL) {
758 		if (check_intlist(verbose_list, sym_name) != 0) {
759 			(void) type_name(ctfp, finfo.ctc_return, buf,
760 			    sizeof (buf));
761 			(void) fprintf(ABISTREAM, "\treturn = (%s) ", buf);
762 			print_value(ctfp, finfo.ctc_return, retval);
763 			(void) fprintf(ABISTREAM, "\n");
764 			(void) fprintf(ABISTREAM, "<- %-8s -> %8s:%s()",
765 			    refname, defname, sym_name);
766 			(void) fprintf(ABISTREAM, " = 0x%p\n", (void *)retval);
767 		}
768 	} else {
769 		(void) fprintf(ABISTREAM, "<- %-8s -> %8s:%s()",
770 		    refname, defname, sym_name);
771 		(void) fprintf(ABISTREAM, " = 0x%p\n", (void *)retval);
772 	}
773 
774 	(void) fflush(ABISTREAM);
775 	abiunlock(&omask);
776 	return (retval);
777 
778 fail:
779 	if (verbose_list != NULL) {
780 		if (check_intlist(verbose_list, sym_name) != 0) {
781 			(void) fprintf(ABISTREAM,
782 			    "\treturn = 0x%p\n", (void *)retval);
783 			(void) fprintf(ABISTREAM, "<- %-8s -> %8s:%s()",
784 			    refname, defname, sym_name);
785 			(void) fprintf(ABISTREAM, " = 0x%p\n", (void *)retval);
786 		}
787 	} else {
788 		(void) fprintf(ABISTREAM, "<- %-8s -> %8s:%s()",
789 		    refname, defname, sym_name);
790 		(void) fprintf(ABISTREAM, " = 0x%p\n", (void *)retval);
791 	}
792 
793 	(void) fflush(ABISTREAM);
794 	abiunlock(&omask);
795 	return (retval);
796 }
797