Implement setjmp cookies on MIPS and MIPS64

Bug: http://b/23942752
Change-Id: Ie58892a97b5075d30d7607667251007cda99d38c
diff --git a/libc/arch-mips/bionic/setjmp.S b/libc/arch-mips/bionic/setjmp.S
index 3ef0f11..73002e8 100644
--- a/libc/arch-mips/bionic/setjmp.S
+++ b/libc/arch-mips/bionic/setjmp.S
@@ -132,10 +132,9 @@
 /*     	field:		byte offset:	size:						*/
 /*     	dynam filler	(0*4)		   0-4 bytes of rounddown filler, DON'T TOUCH!!
 						often overlays user storage!!		*/
-#define	SC_MAGIC_OFFSET	(1*4)		/* 4 bytes, identify jmpbuf, first actual field */
-#define	SC_FLAG_OFFSET	(2*4)		/* 4 bytes, savesigs flag */
-#define	SC_FPSR_OFFSET	(3*4)		/* 4 bytes, floating point control/status reg */
+#define	SC_FPSR_OFFSET	(1*4)		/* 4 bytes, floating point control/status reg */
 /* following fields are 8-byte aligned */
+#define	SC_FLAG_OFFSET	(2*4)		/* 8 bytes, cookie and savesigs flag, first actual field  */
 #define	SC_MASK_OFFSET	(4*4)		/* 16 bytes, mips32/mips64 version of sigset_t */
 #define	SC_SPARE_OFFSET	(8*4)		/* 8 bytes, reserved for future uses */
 
@@ -166,6 +165,16 @@
 #error _JBLEN is too small
 #endif
 
+.macro m_mangle_reg_and_store reg, cookie, temp, offset
+	xor	\temp, \reg, \cookie
+	REG_S	\temp, \offset
+.endm
+
+.macro m_unmangle_reg_and_load reg, cookie, temp, offset
+	REG_L	\temp, \offset
+	xor	\reg, \temp, \cookie
+.endm
+
 /*
  *
  *  GPOFF and FRAMESIZE must be the same for all setjmp/longjmp routines
@@ -190,36 +199,46 @@
 	li	t0, ~7
 	and	a0, t0				# round jmpbuf addr DOWN to 8-byte boundary
 #endif
-	sw	a1, SC_FLAG_OFFSET(a0)		# save savesigs flag
-	beqz	a1, 1f				# do saving of signal mask?
-
 	REG_S	ra, RAOFF(sp)			# spill state
 	REG_S	a0, A0OFF(sp)
+
+	# get the cookie and store it along with the signal flag.
+	move	a0, a1
+	jal	__bionic_setjmp_cookie_get
+	REG_L	a0, A0OFF(sp)
+
+	REG_S	v0, SC_FLAG_OFFSET(a0)		# save cookie and savesigs flag
+	andi	t0, v0, 1			# extract savesigs flag
+
+	beqz	t0, 1f				# do saving of signal mask?
+
 	# call sigprocmask(int how ignored, sigset_t* null, sigset_t* SC_MASK(a0)):
 	LA	a2, SC_MASK_OFFSET(a0)		# gets current signal mask
 	li	a0, 0				# how; ignored when new mask is null
 	li	a1, 0				# null new mask
 	jal	sigprocmask			# get current signal mask
 	REG_L	a0, A0OFF(sp)
-	REG_L	ra, RAOFF(sp)
 1:
-	li	v0, 0xACEDBADE			# sigcontext magic number
-	sw	v0, SC_MAGIC_OFFSET(a0)
+	REG_L	gp, GPOFF(sp)			# restore spills
+	REG_L	ra, RAOFF(sp)
+	REG_L	t0, SC_FLAG_OFFSET(a0)		# move cookie to temp reg
+
 	# callee-saved long-sized regs:
-	REG_S	ra, SC_REGS+0*REGSZ(a0)
-	REG_S	s0, SC_REGS+1*REGSZ(a0)
-	REG_S	s1, SC_REGS+2*REGSZ(a0)
-	REG_S	s2, SC_REGS+3*REGSZ(a0)
-	REG_S	s3, SC_REGS+4*REGSZ(a0)
-	REG_S	s4, SC_REGS+5*REGSZ(a0)
-	REG_S	s5, SC_REGS+6*REGSZ(a0)
-	REG_S	s6, SC_REGS+7*REGSZ(a0)
-	REG_S	s7, SC_REGS+8*REGSZ(a0)
-	REG_S	s8, SC_REGS+9*REGSZ(a0)
-	REG_L	v0, GPOFF(sp)
-	REG_S	v0, SC_REGS+10*REGSZ(a0)	# save gp
-	PTR_ADDU v0, sp, FRAMESZ
-	REG_S	v0, SC_REGS+11*REGSZ(a0)	# save orig sp
+	PTR_ADDU v1, sp, FRAMESZ		# save orig sp
+
+	# m_mangle_reg_and_store reg, cookie, temp, offset
+	m_mangle_reg_and_store	ra, t0, t1, SC_REGS+0*REGSZ(a0)
+	m_mangle_reg_and_store	s0, t0, t2, SC_REGS+1*REGSZ(a0)
+	m_mangle_reg_and_store	s1, t0, t3, SC_REGS+2*REGSZ(a0)
+	m_mangle_reg_and_store	s2, t0, t1, SC_REGS+3*REGSZ(a0)
+	m_mangle_reg_and_store	s3, t0, t2, SC_REGS+4*REGSZ(a0)
+	m_mangle_reg_and_store	s4, t0, t3, SC_REGS+5*REGSZ(a0)
+	m_mangle_reg_and_store	s5, t0, t1, SC_REGS+6*REGSZ(a0)
+	m_mangle_reg_and_store	s6, t0, t2, SC_REGS+7*REGSZ(a0)
+	m_mangle_reg_and_store	s7, t0, t3, SC_REGS+8*REGSZ(a0)
+	m_mangle_reg_and_store	s8, t0, t1, SC_REGS+9*REGSZ(a0)
+	m_mangle_reg_and_store	gp, t0, t2, SC_REGS+10*REGSZ(a0)
+	m_mangle_reg_and_store	v1, t0, t3, SC_REGS+11*REGSZ(a0)
 
 	cfc1	v0, $31
 
@@ -288,36 +307,41 @@
 	li	t0, ~7
 	and	a0, t0				# round jmpbuf addr DOWN to 8-byte boundary
 #endif
-	lw	v0, SC_MAGIC_OFFSET(a0)
-	li	t0, 0xACEDBADE
-	bne	v0, t0, longjmp_botch		# jump if error
 
-	lw	t0, SC_FLAG_OFFSET(a0)		# get savesigs flag
+	move	s1, a1				# temp spill
+	move	s0, a0
+
+	# extract savesigs flag
+	REG_L	s2, SC_FLAG_OFFSET(s0)
+	andi	t0, s2, 1
 	beqz	t0, 1f				# restore signal mask?
 
-	REG_S	a1, A1OFF(sp)			# temp spill
-	REG_S	a0, A0OFF(sp)
-        # call sigprocmask(int how SIG_SETMASK, sigset_t* SC_MASK(a0), sigset_t* null):
-	LA	a1, SC_MASK_OFFSET(a0)		# signals being restored
+	# call sigprocmask(int how SIG_SETMASK, sigset_t* SC_MASK(a0), sigset_t* null):
+	LA	a1, SC_MASK_OFFSET(s0)		# signals being restored
 	li	a0, 3				# mips SIG_SETMASK
 	li	a2, 0				# null
 	jal	sigprocmask			# restore signal mask
-	REG_L	a0, A0OFF(sp)
-	REG_L	a1, A1OFF(sp)
 1:
+	move	t0, s2				# get cookie to temp reg
+	move	a1, s1
+	move	a0, s0
+
 	# callee-saved long-sized regs:
-	REG_L	ra, SC_REGS+0*REGSZ(a0)
-	REG_L	s0, SC_REGS+1*REGSZ(a0)
-	REG_L	s1, SC_REGS+2*REGSZ(a0)
-	REG_L	s2, SC_REGS+3*REGSZ(a0)
-	REG_L	s3, SC_REGS+4*REGSZ(a0)
-	REG_L	s4, SC_REGS+5*REGSZ(a0)
-	REG_L	s5, SC_REGS+6*REGSZ(a0)
-	REG_L	s6, SC_REGS+7*REGSZ(a0)
-	REG_L	s7, SC_REGS+8*REGSZ(a0)
-	REG_L	s8, SC_REGS+9*REGSZ(a0)
-	REG_L	gp, SC_REGS+10*REGSZ(a0)
-	REG_L	sp, SC_REGS+11*REGSZ(a0)
+
+	# m_unmangle_reg_and_load reg, cookie, temp, offset
+	# don't restore gp yet, old value is needed for cookie_check call
+	m_unmangle_reg_and_load ra, t0, t1, SC_REGS+0*REGSZ(a0)
+	m_unmangle_reg_and_load s0, t0, t2, SC_REGS+1*REGSZ(a0)
+	m_unmangle_reg_and_load s1, t0, t3, SC_REGS+2*REGSZ(a0)
+	m_unmangle_reg_and_load s2, t0, t1, SC_REGS+3*REGSZ(a0)
+	m_unmangle_reg_and_load s3, t0, t2, SC_REGS+4*REGSZ(a0)
+	m_unmangle_reg_and_load s4, t0, t3, SC_REGS+5*REGSZ(a0)
+	m_unmangle_reg_and_load s5, t0, t1, SC_REGS+6*REGSZ(a0)
+	m_unmangle_reg_and_load s6, t0, t2, SC_REGS+7*REGSZ(a0)
+	m_unmangle_reg_and_load s7, t0, t3, SC_REGS+8*REGSZ(a0)
+	m_unmangle_reg_and_load s8, t0, t1, SC_REGS+9*REGSZ(a0)
+	m_unmangle_reg_and_load v1, t0, t2, SC_REGS+10*REGSZ(a0)
+	m_unmangle_reg_and_load sp, t0, t3, SC_REGS+11*REGSZ(a0)
 
 	lw	v0, SC_FPSR_OFFSET(a0)
 	ctc1	v0, $31			# restore old fr mode before fp values
@@ -341,15 +365,22 @@
 	l.d	$f28, SC_FPREGS+4*REGSZ_FP(a0)
 	l.d	$f30, SC_FPREGS+5*REGSZ_FP(a0)
 #endif
-	bne	a1, zero, 1f
-	li	a1, 1			# never return 0!
-1:
-	move	v0, a1
-	j	ra			# return to setjmp call site
 
-longjmp_botch:
-	jal	longjmperror
-	jal	abort
+	# check cookie
+	PTR_SUBU sp, FRAMESZ
+	REG_S	v1, GPOFF(sp)
+	REG_S	ra, RAOFF(sp)
+	REG_S	a1, A1OFF(sp)
+	move	a0, t0
+	jal	__bionic_setjmp_cookie_check
+	REG_L	gp, GPOFF(sp)
+	REG_L	ra, RAOFF(sp)
+	REG_L	a1, A1OFF(sp)
+	PTR_ADDU sp, FRAMESZ
+
+	sltiu	t0, a1, 1		# never return 0!
+	xor	v0, a1, t0
+	j	ra			# return to setjmp call site
 END(siglongjmp)
 
 ALIAS_SYMBOL(longjmp, siglongjmp)
diff --git a/tests/setjmp_test.cpp b/tests/setjmp_test.cpp
index 944dac8..c75ab51 100644
--- a/tests/setjmp_test.cpp
+++ b/tests/setjmp_test.cpp
@@ -221,15 +221,24 @@
 #define __JB_SIGFLAG 7
 #elif defined(__x86_64)
 #define __JB_SIGFLAG 8
+#elif defined(__mips__) && defined(__LP64__)
+#define __JB_SIGFLAG 1
+#elif defined(__mips__)
+#define __JB_SIGFLAG 2
 #endif
 
 TEST(setjmp, setjmp_cookie) {
-#if !defined(__mips__)
   jmp_buf jb;
   int value = setjmp(jb);
   ASSERT_EQ(0, value);
 
+#if defined(__mips__) && !defined(__LP64__)
+  // round address to 8-byte boundry
+  uintptr_t jb_aligned = reinterpret_cast<uintptr_t>(jb) & ~7L;
+  long* sigflag = reinterpret_cast<long*>(jb_aligned) + __JB_SIGFLAG;
+#else
   long* sigflag = reinterpret_cast<long*>(jb) + __JB_SIGFLAG;
+#endif
 
   // Make sure there's actually a cookie.
   EXPECT_NE(0, *sigflag & ~1);
@@ -237,5 +246,4 @@
   // Wipe it out
   *sigflag &= 1;
   EXPECT_DEATH(longjmp(jb, 0), "");
-#endif
 }