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