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.
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.
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]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/resource.h>
47 #include <sys/sysmacros.h>
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
);
59 static struct ps_prochandle
*proc_hdl
= NULL
;
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
;
67 static Intlist
*trace_list
;
68 static Intlist
*trace_excl
;
69 static Intlist
*verbose_list
;
70 static Intlist
*verbose_excl
;
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
78 static Liblist
*intlib_listend
;
81 * These globals are sought and used by interceptlib.c
82 * which goes into all interceptor objects.
88 * Strings are printed with "%.*s", abi_strpsz, string
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
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.
106 * The interceptors also need to examine errno which also needs to be
107 * extracted from the base link map.
109 * All of these pointers are initialized in la_preinit().
112 thread_t (*abi_thr_self
)(void);
113 int (*abi_thr_main
)(void);
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);
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";
129 static char const *libcpath
= "/lib/libc.so.1";
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";
145 * The list of functions below are functions for which
146 * apptrace.so will not perform any tracing.
148 * The user visible failure of tracing these functions
149 * is a core dump of the application under observation.
151 * This list was originally discovered during sotruss
152 * development. Attempts lacking sufficient determination
153 * to shrink this list have failed.
155 * There are a number of different kinds of issues here.
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
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.
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.
170 * The leaf functions *getsp can probably be removed given the
171 * presence of an interceptor but that experiment has not been
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.
178 static char *spec_sym
[] = {
206 la_version(uint_t version
)
213 if (version
> LAV_CURRENT
)
214 (void) fprintf(stderr
,
215 dgettext(TEXT_DOMAIN
,
216 "apptrace: unexpected version: %u\n"),
219 build_env_list(&bindto_list
, "APPTRACE_BINDTO");
220 build_env_list(&bindto_excl
, "APPTRACE_BINDTO_EXCLUDE");
222 build_env_list(&bindfrom_list
, "APPTRACE_BINDFROM");
223 build_env_list(&bindfrom_excl
, "APPTRACE_BINDFROM_EXCLUDE");
225 if (checkenv("APPTRACE_PID") != NULL
) {
228 char *str
= "LD_AUDIT=";
229 char *str2
= "LD_AUDIT64=";
231 * This disables apptrace output in subsequent exec'ed
238 if ((str
= checkenv("APPTRACE_OUTPUT")) != NULL
) {
239 int fd
, newfd
, targetfd
, lowerlimit
;
242 if (getrlimit(RLIMIT_NOFILE
, &rl
) == -1) {
243 (void) fprintf(stderr
,
244 dgettext(TEXT_DOMAIN
,
245 "apptrace: getrlimit: %s\n"),
250 fd
= open(str
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
252 (void) fprintf(stderr
,
253 dgettext(TEXT_DOMAIN
,
254 "apptrace: %s: %s\n"),
261 * Those fans of dup2 should note that dup2 cannot
262 * be used below because dup2 closes the target file
263 * descriptor. Thus, if we're apptracing say, ksh
264 * we'd have closed the fd it uses for the history
265 * file (63 on my box).
267 * fcntl with F_DUPFD returns first available >= arg3
268 * so we iterate from the top until we find a available
271 * Not finding an fd after 10 tries is a failure.
273 * Since the _file member of the FILE structure is an
274 * unsigned char, we must clamp our fd request to
277 lowerlimit
= ((rl
.rlim_cur
>
278 UCHAR_MAX
) ? UCHAR_MAX
: rl
.rlim_cur
) - 10;
280 for (targetfd
= lowerlimit
+ 10;
281 targetfd
> lowerlimit
; targetfd
--) {
282 if ((newfd
= fcntl(fd
, F_DUPFD
, targetfd
)) != -1)
287 (void) fprintf(stderr
,
288 dgettext(TEXT_DOMAIN
,
289 "apptrace: F_DUPFD: %s\n"),
295 if (fcntl(newfd
, F_SETFD
, FD_CLOEXEC
) == -1) {
296 (void) fprintf(stderr
,
297 dgettext(TEXT_DOMAIN
,
298 "apptrace: fcntl FD_CLOEXEC: %s\n"),
303 if ((fp
= fdopen(newfd
, "wF")) != NULL
) {
306 (void) fprintf(stderr
,
307 dgettext(TEXT_DOMAIN
,
308 "apptrace: fdopen: %s\n"),
315 build_env_list1(&intlib_list
, &intlib_listend
,
316 "APPTRACE_INTERCEPTORS64");
318 build_env_list1(&intlib_list
, &intlib_listend
,
319 "APPTRACE_INTERCEPTORS");
322 /* Set up lists interfaces to trace or ignore */
323 env_to_intlist(&trace_list
, "APPTRACE_INTERFACES");
324 env_to_intlist(&trace_excl
, "APPTRACE_INTERFACES_EXCLUDE");
325 env_to_intlist(&verbose_list
, "APPTRACE_VERBOSE");
326 env_to_intlist(&verbose_excl
, "APPTRACE_VERBOSE_EXCLUDE");
328 return (LAV_CURRENT
);
333 la_objopen(Link_map
*lmp
, Lmid_t lmid
, uintptr_t *cookie
)
336 static int first
= 1;
340 * If this is the first time in, then l_name is the app
341 * and unless the user gave an explict from list
342 * we will trace calls from it.
344 if (first
&& bindfrom_list
== NULL
) {
345 flags
= LA_FLG_BINDFROM
| LA_FLG_BINDTO
;
351 * If we have no bindto_list, then we assume that we
352 * bindto everything (apptrace -T \*)
354 * Otherwise we make sure that l_name is on the list.
357 if (bindto_list
== NULL
) {
358 flags
= LA_FLG_BINDTO
;
359 } else if (check_list(bindto_list
, lmp
->l_name
) != NULL
) {
360 flags
|= LA_FLG_BINDTO
;
364 * If l_name is on the exclusion list, zero the bit.
366 if ((bindto_excl
!= NULL
) &&
367 check_list(bindto_excl
, lmp
->l_name
) != NULL
) {
368 flags
&= ~LA_FLG_BINDTO
;
372 * If l_name is on the bindfrom list then trace
374 if (check_list(bindfrom_list
, lmp
->l_name
) != NULL
) {
375 flags
|= LA_FLG_BINDFROM
;
379 * If l_name is on the exclusion list, zero the bit
380 * else trace, (this allows "-F !foo" to imply
383 if (check_list(bindfrom_excl
, lmp
->l_name
) != NULL
) {
384 flags
&= ~LA_FLG_BINDFROM
;
385 } else if (bindfrom_excl
!= NULL
&& bindfrom_list
== NULL
) {
386 flags
|= LA_FLG_BINDFROM
;
391 *cookie
= (uintptr_t)abibasename(lmp
->l_name
);
394 * only call Pgrab() once to get the ps_prochandle
396 if (proc_hdl
== NULL
)
397 proc_hdl
= Pgrab(getpid(), PGRAB_RDONLY
, &perr
);
404 apptrace_preinit_fail(void)
406 (void) fprintf(stderr
,
407 dgettext(TEXT_DOMAIN
, "apptrace: la_preinit: %s\n"),
414 la_preinit(uintptr_t *cookie
)
418 (void) sigfillset(&abisigset
);
420 h
= dlmopen(LM_ID_BASE
, libcpath
, RTLD_LAZY
| RTLD_NOLOAD
);
422 apptrace_preinit_fail();
425 (thread_t (*)(void)) dlsym(h
, thr_self_sym
)) == NULL
)
426 apptrace_preinit_fail();
428 (int (*)(void)) dlsym(h
, thr_main_sym
)) == NULL
)
429 apptrace_preinit_fail();
431 /* Do printf style pointers */
433 (int (*)(FILE *, char const *, va_list))
434 dlsym(h
, vfprintf_sym
)) == NULL
)
435 apptrace_preinit_fail();
438 (int (*)(FILE *, const wchar_t *, va_list))
439 dlsym(h
, vfwprintf_sym
)) == NULL
)
440 apptrace_preinit_fail();
443 (int (*)(char const *, va_list))
444 dlsym(h
, vprintf_sym
)) == NULL
)
445 apptrace_preinit_fail();
448 (int (*)(char *, size_t, char const *, va_list))
449 dlsym(h
, vsnprintf_sym
)) == NULL
)
450 apptrace_preinit_fail();
453 (int (*)(char *, char const *, va_list))
454 dlsym(h
, vsprintf_sym
)) == NULL
)
455 apptrace_preinit_fail();
458 (int (*)(wchar_t *, size_t, const wchar_t *, va_list))
459 dlsym(h
, vswprintf_sym
)) == NULL
)
460 apptrace_preinit_fail();
463 (int (*)(const wchar_t *, va_list))
464 dlsym(h
, vwprintf_sym
)) == NULL
)
465 apptrace_preinit_fail();
467 if ((__abi_real_errno
=
469 dlsym(h
, errno_sym
)) == NULL
)
470 apptrace_preinit_fail();
478 la_symbind64(Elf64_Sym
*symp
, uint_t symndx
, uintptr_t *refcook
,
479 uintptr_t *defcook
, uint_t
*sb_flags
, char const *sym_name
)
482 la_symbind32(Elf32_Sym
*symp
, uint_t symndx
, uintptr_t *refcook
,
483 uintptr_t *defcook
, uint_t
*sb_flags
)
487 char const *sym_name
= (char const *) symp
->st_name
;
489 int intercept
= 0, verbose
= 0;
490 uintptr_t ret
= symp
->st_value
;
495 if (ELF64_ST_TYPE(symp
->st_info
) != STT_FUNC
)
498 /* If we're not looking at a function, bug out */
499 if (ELF32_ST_TYPE(symp
->st_info
) != STT_FUNC
)
503 if (verbose_list
!= NULL
) {
504 /* apptrace ... -v verbose_list ... cmd */
505 if (check_intlist(verbose_list
, sym_name
))
508 if (verbose_excl
!= NULL
) {
509 /* apptrace ... -v !verbose_excl ... cmd */
510 if (check_intlist(verbose_excl
, sym_name
))
512 else if (verbose_list
== NULL
&& trace_list
== NULL
&&
514 /* apptrace -v !verbose_excl cmd */
517 if (trace_list
!= NULL
) {
518 /* apptrace ... -t trace_list ... cmd */
519 if (check_intlist(trace_list
, sym_name
))
521 } else if (verbose_list
== NULL
&& verbose_excl
== NULL
)
522 /* default (implies -t '*'): apptrace cmd */
525 if (trace_excl
!= NULL
) {
526 /* apptrace ... -t !trace_excl ... cmd */
527 if (check_intlist(trace_excl
, sym_name
))
531 if (verbose
== 0 && intercept
== 0) {
532 *sb_flags
|= (LA_SYMB_NOPLTEXIT
| LA_SYMB_NOPLTENTER
);
537 * Check to see if this symbol is one of the 'special' symbols.
538 * If so we disable calls for that symbol.
540 for (ndx
= 0; (str
= spec_sym
[ndx
]) != NULL
; ndx
++) {
542 cmpval
= strcmp(sym_name
, str
);
546 intercept
= verbose
= 0;
547 *sb_flags
|= (LA_SYMB_NOPLTEXIT
| LA_SYMB_NOPLTENTER
);
557 #if defined(__sparcv9)
559 la_sparcv9_pltenter(Elf64_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
560 uintptr_t *defcookie
, La_sparcv9_regs
*regset
, uint_t
*sb_flags
,
561 char const *sym_name
)
562 #elif defined(__sparc)
564 la_sparcv8_pltenter(Elf32_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
565 uintptr_t *defcookie
, La_sparcv8_regs
*regset
, uint_t
*sb_flags
)
566 #elif defined(__amd64)
568 la_amd64_pltenter(Elf64_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
569 uintptr_t *defcookie
, La_amd64_regs
*regset
, uint_t
*sb_flags
,
570 char const *sym_name
)
571 #elif defined(__i386)
573 la_i86_pltenter(Elf32_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
574 uintptr_t *defcookie
, La_i86_regs
*regset
, uint_t
*sb_flags
)
577 char *defname
= (char *)(*defcookie
);
578 char *refname
= (char *)(*refcookie
);
581 char const *sym_name
= (char const *)symp
->st_name
;
588 ctf_funcinfo_t finfo
;
590 ctf_id_t argt
[NUM_ARGS
];
591 ulong_t argv
[NUM_ARGS
];
594 ctf_id_t type
, rtype
;
600 (void) fprintf(ABISTREAM
, "%7u:", (unsigned int)getpid());
602 if ((ctfp
= Pname_to_ctf(proc_hdl
, defname
)) == NULL
)
605 if (Pxlookup_by_name(proc_hdl
, PR_LMID_EVERY
, defname
, sym_name
,
609 if (ctf_func_info(ctfp
, si
.prs_id
, &finfo
) == CTF_ERR
)
612 (void) type_name(ctfp
, finfo
.ctc_return
, buf
, sizeof (buf
));
613 (void) fprintf(ABISTREAM
, "-> %-8s -> %8s:%s %s(",
614 refname
, defname
, buf
, sym_name
);
617 * According to bug in la_pltexit(), it can't return
618 * if the type is just a struct/union. So, if the return
619 * type is a struct/union, la_pltexit() should be off.
621 rtype
= ctf_type_resolve(ctfp
, finfo
.ctc_return
);
622 type
= ctf_type_reference(ctfp
, rtype
);
623 rtype
= ctf_type_resolve(ctfp
, type
);
624 kind
= ctf_type_kind(ctfp
, rtype
);
625 if ((kind
== CTF_K_STRUCT
|| kind
== CTF_K_UNION
) &&
626 strpbrk(buf
, "*") == NULL
)
627 *sb_flags
|= LA_SYMB_NOPLTEXIT
;
629 argc
= MIN(sizeof (argt
) / sizeof (argt
[0]), finfo
.ctc_argc
);
630 (void) ctf_func_args(ctfp
, si
.prs_id
, argc
, argt
);
632 argv
[0] = GETARG0(regset
);
634 argv
[1] = GETARG1(regset
);
636 argv
[2] = GETARG2(regset
);
638 argv
[3] = GETARG3(regset
);
640 argv
[4] = GETARG4(regset
);
642 argv
[5] = GETARG5(regset
);
644 for (i
= 6; i
< argc
; i
++)
645 argv
[i
] = GETARG_6NUP(i
, regset
);
648 for (i
= 0; i
< argc
; i
++) {
649 (void) type_name(ctfp
, argt
[i
], buf
, sizeof (buf
));
650 (void) fprintf(ABISTREAM
, "%s%s = ", sep
, buf
);
651 rtype
= ctf_type_resolve(ctfp
, argt
[i
]);
652 type
= ctf_type_reference(ctfp
, rtype
);
653 rtype
= ctf_type_resolve(ctfp
, type
);
654 kind
= ctf_type_kind(ctfp
, rtype
);
655 if (kind
== CTF_K_STRUCT
|| kind
== CTF_K_UNION
)
656 (void) fprintf(ABISTREAM
, "0x%p", (void *)argv
[i
]);
658 print_value(ctfp
, argt
[i
], argv
[i
]);
662 if (finfo
.ctc_flags
& CTF_FUNC_VARARG
)
663 (void) fprintf(ABISTREAM
, "%s...", sep
);
665 (void) fprintf(ABISTREAM
, "void");
667 if ((*sb_flags
& LA_SYMB_NOPLTEXIT
) != 0)
668 (void) fprintf(ABISTREAM
, ") ** ST\n");
670 (void) fprintf(ABISTREAM
, ")\n");
672 if (verbose_list
!= NULL
&&
673 check_intlist(verbose_list
, sym_name
) != 0) {
674 for (i
= 0; i
< argc
; i
++) {
675 (void) type_name(ctfp
, argt
[i
], buf
, sizeof (buf
));
676 (void) fprintf(ABISTREAM
, "\targ%d = (%s) ", i
, buf
);
677 print_value(ctfp
, argt
[i
], argv
[i
]);
678 (void) fprintf(ABISTREAM
, "\n");
680 if ((*sb_flags
& LA_SYMB_NOPLTEXIT
) != 0) {
681 if (kind
== CTF_K_STRUCT
)
682 (void) fprintf(ABISTREAM
,
683 "\treturn = (struct), apptrace "
684 "will not trace the return\n");
686 (void) fprintf(ABISTREAM
,
687 "\treturn = (union), apptrace "
688 "will not trace the return\n");
692 (void) fflush(ABISTREAM
);
694 return (symp
->st_value
);
697 (void) fprintf(ABISTREAM
,
698 "-> %-8s -> %8s:%s(0x%lx, 0x%lx, 0x%lx) ** NR\n",
699 refname
, defname
, sym_name
,
700 (ulong_t
)GETARG0(regset
),
701 (ulong_t
)GETARG1(regset
),
702 (ulong_t
)GETARG2(regset
));
704 *sb_flags
|= LA_SYMB_NOPLTEXIT
;
705 (void) fflush(ABISTREAM
);
707 return (symp
->st_value
);
713 la_pltexit64(Elf64_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
714 uintptr_t *defcookie
, uintptr_t retval
, const char *sym_name
)
717 la_pltexit(Elf32_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
718 uintptr_t *defcookie
, uintptr_t retval
)
722 const char *sym_name
= (const char *)symp
->st_name
;
729 ctf_funcinfo_t finfo
;
730 char *defname
= (char *)(*defcookie
);
731 char *refname
= (char *)(*refcookie
);
736 (void) fprintf(ABISTREAM
, "%7u:", (unsigned int)getpid());
739 if (verbose_list
== NULL
) {
740 (void) fprintf(ABISTREAM
, "<- %-8s -> %8s:%s()\n",
741 refname
, defname
, sym_name
);
742 (void) fflush(ABISTREAM
);
748 if ((ctfp
= Pname_to_ctf(proc_hdl
, defname
)) == NULL
)
751 if (Pxlookup_by_name(proc_hdl
, PR_LMID_EVERY
, defname
,
752 sym_name
, &sym
, &si
) != 0)
755 if (ctf_func_info(ctfp
, si
.prs_id
, &finfo
) == CTF_ERR
)
758 if (verbose_list
!= NULL
) {
759 if (check_intlist(verbose_list
, sym_name
) != 0) {
760 (void) type_name(ctfp
, finfo
.ctc_return
, buf
,
762 (void) fprintf(ABISTREAM
, "\treturn = (%s) ", buf
);
763 print_value(ctfp
, finfo
.ctc_return
, retval
);
764 (void) fprintf(ABISTREAM
, "\n");
765 (void) fprintf(ABISTREAM
, "<- %-8s -> %8s:%s()",
766 refname
, defname
, sym_name
);
767 (void) fprintf(ABISTREAM
, " = 0x%p\n", (void *)retval
);
770 (void) fprintf(ABISTREAM
, "<- %-8s -> %8s:%s()",
771 refname
, defname
, sym_name
);
772 (void) fprintf(ABISTREAM
, " = 0x%p\n", (void *)retval
);
775 (void) fflush(ABISTREAM
);
780 if (verbose_list
!= NULL
) {
781 if (check_intlist(verbose_list
, sym_name
) != 0) {
782 (void) fprintf(ABISTREAM
,
783 "\treturn = 0x%p\n", (void *)retval
);
784 (void) fprintf(ABISTREAM
, "<- %-8s -> %8s:%s()",
785 refname
, defname
, sym_name
);
786 (void) fprintf(ABISTREAM
, " = 0x%p\n", (void *)retval
);
789 (void) fprintf(ABISTREAM
, "<- %-8s -> %8s:%s()",
790 refname
, defname
, sym_name
);
791 (void) fprintf(ABISTREAM
, " = 0x%p\n", (void *)retval
);
794 (void) fflush(ABISTREAM
);