Fix is_negated_errno() check for X32

X32's return value is 64-bit. We were truncating it to 32-bit long
before checking for -errno.

* syscall.c (is_negated_errno_x32): New function.
(get_error): Use is_negated_errno_x32 for X32 architecture.

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/syscall.c b/syscall.c
index f403c44..c4e1993 100644
--- a/syscall.c
+++ b/syscall.c
@@ -1946,6 +1946,24 @@
 	return val > max;
 }
 
+#if defined(X32)
+static inline int
+is_negated_errno_x32(unsigned long long val)
+{
+	unsigned long long max = -(long long) nerrnos;
+	/*
+	 * 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;
+	}
+	return val > max;
+}
+#endif
+
 /* Returns:
  * 1: ok, continue in trace_syscall_exiting().
  * -1: error, trace_syscall_exiting() should print error indicator
@@ -1976,16 +1994,23 @@
 	else {
 		tcp->u_rval = i386_regs.eax;
 	}
-#elif defined(X86_64) || defined(X32)
+#elif defined(X86_64)
 	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)
+	}
+#elif defined(X32)
+	/* Careful: is_negated_errno() works only on longs */
+	if (check_errno && is_negated_errno_x32(x86_64_regs.rax)) {
+		tcp->u_rval = -1;
+		u_error = -x86_64_regs.rax;
+	}
+	else {
+		tcp->u_rval = x86_64_regs.rax; /* truncating */
 		tcp->u_lrval = x86_64_regs.rax;
-# endif
 	}
 #elif defined(IA64)
 	if (ia32) {