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.
84 FILE *ABISTREAM
= stderr
;
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
)
211 if (version
> LAV_CURRENT
)
212 (void) fprintf(stderr
,
213 dgettext(TEXT_DOMAIN
,
214 "apptrace: unexpected version: %u\n"),
217 build_env_list(&bindto_list
, "APPTRACE_BINDTO");
218 build_env_list(&bindto_excl
, "APPTRACE_BINDTO_EXCLUDE");
220 build_env_list(&bindfrom_list
, "APPTRACE_BINDFROM");
221 build_env_list(&bindfrom_excl
, "APPTRACE_BINDFROM_EXCLUDE");
223 if (checkenv("APPTRACE_PID") != NULL
) {
226 char *str
= "LD_AUDIT=";
227 char *str2
= "LD_AUDIT64=";
229 * This disables apptrace output in subsequent exec'ed
236 if ((str
= checkenv("APPTRACE_OUTPUT")) != NULL
) {
237 int fd
, newfd
, targetfd
, lowerlimit
;
240 if (getrlimit(RLIMIT_NOFILE
, &rl
) == -1) {
241 (void) fprintf(stderr
,
242 dgettext(TEXT_DOMAIN
,
243 "apptrace: getrlimit: %s\n"),
248 fd
= open(str
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
250 (void) fprintf(stderr
,
251 dgettext(TEXT_DOMAIN
,
252 "apptrace: %s: %s\n"),
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).
265 * fcntl with F_DUPFD returns first available >= arg3
266 * so we iterate from the top until we find a available
269 * Not finding an fd after 10 tries is a failure.
271 * Since the _file member of the FILE structure is an
272 * unsigned char, we must clamp our fd request to
275 lowerlimit
= ((rl
.rlim_cur
>
276 UCHAR_MAX
) ? UCHAR_MAX
: rl
.rlim_cur
) - 10;
278 for (targetfd
= lowerlimit
+ 10;
279 targetfd
> lowerlimit
; targetfd
--) {
280 if ((newfd
= fcntl(fd
, F_DUPFD
, targetfd
)) != -1)
285 (void) fprintf(stderr
,
286 dgettext(TEXT_DOMAIN
,
287 "apptrace: F_DUPFD: %s\n"),
293 if (fcntl(newfd
, F_SETFD
, FD_CLOEXEC
) == -1) {
294 (void) fprintf(stderr
,
295 dgettext(TEXT_DOMAIN
,
296 "apptrace: fcntl FD_CLOEXEC: %s\n"),
301 if ((fp
= fdopen(newfd
, "wF")) != NULL
) {
304 (void) fprintf(stderr
,
305 dgettext(TEXT_DOMAIN
,
306 "apptrace: fdopen: %s\n"),
313 build_env_list1(&intlib_list
, &intlib_listend
,
314 "APPTRACE_INTERCEPTORS64");
316 build_env_list1(&intlib_list
, &intlib_listend
,
317 "APPTRACE_INTERCEPTORS");
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");
326 return (LAV_CURRENT
);
331 la_objopen(Link_map
*lmp
, Lmid_t lmid
, uintptr_t *cookie
)
334 static int first
= 1;
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.
342 if (first
&& bindfrom_list
== NULL
) {
343 flags
= LA_FLG_BINDFROM
| LA_FLG_BINDTO
;
349 * If we have no bindto_list, then we assume that we
350 * bindto everything (apptrace -T \*)
352 * Otherwise we make sure that l_name is on the list.
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
;
362 * If l_name is on the exclusion list, zero the bit.
364 if ((bindto_excl
!= NULL
) &&
365 check_list(bindto_excl
, lmp
->l_name
) != NULL
) {
366 flags
&= ~LA_FLG_BINDTO
;
370 * If l_name is on the bindfrom list then trace
372 if (check_list(bindfrom_list
, lmp
->l_name
) != NULL
) {
373 flags
|= LA_FLG_BINDFROM
;
377 * If l_name is on the exclusion list, zero the bit
378 * else trace, (this allows "-F !foo" to imply
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
;
389 *cookie
= (uintptr_t)abibasename(lmp
->l_name
);
392 * only call Pgrab() once to get the ps_prochandle
394 if (proc_hdl
== NULL
)
395 proc_hdl
= Pgrab(getpid(), PGRAB_RDONLY
, &perr
);
402 apptrace_preinit_fail(void)
404 (void) fprintf(stderr
,
405 dgettext(TEXT_DOMAIN
, "apptrace: la_preinit: %s\n"),
412 la_preinit(uintptr_t *cookie
)
416 (void) sigfillset(&abisigset
);
418 h
= dlmopen(LM_ID_BASE
, libcpath
, RTLD_LAZY
| RTLD_NOLOAD
);
420 apptrace_preinit_fail();
423 (thread_t (*)(void)) dlsym(h
, thr_self_sym
)) == NULL
)
424 apptrace_preinit_fail();
426 (int (*)(void)) dlsym(h
, thr_main_sym
)) == NULL
)
427 apptrace_preinit_fail();
429 /* Do printf style pointers */
431 (int (*)(FILE *, char const *, va_list))
432 dlsym(h
, vfprintf_sym
)) == NULL
)
433 apptrace_preinit_fail();
436 (int (*)(FILE *, const wchar_t *, va_list))
437 dlsym(h
, vfwprintf_sym
)) == NULL
)
438 apptrace_preinit_fail();
441 (int (*)(char const *, va_list))
442 dlsym(h
, vprintf_sym
)) == NULL
)
443 apptrace_preinit_fail();
446 (int (*)(char *, size_t, char const *, va_list))
447 dlsym(h
, vsnprintf_sym
)) == NULL
)
448 apptrace_preinit_fail();
451 (int (*)(char *, char const *, va_list))
452 dlsym(h
, vsprintf_sym
)) == NULL
)
453 apptrace_preinit_fail();
456 (int (*)(wchar_t *, size_t, const wchar_t *, va_list))
457 dlsym(h
, vswprintf_sym
)) == NULL
)
458 apptrace_preinit_fail();
461 (int (*)(const wchar_t *, va_list))
462 dlsym(h
, vwprintf_sym
)) == NULL
)
463 apptrace_preinit_fail();
465 if ((__abi_real_errno
=
467 dlsym(h
, errno_sym
)) == NULL
)
468 apptrace_preinit_fail();
476 la_symbind64(Elf64_Sym
*symp
, uint_t symndx
, uintptr_t *refcook
,
477 uintptr_t *defcook
, uint_t
*sb_flags
, char const *sym_name
)
480 la_symbind32(Elf32_Sym
*symp
, uint_t symndx
, uintptr_t *refcook
,
481 uintptr_t *defcook
, uint_t
*sb_flags
)
485 char const *sym_name
= (char const *) symp
->st_name
;
487 int intercept
= 0, verbose
= 0;
488 uintptr_t ret
= symp
->st_value
;
493 if (ELF64_ST_TYPE(symp
->st_info
) != STT_FUNC
)
496 /* If we're not looking at a function, bug out */
497 if (ELF32_ST_TYPE(symp
->st_info
) != STT_FUNC
)
501 if (verbose_list
!= NULL
) {
502 /* apptrace ... -v verbose_list ... cmd */
503 if (check_intlist(verbose_list
, sym_name
))
506 if (verbose_excl
!= NULL
) {
507 /* apptrace ... -v !verbose_excl ... cmd */
508 if (check_intlist(verbose_excl
, sym_name
))
510 else if (verbose_list
== NULL
&& trace_list
== NULL
&&
512 /* apptrace -v !verbose_excl cmd */
515 if (trace_list
!= NULL
) {
516 /* apptrace ... -t trace_list ... cmd */
517 if (check_intlist(trace_list
, sym_name
))
519 } else if (verbose_list
== NULL
&& verbose_excl
== NULL
)
520 /* default (implies -t '*'): apptrace cmd */
523 if (trace_excl
!= NULL
) {
524 /* apptrace ... -t !trace_excl ... cmd */
525 if (check_intlist(trace_excl
, sym_name
))
529 if (verbose
== 0 && intercept
== 0) {
530 *sb_flags
|= (LA_SYMB_NOPLTEXIT
| LA_SYMB_NOPLTENTER
);
535 * Check to see if this symbol is one of the 'special' symbols.
536 * If so we disable calls for that symbol.
538 for (ndx
= 0; (str
= spec_sym
[ndx
]) != NULL
; ndx
++) {
540 cmpval
= strcmp(sym_name
, str
);
544 intercept
= verbose
= 0;
545 *sb_flags
|= (LA_SYMB_NOPLTEXIT
| LA_SYMB_NOPLTENTER
);
555 #if defined(__sparcv9)
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)
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)
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)
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
)
575 char *defname
= (char *)(*defcookie
);
576 char *refname
= (char *)(*refcookie
);
579 char const *sym_name
= (char const *)symp
->st_name
;
586 ctf_funcinfo_t finfo
;
588 ctf_id_t argt
[NUM_ARGS
];
589 ulong_t argv
[NUM_ARGS
];
592 ctf_id_t type
, rtype
;
598 (void) fprintf(ABISTREAM
, "%7u:", (unsigned int)getpid());
600 if ((ctfp
= Pname_to_ctf(proc_hdl
, defname
)) == NULL
)
603 if (Pxlookup_by_name(proc_hdl
, PR_LMID_EVERY
, defname
, sym_name
,
607 if (ctf_func_info(ctfp
, si
.prs_id
, &finfo
) == CTF_ERR
)
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
);
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.
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
;
627 argc
= MIN(sizeof (argt
) / sizeof (argt
[0]), finfo
.ctc_argc
);
628 (void) ctf_func_args(ctfp
, si
.prs_id
, argc
, argt
);
630 argv
[0] = GETARG0(regset
);
632 argv
[1] = GETARG1(regset
);
634 argv
[2] = GETARG2(regset
);
636 argv
[3] = GETARG3(regset
);
638 argv
[4] = GETARG4(regset
);
640 argv
[5] = GETARG5(regset
);
642 for (i
= 6; i
< argc
; i
++)
643 argv
[i
] = GETARG_6NUP(i
, regset
);
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
]);
656 print_value(ctfp
, argt
[i
], argv
[i
]);
660 if (finfo
.ctc_flags
& CTF_FUNC_VARARG
)
661 (void) fprintf(ABISTREAM
, "%s...", sep
);
663 (void) fprintf(ABISTREAM
, "void");
665 if ((*sb_flags
& LA_SYMB_NOPLTEXIT
) != 0)
666 (void) fprintf(ABISTREAM
, ") ** ST\n");
668 (void) fprintf(ABISTREAM
, ")\n");
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");
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");
684 (void) fprintf(ABISTREAM
,
685 "\treturn = (union), apptrace "
686 "will not trace the return\n");
690 (void) fflush(ABISTREAM
);
692 return (symp
->st_value
);
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
));
702 *sb_flags
|= LA_SYMB_NOPLTEXIT
;
703 (void) fflush(ABISTREAM
);
705 return (symp
->st_value
);
711 la_pltexit64(Elf64_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
712 uintptr_t *defcookie
, uintptr_t retval
, const char *sym_name
)
715 la_pltexit(Elf32_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
716 uintptr_t *defcookie
, uintptr_t retval
)
720 const char *sym_name
= (const char *)symp
->st_name
;
727 ctf_funcinfo_t finfo
;
728 char *defname
= (char *)(*defcookie
);
729 char *refname
= (char *)(*refcookie
);
734 (void) fprintf(ABISTREAM
, "%7u:", (unsigned int)getpid());
737 if (verbose_list
== NULL
) {
738 (void) fprintf(ABISTREAM
, "<- %-8s -> %8s:%s()\n",
739 refname
, defname
, sym_name
);
740 (void) fflush(ABISTREAM
);
746 if ((ctfp
= Pname_to_ctf(proc_hdl
, defname
)) == NULL
)
749 if (Pxlookup_by_name(proc_hdl
, PR_LMID_EVERY
, defname
,
750 sym_name
, &sym
, &si
) != 0)
753 if (ctf_func_info(ctfp
, si
.prs_id
, &finfo
) == CTF_ERR
)
756 if (verbose_list
!= NULL
) {
757 if (check_intlist(verbose_list
, sym_name
) != 0) {
758 (void) type_name(ctfp
, finfo
.ctc_return
, 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
);
768 (void) fprintf(ABISTREAM
, "<- %-8s -> %8s:%s()",
769 refname
, defname
, sym_name
);
770 (void) fprintf(ABISTREAM
, " = 0x%p\n", (void *)retval
);
773 (void) fflush(ABISTREAM
);
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
);
787 (void) fprintf(ABISTREAM
, "<- %-8s -> %8s:%s()",
788 refname
, defname
, sym_name
);
789 (void) fprintf(ABISTREAM
, " = 0x%p\n", (void *)retval
);
792 (void) fflush(ABISTREAM
);