C++程序  |  1311行  |  30.96 KB

/*
 * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl>
 * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl>
 * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com>
 * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl>
 * Copyright (c) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *                     Linux for s390 port by D.J. Barrow
 *                    <barrow_dj@mail.yahoo.com,djbarrow@de.ibm.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "defs.h"
#include <sys/param.h>

/* for struct iovec */
#include <sys/uio.h>

#include "regs.h"
#include "ptrace.h"

#if defined(SPARC64)
# undef PTRACE_GETREGS
# define PTRACE_GETREGS PTRACE_GETREGS64
# undef PTRACE_SETREGS
# define PTRACE_SETREGS PTRACE_SETREGS64
#endif

#if defined SPARC64
# include <asm/psrcompat.h>
#elif defined SPARC
# include <asm/psr.h>
#endif

#ifdef IA64
# include <asm/rse.h>
#endif

#ifndef NT_PRSTATUS
# define NT_PRSTATUS 1
#endif

#ifndef NSIG
# warning: NSIG is not defined, using 32
# define NSIG 32
#endif

#include "syscall.h"

/* Define these shorthand notations to simplify the syscallent files. */
#define TD TRACE_DESC
#define TF TRACE_FILE
#define TI TRACE_IPC
#define TN TRACE_NETWORK
#define TP TRACE_PROCESS
#define TS TRACE_SIGNAL
#define TM TRACE_MEMORY
#define NF SYSCALL_NEVER_FAILS
#define MA MAX_ARGS
#define SI STACKTRACE_INVALIDATE_CACHE
#define SE STACKTRACE_CAPTURE_ON_ENTER

const struct_sysent sysent0[] = {
#include "syscallent.h"
};

#if SUPPORTED_PERSONALITIES > 1
static const struct_sysent sysent1[] = {
# include "syscallent1.h"
};
#endif

#if SUPPORTED_PERSONALITIES > 2
static const struct_sysent sysent2[] = {
# include "syscallent2.h"
};
#endif

/* Now undef them since short defines cause wicked namespace pollution. */
#undef TD
#undef TF
#undef TI
#undef TN
#undef TP
#undef TS
#undef TM
#undef NF
#undef MA
#undef SI
#undef SE

/*
 * `ioctlent[012].h' files are automatically generated by the auxiliary
 * program `ioctlsort', such that the list is sorted by the `code' field.
 * This has the side-effect of resolving the _IO.. macros into
 * plain integers, eliminating the need to include here everything
 * in "/usr/include".
 */

const char *const errnoent0[] = {
#include "errnoent.h"
};
const char *const signalent0[] = {
#include "signalent.h"
};
const struct_ioctlent ioctlent0[] = {
#include "ioctlent0.h"
};

#if SUPPORTED_PERSONALITIES > 1
static const char *const errnoent1[] = {
# include "errnoent1.h"
};
static const char *const signalent1[] = {
# include "signalent1.h"
};
static const struct_ioctlent ioctlent1[] = {
# include "ioctlent1.h"
};
#endif

#if SUPPORTED_PERSONALITIES > 2
static const char *const errnoent2[] = {
# include "errnoent2.h"
};
static const char *const signalent2[] = {
# include "signalent2.h"
};
static const struct_ioctlent ioctlent2[] = {
# include "ioctlent2.h"
};
#endif

enum {
	nsyscalls0 = ARRAY_SIZE(sysent0)
#if SUPPORTED_PERSONALITIES > 1
	, nsyscalls1 = ARRAY_SIZE(sysent1)
# if SUPPORTED_PERSONALITIES > 2
	, nsyscalls2 = ARRAY_SIZE(sysent2)
# endif
#endif
};

enum {
	nerrnos0 = ARRAY_SIZE(errnoent0)
#if SUPPORTED_PERSONALITIES > 1
	, nerrnos1 = ARRAY_SIZE(errnoent1)
# if SUPPORTED_PERSONALITIES > 2
	, nerrnos2 = ARRAY_SIZE(errnoent2)
# endif
#endif
};

enum {
	nsignals0 = ARRAY_SIZE(signalent0)
#if SUPPORTED_PERSONALITIES > 1
	, nsignals1 = ARRAY_SIZE(signalent1)
# if SUPPORTED_PERSONALITIES > 2
	, nsignals2 = ARRAY_SIZE(signalent2)
# endif
#endif
};

enum {
	nioctlents0 = ARRAY_SIZE(ioctlent0)
#if SUPPORTED_PERSONALITIES > 1
	, nioctlents1 = ARRAY_SIZE(ioctlent1)
# if SUPPORTED_PERSONALITIES > 2
	, nioctlents2 = ARRAY_SIZE(ioctlent2)
# endif
#endif
};

#if SUPPORTED_PERSONALITIES > 1
const struct_sysent *sysent = sysent0;
const char *const *errnoent = errnoent0;
const char *const *signalent = signalent0;
const struct_ioctlent *ioctlent = ioctlent0;
#endif
unsigned nsyscalls = nsyscalls0;
unsigned nerrnos = nerrnos0;
unsigned nsignals = nsignals0;
unsigned nioctlents = nioctlents0;

unsigned num_quals;
qualbits_t *qual_vec[SUPPORTED_PERSONALITIES];

static const unsigned nsyscall_vec[SUPPORTED_PERSONALITIES] = {
	nsyscalls0,
#if SUPPORTED_PERSONALITIES > 1
	nsyscalls1,
#endif
#if SUPPORTED_PERSONALITIES > 2
	nsyscalls2,
#endif
};
static const struct_sysent *const sysent_vec[SUPPORTED_PERSONALITIES] = {
	sysent0,
#if SUPPORTED_PERSONALITIES > 1
	sysent1,
#endif
#if SUPPORTED_PERSONALITIES > 2
	sysent2,
#endif
};

enum {
	MAX_NSYSCALLS1 = (nsyscalls0
#if SUPPORTED_PERSONALITIES > 1
			> nsyscalls1 ? nsyscalls0 : nsyscalls1
#endif
			),
	MAX_NSYSCALLS2 = (MAX_NSYSCALLS1
#if SUPPORTED_PERSONALITIES > 2
			> nsyscalls2 ? MAX_NSYSCALLS1 : nsyscalls2
#endif
			),
	MAX_NSYSCALLS = MAX_NSYSCALLS2,
	/* We are ready for arches with up to 255 signals,
	 * even though the largest known signo is on MIPS and it is 128.
	 * The number of existing syscalls on all arches is
	 * larger that 255 anyway, so it is just a pedantic matter.
	 */
	MIN_QUALS = MAX_NSYSCALLS > 255 ? MAX_NSYSCALLS : 255
};

#if SUPPORTED_PERSONALITIES > 1
unsigned current_personality;

# ifndef current_wordsize
unsigned current_wordsize;
static const int personality_wordsize[SUPPORTED_PERSONALITIES] = {
	PERSONALITY0_WORDSIZE,
	PERSONALITY1_WORDSIZE,
# if SUPPORTED_PERSONALITIES > 2
	PERSONALITY2_WORDSIZE,
# endif
};
# endif

void
set_personality(int personality)
{
	nsyscalls = nsyscall_vec[personality];
	sysent = sysent_vec[personality];

	switch (personality) {
	case 0:
		errnoent = errnoent0;
		nerrnos = nerrnos0;
		ioctlent = ioctlent0;
		nioctlents = nioctlents0;
		signalent = signalent0;
		nsignals = nsignals0;
		break;

	case 1:
		errnoent = errnoent1;
		nerrnos = nerrnos1;
		ioctlent = ioctlent1;
		nioctlents = nioctlents1;
		signalent = signalent1;
		nsignals = nsignals1;
		break;

# if SUPPORTED_PERSONALITIES > 2
	case 2:
		errnoent = errnoent2;
		nerrnos = nerrnos2;
		ioctlent = ioctlent2;
		nioctlents = nioctlents2;
		signalent = signalent2;
		nsignals = nsignals2;
		break;
# endif
	}

	current_personality = personality;
# ifndef current_wordsize
	current_wordsize = personality_wordsize[personality];
# endif
}

static void
update_personality(struct tcb *tcp, unsigned int personality)
{
	if (personality == current_personality)
		return;
	set_personality(personality);

	if (personality == tcp->currpers)
		return;
	tcp->currpers = personality;

# if defined(POWERPC64)
	if (!qflag) {
		static const char *const names[] = {"64 bit", "32 bit"};
		fprintf(stderr, "[ Process PID=%d runs in %s mode. ]\n",
			tcp->pid, names[personality]);
	}
# elif defined(X86_64)
	if (!qflag) {
		static const char *const names[] = {"64 bit", "32 bit", "x32"};
		fprintf(stderr, "[ Process PID=%d runs in %s mode. ]\n",
			tcp->pid, names[personality]);
	}
# elif defined(X32)
	if (!qflag) {
		static const char *const names[] = {"x32", "32 bit"};
		fprintf(stderr, "[ Process PID=%d runs in %s mode. ]\n",
			tcp->pid, names[personality]);
	}
# elif defined(AARCH64)
	if (!qflag) {
		static const char *const names[] = {"32-bit", "AArch64"};
		fprintf(stderr, "[ Process PID=%d runs in %s mode. ]\n",
			tcp->pid, names[personality]);
	}
# elif defined(TILE)
	if (!qflag) {
		static const char *const names[] = {"64-bit", "32-bit"};
		fprintf(stderr, "[ Process PID=%d runs in %s mode. ]\n",
			tcp->pid, names[personality]);
	}
# endif
}
#endif

static int qual_syscall(), qual_signal(), qual_desc();

static const struct qual_options {
	unsigned int bitflag;
	const char *option_name;
	int (*qualify)(const char *, int, int);
	const char *argument_name;
} qual_options[] = {
	{ QUAL_TRACE,	"trace",	qual_syscall,	"system call"	},
	{ QUAL_TRACE,	"t",		qual_syscall,	"system call"	},
	{ QUAL_ABBREV,	"abbrev",	qual_syscall,	"system call"	},
	{ QUAL_ABBREV,	"a",		qual_syscall,	"system call"	},
	{ QUAL_VERBOSE,	"verbose",	qual_syscall,	"system call"	},
	{ QUAL_VERBOSE,	"v",		qual_syscall,	"system call"	},
	{ QUAL_RAW,	"raw",		qual_syscall,	"system call"	},
	{ QUAL_RAW,	"x",		qual_syscall,	"system call"	},
	{ QUAL_SIGNAL,	"signal",	qual_signal,	"signal"	},
	{ QUAL_SIGNAL,	"signals",	qual_signal,	"signal"	},
	{ QUAL_SIGNAL,	"s",		qual_signal,	"signal"	},
	{ QUAL_READ,	"read",		qual_desc,	"descriptor"	},
	{ QUAL_READ,	"reads",	qual_desc,	"descriptor"	},
	{ QUAL_READ,	"r",		qual_desc,	"descriptor"	},
	{ QUAL_WRITE,	"write",	qual_desc,	"descriptor"	},
	{ QUAL_WRITE,	"writes",	qual_desc,	"descriptor"	},
	{ QUAL_WRITE,	"w",		qual_desc,	"descriptor"	},
	{ 0,		NULL,		NULL,		NULL		},
};

static void
reallocate_qual(const unsigned int n)
{
	unsigned p;
	qualbits_t *qp;
	for (p = 0; p < SUPPORTED_PERSONALITIES; p++) {
		qp = qual_vec[p] = realloc(qual_vec[p], n * sizeof(qualbits_t));
		if (!qp)
			die_out_of_memory();
		memset(&qp[num_quals], 0, (n - num_quals) * sizeof(qualbits_t));
	}
	num_quals = n;
}

static void
qualify_one(const unsigned int n, unsigned int bitflag, const int not, const int pers)
{
	int p;

	if (num_quals <= n)
		reallocate_qual(n + 1);

	for (p = 0; p < SUPPORTED_PERSONALITIES; p++) {
		if (pers == p || pers < 0) {
			if (not)
				qual_vec[p][n] &= ~bitflag;
			else
				qual_vec[p][n] |= bitflag;
		}
	}
}

static int
qual_syscall(const char *s, const unsigned int bitflag, const int not)
{
	int p;
	unsigned int i;
	int rc = -1;

	if (*s >= '0' && *s <= '9') {
		i = string_to_uint(s);
		if (i >= MAX_NSYSCALLS)
			return -1;
		qualify_one(i, bitflag, not, -1);
		return 0;
	}

	for (p = 0; p < SUPPORTED_PERSONALITIES; p++) {
		for (i = 0; i < nsyscall_vec[p]; i++) {
			if (sysent_vec[p][i].sys_name
			 && strcmp(s, sysent_vec[p][i].sys_name) == 0
			) {
				qualify_one(i, bitflag, not, p);
				rc = 0;
			}
		}
	}

	return rc;
}

static int
qual_signal(const char *s, const unsigned int bitflag, const int not)
{
	unsigned int i;

	if (*s >= '0' && *s <= '9') {
		int signo = string_to_uint(s);
		if (signo < 0 || signo > 255)
			return -1;
		qualify_one(signo, bitflag, not, -1);
		return 0;
	}
	if (strncasecmp(s, "SIG", 3) == 0)
		s += 3;
	for (i = 0; i <= NSIG; i++) {
		if (strcasecmp(s, signame(i) + 3) == 0) {
			qualify_one(i, bitflag, not, -1);
			return 0;
		}
	}
	return -1;
}

static int
qual_desc(const char *s, const unsigned int bitflag, const int not)
{
	if (*s >= '0' && *s <= '9') {
		int desc = string_to_uint(s);
		if (desc < 0 || desc > 0x7fff) /* paranoia */
			return -1;
		qualify_one(desc, bitflag, not, -1);
		return 0;
	}
	return -1;
}

static int
lookup_class(const char *s)
{
	if (strcmp(s, "file") == 0)
		return TRACE_FILE;
	if (strcmp(s, "ipc") == 0)
		return TRACE_IPC;
	if (strcmp(s, "network") == 0)
		return TRACE_NETWORK;
	if (strcmp(s, "process") == 0)
		return TRACE_PROCESS;
	if (strcmp(s, "signal") == 0)
		return TRACE_SIGNAL;
	if (strcmp(s, "desc") == 0)
		return TRACE_DESC;
	if (strcmp(s, "memory") == 0)
		return TRACE_MEMORY;
	return -1;
}

void
qualify(const char *s)
{
	const struct qual_options *opt;
	char *copy;
	const char *p;
	int not;
	unsigned int i;

	if (num_quals == 0)
		reallocate_qual(MIN_QUALS);

	opt = &qual_options[0];
	for (i = 0; (p = qual_options[i].option_name); i++) {
		unsigned int len = strlen(p);
		if (strncmp(s, p, len) == 0 && s[len] == '=') {
			opt = &qual_options[i];
			s += len + 1;
			break;
		}
	}
	not = 0;
	if (*s == '!') {
		not = 1;
		s++;
	}
	if (strcmp(s, "none") == 0) {
		not = 1 - not;
		s = "all";
	}
	if (strcmp(s, "all") == 0) {
		for (i = 0; i < num_quals; i++) {
			qualify_one(i, opt->bitflag, not, -1);
		}
		return;
	}
	for (i = 0; i < num_quals; i++) {
		qualify_one(i, opt->bitflag, !not, -1);
	}
	copy = strdup(s);
	if (!copy)
		die_out_of_memory();
	for (p = strtok(copy, ","); p; p = strtok(NULL, ",")) {
		int n;
		if (opt->bitflag == QUAL_TRACE && (n = lookup_class(p)) > 0) {
			unsigned pers;
			for (pers = 0; pers < SUPPORTED_PERSONALITIES; pers++) {
				for (i = 0; i < nsyscall_vec[pers]; i++)
					if (sysent_vec[pers][i].sys_flags & n)
						qualify_one(i, opt->bitflag, not, pers);
			}
			continue;
		}
		if (opt->qualify(p, opt->bitflag, not)) {
			error_msg_and_die("invalid %s '%s'",
				opt->argument_name, p);
		}
	}
	free(copy);
	return;
}

#ifdef SYS_socket_subcall
static void
decode_socket_subcall(struct tcb *tcp)
{
	unsigned long addr;
	unsigned int n;

	if (tcp->u_arg[0] < 0 || tcp->u_arg[0] >= SYS_socket_nsubcalls)
		return;

	tcp->scno = SYS_socket_subcall + tcp->u_arg[0];
	tcp->qual_flg = qual_flags[tcp->scno];
	tcp->s_ent = &sysent[tcp->scno];
	addr = tcp->u_arg[1];
	n = tcp->s_ent->nargs;
	if (sizeof(tcp->u_arg[0]) == current_wordsize) {
		memset(tcp->u_arg, 0, n * sizeof(tcp->u_arg[0]));
		(void) umoven(tcp, addr, n * sizeof(tcp->u_arg[0]), tcp->u_arg);
	} else {
		unsigned int args[n];
		unsigned int i;

		memset(args, 0, sizeof(args));
		(void) umove(tcp, addr, &args);
		for (i = 0; i < n; ++i)
			tcp->u_arg[i] = args[i];
	}
}
#endif

#ifdef SYS_ipc_subcall
static void
decode_ipc_subcall(struct tcb *tcp)
{
	unsigned int i, n;

	if (tcp->u_arg[0] < 0 || tcp->u_arg[0] >= SYS_ipc_nsubcalls)
		return;

	tcp->scno = SYS_ipc_subcall + tcp->u_arg[0];
	tcp->qual_flg = qual_flags[tcp->scno];
	tcp->s_ent = &sysent[tcp->scno];
	n = tcp->s_ent->nargs;
	for (i = 0; i < n; i++)
		tcp->u_arg[i] = tcp->u_arg[i + 1];
}
#endif

int
printargs(struct tcb *tcp)
{
	if (entering(tcp)) {
		int i;
		int n = tcp->s_ent->nargs;
		for (i = 0; i < n; i++)
			tprintf("%s%#lx", i ? ", " : "", tcp->u_arg[i]);
	}
	return 0;
}

int
printargs_lu(struct tcb *tcp)
{
	if (entering(tcp)) {
		int i;
		int n = tcp->s_ent->nargs;
		for (i = 0; i < n; i++)
			tprintf("%s%lu", i ? ", " : "", tcp->u_arg[i]);
	}
	return 0;
}

int
printargs_ld(struct tcb *tcp)
{
	if (entering(tcp)) {
		int i;
		int n = tcp->s_ent->nargs;
		for (i = 0; i < n; i++)
			tprintf("%s%ld", i ? ", " : "", tcp->u_arg[i]);
	}
	return 0;
}

static void
dumpio(struct tcb *tcp)
{
	int (*func)();

	if (syserror(tcp))
		return;
	if ((unsigned long) tcp->u_arg[0] >= num_quals)
		return;
	func = tcp->s_ent->sys_func;
	if (func == printargs)
		return;
	if (qual_flags[tcp->u_arg[0]] & QUAL_READ) {
		if (func == sys_read ||
		    func == sys_pread ||
		    func == sys_recv ||
		    func == sys_recvfrom) {
			dumpstr(tcp, tcp->u_arg[1], tcp->u_rval);
			return;
		} else if (func == sys_readv) {
			dumpiov(tcp, tcp->u_arg[2], tcp->u_arg[1]);
			return;
#if HAVE_SENDMSG
		} else if (func == sys_recvmsg) {
			dumpiov_in_msghdr(tcp, tcp->u_arg[1]);
			return;
		} else if (func == sys_recvmmsg) {
			dumpiov_in_mmsghdr(tcp, tcp->u_arg[1]);
			return;
#endif
		}
	}
	if (qual_flags[tcp->u_arg[0]] & QUAL_WRITE) {
		if (func == sys_write ||
		    func == sys_pwrite ||
		    func == sys_send ||
		    func == sys_sendto)
			dumpstr(tcp, tcp->u_arg[1], tcp->u_arg[2]);
		else if (func == sys_writev)
			dumpiov(tcp, tcp->u_arg[2], tcp->u_arg[1]);
#if HAVE_SENDMSG
		else if (func == sys_sendmsg)
			dumpiov_in_msghdr(tcp, tcp->u_arg[1]);
		else if (func == sys_sendmmsg)
			dumpiov_in_mmsghdr(tcp, tcp->u_arg[1]);
#endif
	}
}

/*
 * Shuffle syscall numbers so that we don't have huge gaps in syscall table.
 * The shuffling should be an involution: shuffle_scno(shuffle_scno(n)) == n.
 */
#if defined(ARM) || defined(AARCH64) /* So far only 32-bit ARM needs this */
static long
shuffle_scno(unsigned long scno)
{
	if (scno < ARM_FIRST_SHUFFLED_SYSCALL)
		return scno;

	/* __ARM_NR_cmpxchg? Swap with LAST_ORDINARY+1 */
	if (scno == ARM_FIRST_SHUFFLED_SYSCALL)
		return 0x000ffff0;
	if (scno == 0x000ffff0)
		return ARM_FIRST_SHUFFLED_SYSCALL;

#define ARM_SECOND_SHUFFLED_SYSCALL (ARM_FIRST_SHUFFLED_SYSCALL + 1)
	/*
	 * Is it ARM specific syscall?
	 * Swap [0x000f0000, 0x000f0000 + LAST_SPECIAL] range
	 * with [SECOND_SHUFFLED, SECOND_SHUFFLED + LAST_SPECIAL] range.
	 */
	if (scno >= 0x000f0000 &&
	    scno <= 0x000f0000 + ARM_LAST_SPECIAL_SYSCALL) {
		return scno - 0x000f0000 + ARM_SECOND_SHUFFLED_SYSCALL;
	}
	if (scno <= ARM_SECOND_SHUFFLED_SYSCALL + ARM_LAST_SPECIAL_SYSCALL) {
		return scno + 0x000f0000 - ARM_SECOND_SHUFFLED_SYSCALL;
	}

	return scno;
}
#else
# define shuffle_scno(scno) ((long)(scno))
#endif

static char*
undefined_scno_name(struct tcb *tcp)
{
	static char buf[sizeof("syscall_%lu") + sizeof(long)*3];

	sprintf(buf, "syscall_%lu", shuffle_scno(tcp->scno));
	return buf;
}

static long get_regs_error;

void
clear_regs(void)
{
	get_regs_error = -1;
}

static int get_syscall_args(struct tcb *);
static int get_syscall_result(struct tcb *);

static int
trace_syscall_entering(struct tcb *tcp)
{
	int res, scno_good;

	scno_good = res = get_scno(tcp);
	if (res == 0)
		return res;
	if (res == 1)
		res = get_syscall_args(tcp);

	if (res != 1) {
		printleader(tcp);
		if (scno_good != 1)
			tprints("????" /* anti-trigraph gap */ "(");
		else if (tcp->qual_flg & UNDEFINED_SCNO)
			tprintf("%s(", undefined_scno_name(tcp));
		else
			tprintf("%s(", tcp->s_ent->sys_name);
		/*
		 * " <unavailable>" will be added later by the code which
		 * detects ptrace errors.
		 */
		goto ret;
	}

	if (   sys_execve == tcp->s_ent->sys_func
# if defined(SPARC) || defined(SPARC64)
	    || sys_execv == tcp->s_ent->sys_func
# endif
	   ) {
		hide_log_until_execve = 0;
	}

#if defined(SYS_socket_subcall) || defined(SYS_ipc_subcall)
	while (1) {
# ifdef SYS_socket_subcall
		if (tcp->s_ent->sys_func == sys_socketcall) {
			decode_socket_subcall(tcp);
			break;
		}
# endif
# ifdef SYS_ipc_subcall
		if (tcp->s_ent->sys_func == sys_ipc) {
			decode_ipc_subcall(tcp);
			break;
		}
# endif
		break;
	}
#endif

	if (!(tcp->qual_flg & QUAL_TRACE)
	 || (tracing_paths && !pathtrace_match(tcp))
	) {
		tcp->flags |= TCB_INSYSCALL | TCB_FILTERED;
		return 0;
	}

	tcp->flags &= ~TCB_FILTERED;

	if (cflag == CFLAG_ONLY_STATS || hide_log_until_execve) {
		res = 0;
		goto ret;
	}

#ifdef USE_LIBUNWIND
	if (stack_trace_enabled) {
		if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
			unwind_capture_stacktrace(tcp);
	}
#endif

	printleader(tcp);
	if (tcp->qual_flg & UNDEFINED_SCNO)
		tprintf("%s(", undefined_scno_name(tcp));
	else
		tprintf("%s(", tcp->s_ent->sys_name);
	if ((tcp->qual_flg & QUAL_RAW) && tcp->s_ent->sys_func != sys_exit)
		res = printargs(tcp);
	else
		res = tcp->s_ent->sys_func(tcp);

	fflush(tcp->outf);
 ret:
	tcp->flags |= TCB_INSYSCALL;
	/* Measure the entrance time as late as possible to avoid errors. */
	if (Tflag || cflag)
		gettimeofday(&tcp->etime, NULL);
	return res;
}

static int
trace_syscall_exiting(struct tcb *tcp)
{
	int sys_res;
	struct timeval tv;
	int res;
	long u_error;

	/* Measure the exit time as early as possible to avoid errors. */
	if (Tflag || cflag)
		gettimeofday(&tv, NULL);

#ifdef USE_LIBUNWIND
	if (stack_trace_enabled) {
		if (tcp->s_ent->sys_flags & STACKTRACE_INVALIDATE_CACHE)
			unwind_cache_invalidate(tcp);
	}
#endif

#if SUPPORTED_PERSONALITIES > 1
	update_personality(tcp, tcp->currpers);
#endif
	res = (get_regs_error ? -1 : get_syscall_result(tcp));
	if (res == 1) {
		if (filtered(tcp) || hide_log_until_execve)
			goto ret;
	}

	if (cflag) {
		count_syscall(tcp, &tv);
		if (cflag == CFLAG_ONLY_STATS) {
			goto ret;
		}
	}

	/* If not in -ff mode, and printing_tcp != tcp,
	 * then the log currently does not end with output
	 * of _our syscall entry_, but with something else.
	 * We need to say which syscall's return is this.
	 *
	 * Forced reprinting via TCB_REPRINT is used only by
	 * "strace -ff -oLOG test/threaded_execve" corner case.
	 * It's the only case when -ff mode needs reprinting.
	 */
	if ((followfork < 2 && printing_tcp != tcp) || (tcp->flags & TCB_REPRINT)) {
		tcp->flags &= ~TCB_REPRINT;
		printleader(tcp);
		if (tcp->qual_flg & UNDEFINED_SCNO)
			tprintf("<... %s resumed> ", undefined_scno_name(tcp));
		else
			tprintf("<... %s resumed> ", tcp->s_ent->sys_name);
	}
	printing_tcp = tcp;

	tcp->s_prev_ent = NULL;
	if (res != 1) {
		/* There was error in one of prior ptrace ops */
		tprints(") ");
		tabto();
		tprints("= ? <unavailable>\n");
		line_ended();
		tcp->flags &= ~TCB_INSYSCALL;
		return res;
	}
	tcp->s_prev_ent = tcp->s_ent;

	sys_res = 0;
	if (tcp->qual_flg & QUAL_RAW) {
		/* sys_res = printargs(tcp); - but it's nop on sysexit */
	} else {
	/* FIXME: not_failing_only (IOW, option -z) is broken:
	 * failure of syscall is known only after syscall return.
	 * Thus we end up with something like this on, say, ENOENT:
	 *     open("doesnt_exist", O_RDONLY <unfinished ...>
	 *     {next syscall decode}
	 * whereas the intended result is that open(...) line
	 * is not shown at all.
	 */
		if (not_failing_only && tcp->u_error)
			goto ret;	/* ignore failed syscalls */
		sys_res = tcp->s_ent->sys_func(tcp);
	}

	tprints(") ");
	tabto();
	u_error = tcp->u_error;
	if (tcp->qual_flg & QUAL_RAW) {
		if (u_error)
			tprintf("= -1 (errno %ld)", u_error);
		else
			tprintf("= %#lx", tcp->u_rval);
	}
	else if (!(sys_res & RVAL_NONE) && u_error) {
		switch (u_error) {
		/* Blocked signals do not interrupt any syscalls.
		 * In this case syscalls don't return ERESTARTfoo codes.
		 *
		 * Deadly signals set to SIG_DFL interrupt syscalls
		 * and kill the process regardless of which of the codes below
		 * is returned by the interrupted syscall.
		 * In some cases, kernel forces a kernel-generated deadly
		 * signal to be unblocked and set to SIG_DFL (and thus cause
		 * death) if it is blocked or SIG_IGNed: for example, SIGSEGV
		 * or SIGILL. (The alternative is to leave process spinning
		 * forever on the faulty instruction - not useful).
		 *
		 * SIG_IGNed signals and non-deadly signals set to SIG_DFL
		 * (for example, SIGCHLD, SIGWINCH) interrupt syscalls,
		 * but kernel will always restart them.
		 */
		case ERESTARTSYS:
			/* Most common type of signal-interrupted syscall exit code.
			 * The system call will be restarted with the same arguments
			 * if SA_RESTART is set; otherwise, it will fail with EINTR.
			 */
			tprints("= ? ERESTARTSYS (To be restarted if SA_RESTART is set)");
			break;
		case ERESTARTNOINTR:
			/* Rare. For example, fork() returns this if interrupted.
			 * SA_RESTART is ignored (assumed set): the restart is unconditional.
			 */
			tprints("= ? ERESTARTNOINTR (To be restarted)");
			break;
		case ERESTARTNOHAND:
			/* pause(), rt_sigsuspend() etc use this code.
			 * SA_RESTART is ignored (assumed not set):
			 * syscall won't restart (will return EINTR instead)
			 * even after signal with SA_RESTART set. However,
			 * after SIG_IGN or SIG_DFL signal it will restart
			 * (thus the name "restart only if has no handler").
			 */
			tprints("= ? ERESTARTNOHAND (To be restarted if no handler)");
			break;
		case ERESTART_RESTARTBLOCK:
			/* Syscalls like nanosleep(), poll() which can't be
			 * restarted with their original arguments use this
			 * code. Kernel will execute restart_syscall() instead,
			 * which changes arguments before restarting syscall.
			 * SA_RESTART is ignored (assumed not set) similarly
			 * to ERESTARTNOHAND. (Kernel can't honor SA_RESTART
			 * since restart data is saved in "restart block"
			 * in task struct, and if signal handler uses a syscall
			 * which in turn saves another such restart block,
			 * old data is lost and restart becomes impossible)
			 */
			tprints("= ? ERESTART_RESTARTBLOCK (Interrupted by signal)");
			break;
		default:
			if ((unsigned long) u_error < nerrnos
			    && errnoent[u_error])
				tprintf("= -1 %s (%s)", errnoent[u_error],
					strerror(u_error));
			else
				tprintf("= -1 ERRNO_%lu (%s)", u_error,
					strerror(u_error));
			break;
		}
		if ((sys_res & RVAL_STR) && tcp->auxstr)
			tprintf(" (%s)", tcp->auxstr);
	}
	else {
		if (sys_res & RVAL_NONE)
			tprints("= ?");
		else {
			switch (sys_res & RVAL_MASK) {
			case RVAL_HEX:
#if SUPPORTED_PERSONALITIES > 1
				if (current_wordsize < sizeof(long))
					tprintf("= %#x",
						(unsigned int) tcp->u_rval);
				else
#endif
					tprintf("= %#lx", tcp->u_rval);
				break;
			case RVAL_OCTAL:
				tprintf("= %#lo", tcp->u_rval);
				break;
			case RVAL_UDECIMAL:
				tprintf("= %lu", tcp->u_rval);
				break;
			case RVAL_DECIMAL:
				tprintf("= %ld", tcp->u_rval);
				break;
			case RVAL_FD:
				if (show_fd_path) {
					tprints("= ");
					printfd(tcp, tcp->u_rval);
				}
				else
					tprintf("= %ld", tcp->u_rval);
				break;
#if defined(LINUX_MIPSN32) || defined(X32)
			/*
			case RVAL_LHEX:
				tprintf("= %#llx", tcp->u_lrval);
				break;
			case RVAL_LOCTAL:
				tprintf("= %#llo", tcp->u_lrval);
				break;
			*/
			case RVAL_LUDECIMAL:
				tprintf("= %llu", tcp->u_lrval);
				break;
			/*
			case RVAL_LDECIMAL:
				tprintf("= %lld", tcp->u_lrval);
				break;
			*/
#endif
			default:
				fprintf(stderr,
					"invalid rval format\n");
				break;
			}
		}
		if ((sys_res & RVAL_STR) && tcp->auxstr)
			tprintf(" (%s)", tcp->auxstr);
	}
	if (Tflag) {
		tv_sub(&tv, &tv, &tcp->etime);
		tprintf(" <%ld.%06ld>",
			(long) tv.tv_sec, (long) tv.tv_usec);
	}
	tprints("\n");
	dumpio(tcp);
	line_ended();

#ifdef USE_LIBUNWIND
	if (stack_trace_enabled)
		unwind_print_stacktrace(tcp);
#endif

 ret:
	tcp->flags &= ~TCB_INSYSCALL;
	return 0;
}

int
trace_syscall(struct tcb *tcp)
{
	return exiting(tcp) ?
		trace_syscall_exiting(tcp) : trace_syscall_entering(tcp);
}

/*
 * Cannot rely on __kernel_[u]long_t being defined,
 * it is quite a recent feature of <asm/posix_types.h>.
 */
#ifdef __kernel_long_t
typedef __kernel_long_t kernel_long_t;
typedef __kernel_ulong_t kernel_ulong_t;
#else
# ifdef X32
typedef long long kernel_long_t;
typedef unsigned long long kernel_ulong_t;
# else
typedef long kernel_long_t;
typedef unsigned long kernel_ulong_t;
# endif
#endif

/*
 * Check the syscall return value register value for whether it is
 * a negated errno code indicating an error, or a success return value.
 */
static inline bool
is_negated_errno(kernel_ulong_t val)
{
	/* Linux kernel defines MAX_ERRNO to 4095. */
	kernel_ulong_t max = -(kernel_long_t) 4095;

#if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4
	if (current_wordsize < sizeof(val)) {
		val = (uint32_t) val;
		max = (uint32_t) max;
	}
#elif defined X32
	/*
	 * current_wordsize is 4 even in personality 0 (native X32)
	 * but truncation _must not_ be done in it.
	 * can't check current_wordsize here!
	 */
	if (current_personality != 0) {
		val = (uint32_t) val;
		max = (uint32_t) max;
	}
#endif

	return val >= max;
}

#include "arch_regs.c"

#ifdef HAVE_GETRVAL2
# include "arch_getrval2.c"
#endif

void
print_pc(struct tcb *tcp)
{
	const char *fmt;
	const char *bad;

#ifdef current_wordsize
# define pc_wordsize current_wordsize
#else
# define pc_wordsize personality_wordsize[tcp->currpers]
#endif

	if (pc_wordsize == 4) {
		fmt = "[%08lx] ";
		bad = "[????????] ";
	} else {
		fmt = "[%016lx] ";
		bad = "[????????????????] ";
	}

#undef pc_wordsize
#define PRINTBADPC tprints(bad)

	if (get_regs_error) {
		PRINTBADPC;
		return;
	}

#include "print_pc.c"
}

#if defined X86_64 || defined POWERPC
# include "getregs_old.c"
#endif

#if defined ARCH_REGS_FOR_GETREGSET
static long
get_regset(pid_t pid)
{
# ifdef ARCH_IOVEC_FOR_GETREGSET
	/* variable iovec */
	ARCH_IOVEC_FOR_GETREGSET.iov_len = sizeof(ARCH_REGS_FOR_GETREGSET);
	return ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS,
		      &ARCH_IOVEC_FOR_GETREGSET);
# else
	/* constant iovec */
	static struct iovec io = {
		.iov_base = &ARCH_REGS_FOR_GETREGSET,
		.iov_len = sizeof(ARCH_REGS_FOR_GETREGSET)
	};
	return ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &io);

# endif
}
#endif /* ARCH_REGS_FOR_GETREGSET */

void
get_regs(pid_t pid)
{
#ifdef ARCH_REGS_FOR_GETREGSET
# ifdef X86_64
	/* Try PTRACE_GETREGSET first, fallback to PTRACE_GETREGS. */
	static int getregset_support;

	if (getregset_support >= 0) {
		get_regs_error = get_regset(pid);
		if (getregset_support > 0)
			return;
		if (get_regs_error >= 0) {
			getregset_support = 1;
			return;
		}
		if (errno == EPERM || errno == ESRCH)
			return;
		getregset_support = -1;
	}
	getregs_old(pid);
# else /* !X86_64 */
	/* Assume that PTRACE_GETREGSET works. */
	get_regs_error = get_regset(pid);
# endif
#elif defined ARCH_REGS_FOR_GETREGS
# if defined SPARC || defined SPARC64
	/* SPARC systems have the meaning of data and addr reversed */
	get_regs_error = ptrace(PTRACE_GETREGS, pid, (char *)&ARCH_REGS_FOR_GETREGS, 0);
# elif defined POWERPC
	static bool old_kernel = 0;
	if (old_kernel)
		goto old;
	get_regs_error = ptrace(PTRACE_GETREGS, pid, NULL, &ARCH_REGS_FOR_GETREGS);
	if (get_regs_error && errno == EIO) {
		old_kernel = 1;
 old:
		get_regs_error = getregs_old(pid);
	}
# else
	/* Assume that PTRACE_GETREGS works. */
	get_regs_error = ptrace(PTRACE_GETREGS, pid, NULL, &ARCH_REGS_FOR_GETREGS);
# endif

#else /* !ARCH_REGS_FOR_GETREGSET && !ARCH_REGS_FOR_GETREGS */
#  warning get_regs is not implemented for this architecture yet
	get_regs_error = 0;
#endif
}

/* Returns:
 * 0: "ignore this ptrace stop", bail out of trace_syscall_entering() silently.
 * 1: ok, continue in trace_syscall_entering().
 * other: error, trace_syscall_entering() should print error indicator
 *    ("????" etc) and bail out.
 */
int
get_scno(struct tcb *tcp)
{
	if (get_regs_error)
		return -1;

	long scno = 0;

#include "get_scno.c"

	tcp->scno = scno;
	if (SCNO_IS_VALID(tcp->scno)) {
		tcp->s_ent = &sysent[scno];
		tcp->qual_flg = qual_flags[scno];
	} else {
		static const struct_sysent unknown = {
			.nargs = MAX_ARGS,
			.sys_flags = 0,
			.sys_func = printargs,
			.sys_name = "system call",
		};
		tcp->s_ent = &unknown;
		tcp->qual_flg = UNDEFINED_SCNO | QUAL_RAW | DEFAULT_QUAL_FLAGS;
		if (debug_flag)
			fprintf(stderr, "pid %d invalid syscall %ld\n",
				tcp->pid, scno);
	}
	return 1;
}

/* Return -1 on error or 1 on success (never 0!) */
static int
get_syscall_args(struct tcb *tcp)
{
#include "get_syscall_args.c"
	return 1;
}

static void
get_error(struct tcb *tcp)
{
	const bool check_errno = !(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS);
	tcp->u_error = 0;

#include "get_error.c"
}

/* Returns:
 * 1: ok, continue in trace_syscall_exiting().
 * -1: error, trace_syscall_exiting() should print error indicator
 *    ("????" etc) and bail out.
 */
static int
get_syscall_result(struct tcb *tcp)
{
#if defined ARCH_REGS_FOR_GETREGSET || defined ARCH_REGS_FOR_GETREGS
	/* already done by get_regs */
#else
# include "get_syscall_result.c"
#endif
	get_error(tcp);
	return 1;
}