Add x32 support to strace

X32 support is added to Linux kernel 3.4. In a nutshell, x32 is x86-64 with
32bit pointers.  At system call level, x32 is also identical to x86-64,
as shown by many changes like "defined(X86_64) || defined(X32)".  The
main differerence bewteen x32 and x86-64 is off_t in x32 is long long
instead of long.

This patch adds x32 support to strace.  Tested on Linux/x32.

* configure.ac: Support X32.
* defs.h: Set SUPPORTED_PERSONALITIES to 3 for X86_64,
Set PERSONALITY2_WORDSIZE to 4 for X86_64.
Add tcb::ext_arg for X32.
* file.c (stat): New for X32.
(sys_lseek): Use 64-bit version for X32.
(printstat64): Check current_personality != 1 for X86_64.
* ipc.c (indirect_ipccall): Check current_personality == 1
for X86_64.
* mem.c (sys_mmap64): Also use tcp->u_arg for X32.  Print NULL
for zero address.  Call printllval for offset for X32.
* pathtrace.c (pathtrace_match): Don't check sys_old_mmap for
X32.
* process.c (ARG_FLAGS): Defined for X32.
(ARG_STACK): Likewise.
(ARG_PTID): Likewise.
(change_syscall): Handle X32.
(struct_user_offsets): Support X32.
(sys_arch_prctl): Likewise.
* signal.c: Include <asm/sigcontext.h> for X32.
(SA_RESTORER): Also define for X32.
* syscall.c (update_personality): Support X32 for X86_64.
(is_restart_error): Likewise.
(syscall_fixup_on_sysenter): Likewise.
(get_syscall_args): Likewise.
(get_syscall_result): Likewise.
(get_error): Likewise.
(__X32_SYSCALL_BIT): Define if not defined.
(__X32_SYSCALL_MASK): Likewise.
(get_scno): Check DS register value for X32.  Use
__X32_SYSCALL_MASK on X32 system calls.
* util.c (printllval): Use ext_arg for X32.
(printcall): Support X32.
(change_syscall): Likewise.
(arg0_offset): Likewise.
(arg1_offset): Likewise.
* Makefile.am (EXTRA_DIST): Add linux/x32/errnoent.h,
linux/x32/ioctlent.h.in, linux/x32/signalent.h,
linux/x32/syscallent.h, linux/x86_64/errnoent2.h,
linux/x86_64/ioctlent2.h, linux/x86_64/signalent2.h and
linux/x86_64/syscallent2.h.
* linux/x32/errnoent.h: New.
* linux/x32/ioctlent.h.in: Likewise.
* linux/x32/signalent.h: Likewise.
* linux/x32/syscallent.h: Likewise.
* linux/x86_64/errnoent2.h: Likewise.
* linux/x86_64/ioctlent2.h: Likewise.
* linux/x86_64/signalent2.h: Likewise.
* linux/x86_64/syscallent2.h: Likewise.

Signed-off-by: H.J. Lu <hongjiu.lu@intel.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/syscall.c b/syscall.c
index 7f3ff34..44964bc 100644
--- a/syscall.c
+++ b/syscall.c
@@ -261,12 +261,18 @@
 		return;
 	tcp->currpers = personality;
 
-# if defined(POWERPC64) || defined(X86_64)
+# 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]);
+	}
 # endif
 }
 #endif
@@ -622,7 +628,7 @@
 
 #if defined(I386)
 struct pt_regs i386_regs;
-#elif defined(X86_64)
+#elif defined(X86_64) || defined(X32)
 /*
  * On 32 bits, pt_regs and user_regs_struct are the same,
  * but on 64 bits, user_regs_struct has six more fields:
@@ -795,7 +801,14 @@
 	if (ptrace(PTRACE_GETREGS, tcp->pid, NULL, (long) &i386_regs) < 0)
 		return -1;
 	scno = i386_regs.orig_eax;
-#elif defined(X86_64)
+#elif defined(X86_64) || defined(X32)
+# ifndef __X32_SYSCALL_BIT
+#  define __X32_SYSCALL_BIT	0x40000000
+# endif
+# ifndef __X32_SYSCALL_MASK
+#  define __X32_SYSCALL_MASK	__X32_SYSCALL_BIT
+# endif
+
 	int currpers;
 	if (ptrace(PTRACE_GETREGS, tcp->pid, NULL, (long) &x86_64_regs) < 0)
 		return -1;
@@ -804,10 +817,18 @@
 	/* Check CS register value. On x86-64 linux it is:
 	 *	0x33	for long mode (64 bit)
 	 *	0x23	for compatibility mode (32 bit)
+	 * Check DS register value. On x86-64 linux it is:
+	 *	0x2b	for x32 mode (x86-64 in 32 bit)
 	 */
 	switch (x86_64_regs.cs) {
 		case 0x23: currpers = 1; break;
-		case 0x33: currpers = 0; break;
+		case 0x33:
+			if (x86_64_regs.ds == 0x2b) {
+				currpers = 2;
+				scno &= ~__X32_SYSCALL_MASK;
+			} else
+				currpers = 0;
+			break;
 		default:
 			fprintf(stderr, "Unknown value CS=0x%08X while "
 				 "detecting personality of process "
@@ -846,7 +867,16 @@
 			break;
 	}
 # endif
+# ifdef X32
+	if (currpers == 0 || currpers == 1) {
+		fprintf(stderr, "syscall_%lu (...) in unsupported %s "
+			"mode of process PID=%d\n", scno,
+			currpers == 0 ? "64-bit" : "32-bit", tcp->pid);
+		return 0;
+	}
+# else
 	update_personality(tcp, currpers);
+# endif
 #elif defined(IA64)
 #	define IA64_PSR_IS	((long)1 << 34)
 	if (upeek(tcp, PT_CR_IPSR, &psr) >= 0)
@@ -1096,7 +1126,7 @@
 			fprintf(stderr, "not a syscall entry (eax = %ld)\n", i386_regs.eax);
 		return 0;
 	}
-#elif defined(X86_64)
+#elif defined(X86_64) || defined(X32)
 	{
 		long rax = x86_64_regs.rax;
 		if (current_personality == 1)
@@ -1385,16 +1415,24 @@
 	for (i = 0; i < nargs; ++i)
 		if (upeek(tcp, REG_GENERAL(syscall_regs[i]), &tcp->u_arg[i]) < 0)
 			return -1;
-#elif defined(X86_64)
+#elif defined(X86_64) || defined(X32)
 	(void)i;
 	(void)nargs;
-	if (current_personality == 0) { /* x86-64 ABI */
+	if (current_personality != 1) { /* x86-64 or x32 ABI */
 		tcp->u_arg[0] = x86_64_regs.rdi;
 		tcp->u_arg[1] = x86_64_regs.rsi;
 		tcp->u_arg[2] = x86_64_regs.rdx;
 		tcp->u_arg[3] = x86_64_regs.r10;
 		tcp->u_arg[4] = x86_64_regs.r8;
 		tcp->u_arg[5] = x86_64_regs.r9;
+#  ifdef X32
+		tcp->ext_arg[0] = x86_64_regs.rdi;
+		tcp->ext_arg[1] = x86_64_regs.rsi;
+		tcp->ext_arg[2] = x86_64_regs.rdx;
+		tcp->ext_arg[3] = x86_64_regs.r10;
+		tcp->ext_arg[4] = x86_64_regs.r8;
+		tcp->ext_arg[5] = x86_64_regs.r9;
+#  endif
 	} else { /* i386 ABI */
 		/* Sign-extend lower 32 bits */
 		tcp->u_arg[0] = (long)(int)x86_64_regs.rbx;
@@ -1569,7 +1607,7 @@
 #elif defined(I386)
 	if (ptrace(PTRACE_GETREGS, tcp->pid, NULL, (long) &i386_regs) < 0)
 		return -1;
-#elif defined(X86_64)
+#elif defined(X86_64) || defined(X32)
 	if (ptrace(PTRACE_GETREGS, tcp->pid, NULL, (long) &x86_64_regs) < 0)
 		return -1;
 #elif defined(IA64)
@@ -1701,13 +1739,16 @@
 	else {
 		tcp->u_rval = i386_regs.eax;
 	}
-#elif defined(X86_64)
+#elif defined(X86_64) || defined(X32)
 	if (check_errno && is_negated_errno(x86_64_regs.rax)) {
 		tcp->u_rval = -1;
 		u_error = -x86_64_regs.rax;
 	}
 	else {
 		tcp->u_rval = x86_64_regs.rax;
+# if defined(X32)
+		tcp->u_lrval = x86_64_regs.rax;
+# endif
 	}
 #elif defined(IA64)
 	if (ia32) {