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