/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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"
/*
* Includes
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <locale.h>
#include <libintl.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/procfs.h>
#include <libelf.h>
#include <gelf.h>
#include <sys/systeminfo.h>
#include <tnf/tnfctl.h>
#include "set.h"
#include "cmd.h"
#include "spec.h"
#include "expr.h"
#include "source.h"
#include "list.h"
#include "prbk.h"
/*
* Defines - Project private interfaces
*/
#define DEBUG_ENTRY "tnf_probe_debug"
#ifdef TESTING
#define EMPTY_ENTRY "tnf_probe_empty"
#endif
#define USER_OUTSIZE (4*1024*1024)
#define KERNEL_OUTSIZE (384*1024)
#if defined(__sparc)
#define PREX32DIR "/sparcv7/"
#elif defined(__i386) || defined(__amd64)
#define PREX32DIR "/i86/"
#endif
#define PREX32EXEC "/usr/bin" PREX32DIR "prex"
/*
* Globals
*/
char **g_argv; /* copy of argv pointer */
tnfctl_handle_t *g_hndl; /* handle on target or kernel */
static int g_verbose; /* debugging to stderr */
static char *g_cmdname; /* target command name */
static char **g_cmdargs; /* target command args */
static pid_t g_targetpid; /* target process id */
static volatile boolean_t g_getcmds; /* accept input flag */
static boolean_t g_testflag; /* asserted in test mode */
static char *g_preload; /* objects to preload */
static char *g_outname; /* tracefile name */
static char *tracefile; /* tracefile name used by list cmd */
int g_outsize; /* tracefile size */
boolean_t g_kernelmode; /* -k flag: kernel mode */
static int prex_dmodel; /* prex data model */
/*
* Local Declarations
*/
static void usage(char **argv, const char *msg);
static void scanargs(int argc, char **argv);
static int set_signal(void);
static int get_data_model(pid_t pid);
static int get_elf_class(char *filename);
static int get_executable(char *);
static void prex_isaexec(char **argv, char **envp);
static void check_pid_model(char **argv, char **envp);
static void check_exec_model(char **argv, char **envp);
/* #### - FIXME - need to put this in a private header file */
extern void err_fatal(char *s, ...);
extern int yyparse(void);
static tnfctl_errcode_t check_trace_error(tnfctl_handle_t *hndl);
static void set_default_cmd(void);
static void get_commands(void);
static tnfctl_errcode_t set_tracefile(tnfctl_handle_t *hndl);
static tnfctl_errcode_t set_probe_discovery_callback(tnfctl_handle_t *hndl);
static void * perprobe(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p);
static tnfctl_errcode_t perprobe2(tnfctl_handle_t *hndl,
tnfctl_probe_t *probe_p, void *ignored);
static tnfctl_errcode_t percmd(expr_t *expr_p, cmd_kind_t kind, fcn_t *fcn_p,
boolean_t isnew, void *calldata_p);
void quit(boolean_t killtarget, boolean_t runtarget);
void cmd_listtracefile();
/*
* usage() - gives a description of the arguments, and exits
*/
static void
usage(char *argv[], const char *msg)
{
if (msg)
(void) fprintf(stderr,
gettext("%s: %s\n"), argv[0], msg);
(void) fprintf(stderr, gettext(
"usage: %s [options] <cmd> [cmd-args...]\n"), argv[0]);
(void) fprintf(stderr, gettext(
"usage: %s [options] -p <pid>\n"), argv[0]);
(void) fprintf(stderr, gettext(
"usage: %s -s <kbytes-size> -k\n"), argv[0]);
(void) fprintf(stderr, gettext(
"options:\n"));
(void) fprintf(stderr, gettext(
" -o <outfilename> set trace output file name\n"));
(void) fprintf(stderr, gettext(
" -s <kbytes-size> set trace file size\n"));
(void) fprintf(stderr, gettext(
" -l <sharedobjs> shared objects to "
"be preloaded (cmd only)\n"));
exit(1);
}
/*
* main() -
*/
int
main(int argc, char **argv, char **envp)
{
tnfctl_errcode_t err = TNFCTL_ERR_NONE;
int sys_err;
tnfctl_trace_attrs_t trace_attrs;
tnfctl_event_t event = TNFCTL_EVENT_EINTR;
pid_t prex_pid;
/* internationalization stuff */
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
g_argv = argv;
prex_pid = getpid();
#if defined(DEBUG)
fprintf(stderr, "### prex_pid = %d ###\n", prex_pid);
#endif
prex_dmodel = get_data_model(prex_pid);
#if defined(DEBUG)
fprintf(stderr, "### prex_dmodel = %d ###\n", prex_dmodel);
#endif
scanargs(argc, argv);
if (g_kernelmode) {
/* prexing the kernel */
err = tnfctl_kernel_open(&g_hndl);
if (err) {
err_fatal(gettext(
"%s: trouble attaching to the kernel: %s\n"),
argv[0], tnfctl_strerror(err));
}
} else {
/* prexing a user process */
if (g_targetpid != 0) {
/* check data model */
check_pid_model(argv, envp);
/* attach case */
err = tnfctl_pid_open(g_targetpid, &g_hndl);
if (err == TNFCTL_ERR_NOLIBTNFPROBE) {
err_fatal(gettext(
"%s: missing symbols, is "
"libtnfprobe.so loaded in target?\n"),
argv[0], tnfctl_strerror(err));
} else if (err) {
err_fatal(gettext(
"%s: trouble attaching to target "
"process: %s\n"),
argv[0], tnfctl_strerror(err));
}
} else {
/* check elf class model */
check_exec_model(argv, envp);
/* exec case */
err = tnfctl_exec_open(g_cmdname, g_cmdargs, NULL,
g_preload, NULL, &g_hndl);
if (err == TNFCTL_ERR_NONE)
err = tnfctl_trace_attrs_get(g_hndl,
&trace_attrs);
if (err) {
err_fatal(gettext(
"%s: trouble creating target process: "
"%s\n"),
argv[0], tnfctl_strerror(err));
}
g_targetpid = trace_attrs.targ_pid;
}
sys_err = set_signal();
if (sys_err)
err_fatal(gettext(
"%s: trouble setting up signal handler: %s\n"),
argv[0], strerror(err));
}
/* initialize the source stack for the parser */
source_init();
if (!g_kernelmode) {
/* set the tracefile name and size */
err = set_tracefile(g_hndl);
if (err) {
(void) fprintf(stderr, gettext(
"%s: trouble initializing tracefile: %s\n"),
argv[0], tnfctl_strerror(err));
goto Cleanup;
}
err = check_trace_error(g_hndl);
if (err) {
(void) fprintf(stderr, gettext(
"%s: cannot read tracing status : %s\n"),
argv[0], tnfctl_strerror(err));
goto Cleanup;
}
}
/* accept commands from stdin the first time through */
g_getcmds = B_TRUE;
/* set up default aliases */
set_default_cmd();
/* set up creator/destructor function to call for new probes */
err = set_probe_discovery_callback(g_hndl);
if (err) {
(void) fprintf(stderr, gettext(
"%s: error in probe discovery : %s\n"),
argv[0], tnfctl_strerror(err));
goto Cleanup;
}
if (g_kernelmode) {
prbk_warn_pfilter_empty();
}
while (err == TNFCTL_ERR_NONE) {
if (g_kernelmode || g_getcmds) {
g_getcmds = B_FALSE;
get_commands();
}
if (!g_kernelmode && (g_getcmds == B_FALSE)) {
err = tnfctl_continue(g_hndl, &event, NULL);
if (err) {
(void) fprintf(stderr, gettext(
"%s: cannot continue target : %s\n"),
argv[0], tnfctl_strerror(err));
goto Cleanup;
}
}
err = check_trace_error(g_hndl);
if (err) {
(void) fprintf(stderr, gettext(
"%s: cannot read tracing status : %s\n"),
argv[0], tnfctl_strerror(err));
goto Cleanup;
}
if (!g_kernelmode) {
if (event == TNFCTL_EVENT_EXEC) {
(void) printf(gettext(
"Target process exec'd\n"));
quit(B_FALSE, B_TRUE); /* quit resume */
} else if (event == TNFCTL_EVENT_EXIT) {
/* target exited */
(void) fprintf(stderr, gettext(
"%s: target process exited\n"),
g_argv[0]);
goto Cleanup;
} else if (event == TNFCTL_EVENT_TARGGONE) {
/* target terminated */
(void) fprintf(stderr,
gettext("%s: target process disappeared (without calling exit)\n"),
g_argv[0]);
goto Cleanup;
}
}
}
Cleanup:
err = tnfctl_close(g_hndl, TNFCTL_TARG_DEFAULT);
if (err)
(void) fprintf(stderr, gettext(
"%s: error on closing : %s\n"),
argv[0], tnfctl_strerror(err));
exit(0);
return (0);
}
/*
* check_trace_error() - checks whether there was an error in tracing
*/
static tnfctl_errcode_t
check_trace_error(tnfctl_handle_t *hndl)
{
tnfctl_trace_attrs_t trace_attrs;
tnfctl_errcode_t err;
err = tnfctl_trace_attrs_get(hndl, &trace_attrs);
if (err)
return (err);
if (trace_attrs.trace_buf_state == TNFCTL_BUF_BROKEN) {
(void) printf(gettext("Tracing shut down in target program "
"due to an internal error - Please restart prex "
"and target\n"));
}
return (TNFCTL_ERR_NONE);
}
/*
* set_default_cmd() - set the default debug entry and $all
*/
static void
set_default_cmd(void)
{
if (!g_kernelmode)
fcn(strdup("debug"), DEBUG_ENTRY);
#ifdef TESTING
fcn(strdup("empty"), EMPTY_ENTRY);
#endif
(void) set(strdup("all"), expr(spec(strdup("keys"), SPEC_EXACT),
spec(strdup(".*"), SPEC_REGEXP)));
}
/*
* process() - enable and disable selected probes
*/
typedef struct {
tnfctl_probe_t *probe_p;
tnfctl_handle_t *hndl;
} process_args_t;
static tnfctl_errcode_t
percmd(expr_t *expr_p, cmd_kind_t kind, fcn_t *fcn_p, boolean_t isnew,
void *calldata_p)
{
process_args_t *args_p = (process_args_t *)calldata_p;
tnfctl_handle_t *hndl = args_p->hndl;
tnfctl_probe_t *probe_p = args_p->probe_p;
tnfctl_errcode_t err = TNFCTL_ERR_NONE;
char *attrs;
attrs = list_getattrs(probe_p);
if (expr_match(expr_p, attrs)) {
#if defined(DEBUG) || defined(lint)
if (g_verbose) {
char *cmdstr[] = {
"enable", "disable",
"connect", "clear",
"trace", "untrace"};
(void) fprintf(stderr, ": %s command: %s ",
(isnew) ? "new" : "old", cmdstr[kind]);
expr_print(stderr, expr_p);
}
#endif
switch (kind) {
case CMD_ENABLE:
err = tnfctl_probe_enable(hndl, probe_p, NULL);
break;
case CMD_DISABLE:
err = tnfctl_probe_disable(hndl, probe_p, NULL);
break;
case CMD_TRACE:
err = tnfctl_probe_trace(hndl, probe_p, NULL);
break;
case CMD_UNTRACE:
err = tnfctl_probe_untrace(hndl, probe_p, NULL);
break;
case CMD_CONNECT:
err = tnfctl_probe_connect(hndl, probe_p, NULL,
fcn_p->entry_name_p);
break;
case CMD_CLEAR:
err = tnfctl_probe_disconnect_all(hndl, probe_p, NULL);
break;
}
#if defined(DEBUG) || defined(lint)
if (g_verbose)
(void) fprintf(stderr, "\n");
#endif
}
if (attrs)
free(attrs);
return (err);
}
/*ARGSUSED*/
static void *
perprobe(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p)
{
process_args_t args;
tnfctl_errcode_t err;
args.probe_p = probe_p;
args.hndl = hndl;
err = cmd_traverse(percmd, &args);
if (err) {
(void) fprintf(stderr, gettext(
"%s: error on new (dlopened) probe : %s\n"),
g_argv[0], tnfctl_strerror(err));
}
return (NULL);
}
static tnfctl_errcode_t
set_probe_discovery_callback(tnfctl_handle_t *hndl)
{
tnfctl_errcode_t err;
err = tnfctl_register_funcs(hndl, perprobe, NULL);
if (err)
return (err);
return (TNFCTL_ERR_NONE);
}
static tnfctl_errcode_t
perprobe2(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p, void *cd)
{
cmd_t *cmd = cd;
process_args_t args;
tnfctl_errcode_t err;
args.probe_p = probe_p;
args.hndl = hndl;
err = cmd_callback(cmd, percmd, &args);
if (err) {
(void) fprintf(stderr, gettext(
"%s: error on probe operation: %s\n"),
g_argv[0], tnfctl_strerror(err));
}
return (err);
}
void
process_cmd(tnfctl_handle_t *hndl, cmd_t *cmd)
{
#if defined(DEBUG) || defined(lint)
if (g_verbose)
(void) fprintf(stderr, "processing commands\n");
#endif
(void) tnfctl_probe_apply(hndl, perprobe2, cmd);
}
/*
* get_commands() - process commands from stdin
*/
static void
get_commands(void)
{
/* Read commands from STDIN */
if (g_kernelmode) {
(void) printf(gettext("Type \"help\" for help ...\n"));
} else {
if (g_testflag)
(void) printf("prex(%ld), target(%ld): ",
getpid(), g_targetpid);
(void) printf(gettext("Target process stopped\n"));
(void) printf(gettext(
"Type \"continue\" to resume the target, "
"\"help\" for help ...\n"));
}
while (yyparse());
}
/*
* quit() - called to quit the controlling process. The boolean argument
* specifies whether to terminate the target as well.
*/
void
quit(boolean_t killtarget, boolean_t runtarget)
{
tnfctl_errcode_t err;
if (killtarget && runtarget)
err = tnfctl_close(g_hndl, TNFCTL_TARG_DEFAULT);
else if (killtarget && !runtarget)
err = tnfctl_close(g_hndl, TNFCTL_TARG_KILL);
else if (!killtarget && runtarget)
err = tnfctl_close(g_hndl, TNFCTL_TARG_RESUME);
else if (!killtarget && !runtarget)
err = tnfctl_close(g_hndl, TNFCTL_TARG_SUSPEND);
if (err) {
(void) fprintf(stderr, gettext(
"%s: trouble quitting : %s\n"),
g_argv[0], tnfctl_strerror(err));
exit(1);
}
exit(0);
}
/*
* scanargs() - processes the command line arguments
*/
#define strneq(s1, s2, n) (strncmp(s1, s2, n) == 0)
static void
scanargs(int argc,
char **argv)
{
int c;
#if defined(DEBUG) || defined(lint)
char *optstr = "l:o:p:s:tkv:"; /* debugging options */
#else
char *optstr = "l:o:p:s:tk"; /* production options */
#endif
/* set up some defaults */
g_targetpid = 0;
g_cmdname = NULL;
g_cmdargs = NULL;
g_preload = NULL;
g_outname = NULL;
g_outsize = -1;
while ((c = getopt(argc, argv, optstr)) != EOF) {
switch (c) {
case 'l': /* preload objects */
g_preload = optarg;
break;
case 'o': /* tracefile name */
g_outname = optarg;
break;
case 'p': /* target pid (attach case) */
g_targetpid = atoi(optarg);
break;
case 's': /* tracefile size */
g_outsize = atoi(optarg) * 1024;
break;
case 't': /* test flag */
g_testflag = B_TRUE;
(void) setvbuf(stdout, NULL, _IOLBF, 0);
break;
case 'k': /* kernel mode */
g_kernelmode = B_TRUE;
break;
#if defined(DEBUG) || defined(lint)
case 'v': /* verbose flag */
g_verbose = atoi(optarg);
break;
#endif
case '?': /* error case */
usage(argv, gettext("unrecognized argument"));
}
}
if (optind < argc) {
g_cmdname = strdup(argv[optind]);
g_cmdargs = &argv[optind];
}
/* sanity clause */
if (!g_kernelmode && (g_cmdname == NULL && g_targetpid == 0))
usage(argv, gettext("need to specify cmd or pid"));
if (g_cmdname != NULL && g_targetpid != 0)
usage(argv, gettext("can't specify both cmd and pid"));
if (g_targetpid && g_preload)
usage(argv, gettext("can't use preload option with attach"));
if (g_kernelmode) {
if (g_outname)
usage(argv, "can't specify a filename in kernel mode");
if (g_cmdname)
usage(argv, "can't specify a command in kernel mode");
if (g_targetpid)
usage(argv, "can't specify pid in kernel mode");
if (g_preload)
usage(argv, "can't use preload option in kernel mode");
}
/* default output size */
if (g_outsize == -1)
g_outsize = g_kernelmode ? KERNEL_OUTSIZE : USER_OUTSIZE;
#ifdef OLD
int i;
for (i = 1; i < argc; i++) {
if (strneq(argv[i], "-v", 2)) {
int vlevel;
vlevel = (strlen(argv[i]) > 2)? atoi(&argv[i][2]) : 1;
g_verbose = B_TRUE;
prb_verbose_set(vlevel);
} else if (strneq(argv[i], "-pid", 2)) {
if (++i >= argc)
usage(argv, gettext("missing pid argument"));
g_targetpid = atoi(argv[i]);
} else if (strneq(argv[i], "-t", 2)) {
g_testflag = B_TRUE;
(void) setvbuf(stdout, NULL, _IOLBF, 0);
} else if (argv[i][0] != '-') {
g_cmdname = strdup(argv[i]);
if (!g_cmdname) {
err_fatal(gettext(
"%s: out of memory"), argv[0]);
}
if (g_verbose >= 2) {
(void) fprintf(stderr,
"cmdname=%s\n", g_cmdname);
}
/*
* rest of arguments are the args to the executable -
* by convention argv[0] should be name of
* executable, so we don't increment i
*/
g_cmdargs = &argv[i];
break;
} else {
usage(argv, gettext("unrecognized argument"));
}
}
#endif
} /* end scanargs */
/*
* sig_handler() - cleans up if a signal is received
*/
/*ARGSUSED*/
static void
sig_handler(int signo)
{
g_getcmds = B_TRUE;
} /* end sig_handler */
/*
* set_signal() - sets up function to call for clean up
*/
static int
set_signal(void)
{
struct sigaction newact;
newact.sa_handler = sig_handler;
(void) sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
if (sigaction(SIGINT, &newact, NULL) < 0) {
return (errno);
}
return (0);
}
/*
* set_tracefile() - initializes tracefile, sets the tracefile name and size
*/
static tnfctl_errcode_t
set_tracefile(tnfctl_handle_t *hndl)
{
tnfctl_errcode_t err;
tnfctl_trace_attrs_t attrs;
size_t minoutsize;
char path[MAXPATHLEN];
char *outfile_name;
char *tmpdir;
/* Init tracefile name used by list cmd */
tracefile = NULL;
err = tnfctl_trace_attrs_get(hndl, &attrs);
if (err)
return (err);
if (attrs.trace_buf_state == TNFCTL_BUF_BROKEN)
return (TNFCTL_ERR_BUFBROKEN);
if (attrs.trace_buf_state == TNFCTL_BUF_OK) {
/* trace file set already - can't change it */
return (TNFCTL_ERR_NONE);
}
minoutsize = attrs.trace_min_size;
if (g_outsize < minoutsize) {
(void) fprintf(stderr,
gettext("specified tracefile size smaller then "
"minimum; setting to %d kbytes\n"),
minoutsize / 1024);
g_outsize = minoutsize;
}
/* where is $TMPDIR? */
tmpdir = getenv("TMPDIR");
if (!tmpdir || *tmpdir == '\0') {
tmpdir = "/tmp";
}
/* do we have an absolute, relative or no pathname specified? */
if (g_outname == NULL) {
/* default, no tracefile specified */
if ((strlen(tmpdir) + 1 + 20) > (size_t)MAXPATHLEN) {
(void) fprintf(stderr, gettext(
"%s: $TMPDIR too long\n"), g_argv[0]);
exit(1);
}
(void) sprintf(path, "%s/trace-%ld", tmpdir, g_targetpid);
outfile_name = path;
} else {
/* filename specified */
outfile_name = g_outname;
}
tracefile = strdup(outfile_name);
if (tracefile == NULL) {
if ((errno == ENOMEM) || (errno == EAGAIN)) {
return (TNFCTL_ERR_ALLOCFAIL);
} else {
return (TNFCTL_ERR_INTERNAL);
}
}
#if defined(DEBUG) || defined(lint)
if (g_verbose)
(void) fprintf(stderr,
"setting tracefile name=\"%s\", size=%d\n",
path, g_outsize);
#endif
err = tnfctl_buffer_alloc(hndl, outfile_name, g_outsize);
return (err);
}
/*
* get_data_model() - get the process data model from psinfo
* structure.
*/
#define PROCFORMAT "/proc/%d"
static int
get_data_model(pid_t pid)
{
char path[MAXPATHLEN];
int fd, dmodel = -1;
prpsinfo_t psinfo;
(void) sprintf(path, PROCFORMAT, (int)pid);
fd = open(path, O_RDONLY);
if (fd == -1)
return (dmodel);
if ((dmodel = ioctl(fd, PIOCPSINFO, &psinfo)) == -1)
return (dmodel);
return ((int)psinfo.pr_dmodel);
}
/*
* get_executable - return file descriptor for PATH-resolved
* target file.
*
*/
static int
get_executable(char *name) {
int fd = -1;
if (name != NULL) {
char path[PATH_MAX + 1];
char line[MAX_INPUT + 1];
char *p = line;
char *fname = name;
int N = sizeof (line);
struct stat file_att;
while (*fname == ' ') fname++;
if (fname[0] == '-' || strchr(fname, '/')) {
fd = open(fname, O_RDONLY);
} else {
int len = strlen(fname);
char *dirlist = getenv("PATH");
char *dir = NULL;
if (dirlist != NULL) {
dirlist = strdup(dirlist);
dir = strtok(dirlist, ":");
}
while (fd < 0 && dir != NULL) {
if ((strlen(dir) + len + 1) < sizeof (path)) {
strcat(strcat(strcpy(path, dir), "/"), fname);
fd = open(path, O_RDONLY);
}
dir = strtok(NULL, ":");
}
if (dirlist != NULL) free(dirlist);
}
if (fstat(fd, &file_att) || !S_ISREG(file_att.st_mode)) {
if (fd >= 0)
close(fd);
return (-1);
}
if (read(fd, p, 2) && p[0] == '#' && p[1] == '!') {
while (N-- > 1 && read(fd, p, 1) && *p != '\n')
p++;
*p = '\0';
close(fd);
return (get_executable(line));
}
if (fd >= 0) lseek(fd, 0, SEEK_SET);
} /* %$#@! cstyle complaint */
return (fd);
}
/*
* get_elf_class - get the target executable elf class
* i.e. ELFCLASS64 or ELFCLASS32.
*/
static int
get_elf_class(char *filename)
{
int elfclass = -1;
int elffd = get_executable(filename);
Elf *elf;
size_t size;
char *ident;
GElf_Ehdr ehdr;
if (elffd < 0)
return (elfclass);
if (elf_version(EV_CURRENT) == EV_NONE) {
(void) close(elffd);
return (elfclass);
}
elf = elf_begin(elffd, ELF_C_READ, (Elf *) 0);
/*
* verify information in file header
*/
if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *) 0) {
close(elffd);
return (elfclass);
}
ident = elf_getident(elf, &size);
if (ident[EI_CLASS] == ELFCLASS32)
elfclass = ELFCLASS32;
if (ident[EI_CLASS] == ELFCLASS64)
elfclass = ELFCLASS64;
close(elffd);
return (elfclass);
}
/*
* check_exec_model() - check the consistency between prex data model
* and target elf class and act accordingly
*/
static void
check_exec_model(char **argv, char **envp)
{
int elfclass;
elfclass = get_elf_class(g_cmdname);
if (((elfclass == ELFCLASS32) && (prex_dmodel == PR_MODEL_ILP32)) ||
((elfclass == ELFCLASS64) && (prex_dmodel == PR_MODEL_LP64)))
return;
if ((prex_dmodel == PR_MODEL_ILP32) &&
(elfclass == ELFCLASS64)) {
(void) fprintf(stderr, gettext(
"Error: 32 bit prex can not exec 64 bit target\n"));
exit(1);
}
if ((prex_dmodel == PR_MODEL_LP64) &&
(elfclass == ELFCLASS32))
prex_isaexec(argv, envp);
}
/*
* check_pid_model() - check the consistency between prex data model
* and target data model and act accordingly
*/
static void
check_pid_model(char **argv, char **envp)
{
int dmodel;
dmodel = get_data_model(g_targetpid);
if (prex_dmodel == dmodel)
return;
if ((prex_dmodel == PR_MODEL_ILP32) &&
(dmodel == PR_MODEL_LP64)) {
(void) fprintf(stderr, gettext(
"Error: 32 bit prex can not exec 64 bit target\n"));
exit(1);
}
if ((prex_dmodel == PR_MODEL_LP64) &&
(dmodel == PR_MODEL_ILP32))
prex_isaexec(argv, envp);
}
/*
* prex_isaexec() - there is only one case this function get called
* 64 bit prex, 32 bit target, need to exec 32 bit
* prex here.
*/
static void
prex_isaexec(char **argv, char **envp)
{
char path[PATH_MAX + sizeof (PREX32DIR)];
strcat(strcat(strcpy(path, dirname(dirname(argv[0]))), PREX32DIR),
basename(argv[0]));
if (get_elf_class(path) != ELFCLASS32)
strcpy(path, PREX32EXEC);
argv[0] = path;
(void) execve(path, argv, envp);
(void) fprintf(stderr,
gettext("%s: execve(\"%s\") failed\n"),
argv[0], path);
exit(1);
}
void
cmd_listtracefile()
{
if (g_kernelmode) {
(void) fprintf(stderr,
gettext("There is no trace file in kernel mode!\n"));
} else {
(void) printf(gettext("Current trace file is: %s\n"), tracefile);
}
}