/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <link.h>
#include <dlfcn.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <regex.h>
#include <signal.h>
#include <synch.h>
#include <fcntl.h>
#include <apptrace.h>
#include <libintl.h>
#include <locale.h>
#include <limits.h>
#include <sys/sysmacros.h>
#include "abienv.h"
#include "mach.h"
#include <libproc.h>
#include <libctf.h>
/*
* Required for calls to build_env_list1 where
* things are added to the end of the list (preserving
* search order implied by the setting of env variables
* in apptracecmd.c)
*/
/*
* These globals are sought and used by interceptlib.c
* which goes into all interceptor objects.
*/
/*
* Strings are printed with "%.*s", abi_strpsz, string
*/
/*
* Special function pointers that'll be set up to point at the
* to our own).
*
* Additionally, it is impossible to generalize the programmatic
* creation of interceptor functions for variable argument list
* functions. However, in the case of the printf family, there is a
* vprintf equivalent. The interceptors for the printf family live in
* interceptor.c and they call the appropriate vprintf interface
* instead of the printf interface that they're intercepting. The
* link map issue remains, however, so function pointers for the
* vprintf family in the application's link map are set up here.
*
* The interceptors also need to examine errno which also needs to be
* extracted from the base link map.
*
* All of these pointers are initialized in la_preinit().
*/
int (*abi_thr_main)(void);
int *(*__abi_real_errno)(void);
#if defined(__sparcv9)
#else
#endif
/* Used as arguments later to dlsym */
/*
* The list of functions below are functions for which
* apptrace.so will not perform any tracing.
*
* The user visible failure of tracing these functions
* is a core dump of the application under observation.
*
* This list was originally discovered during sotruss
* development. Attempts lacking sufficient determination
* to shrink this list have failed.
*
* There are a number of different kinds of issues here.
*
* The .stretX functions have to do with the relationship
* that the caller and callee has with functions that
* return structures and the altered calling convention
* that results.
*
* We cannot trace *setjmp because the caller of these routines
* is not allow to return which is exactly what an interceptor
* function is going to do.
*
* The *context functions are on the list because we cannot trace
* netscape without them on the list, but the exact mechanics of the
* failure are not known at this time.
*
* The leaf functions *getsp can probably be removed given the
* presence of an interceptor but that experiment has not been
* conducted.
*
* NOTE: this list *must* be maintained in alphabetical order.
* if this list ever became too long a faster search mechanism
* should be considered.
*/
static char *spec_sym[] = {
#if defined(sparc)
".stret1",
".stret2",
".stret4",
".stret8",
#endif
"__getcontext",
"_getcontext",
"_getsp",
"_longjmp",
"_setcontext",
"_setjmp",
"_siglongjmp",
"_sigsetjmp",
"_vfork",
"getcontext",
"getsp",
"longjmp",
"setcontext",
"setjmp",
"siglongjmp",
"sigsetjmp",
"vfork",
};
{
char *str;
if (version > LAV_CURRENT)
"apptrace: unexpected version: %u\n"),
version);
pidout = 1;
} else {
/*
* This disables apptrace output in subsequent exec'ed
* processes.
*/
}
"apptrace: getrlimit: %s\n"),
}
if (fd == -1) {
"apptrace: %s: %s\n"),
str,
}
/*
* Those fans of dup2 should note that dup2 cannot
* be used below because dup2 closes the target file
* descriptor. Thus, if we're apptracing say, ksh
* we'd have closed the fd it uses for the history
* file (63 on my box).
*
* fcntl with F_DUPFD returns first available >= arg3
* so we iterate from the top until we find a available
* fd.
*
* Not finding an fd after 10 tries is a failure.
*
* Since the _file member of the FILE structure is an
* unsigned char, we must clamp our fd request to
* UCHAR_MAX
*/
break;
}
if (newfd == -1) {
"apptrace: F_DUPFD: %s\n"),
}
"apptrace: fcntl FD_CLOEXEC: %s\n"),
}
} else {
"apptrace: fdopen: %s\n"),
}
}
#if defined(_LP64)
"APPTRACE_INTERCEPTORS64");
#else
"APPTRACE_INTERCEPTORS");
#endif
/* Set up lists interfaces to trace or ignore */
return (LAV_CURRENT);
}
/* ARGSUSED1 */
{
int perr;
/*
* If this is the first time in, then l_name is the app
* and unless the user gave an explict from list
* we will trace calls from it.
*/
first = 0;
goto work;
}
/*
* If we have no bindto_list, then we assume that we
* bindto everything (apptrace -T \*)
*
* Otherwise we make sure that l_name is on the list.
*/
flags = 0;
if (bindto_list == NULL) {
flags |= LA_FLG_BINDTO;
}
/*
* If l_name is on the exclusion list, zero the bit.
*/
if ((bindto_excl != NULL) &&
flags &= ~LA_FLG_BINDTO;
}
/*
* If l_name is on the bindfrom list then trace
*/
flags |= LA_FLG_BINDFROM;
}
/*
* If l_name is on the exclusion list, zero the bit
* else trace, (this allows "-F !foo" to imply
* "-F '*' -F !foo")
*/
flags &= ~LA_FLG_BINDFROM;
flags |= LA_FLG_BINDFROM;
}
work:
if (flags) {
/*
* only call Pgrab() once to get the ps_prochandle
*/
}
return (flags);
}
static void
apptrace_preinit_fail(void)
{
dlerror());
}
/* ARGSUSED */
void
{
void *h = NULL;
(void) sigfillset(&abisigset);
if (h == NULL)
if ((abi_thr_self =
if ((abi_thr_main =
/* Do printf style pointers */
if ((ABI_VFPRINTF =
if ((ABI_VFWPRINTF =
if ((ABI_VPRINTF =
(int (*)(char const *, va_list))
if ((ABI_VSNPRINTF =
if ((ABI_VSPRINTF =
(int (*)(char *, char const *, va_list))
if ((ABI_VSWPRINTF =
if ((ABI_VWPRINTF =
if ((__abi_real_errno =
(int *(*)(void))
(void) dlclose(h);
}
/* ARGSUSED1 */
#if defined(_LP64)
#else
#endif
{
#if !defined(_LP64)
#endif
char *str;
#if defined(_LP64)
goto end;
#else
/* If we're not looking at a function, bug out */
goto end;
#endif
if (verbose_list != NULL) {
/* apptrace ... -v verbose_list ... cmd */
verbose = 1;
}
if (verbose_excl != NULL) {
/* apptrace ... -v !verbose_excl ... cmd */
verbose = 0;
trace_excl == NULL)
/* apptrace -v !verbose_excl cmd */
intercept = 1;
}
if (trace_list != NULL) {
/* apptrace ... -t trace_list ... cmd */
intercept = 1;
/* default (implies -t '*'): apptrace cmd */
intercept = 1;
if (trace_excl != NULL) {
/* apptrace ... -t !trace_excl ... cmd */
intercept = 0;
}
goto end;
}
/*
* Check to see if this symbol is one of the 'special' symbols.
* If so we disable calls for that symbol.
*/
int cmpval;
if (cmpval < 0)
break;
if (cmpval == 0) {
break;
}
}
end:
return (ret);
}
/* ARGSUSED1 */
#if defined(__sparcv9)
char const *sym_name)
char const *sym_name)
#endif
{
#if !defined(_LP64)
#endif
int argc;
int i;
int kind;
if (pidout)
goto fail;
goto fail;
goto fail;
/*
* According to bug in la_pltexit(), it can't return
*/
if (argc > 1)
if (argc > 2)
if (argc > 3)
if (argc > 4)
if (argc > 5)
if (argc > 6) {
for (i = 6; i < argc; i++)
}
for (i = 0; i < argc; i++) {
else
sep = ", ";
}
else if (argc == 0)
if ((*sb_flags & LA_SYMB_NOPLTEXIT) != 0)
else
if (verbose_list != NULL &&
for (i = 0; i < argc; i++) {
}
if ((*sb_flags & LA_SYMB_NOPLTEXIT) != 0) {
if (kind == CTF_K_STRUCT)
"\treturn = (struct), apptrace "
"will not trace the return\n");
else
"\treturn = (union), apptrace "
"will not trace the return\n");
}
}
fail:
"-> %-8s -> %8s:%s(0x%lx, 0x%lx, 0x%lx) ** NR\n",
}
/* ARGSUSED */
#if defined(_LP64)
#else
#endif
{
#if !defined(_LP64)
#endif
if (pidout)
if (retval == 0) {
if (verbose_list == NULL) {
}
return (retval);
}
goto fail;
goto fail;
goto fail;
if (verbose_list != NULL) {
sizeof (buf));
}
} else {
}
return (retval);
fail:
if (verbose_list != NULL) {
"\treturn = 0x%p\n", (void *)retval);
}
} else {
}
return (retval);
}