+void
+printcall(struct tcb *tcp)
+{
+#define PRINTBADPC tprintf(sizeof(long) == 4 ? "[????????] " : \
+ sizeof(long) == 8 ? "[????????????????] " : \
+ NULL /* crash */)
+ if (get_regs_error) {
+ PRINTBADPC;
+ return;
+ }
+#if defined(I386)
+ tprintf("[%08lx] ", i386_regs.eip);
+#elif defined(S390) || defined(S390X)
+ long psw;
+ if (upeek(tcp, PT_PSWADDR, &psw) < 0) {
+ PRINTBADPC;
+ return;
+ }
+# ifdef S390
+ tprintf("[%08lx] ", psw);
+# elif S390X
+ tprintf("[%016lx] ", psw);
+# endif
+#elif defined(X86_64) || defined(X32)
+ if (x86_io.iov_len == sizeof(i386_regs)) {
+ tprintf("[%08x] ", (unsigned) i386_regs.eip);
+ } else {
+# if defined(X86_64)
+ tprintf("[%016lx] ", (unsigned long) x86_64_regs.rip);
+# elif defined(X32)
+ /* Note: this truncates 64-bit rip to 32 bits */
+ tprintf("[%08lx] ", (unsigned long) x86_64_regs.rip);
+# endif
+ }
+#elif defined(IA64)
+ long ip;
+ if (upeek(tcp, PT_B0, &ip) < 0) {
+ PRINTBADPC;
+ return;
+ }
+ tprintf("[%08lx] ", ip);
+#elif defined(POWERPC)
+ long pc;
+ if (upeek(tcp, sizeof(unsigned long)*PT_NIP, &pc) < 0) {
+ PRINTBADPC;
+ return;
+ }
+# ifdef POWERPC64
+ tprintf("[%016lx] ", pc);
+# else
+ tprintf("[%08lx] ", pc);
+# endif
+#elif defined(M68K)
+ long pc;
+ if (upeek(tcp, 4*PT_PC, &pc) < 0) {
+ tprints("[????????] ");
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(ALPHA)
+ long pc;
+ if (upeek(tcp, REG_PC, &pc) < 0) {
+ tprints("[????????????????] ");
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(SPARC)
+ tprintf("[%08lx] ", sparc_regs.pc);
+#elif defined(SPARC64)
+ tprintf("[%08lx] ", sparc_regs.tpc);
+#elif defined(HPPA)
+ long pc;
+ if (upeek(tcp, PT_IAOQ0, &pc) < 0) {
+ tprints("[????????] ");
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(MIPS)
+ long pc;
+ if (upeek(tcp, REG_EPC, &pc) < 0) {
+ tprints("[????????] ");
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(SH)
+ long pc;
+ if (upeek(tcp, 4*REG_PC, &pc) < 0) {
+ tprints("[????????] ");
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(SH64)
+ long pc;
+ if (upeek(tcp, REG_PC, &pc) < 0) {
+ tprints("[????????????????] ");
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(ARM)
+ tprintf("[%08lx] ", arm_regs.ARM_pc);
+#elif defined(AARCH64)
+ /* tprintf("[%016lx] ", aarch64_regs.regs[???]); */
+#elif defined(AVR32)
+ tprintf("[%08lx] ", avr32_regs.pc);
+#elif defined(BFIN)
+ long pc;
+ if (upeek(tcp, PT_PC, &pc) < 0) {
+ PRINTBADPC;
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(CRISV10)
+ long pc;
+ if (upeek(tcp, 4*PT_IRP, &pc) < 0) {
+ PRINTBADPC;
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(CRISV32)
+ long pc;
+ if (upeek(tcp, 4*PT_ERP, &pc) < 0) {
+ PRINTBADPC;
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#elif defined(TILE)
+# ifdef _LP64
+ tprintf("[%016lx] ", (unsigned long) tile_regs.pc);
+# else
+ tprintf("[%08lx] ", (unsigned long) tile_regs.pc);
+# endif
+#elif defined(OR1K)
+ tprintf("[%08lx] ", or1k_regs.pc);
+#elif defined(METAG)
+ tprintf("[%08lx] ", metag_regs.pc);
+#elif defined(XTENSA)
+ long pc;
+ if (upeek(tcp, REG_PC, &pc) < 0) {
+ PRINTBADPC;
+ return;
+ }
+ tprintf("[%08lx] ", pc);
+#endif /* architecture */
+}
+
+/* Shuffle syscall numbers so that we don't have huge gaps in syscall table.
+ * The shuffling should be reversible: shuffle_scno(shuffle_scno(n)) == n.
+ */
+#if defined(ARM) /* So far only ARM needs this */
+static long
+shuffle_scno(unsigned long scno)
+{
+ if (scno <= ARM_LAST_ORDINARY_SYSCALL)
+ return scno;
+
+ /* __ARM_NR_cmpxchg? Swap with LAST_ORDINARY+1 */
+ if (scno == 0x000ffff0)
+ return ARM_LAST_ORDINARY_SYSCALL+1;
+ if (scno == ARM_LAST_ORDINARY_SYSCALL+1)
+ return 0x000ffff0;
+
+ /* Is it ARM specific syscall?
+ * Swap with [LAST_ORDINARY+2, LAST_ORDINARY+2 + LAST_SPECIAL] range.
+ */
+ if (scno >= 0x000f0000
+ && scno <= 0x000f0000 + ARM_LAST_SPECIAL_SYSCALL
+ ) {
+ return scno - 0x000f0000 + (ARM_LAST_ORDINARY_SYSCALL+2);
+ }
+ if (/* scno >= ARM_LAST_ORDINARY_SYSCALL+2 - always true */ 1
+ && scno <= (ARM_LAST_ORDINARY_SYSCALL+2) + ARM_LAST_SPECIAL_SYSCALL
+ ) {
+ return scno + 0x000f0000 - (ARM_LAST_ORDINARY_SYSCALL+2);
+ }
+
+ 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;
+}
+
+#ifndef get_regs
+long get_regs_error;
+
+#if defined(PTRACE_GETREGSET) && defined(NT_PRSTATUS)
+static void get_regset(pid_t pid)
+{
+/* constant iovec */
+# if defined(ARM) \
+ || defined(I386) \
+ || defined(METAG) \
+ || defined(OR1K)
+ static struct iovec io = {
+ .iov_base = &ARCH_REGS_FOR_GETREGSET,
+ .iov_len = sizeof(ARCH_REGS_FOR_GETREGSET)
+ };
+ get_regs_error = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &io);
+
+/* variable iovec */
+# elif defined(X86_64) || defined(X32)
+ /* x86_io.iov_base = &x86_regs_union; - already is */
+ x86_io.iov_len = sizeof(x86_regs_union);
+ get_regs_error = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &x86_io);
+# elif defined(AARCH64)
+ /* aarch64_io.iov_base = &arm_regs_union; - already is */
+ aarch64_io.iov_len = sizeof(arm_regs_union);
+ get_regs_error = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &aarch64_io);
+# else
+# warning both PTRACE_GETREGSET and NT_PRSTATUS are available but not yet used
+# endif
+}
+#endif /* PTRACE_GETREGSET && NT_PRSTATUS */
+
+void
+get_regs(pid_t pid)
+{
+/* PTRACE_GETREGSET only */
+# if defined(METAG) || defined(OR1K) || defined(X32) || defined(AARCH64)
+ get_regset(pid);
+
+/* PTRACE_GETREGS only */
+# elif defined(AVR32)
+ get_regs_error = ptrace(PTRACE_GETREGS, pid, NULL, &avr32_regs);
+# elif defined(TILE)
+ get_regs_error = ptrace(PTRACE_GETREGS, pid, NULL, &tile_regs);
+# elif defined(SPARC) || defined(SPARC64)
+ get_regs_error = ptrace(PTRACE_GETREGS, pid, (char *)&sparc_regs, 0);
+
+/* try PTRACE_GETREGSET first, fallback to PTRACE_GETREGS */
+# else
+# if defined(PTRACE_GETREGSET) && defined(NT_PRSTATUS)
+ static int getregset_support;
+
+ if (getregset_support >= 0) {
+ 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;
+ }
+# endif /* PTRACE_GETREGSET && NT_PRSTATUS */
+# if defined(ARM)
+ get_regs_error = ptrace(PTRACE_GETREGS, pid, NULL, &arm_regs);
+# elif defined(I386)
+ get_regs_error = ptrace(PTRACE_GETREGS, pid, NULL, &i386_regs);
+# elif defined(X86_64)
+ /* Use old method, with unreliable heuristical detection of 32-bitness. */
+ x86_io.iov_len = sizeof(x86_64_regs);
+ get_regs_error = ptrace(PTRACE_GETREGS, pid, NULL, &x86_64_regs);
+ if (!get_regs_error && x86_64_regs.cs == 0x23) {
+ x86_io.iov_len = sizeof(i386_regs);
+ /*
+ * The order is important: i386_regs and x86_64_regs
+ * are overlaid in memory!
+ */
+ i386_regs.ebx = x86_64_regs.rbx;
+ i386_regs.ecx = x86_64_regs.rcx;
+ i386_regs.edx = x86_64_regs.rdx;
+ i386_regs.esi = x86_64_regs.rsi;
+ i386_regs.edi = x86_64_regs.rdi;
+ i386_regs.ebp = x86_64_regs.rbp;
+ i386_regs.eax = x86_64_regs.rax;
+ /* i386_regs.xds = x86_64_regs.ds; unused by strace */
+ /* i386_regs.xes = x86_64_regs.es; ditto... */
+ /* i386_regs.xfs = x86_64_regs.fs; */
+ /* i386_regs.xgs = x86_64_regs.gs; */
+ i386_regs.orig_eax = x86_64_regs.orig_rax;
+ i386_regs.eip = x86_64_regs.rip;
+ /* i386_regs.xcs = x86_64_regs.cs; */
+ /* i386_regs.eflags = x86_64_regs.eflags; */
+ i386_regs.esp = x86_64_regs.rsp;
+ /* i386_regs.xss = x86_64_regs.ss; */
+ }
+# else
+# error unhandled architecture
+# endif /* ARM || I386 || X86_64 */
+# endif
+}
+#endif /* !get_regs */
+