/*
 * 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.
 *
 *	$Id: util.c,v 1.66 2007/01/11 22:05:06 ldv Exp $
 */

#include "defs.h"

#include <signal.h>
#include <sys/param.h>
#include <sys/syscall.h>
#ifndef NETBSD
#include <sys/user.h>
#endif
#include <fcntl.h>
#if HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef SUNOS4
#include <machine/reg.h>
#include <a.out.h>
#include <link.h>
#endif /* SUNOS4 */

#if defined(linux) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 1))
#include <linux/ptrace.h>
#endif

#if defined(LINUX) && defined(IA64)
# include <asm/ptrace_offsets.h>
# include <asm/rse.h>
#endif

#ifdef NETBSD
#include <machine/reg.h>
#endif
#ifdef HAVE_SYS_REG_H
#include <sys/reg.h>
# define PTRACE_PEEKUSR PTRACE_PEEKUSER
#elif defined(HAVE_LINUX_PTRACE_H)
#undef PTRACE_SYSCALL
# ifdef HAVE_STRUCT_IA64_FPREG
#  define ia64_fpreg XXX_ia64_fpreg
# endif
# ifdef HAVE_STRUCT_PT_ALL_USER_REGS
#  define pt_all_user_regs XXX_pt_all_user_regs
# endif
#include <linux/ptrace.h>
# undef ia64_fpreg
# undef pt_all_user_regs
#endif

#ifdef SUNOS4_KERNEL_ARCH_KLUDGE
#include <sys/utsname.h>
#endif /* SUNOS4_KERNEL_ARCH_KLUDGE */

#if defined(LINUXSPARC)

# define fpq kernel_fpq
# define fq kernel_fq
# define fpu kernel_fpu
# include <asm/reg.h>
# undef fpq
# undef fq
# undef fpu

#if defined (SPARC64)
# define r_pc r_tpc
# undef PTRACE_GETREGS
# define PTRACE_GETREGS PTRACE_GETREGS64
# undef PTRACE_SETREGS
# define PTRACE_SETREGS PTRACE_SETREGS64
#endif /* SPARC64 */


#if !defined(__GLIBC__)

#include <linux/unistd.h>

#define _hack_syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,\
          type5,arg5,syscall) \
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
{ \
      long __res; \
\
__asm__ volatile ("or %%g0, %1, %%o0\n\t" \
                  "or %%g0, %2, %%o1\n\t" \
                  "or %%g0, %3, %%o2\n\t" \
                  "or %%g0, %4, %%o3\n\t" \
                  "or %%g0, %5, %%o4\n\t" \
                  "or %%g0, %6, %%g1\n\t" \
#if defined (SPARC64)
                  "t 0x6d\n\t" \
#else
                  "t 0x10\n\t" \
#endif
                  "bcc 1f\n\t" \
                  "or %%g0, %%o0, %0\n\t" \
                  "sub %%g0, %%o0, %0\n\t" \
                  "1:\n\t" \
                  : "=r" (__res) \
                  : "0" ((long)(arg1)),"1" ((long)(arg2)), \
                    "2" ((long)(arg3)),"3" ((long)(arg4)),"4" ((long)(arg5)), \
                    "i" (__NR_##syscall)  \
                  : "g1", "o0", "o1", "o2", "o3", "o4"); \
if (__res>=0) \
        return (type) __res; \
errno = -__res; \
return -1; \
}

static _hack_syscall5(int,_ptrace,int,__request,int,__pid,int,__addr,int,__data,int,__addr2,ptrace)

#define _ptrace

#endif

#endif

/* macros */
#ifndef MAX
#define MAX(a,b)		(((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b)		(((a) < (b)) ? (a) : (b))
#endif

#if 0
void
tv_tv(tv, a, b)
struct timeval *tv;
int a;
int b;
{
	tv->tv_sec = a;
	tv->tv_usec = b;
}
#endif

int
tv_nz(a)
struct timeval *a;
{
	return a->tv_sec || a->tv_usec;
}

int
tv_cmp(a, b)
struct timeval *a, *b;
{
	if (a->tv_sec < b->tv_sec
	    || (a->tv_sec == b->tv_sec && a->tv_usec < b->tv_usec))
		return -1;
	if (a->tv_sec > b->tv_sec
	    || (a->tv_sec == b->tv_sec && a->tv_usec > b->tv_usec))
		return 1;
	return 0;
}

double
tv_float(tv)
struct timeval *tv;
{
	return tv->tv_sec + tv->tv_usec/1000000.0;
}

void
tv_add(tv, a, b)
struct timeval *tv, *a, *b;
{
	tv->tv_sec = a->tv_sec + b->tv_sec;
	tv->tv_usec = a->tv_usec + b->tv_usec;
	if (tv->tv_usec > 1000000) {
		tv->tv_sec++;
		tv->tv_usec -= 1000000;
	}
}

void
tv_sub(tv, a, b)
struct timeval *tv, *a, *b;
{
	tv->tv_sec = a->tv_sec - b->tv_sec;
	tv->tv_usec = a->tv_usec - b->tv_usec;
	if (((long) tv->tv_usec) < 0) {
		tv->tv_sec--;
		tv->tv_usec += 1000000;
	}
}

void
tv_div(tv, a, n)
struct timeval *tv, *a;
int n;
{
	tv->tv_usec = (a->tv_sec % n * 1000000 + a->tv_usec + n / 2) / n;
	tv->tv_sec = a->tv_sec / n + tv->tv_usec / 1000000;
	tv->tv_usec %= 1000000;
}

void
tv_mul(tv, a, n)
struct timeval *tv, *a;
int n;
{
	tv->tv_usec = a->tv_usec * n;
	tv->tv_sec = a->tv_sec * n + a->tv_usec / 1000000;
	tv->tv_usec %= 1000000;
}

const char *
xlookup(const struct xlat *xlat, int val)
{
	for (; xlat->str != NULL; xlat++)
		if (xlat->val == val)
			return xlat->str;
	return NULL;
}

/*
 * Print entry in struct xlat table, if there.
 */
void
printxval(const struct xlat *xlat, int val, const char *dflt)
{
	const char *str = xlookup(xlat, val);

	if (str)
		tprintf("%s", str);
	else
		tprintf("%#x /* %s */", val, dflt);
}

/*
 * Interpret `xlat' as an array of flags
 * print the entries whose bits are on in `flags'
 * return # of flags printed.
 */
int
addflags(xlat, flags)
const struct xlat *xlat;
int flags;
{
	int n;

	for (n = 0; xlat->str; xlat++) {
		if (xlat->val && (flags & xlat->val) == xlat->val) {
			tprintf("|%s", xlat->str);
			flags &= ~xlat->val;
			n++;
		}
	}
	if (flags) {
		tprintf("|%#x", flags);
		n++;
	}
	return n;
}

int
printflags(xlat, flags, dflt)
const struct xlat *xlat;
int flags;
const char *dflt;
{
	int n;
	char *sep;

	if (flags == 0 && xlat->val == 0) {
		tprintf("%s", xlat->str);
		return 1;
	}

	sep = "";
	for (n = 0; xlat->str; xlat++) {
		if (xlat->val && (flags & xlat->val) == xlat->val) {
			tprintf("%s%s", sep, xlat->str);
			flags &= ~xlat->val;
			sep = "|";
			n++;
		}
	}

	if (n) {
		if (flags) {
			tprintf("%s%#x", sep, flags);
			n++;
		}
	} else {
		if (flags) {
			tprintf("%#x", flags);
			if (dflt)
				tprintf(" /* %s */", dflt);
		} else {
			if (dflt)
				tprintf("0");
		}
	}

	return n;
}

void
printnum(tcp, addr, fmt)
struct tcb *tcp;
long addr;
char *fmt;
{
	long num;

	if (!addr) {
		tprintf("NULL");
		return;
	}
	if (umove(tcp, addr, &num) < 0) {
		tprintf("%#lx", addr);
		return;
	}
	tprintf("[");
	tprintf(fmt, num);
	tprintf("]");
}

void
printnum_int(tcp, addr, fmt)
struct tcb *tcp;
long addr;
char *fmt;
{
	int num;

	if (!addr) {
		tprintf("NULL");
		return;
	}
	if (umove(tcp, addr, &num) < 0) {
		tprintf("%#lx", addr);
		return;
	}
	tprintf("[");
	tprintf(fmt, num);
	tprintf("]");
}

void
printuid(text, uid)
const char *text;
unsigned long uid;
{
	tprintf("%s", text);
	tprintf((uid == -1) ? "%ld" : "%lu", uid);
}

static char path[MAXPATHLEN + 1];

static void
string_quote(str)
const char *str;
{
	char buf[2 * MAXPATHLEN + 1];
	char *s;

	if (!strpbrk(str, "\"\'\\")) {
		tprintf("\"%s\"", str);
		return;
	}
	for (s = buf; *str; str++) {
		switch (*str) {
		case '\"': case '\'': case '\\':
			*s++ = '\\'; *s++ = *str; break;
		default:
			*s++ = *str; break;
		}
	}
	*s = '\0';
	tprintf("\"%s\"", buf);
}

void
printpath(tcp, addr)
struct tcb *tcp;
long addr;
{
	if (addr == 0)
		tprintf("NULL");
	else if (umovestr(tcp, addr, MAXPATHLEN, path) < 0)
		tprintf("%#lx", addr);
	else
		string_quote(path);
	return;
}

void
printpathn(tcp, addr, n)
struct tcb *tcp;
long addr;
int n;
{
	if (n >= sizeof path)
		n = sizeof path - 1;

	if (addr == 0)
		tprintf("NULL");
	else 	if (umovestr(tcp, addr, n, path) < 0)
		tprintf("%#lx", addr);
	else {
		path[n] = '\0';
		string_quote(path);
	}
}

void
printstr(tcp, addr, len)
struct tcb *tcp;
long addr;
int len;
{
	static unsigned char *str = NULL;
	static char *outstr;
	int i, n, c, usehex;
	char *s, *outend;

	if (!addr) {
		tprintf("NULL");
		return;
	}
	if (!str) {
		if ((str = malloc(max_strlen)) == NULL
		    || (outstr = malloc(2*max_strlen)) == NULL) {
			fprintf(stderr, "out of memory\n");
			tprintf("%#lx", addr);
			return;
		}
	}
	outend = outstr + max_strlen * 2 - 10;
	if (len < 0) {
		n = max_strlen;
		if (umovestr(tcp, addr, n, (char *) str) < 0) {
			tprintf("%#lx", addr);
			return;
		}
	}
	else {
		n = MIN(len, max_strlen);
		if (umoven(tcp, addr, n, (char *) str) < 0) {
			tprintf("%#lx", addr);
			return;
		}
	}

	usehex = 0;
	if (xflag > 1)
		usehex = 1;
	else if (xflag) {
		for (i = 0; i < n; i++) {
			c = str[i];
			if (len < 0 && c == '\0')
				break;
			if (!isprint(c) && !isspace(c)) {
				usehex = 1;
				break;
			}
		}
	}

	s = outstr;
	*s++ = '\"';

	if (usehex) {
		for (i = 0; i < n; i++) {
			c = str[i];
			if (len < 0 && c == '\0')
				break;
			sprintf(s, "\\x%02x", c);
			s += 4;
			if (s > outend)
				break;
		}
	}
	else {
		for (i = 0; i < n; i++) {
			c = str[i];
			if (len < 0 && c == '\0')
				break;
			switch (c) {
			case '\"': case '\'': case '\\':
				*s++ = '\\'; *s++ = c; break;
			case '\f':
				*s++ = '\\'; *s++ = 'f'; break;
			case '\n':
				*s++ = '\\'; *s++ = 'n'; break;
			case '\r':
				*s++ = '\\'; *s++ = 'r'; break;
			case '\t':
				*s++ = '\\'; *s++ = 't'; break;
			case '\v':
				*s++ = '\\'; *s++ = 'v'; break;
			default:
				if (isprint(c))
					*s++ = c;
				else if (i < n - 1 && isdigit(str[i + 1])) {
					sprintf(s, "\\%03o", c);
					s += 4;
				}
				else {
					sprintf(s, "\\%o", c);
					s += strlen(s);
				}
				break;
			}
			if (s > outend)
				break;
		}
	}

	*s++ = '\"';
	if (i < len || (len < 0 && (i == n || s > outend))) {
		*s++ = '.'; *s++ = '.'; *s++ = '.';
	}
	*s = '\0';
	tprintf("%s", outstr);
}

#if HAVE_SYS_UIO_H
void
dumpiov(tcp, len, addr)
struct tcb * tcp;
int len;
long addr;
{
#if defined(LINUX) && SUPPORTED_PERSONALITIES > 1
	union {
		struct { u_int32_t base; u_int32_t len; } *iov32;
		struct { u_int64_t base; u_int64_t len; } *iov64;
	} iovu;
#define iov iovu.iov64
#define sizeof_iov \
  (personality_wordsize[current_personality] == 4 \
   ? sizeof(*iovu.iov32) : sizeof(*iovu.iov64))
#define iov_iov_base(i) \
  (personality_wordsize[current_personality] == 4 \
   ? (u_int64_t) iovu.iov32[i].base : iovu.iov64[i].base)
#define iov_iov_len(i) \
  (personality_wordsize[current_personality] == 4 \
   ? (u_int64_t) iovu.iov32[i].len : iovu.iov64[i].len)
#else
	struct iovec *iov;
#define sizeof_iov sizeof(*iov)
#define iov_iov_base(i) iov[i].iov_base
#define iov_iov_len(i) iov[i].iov_len
#endif
	int i;
	unsigned long size;

	size = sizeof_iov * (unsigned long) len;
	if (size / sizeof_iov != len
	    || (iov = malloc(size)) == NULL) {
		fprintf(stderr, "out of memory\n");
		return;
	}
	if (umoven(tcp, addr, size, (char *) iov) >= 0) {
		for (i = 0; i < len; i++) {
                        /* include the buffer number to make it easy to
                         * match up the trace with the source */
                        tprintf(" * %lu bytes in buffer %d\n",
                                (unsigned long)iov_iov_len(i), i);
                        dumpstr(tcp, (long) iov_iov_base(i),
                                iov_iov_len(i));
                }
	}
	free((char *) iov);
#undef sizeof_iov
#undef iov_iov_base
#undef iov_iov_len
#undef iov
}
#endif

void
dumpstr(tcp, addr, len)
struct tcb *tcp;
long addr;
int len;
{
	static int strsize = -1;
	static unsigned char *str;
	static char outstr[80];
	char *s;
	int i, j;

	if (strsize < len) {
		if (str)
			free(str);
		if ((str = malloc(len)) == NULL) {
			fprintf(stderr, "out of memory\n");
			return;
		}
		strsize = len;
	}

	if (umoven(tcp, addr, len, (char *) str) < 0)
		return;

	for (i = 0; i < len; i += 16) {
		s = outstr;
		sprintf(s, " | %05x ", i);
		s += 9;
		for (j = 0; j < 16; j++) {
			if (j == 8)
				*s++ = ' ';
			if (i + j < len) {
				sprintf(s, " %02x", str[i + j]);
				s += 3;
			}
			else {
				*s++ = ' '; *s++ = ' '; *s++ = ' ';
			}
		}
		*s++ = ' '; *s++ = ' ';
		for (j = 0; j < 16; j++) {
			if (j == 8)
				*s++ = ' ';
			if (i + j < len) {
				if (isprint(str[i + j]))
					*s++ = str[i + j];
				else
					*s++ = '.';
			}
			else
				*s++ = ' ';
		}
		tprintf("%s |\n", outstr);
	}
}

#define PAGMASK	(~(PAGSIZ - 1))
/*
 * move `len' bytes of data from process `pid'
 * at address `addr' to our space at `laddr'
 */
int
umoven(tcp, addr, len, laddr)
struct tcb *tcp;
long addr;
int len;
char *laddr;
{

#ifdef LINUX
	int pid = tcp->pid;
	int n, m;
	int started = 0;
	union {
		long val;
		char x[sizeof(long)];
	} u;

	if (addr & (sizeof(long) - 1)) {
		/* addr not a multiple of sizeof(long) */
		n = addr - (addr & -sizeof(long)); /* residue */
		addr &= -sizeof(long); /* residue */
		errno = 0;
		u.val = ptrace(PTRACE_PEEKDATA, pid, (char *) addr, 0);
		if (errno) {
			if (started && (errno==EPERM || errno==EIO)) {
				/* Ran into 'end of memory' - stupid "printpath" */
				return 0;
			}
			/* But if not started, we had a bogus address. */
			perror("ptrace: umoven");
			return -1;
		}
		started = 1;
		memcpy(laddr, &u.x[n], m = MIN(sizeof(long) - n, len));
		addr += sizeof(long), laddr += m, len -= m;
	}
	while (len) {
		errno = 0;
		u.val = ptrace(PTRACE_PEEKDATA, pid, (char *) addr, 0);
		if (errno) {
			if (started && (errno==EPERM || errno==EIO)) {
				/* Ran into 'end of memory' - stupid "printpath" */
				return 0;
			}
			if (addr != 0)
				perror("ptrace: umoven");
			return -1;
		}
		started = 1;
		memcpy(laddr, u.x, m = MIN(sizeof(long), len));
		addr += sizeof(long), laddr += m, len -= m;
	}
#endif /* LINUX */

#ifdef SUNOS4
	int pid = tcp->pid;
#if 0
	int n, m;
	union {
		long val;
		char x[sizeof(long)];
	} u;

	if (addr & (sizeof(long) - 1)) {
		/* addr not a multiple of sizeof(long) */
		n = addr - (addr & -sizeof(long)); /* residue */
		addr &= -sizeof(long); /* residue */
		errno = 0;
		u.val = ptrace(PTRACE_PEEKDATA, pid, (char *) addr, 0);
		if (errno) {
			perror("umoven");
			return -1;
		}
		memcpy(laddr, &u.x[n], m = MIN(sizeof(long) - n, len));
		addr += sizeof(long), laddr += m, len -= m;
	}
	while (len) {
		errno = 0;
		u.val = ptrace(PTRACE_PEEKDATA, pid, (char *) addr, 0);
		if (errno) {
			perror("umoven");
			return -1;
		}
		memcpy(laddr, u.x, m = MIN(sizeof(long), len));
		addr += sizeof(long), laddr += m, len -= m;
	}
#else /* !oldway */
	int n;

	while (len) {
		n = MIN(len, PAGSIZ);
		n = MIN(n, ((addr + PAGSIZ) & PAGMASK) - addr);
		if (ptrace(PTRACE_READDATA, pid,
			   (char *) addr, len, laddr) < 0) {
			perror("umoven: ptrace(PTRACE_READDATA, ...)");
			abort();
			return -1;
		}
		len -= n;
		addr += n;
		laddr += n;
	}
#endif /* !oldway */
#endif /* SUNOS4 */

#ifdef USE_PROCFS
#ifdef HAVE_MP_PROCFS
	int fd = tcp->pfd_as;
#else
	int fd = tcp->pfd;
#endif
	lseek(fd, addr, SEEK_SET);
	if (read(fd, laddr, len) == -1)
		return -1;
#endif /* USE_PROCFS */

#ifdef NETBSD
	struct ptrace_io_desc piod;

	piod.piod_op = PIOD_READ_D;
	piod.piod_offs = (void *)addr;
	piod.piod_addr = laddr;
	piod.piod_len = len;

	if (ptrace(PT_IO, tcp->pid, (char *)&piod, sizeof(piod)) < 0) {
		perror("PT_IO");
		return -1;
	}
#endif

	return 0;
}

/*
 * like `umove' but make the additional effort of looking
 * for a terminating zero byte.
 */
int
umovestr(tcp, addr, len, laddr)
struct tcb *tcp;
long addr;
int len;
char *laddr;
{
#ifdef USE_PROCFS
#ifdef HAVE_MP_PROCFS
	int fd = tcp->pfd_as;
#else
	int fd = tcp->pfd;
#endif
	/* Some systems (e.g. FreeBSD) can be upset if we read off the
	   end of valid memory,  avoid this by trying to read up
	   to page boundaries.  But we don't know what a page is (and
	   getpagesize(2) (if it exists) doesn't necessarily return
	   hardware page size).  Assume all pages >= 1024 (a-historical
	   I know) */

	int page = 1024; 	/* How to find this? */
	int move = page - (addr & (page - 1));
	int left = len;

	lseek(fd, addr, SEEK_SET);

	while (left) {
		if (move > left) move = left;
		if ((move = read(fd, laddr, move)) <= 0)
			return left != len ? 0 : -1;
		if (memchr (laddr, 0, move)) break;
		left -= move;
		laddr += move;
		addr += move;
		move = page;
	}
#else /* !USE_PROCFS */
	int started = 0;
	int pid = tcp->pid;
	int i, n, m;
	union {
		long val;
		char x[sizeof(long)];
	} u;

	if (addr & (sizeof(long) - 1)) {
		/* addr not a multiple of sizeof(long) */
		n = addr - (addr & -sizeof(long)); /* residue */
		addr &= -sizeof(long); /* residue */
		errno = 0;
		u.val = ptrace(PTRACE_PEEKDATA, pid, (char *)addr, 0);
		if (errno) {
			if (started && (errno==EPERM || errno==EIO)) {
				/* Ran into 'end of memory' - stupid "printpath" */
				return 0;
			}
			perror("umovestr");
			return -1;
		}
		started = 1;
		memcpy(laddr, &u.x[n], m = MIN(sizeof(long)-n,len));
		while (n & (sizeof(long) - 1))
			if (u.x[n++] == '\0')
				return 0;
		addr += sizeof(long), laddr += m, len -= m;
	}
	while (len) {
		errno = 0;
		u.val = ptrace(PTRACE_PEEKDATA, pid, (char *)addr, 0);
		if (errno) {
			if (started && (errno==EPERM || errno==EIO)) {
				/* Ran into 'end of memory' - stupid "printpath" */
				return 0;
			}
			perror("umovestr");
			return -1;
		}
		started = 1;
		memcpy(laddr, u.x, m = MIN(sizeof(long), len));
		for (i = 0; i < sizeof(long); i++)
			if (u.x[i] == '\0')
				return 0;

		addr += sizeof(long), laddr += m, len -= m;
	}
#endif /* !USE_PROCFS */
	return 0;
}

#ifdef LINUX
#if !defined (SPARC) && !defined(SPARC64)
#define PTRACE_WRITETEXT	101
#define PTRACE_WRITEDATA	102
#endif /* !SPARC && !SPARC64 */
#endif /* LINUX */

#ifdef SUNOS4

static int
uload(cmd, pid, addr, len, laddr)
int cmd;
int pid;
long addr;
int len;
char *laddr;
{
#if 0
	int n;

	while (len) {
		n = MIN(len, PAGSIZ);
		n = MIN(n, ((addr + PAGSIZ) & PAGMASK) - addr);
		if (ptrace(cmd, pid, (char *)addr, n, laddr) < 0) {
			perror("uload: ptrace(PTRACE_WRITE, ...)");
			return -1;
		}
		len -= n;
		addr += n;
		laddr += n;
	}
#else
	int peek, poke;
	int n, m;
	union {
		long val;
		char x[sizeof(long)];
	} u;

	if (cmd == PTRACE_WRITETEXT) {
		peek = PTRACE_PEEKTEXT;
		poke = PTRACE_POKETEXT;
	}
	else {
		peek = PTRACE_PEEKDATA;
		poke = PTRACE_POKEDATA;
	}
	if (addr & (sizeof(long) - 1)) {
		/* addr not a multiple of sizeof(long) */
		n = addr - (addr & -sizeof(long)); /* residue */
		addr &= -sizeof(long);
		errno = 0;
		u.val = ptrace(peek, pid, (char *) addr, 0);
		if (errno) {
			perror("uload: POKE");
			return -1;
		}
		memcpy(&u.x[n], laddr, m = MIN(sizeof(long) - n, len));
		if (ptrace(poke, pid, (char *)addr, u.val) < 0) {
			perror("uload: POKE");
			return -1;
		}
		addr += sizeof(long), laddr += m, len -= m;
	}
	while (len) {
		if (len < sizeof(long))
			u.val = ptrace(peek, pid, (char *) addr, 0);
		memcpy(u.x, laddr, m = MIN(sizeof(long), len));
		if (ptrace(poke, pid, (char *) addr, u.val) < 0) {
			perror("uload: POKE");
			return -1;
		}
		addr += sizeof(long), laddr += m, len -= m;
	}
#endif
	return 0;
}

int
tload(pid, addr, len, laddr)
int pid;
int addr, len;
char *laddr;
{
	return uload(PTRACE_WRITETEXT, pid, addr, len, laddr);
}

int
dload(pid, addr, len, laddr)
int pid;
int addr;
int len;
char *laddr;
{
	return uload(PTRACE_WRITEDATA, pid, addr, len, laddr);
}

#endif /* SUNOS4 */

#ifndef USE_PROCFS

int
upeek(pid, off, res)
int pid;
long off;
long *res;
{
	long val;

#ifdef SUNOS4_KERNEL_ARCH_KLUDGE
	{
		static int is_sun4m = -1;
		struct utsname name;

		/* Round up the usual suspects. */
		if (is_sun4m == -1) {
			if (uname(&name) < 0) {
				perror("upeek: uname?");
				exit(1);
			}
			is_sun4m = strcmp(name.machine, "sun4m") == 0;
			if (is_sun4m) {
				extern const struct xlat struct_user_offsets[];
				const struct xlat *x;

				for (x = struct_user_offsets; x->str; x++)
					x->val += 1024;
			}
		}
		if (is_sun4m)
			off += 1024;
	}
#endif /* SUNOS4_KERNEL_ARCH_KLUDGE */
	errno = 0;
	val = ptrace(PTRACE_PEEKUSER, pid, (char *) off, 0);
	if (val == -1 && errno) {
		char buf[60];
		sprintf(buf,"upeek: ptrace(PTRACE_PEEKUSER,%d,%lu,0)",pid,off);
		perror(buf);
		return -1;
	}
	*res = val;
	return 0;
}

#endif /* !USE_PROCFS */

#if 0
long
getpc(tcp)
struct tcb *tcp;
{

#ifdef LINUX
	long pc;
#if defined(I386)
	if (upeek(tcp->pid, 4*EIP, &pc) < 0)
		return -1;
#elif defined(X86_64)
	if (upeek(tcp->pid, 8*RIP, &pc) < 0)
		return -1;
#elif defined(IA64)
	if (upeek(tcp->pid, PT_B0, &pc) < 0)
		return -1;
#elif defined(ARM)
	if (upeek(tcp->pid, 4*15, &pc) < 0)
		return -1;
#elif defined(POWERPC)
	if (upeek(tcp->pid, sizeof(unsigned long)*PT_NIP, &pc) < 0)
		return -1;
#elif defined(M68K)
	if (upeek(tcp->pid, 4*PT_PC, &pc) < 0)
		return -1;
#elif defined(ALPHA)
	if (upeek(tcp->pid, REG_PC, &pc) < 0)
		return -1;
#elif defined(MIPS)
 	if (upeek(tcp->pid, REG_EPC, &pc) < 0)
 		return -1;
#elif defined(SPARC) || defined(SPARC64)
	struct regs regs;
	if (ptrace(PTRACE_GETREGS,tcp->pid,(char *)&regs,0) < 0)
		return -1;
	pc = regs.r_pc;
#elif defined(S390) || defined(S390X)
	if(upeek(tcp->pid,PT_PSWADDR,&pc) < 0)
		return -1;
#elif defined(HPPA)
	if(upeek(tcp->pid,PT_IAOQ0,&pc) < 0)
		return -1;
#elif defined(SH)
       if (upeek(tcp->pid, 4*REG_PC ,&pc) < 0)
               return -1;
#elif defined(SH64)
       if (upeek(tcp->pid, REG_PC ,&pc) < 0)
               return -1;
#endif
	return pc;
#endif /* LINUX */

#ifdef SUNOS4
	/*
	 * Return current program counter for `pid'
	 * Assumes PC is never 0xffffffff
	 */
	struct regs regs;

	if (ptrace(PTRACE_GETREGS, tcp->pid, (char *) &regs, 0) < 0) {
		perror("getpc: ptrace(PTRACE_GETREGS, ...)");
		return -1;
	}
	return regs.r_pc;
#endif /* SUNOS4 */

#ifdef SVR4
	/* XXX */
	return 0;
#endif /* SVR4 */

#ifdef FREEBSD
	struct reg regs;
	pread(tcp->pfd_reg, &regs, sizeof(regs), 0);
	return regs.r_eip;
#endif /* FREEBSD */
#ifdef NETBSD
	struct reg regs;
	if (ptrace(PTRACE_GETREGS, tcp->pid, (char *) &regs, 0) < 0) {
		perror("getpc: ptrace(PTRACE_GETREGS, ...)");
		return -1;
	}
	return regs.r_eip;
#endif /* NETBSD */
}
#endif

void
printcall(tcp)
struct tcb *tcp;
{
#define PRINTBADPC tprintf(sizeof(long) == 4 ? "[????????] " : \
			   sizeof(long) == 8 ? "[????????????????] " : \
			   NULL /* crash */)

#ifdef LINUX
#ifdef I386
	long eip;

	if (upeek(tcp->pid, 4*EIP, &eip) < 0) {
		PRINTBADPC;
		return;
	}
	tprintf("[%08lx] ", eip);

#elif defined(S390) || defined(S390X)
         long psw;
         if(upeek(tcp->pid,PT_PSWADDR,&psw) < 0) {
                 PRINTBADPC;
                 return;
         }
#ifdef S390
         tprintf("[%08lx] ", psw);
#elif S390X
       tprintf("[%16lx] ", psw);
#endif

#elif defined(X86_64)
	long rip;

	if (upeek(tcp->pid, 8*RIP, &rip) < 0) {
		PRINTBADPC;
		return;
	}
	tprintf("[%16lx] ", rip);
#elif defined(IA64)
	long ip;

	if (upeek(tcp->pid, PT_B0, &ip) < 0) {
		PRINTBADPC;
		return;
	}
	tprintf("[%08lx] ", ip);
#elif defined(POWERPC)
	long pc;

	if (upeek(tcp->pid, sizeof(unsigned long)*PT_NIP, &pc) < 0) {
		tprintf ("[????????] ");
		return;
	}
	tprintf("[%08lx] ", pc);
#elif defined(M68K)
	long pc;

	if (upeek(tcp->pid, 4*PT_PC, &pc) < 0) {
		tprintf ("[????????] ");
		return;
	}
	tprintf("[%08lx] ", pc);
#elif defined(ALPHA)
	long pc;

	if (upeek(tcp->pid, REG_PC, &pc) < 0) {
		tprintf ("[????????????????] ");
		return;
	}
	tprintf("[%08lx] ", pc);
#elif defined(SPARC) || defined(SPARC64)
	struct regs regs;
	if (ptrace(PTRACE_GETREGS,tcp->pid,(char *)&regs,0) < 0) {
		PRINTBADPC;
		return;
	}
	tprintf("[%08lx] ", regs.r_pc);
#elif defined(HPPA)
	long pc;

	if(upeek(tcp->pid,PT_IAOQ0,&pc) < 0) {
		tprintf ("[????????] ");
		return;
	}
	tprintf("[%08lx] ", pc);
#elif defined(MIPS)
	long pc;

	if (upeek(tcp->pid, REG_EPC, &pc) < 0) {
		tprintf ("[????????] ");
		return;
	}
	tprintf("[%08lx] ", pc);
#elif defined(SH)
       long pc;

       if (upeek(tcp->pid, 4*REG_PC, &pc) < 0) {
               tprintf ("[????????] ");
               return;
       }
       tprintf("[%08lx] ", pc);
#elif defined(SH64)
	long pc;

	if (upeek(tcp->pid, REG_PC, &pc) < 0) {
		tprintf ("[????????????????] ");
		return;
	}
	tprintf("[%08lx] ", pc);
#elif defined(ARM)
	long pc;

	if (upeek(tcp->pid, 4*15, &pc) < 0) {
		PRINTBADPC;
		return;
	}
	tprintf("[%08lx] ", pc);
#endif /* !architecture */
#endif /* LINUX */

#ifdef SUNOS4
	struct regs regs;

	if (ptrace(PTRACE_GETREGS, tcp->pid, (char *) &regs, 0) < 0) {
		perror("printcall: ptrace(PTRACE_GETREGS, ...)");
		PRINTBADPC;
		return;
	}
	tprintf("[%08x] ", regs.r_o7);
#endif /* SUNOS4 */

#ifdef SVR4
	/* XXX */
	PRINTBADPC;
#endif

#ifdef FREEBSD
	struct reg regs;
	pread(tcp->pfd_reg, &regs, sizeof(regs), 0);
	tprintf("[%08x] ", regs.r_eip);
#endif /* FREEBSD */
}

#ifndef USE_PROCFS

#if defined LINUX

#include <sys/syscall.h>
#ifndef CLONE_PTRACE
# define CLONE_PTRACE    0x00002000
#endif
#ifndef CLONE_STOPPED
# define CLONE_STOPPED   0x02000000
#endif

#ifdef IA64

/* We don't have fork()/vfork() syscalls on ia64 itself, but the ia32
   subsystem has them for x86... */
#define SYS_fork	2
#define SYS_vfork	190

typedef unsigned long *arg_setup_state;

static int
arg_setup(struct tcb *tcp, arg_setup_state *state)
{
	unsigned long *bsp, cfm, sof, sol;

	if (ia32)
		return 0;

	if (upeek(tcp->pid, PT_AR_BSP, (long *) &bsp) < 0)
		return -1;
	if (upeek(tcp->pid, PT_CFM, (long *) &cfm) < 0)
		return -1;

	sof = (cfm >> 0) & 0x7f;
	sol = (cfm >> 7) & 0x7f;
	bsp = ia64_rse_skip_regs(bsp, -sof + sol);

	*state = bsp;
	return 0;
}

# define arg_finish_change(tcp, state)	0

#ifdef SYS_fork
static int
get_arg0 (struct tcb *tcp, arg_setup_state *state, long *valp)
{
	int ret;

	if (ia32)
		ret = upeek (tcp->pid, PT_R11, valp);
	else
		ret = umoven (tcp,
			      (unsigned long) ia64_rse_skip_regs(*state, 0),
			      sizeof(long), (void *) valp);
	return ret;
}

static int
get_arg1 (struct tcb *tcp, arg_setup_state *state, long *valp)
{
	int ret;

	if (ia32)
		ret = upeek (tcp->pid, PT_R9, valp);
	else
		ret = umoven (tcp,
			      (unsigned long) ia64_rse_skip_regs(*state, 1),
			      sizeof(long), (void *) valp);
	return ret;
}
#endif

static int
set_arg0 (struct tcb *tcp, arg_setup_state *state, long val)
{
	int req = PTRACE_POKEDATA;
	void *ap;

	if (ia32) {
		ap = (void *) (intptr_t) PT_R11;	 /* r11 == EBX */
		req = PTRACE_POKEUSER;
	} else
		ap = ia64_rse_skip_regs(*state, 0);
	errno = 0;
	ptrace(req, tcp->pid, ap, val);
	return errno ? -1 : 0;
}

static int
set_arg1 (struct tcb *tcp, arg_setup_state *state, long val)
{
	int req = PTRACE_POKEDATA;
	void *ap;

	if (ia32) {
		ap = (void *) (intptr_t) PT_R9;		/* r9 == ECX */
		req = PTRACE_POKEUSER;
	} else
		ap = ia64_rse_skip_regs(*state, 1);
	errno = 0;
	ptrace(req, tcp->pid, ap, val);
	return errno ? -1 : 0;
}

#elif defined (SPARC) || defined (SPARC64)

typedef struct regs arg_setup_state;

# define arg_setup(tcp, state) \
  (ptrace (PTRACE_GETREGS, tcp->pid, (char *) (state), 0))
# define arg_finish_change(tcp, state) \
  (ptrace (PTRACE_SETREGS, tcp->pid, (char *) (state), 0))

# define get_arg0(tcp, state, valp) (*(valp) = (state)->r_o0, 0)
# define get_arg1(tcp, state, valp) (*(valp) = (state)->r_o1, 0)
# define set_arg0(tcp, state, val) ((state)->r_o0 = (val), 0)
# define set_arg1(tcp, state, val) ((state)->r_o1 = (val), 0)
# define restore_arg0(tcp, state, val) 0

#else

# if defined S390 || defined S390X
/* Note: this is only true for the `clone' system call, which handles
   arguments specially.  We could as well say that its first two arguments
   are swapped relative to other architectures, but that would just be
   another #ifdef in the calls.  */
#  define arg0_offset	PT_GPR3
#  define arg1_offset	PT_ORIGGPR2
#  define restore_arg0(tcp, state, val) ((void) (state), 0)
#  define restore_arg1(tcp, state, val) ((void) (state), 0)
#  define arg0_index	1
#  define arg1_index	0
# elif defined (ALPHA) || defined (MIPS)
#  define arg0_offset	REG_A0
#  define arg1_offset	(REG_A0+1)
# elif defined (POWERPC)
#  define arg0_offset	(sizeof(unsigned long)*PT_R3)
#  define arg1_offset	(sizeof(unsigned long)*PT_R4)
#  define restore_arg0(tcp, state, val) ((void) (state), 0)
# elif defined (HPPA)
#  define arg0_offset	 PT_GR26
#  define arg1_offset	 (PT_GR26-4)
# elif defined (X86_64)
#  define arg0_offset	((long)(8*(current_personality ? RBX : RDI)))
#  define arg1_offset	((long)(8*(current_personality ? RCX : RSI)))
# elif defined (SH)
#  define arg0_offset	(4*(REG_REG0+4))
#  define arg1_offset	(4*(REG_REG0+5))
# elif defined (SH64)
   /* ABI defines arg0 & 1 in r2 & r3 */
#  define arg0_offset   (REG_OFFSET+16)
#  define arg1_offset   (REG_OFFSET+24)
#  define restore_arg0(tcp, state, val) 0
# else
#  define arg0_offset	0
#  define arg1_offset	4
#  if defined ARM
#   define restore_arg0(tcp, state, val) 0
#  endif
# endif

typedef int arg_setup_state;

# define arg_setup(tcp, state) (0)
# define arg_finish_change(tcp, state)	0
# define get_arg0(tcp, cookie, valp) \
  (upeek ((tcp)->pid, arg0_offset, (valp)))
# define get_arg1(tcp, cookie, valp) \
  (upeek ((tcp)->pid, arg1_offset, (valp)))

static int
set_arg0 (struct tcb *tcp, void *cookie, long val)
{
	return ptrace (PTRACE_POKEUSER, tcp->pid, (char*)arg0_offset, val);
}

static int
set_arg1 (struct tcb *tcp, void *cookie, long val)
{
	return ptrace (PTRACE_POKEUSER, tcp->pid, (char*)arg1_offset, val);
}

#endif

#ifndef restore_arg0
# define restore_arg0(tcp, state, val) set_arg0((tcp), (state), (val))
#endif
#ifndef restore_arg1
# define restore_arg1(tcp, state, val) set_arg1((tcp), (state), (val))
#endif

#ifndef arg0_index
# define arg0_index 0
# define arg1_index 1
#endif

int
setbpt(tcp)
struct tcb *tcp;
{
	extern int change_syscall(struct tcb *, int);
	arg_setup_state state;

	if (tcp->flags & TCB_BPTSET) {
		fprintf(stderr, "PANIC: TCB already set in pid %u\n", tcp->pid);
		return -1;
	}

	switch (known_scno(tcp)) {
#ifdef SYS_vfork
	case SYS_vfork:
#endif
#ifdef SYS_fork
	case SYS_fork:
#endif
#if defined SYS_fork || defined SYS_vfork
		if (arg_setup (tcp, &state) < 0
		    || get_arg0 (tcp, &state, &tcp->inst[0]) < 0
		    || get_arg1 (tcp, &state, &tcp->inst[1]) < 0
		    || change_syscall(tcp, SYS_clone) < 0
		    || set_arg0 (tcp, &state, CLONE_PTRACE|SIGCHLD) < 0
		    || set_arg1 (tcp, &state, 0) < 0
		    || arg_finish_change (tcp, &state) < 0)
			return -1;
		tcp->u_arg[arg0_index] = CLONE_PTRACE|SIGCHLD;
		tcp->u_arg[arg1_index] = 0;
		tcp->flags |= TCB_BPTSET;
		return 0;
#endif

	case SYS_clone:
#ifdef SYS_clone2
	case SYS_clone2:
#endif
		if ((tcp->u_arg[arg0_index] & CLONE_PTRACE) == 0
		    && (arg_setup (tcp, &state) < 0
			|| set_arg0 (tcp, &state,
				     tcp->u_arg[arg0_index] | CLONE_PTRACE) < 0
			|| arg_finish_change (tcp, &state) < 0))
			return -1;
		tcp->flags |= TCB_BPTSET;
		tcp->inst[0] = tcp->u_arg[arg0_index];
		tcp->inst[1] = tcp->u_arg[arg1_index];
		return 0;

	default:
		fprintf(stderr, "PANIC: setbpt for syscall %ld on %u???\n",
			tcp->scno, tcp->pid);
		break;
	}

	return -1;
}

int
clearbpt(tcp)
struct tcb *tcp;
{
	arg_setup_state state;
	if (arg_setup (tcp, &state) < 0
	    || restore_arg0 (tcp, &state, tcp->inst[0]) < 0
	    || restore_arg1 (tcp, &state, tcp->inst[1]) < 0
	    || arg_finish_change (tcp, &state))
		return -1;
	tcp->flags &= ~TCB_BPTSET;
	return 0;
}

#else

int
setbpt(tcp)
struct tcb *tcp;
{

#ifdef LINUX
#if defined (SPARC) || defined (SPARC64)
	/* We simply use the SunOS breakpoint code. */

	struct regs regs;
	unsigned long inst;
#define LOOPA	0x30800000	/* ba,a	0 */

	if (tcp->flags & TCB_BPTSET) {
		fprintf(stderr, "PANIC: TCB already set in pid %u\n", tcp->pid);
		return -1;
	}
	if (ptrace(PTRACE_GETREGS, tcp->pid, (char *)&regs, 0) < 0) {
		perror("setbpt: ptrace(PTRACE_GETREGS, ...)");
		return -1;
	}
	tcp->baddr = regs.r_o7 + 8;
	errno = 0;
	tcp->inst[0] = ptrace(PTRACE_PEEKTEXT, tcp->pid, (char *)tcp->baddr, 0);
	if(errno) {
		perror("setbpt: ptrace(PTRACE_PEEKTEXT, ...)");
		return -1;
	}

	/*
	 * XXX - BRUTAL MODE ON
	 * We cannot set a real BPT in the child, since it will not be
	 * traced at the moment it will reach the trap and would probably
	 * die with a core dump.
	 * Thus, we are force our way in by taking out two instructions
	 * and insert an eternal loop instead, in expectance of the SIGSTOP
	 * generated by out PTRACE_ATTACH.
	 * Of cause, if we evaporate ourselves in the middle of all this...
	 */
	errno = 0;
	inst = LOOPA;
#if defined (SPARC64)
	inst <<= 32;
	inst |= (tcp->inst[0] & 0xffffffffUL);
#endif
	ptrace(PTRACE_POKETEXT, tcp->pid, (char *) tcp->baddr, inst);
	if(errno) {
		perror("setbpt: ptrace(PTRACE_POKETEXT, ...)");
		return -1;
	}
	tcp->flags |= TCB_BPTSET;

#else /* !SPARC && !SPARC64 */
#ifdef IA64
	if (ia32) {
#		define LOOP	0x0000feeb
		if (tcp->flags & TCB_BPTSET) {
			fprintf(stderr, "PANIC: bpt already set in pid %u\n",
				tcp->pid);
			return -1;
		}
		if (upeek(tcp->pid, PT_CR_IIP, &tcp->baddr) < 0)
			return -1;
		if (debug)
			fprintf(stderr, "[%d] setting bpt at %lx\n",
				tcp->pid, tcp->baddr);
		tcp->inst[0] = ptrace(PTRACE_PEEKTEXT, tcp->pid,
				      (char *) tcp->baddr, 0);
		if (errno) {
			perror("setbpt: ptrace(PTRACE_PEEKTEXT, ...)");
			return -1;
		}
		ptrace(PTRACE_POKETEXT, tcp->pid, (char *) tcp->baddr, LOOP);
		if (errno) {
			perror("setbpt: ptrace(PTRACE_POKETEXT, ...)");
			return -1;
		}
		tcp->flags |= TCB_BPTSET;
	} else {
		/*
		 * Our strategy here is to replace the bundle that
		 * contained the clone() syscall with a bundle of the
		 * form:
		 *
		 *	{ 1: br 1b; br 1b; br 1b }
		 *
		 * This ensures that the newly forked child will loop
		 * endlessly until we've got a chance to attach to it.
		 */
#		define LOOP0	0x0000100000000017
#		define LOOP1	0x4000000000200000
		unsigned long addr, ipsr;
		pid_t pid;

		pid = tcp->pid;
		if (upeek(pid, PT_CR_IPSR, &ipsr) < 0)
			return -1;
		if (upeek(pid, PT_CR_IIP, &addr) < 0)
			return -1;
		/* store "ri" in low two bits */
		tcp->baddr = addr | ((ipsr >> 41) & 0x3);

		errno = 0;
		tcp->inst[0] = ptrace(PTRACE_PEEKTEXT, pid, (char *) addr + 0,
				      0);
		tcp->inst[1] = ptrace(PTRACE_PEEKTEXT, pid, (char *) addr + 8,
				      0);
		if (errno) {
			perror("setbpt: ptrace(PTRACE_PEEKTEXT, ...)");
			return -1;
		}

		errno = 0;
		ptrace(PTRACE_POKETEXT, pid, (char *) addr + 0, LOOP0);
		ptrace(PTRACE_POKETEXT, pid, (char *) addr + 8, LOOP1);
		if (errno) {
			perror("setbpt: ptrace(PTRACE_POKETEXT, ...)");
			return -1;
		}
		tcp->flags |= TCB_BPTSET;
	}
#else /* !IA64 */

#if defined (I386) || defined(X86_64)
#define LOOP	0x0000feeb
#elif defined (M68K)
#define LOOP	0x60fe0000
#elif defined (ALPHA)
#define LOOP	0xc3ffffff
#elif defined (POWERPC)
#define LOOP	0x48000000
#elif defined(ARM)
#define LOOP	0xEAFFFFFE
#elif defined(MIPS)
#define LOOP	0x1000ffff
#elif defined(S390)
#define LOOP	0xa7f40000	/* BRC 15,0 */
#elif defined(S390X)
#define LOOP   0xa7f4000000000000UL /* BRC 15,0 */
#elif defined(HPPA)
#define LOOP	0xe81f1ff7	/* b,l,n <loc>,r0 */
#elif defined(SH)
#ifdef __LITTLE_ENDIAN__
#define LOOP   0x0000affe
#else
#define LOOP   0xfeaf0000
#endif
#else
#error unknown architecture
#endif

	if (tcp->flags & TCB_BPTSET) {
		fprintf(stderr, "PANIC: bpt already set in pid %u\n", tcp->pid);
		return -1;
	}
#if defined (I386)
	if (upeek(tcp->pid, 4*EIP, &tcp->baddr) < 0)
		return -1;
#elif defined (X86_64)
	if (upeek(tcp->pid, 8*RIP, &tcp->baddr) < 0)
		return -1;
#elif defined (M68K)
	if (upeek(tcp->pid, 4*PT_PC, &tcp->baddr) < 0)
	  return -1;
#elif defined (ALPHA)
	return -1;
#elif defined (ARM)
	return -1;
#elif defined (MIPS)
	return -1;		/* FIXME: I do not know what i do - Flo */
#elif defined (POWERPC)
	if (upeek(tcp->pid, sizeof(unsigned long)*PT_NIP, &tcp->baddr) < 0)
		return -1;
#elif defined(S390) || defined(S390X)
	if (upeek(tcp->pid,PT_PSWADDR, &tcp->baddr) < 0)
		return -1;
#elif defined(HPPA)
	if (upeek(tcp->pid, PT_IAOQ0, &tcp->baddr) < 0)
		return -1;
	tcp->baddr &= ~0x03;
#elif defined(SH)
       if (upeek(tcp->pid, 4*REG_PC, &tcp->baddr) < 0)
               return -1;
#else
#error unknown architecture
#endif
	if (debug)
		fprintf(stderr, "[%d] setting bpt at %lx\n", tcp->pid, tcp->baddr);
	tcp->inst[0] = ptrace(PTRACE_PEEKTEXT, tcp->pid, (char *) tcp->baddr, 0);
	if (errno) {
		perror("setbpt: ptrace(PTRACE_PEEKTEXT, ...)");
		return -1;
	}
	ptrace(PTRACE_POKETEXT, tcp->pid, (char *) tcp->baddr, LOOP);
	if (errno) {
		perror("setbpt: ptrace(PTRACE_POKETEXT, ...)");
		return -1;
	}
	tcp->flags |= TCB_BPTSET;

#endif /* !IA64 */
#endif /* SPARC || SPARC64 */
#endif /* LINUX */

#ifdef SUNOS4
#ifdef SPARC	/* This code is slightly sparc specific */

	struct regs regs;
#define BPT	0x91d02001	/* ta	1 */
#define LOOP	0x10800000	/* ba	0 */
#define LOOPA	0x30800000	/* ba,a	0 */
#define NOP	0x01000000
#if LOOPA
	static int loopdeloop[1] = {LOOPA};
#else
	static int loopdeloop[2] = {LOOP, NOP};
#endif

	if (tcp->flags & TCB_BPTSET) {
		fprintf(stderr, "PANIC: TCB already set in pid %u\n", tcp->pid);
		return -1;
	}
	if (ptrace(PTRACE_GETREGS, tcp->pid, (char *)&regs, 0) < 0) {
		perror("setbpt: ptrace(PTRACE_GETREGS, ...)");
		return -1;
	}
	tcp->baddr = regs.r_o7 + 8;
	if (ptrace(PTRACE_READTEXT, tcp->pid, (char *)tcp->baddr,
				sizeof tcp->inst, (char *)tcp->inst) < 0) {
		perror("setbpt: ptrace(PTRACE_READTEXT, ...)");
		return -1;
	}

	/*
	 * XXX - BRUTAL MODE ON
	 * We cannot set a real BPT in the child, since it will not be
	 * traced at the moment it will reach the trap and would probably
	 * die with a core dump.
	 * Thus, we are force our way in by taking out two instructions
	 * and insert an eternal loop in stead, in expectance of the SIGSTOP
	 * generated by out PTRACE_ATTACH.
	 * Of cause, if we evaporate ourselves in the middle of all this...
	 */
	if (ptrace(PTRACE_WRITETEXT, tcp->pid, (char *) tcp->baddr,
			sizeof loopdeloop, (char *) loopdeloop) < 0) {
		perror("setbpt: ptrace(PTRACE_WRITETEXT, ...)");
		return -1;
	}
	tcp->flags |= TCB_BPTSET;

#endif /* SPARC */
#endif /* SUNOS4 */

	return 0;
}

int
clearbpt(tcp)
struct tcb *tcp;
{

#ifdef LINUX
#if defined(I386) || defined(X86_64)
	long eip;
#elif defined(POWERPC)
	long pc;
#elif defined(M68K)
	long pc;
#elif defined(ALPHA)
	long pc;
#elif defined(HPPA)
	long iaoq;
#elif defined(SH)
       long pc;
#endif /* architecture */

#if defined (SPARC) || defined (SPARC64)
	/* Again, we borrow the SunOS breakpoint code. */
	if (!(tcp->flags & TCB_BPTSET)) {
		fprintf(stderr, "PANIC: TCB not set in pid %u\n", tcp->pid);
		return -1;
	}
	errno = 0;
	ptrace(PTRACE_POKETEXT, tcp->pid, (char *) tcp->baddr, tcp->inst[0]);
	if(errno) {
		perror("clearbtp: ptrace(PTRACE_POKETEXT, ...)");
		return -1;
	}
	tcp->flags &= ~TCB_BPTSET;
#elif defined(IA64)
	if (ia32) {
		unsigned long addr;

		if (debug)
			fprintf(stderr, "[%d] clearing bpt\n", tcp->pid);
		if (!(tcp->flags & TCB_BPTSET)) {
			fprintf(stderr, "PANIC: TCB not set in pid %u\n", tcp->pid);
			return -1;
		}
		errno = 0;
		ptrace(PTRACE_POKETEXT, tcp->pid, (char *) tcp->baddr, tcp->inst[0]);
		if (errno) {
			perror("clearbtp: ptrace(PTRACE_POKETEXT, ...)");
			return -1;
		}
		tcp->flags &= ~TCB_BPTSET;

		if (upeek(tcp->pid, PT_CR_IIP, &addr) < 0)
			return -1;
		if (addr != tcp->baddr) {
			/* The breakpoint has not been reached yet.  */
			if (debug)
				fprintf(stderr,
					"NOTE: PC not at bpt (pc %#lx baddr %#lx)\n",
						addr, tcp->baddr);
			return 0;
		}
	} else {
		unsigned long addr, ipsr;
		pid_t pid;

		pid = tcp->pid;

		if (upeek(pid, PT_CR_IPSR, &ipsr) < 0)
			return -1;
		if (upeek(pid, PT_CR_IIP, &addr) < 0)
			return -1;

		/* restore original bundle: */
		errno = 0;
		ptrace(PTRACE_POKETEXT, pid, (char *) addr + 0, tcp->inst[0]);
		ptrace(PTRACE_POKETEXT, pid, (char *) addr + 8, tcp->inst[1]);
		if (errno) {
			perror("clearbpt: ptrace(PTRACE_POKETEXT, ...)");
			return -1;
		}

		/* restore original "ri" in ipsr: */
		ipsr = (ipsr & ~(0x3ul << 41)) | ((tcp->baddr & 0x3) << 41);
		errno = 0;
		ptrace(PTRACE_POKEUSER, pid, (char *) PT_CR_IPSR, ipsr);
		if (errno) {
			perror("clrbpt: ptrace(PTRACE_POKEUSER, ...)");
			return -1;
		}

		tcp->flags &= ~TCB_BPTSET;

		if (addr != (tcp->baddr & ~0x3)) {
			/* the breakpoint has not been reached yet.  */
			if (debug)
				fprintf(stderr, "NOTE: PC not at bpt (pc %#lx baddr %#lx)\n",
					addr, tcp->baddr);
			return 0;
		}
	}
#else /* !IA64  && !SPARC && !SPARC64 */

	if (debug)
		fprintf(stderr, "[%d] clearing bpt\n", tcp->pid);
	if (!(tcp->flags & TCB_BPTSET)) {
		fprintf(stderr, "PANIC: TCB not set in pid %u\n", tcp->pid);
		return -1;
	}
	errno = 0;
	ptrace(PTRACE_POKETEXT, tcp->pid, (char *) tcp->baddr, tcp->inst[0]);
	if (errno) {
		perror("clearbtp: ptrace(PTRACE_POKETEXT, ...)");
		return -1;
	}
	tcp->flags &= ~TCB_BPTSET;

#ifdef I386
	if (upeek(tcp->pid, 4*EIP, &eip) < 0)
		return -1;
	if (eip != tcp->baddr) {
		/* The breakpoint has not been reached yet.  */
		if (debug)
			fprintf(stderr,
				"NOTE: PC not at bpt (pc %#lx baddr %#lx)\n",
					eip, tcp->baddr);
		return 0;
	}
#elif defined(X86_64)
	if (upeek(tcp->pid, 8*RIP, &eip) < 0)
		return -1;
	if (eip != tcp->baddr) {
		/* The breakpoint has not been reached yet.  */
		if (debug)
			fprintf(stderr,
				"NOTE: PC not at bpt (pc %#lx baddr %#lx)\n",
					eip, tcp->baddr);
		return 0;
	}
#elif defined(POWERPC)
	if (upeek(tcp->pid, sizeof(unsigned long)*PT_NIP, &pc) < 0)
		return -1;
	if (pc != tcp->baddr) {
		/* The breakpoint has not been reached yet.  */
		if (debug)
			fprintf(stderr, "NOTE: PC not at bpt (pc %#lx baddr %#lx)\n",
				pc, tcp->baddr);
		return 0;
	}
#elif defined(M68K)
	if (upeek(tcp->pid, 4*PT_PC, &pc) < 0)
		return -1;
	if (pc != tcp->baddr) {
		/* The breakpoint has not been reached yet.  */
		if (debug)
			fprintf(stderr, "NOTE: PC not at bpt (pc %#lx baddr %#lx)\n",
				pc, tcp->baddr);
		return 0;
	}
#elif defined(ALPHA)
	if (upeek(tcp->pid, REG_PC, &pc) < 0)
		return -1;
	if (pc != tcp->baddr) {
		/* The breakpoint has not been reached yet.  */
		if (debug)
			fprintf(stderr, "NOTE: PC not at bpt (pc %#lx baddr %#lx)\n",
				pc, tcp->baddr);
		return 0;
	}
#elif defined(HPPA)
	if (upeek(tcp->pid, PT_IAOQ0, &iaoq) < 0)
		return -1;
	iaoq &= ~0x03;
	if (iaoq != tcp->baddr && iaoq != tcp->baddr + 4) {
		/* The breakpoint has not been reached yet.  */
		if (debug)
			fprintf(stderr, "NOTE: PC not at bpt (iaoq %#lx baddr %#lx)\n",
				iaoq, tcp->baddr);
		return 0;
	}
	iaoq = tcp->baddr | 3;
	/* We should be pointing at a 'ldi -1000,r1' in glibc, so it is
	 * safe to set both IAOQ0 and IAOQ1 to that so the PSW N bit
	 * has no significant effect.
	 */
	ptrace(PTRACE_POKEUSER, tcp->pid, (void *)PT_IAOQ0, iaoq);
	ptrace(PTRACE_POKEUSER, tcp->pid, (void *)PT_IAOQ1, iaoq);
#elif defined(SH)
       if (upeek(tcp->pid, 4*REG_PC, &pc) < 0)
               return -1;
        if (pc != tcp->baddr) {
                /* The breakpoint has not been reached yet.  */
                if (debug)
                        fprintf(stderr, "NOTE: PC not at bpt (pc %#lx baddr %#lx)\n",
                                pc, tcp->baddr);
                return 0;
        }

#endif /* arch */
#endif /* !SPARC && !SPARC64 && !IA64 */
#endif /* LINUX */

#ifdef SUNOS4
#ifdef SPARC

#if !LOOPA
	struct regs regs;
#endif

	if (!(tcp->flags & TCB_BPTSET)) {
		fprintf(stderr, "PANIC: TCB not set in pid %u\n", tcp->pid);
		return -1;
	}
	if (ptrace(PTRACE_WRITETEXT, tcp->pid, (char *) tcp->baddr,
				sizeof tcp->inst, (char *) tcp->inst) < 0) {
		perror("clearbtp: ptrace(PTRACE_WRITETEXT, ...)");
		return -1;
	}
	tcp->flags &= ~TCB_BPTSET;

#if !LOOPA
	/*
	 * Since we don't have a single instruction breakpoint, we may have
	 * to adjust the program counter after removing the our `breakpoint'.
	 */
	if (ptrace(PTRACE_GETREGS, tcp->pid, (char *)&regs, 0) < 0) {
		perror("clearbpt: ptrace(PTRACE_GETREGS, ...)");
		return -1;
	}
	if ((regs.r_pc < tcp->baddr) ||
				(regs.r_pc > tcp->baddr + 4)) {
		/* The breakpoint has not been reached yet */
		if (debug)
			fprintf(stderr,
				"NOTE: PC not at bpt (pc %#x baddr %#x)\n",
					regs.r_pc, tcp->parent->baddr);
		return 0;
	}
	if (regs.r_pc != tcp->baddr)
		if (debug)
			fprintf(stderr, "NOTE: PC adjusted (%#x -> %#x\n",
				regs.r_pc, tcp->baddr);

	regs.r_pc = tcp->baddr;
	if (ptrace(PTRACE_SETREGS, tcp->pid, (char *)&regs, 0) < 0) {
		perror("clearbpt: ptrace(PTRACE_SETREGS, ...)");
		return -1;
	}
#endif /* LOOPA */
#endif /* SPARC */
#endif /* SUNOS4 */

	return 0;
}

#endif

#endif /* !USE_PROCFS */

#ifdef SUNOS4

static int
getex(pid, hdr)
int pid;
struct exec *hdr;
{
	int n;

	for (n = 0; n < sizeof *hdr; n += 4) {
		long res;
		if (upeek(pid, uoff(u_exdata) + n, &res) < 0)
			return -1;
		memcpy(((char *) hdr) + n, &res, 4);
	}
	if (debug) {
		fprintf(stderr, "[struct exec: magic: %o version %u Mach %o\n",
			hdr->a_magic, hdr->a_toolversion, hdr->a_machtype);
		fprintf(stderr, "Text %lu Data %lu Bss %lu Syms %lu Entry %#lx]\n",
			hdr->a_text, hdr->a_data, hdr->a_bss, hdr->a_syms, hdr->a_entry);
	}
	return 0;
}

int
fixvfork(tcp)
struct tcb *tcp;
{
	int pid = tcp->pid;
	/*
	 * Change `vfork' in a freshly exec'ed dynamically linked
	 * executable's (internal) symbol table to plain old `fork'
	 */

	struct exec hdr;
	struct link_dynamic dyn;
	struct link_dynamic_2 ld;
	char *strtab, *cp;

	if (getex(pid, &hdr) < 0)
		return -1;
	if (!hdr.a_dynamic)
		return -1;

	if (umove(tcp, (int) N_DATADDR(hdr), &dyn) < 0) {
		fprintf(stderr, "Cannot read DYNAMIC\n");
		return -1;
	}
	if (umove(tcp, (int) dyn.ld_un.ld_2, &ld) < 0) {
		fprintf(stderr, "Cannot read link_dynamic_2\n");
		return -1;
	}
	if ((strtab = malloc((unsigned)ld.ld_symb_size)) == NULL) {
		fprintf(stderr, "out of memory\n");
		return -1;
	}
	if (umoven(tcp, (int)ld.ld_symbols+(int)N_TXTADDR(hdr),
					(int)ld.ld_symb_size, strtab) < 0)
		goto err;

#if 0
	for (cp = strtab; cp < strtab + ld.ld_symb_size; ) {
		fprintf(stderr, "[symbol: %s]\n", cp);
		cp += strlen(cp)+1;
	}
	return 0;
#endif
	for (cp = strtab; cp < strtab + ld.ld_symb_size; ) {
		if (strcmp(cp, "_vfork") == 0) {
			if (debug)
				fprintf(stderr, "fixvfork: FOUND _vfork\n");
			strcpy(cp, "_fork");
			break;
		}
		cp += strlen(cp)+1;
	}
	if (cp < strtab + ld.ld_symb_size)
		/*
		 * Write entire symbol table back to avoid
		 * memory alignment bugs in ptrace
		 */
		if (tload(pid, (int)ld.ld_symbols+(int)N_TXTADDR(hdr),
					(int)ld.ld_symb_size, strtab) < 0)
			goto err;

	free(strtab);
	return 0;

err:
	free(strtab);
	return -1;
}

#endif /* SUNOS4 */
