[libsanitizer] SanitizerCommon: fixes for unwinding & backtrace on SPARC

Message ID 1592339.J6cibDRgSW@polaris
State New
Headers show
Series
  • [libsanitizer] SanitizerCommon: fixes for unwinding & backtrace on SPARC
Related show

Commit Message

Eric Botcazou March 13, 2019, 9:20 a.m.
This patch contains various fixes for the unwinding and backtrace machinery 
on the SPARC, which doesn't work correctly in some cases.  It only affects the 
SPARC ports and has been tested on SPARC/Solaris and SPARC64/Linux.

It merges r355965 of the LLVM repository.  Installed on the mainline.


2019-03-13  Eric Botcazou  <ebotcazou@adacore.com>

	PR sanitizer/80953
	Merge from LLVM revision 355965
	* sanitizer_common/sanitizer_linux.cc (GetWriteFlag): Implement for
	SPARC/Linux.
	(GetPcSpBp): Likewise.
	* sanitizer_common/sanitizer_stacktrace.cc (GetNextInstructionPc):
	Adjust for SPARC.
	* sanitizer_common/sanitizer_stacktrace.h (SANITIZER_CAN_FAST_UNWIND):
	Define to 1 for SPARC.
	* sanitizer_common/sanitizer_stacktrace_sparc.cc: Rewrite.
	* sanitizer_common/sanitizer_unwind_linux_libcdep.cc (SlowUnwindStack):
	Adjust the PC address for SPARC with GCC.

-- 
Eric Botcazou

Patch

Index: sanitizer_common/sanitizer_linux.cc
===================================================================
--- sanitizer_common/sanitizer_linux.cc	(revision 269546)
+++ sanitizer_common/sanitizer_linux.cc	(working copy)
@@ -1848,10 +1850,20 @@  SignalContext::WriteFlag SignalContext::
   u64 esr;
   if (!Aarch64GetESR(ucontext, &esr)) return UNKNOWN;
   return esr & ESR_ELx_WNR ? WRITE : READ;
-#elif SANITIZER_SOLARIS && defined(__sparc__)
+#elif defined(__sparc__)
   // Decode the instruction to determine the access type.
   // From OpenSolaris $SRC/uts/sun4/os/trap.c (get_accesstype).
+# if SANITIZER_SOLARIS
   uptr pc = ucontext->uc_mcontext.gregs[REG_PC];
+# else
+  // Historical BSDism here.
+  struct sigcontext *scontext = (struct sigcontext *)context;
+#  if defined(__arch64__)
+  uptr pc = scontext->sigc_regs.tpc;
+#  else
+  uptr pc = scontext->si_regs.pc;
+#  endif
+# endif
   u32 instr = *(u32 *)pc;
   return (instr >> 21) & 1 ? WRITE: READ;
 #else
@@ -1942,28 +1954,27 @@  static void GetPcSpBp(void *context, upt
   // pointer, but GCC always uses r31 when we need a frame pointer.
   *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
 #elif defined(__sparc__)
-  ucontext_t *ucontext = (ucontext_t*)context;
-  uptr *stk_ptr;
-# if defined(__sparcv9) || defined (__arch64__)
-# ifndef MC_PC
-#  define MC_PC REG_PC
-# endif
-# ifndef MC_O6
-#  define MC_O6 REG_O6
+# if defined(__arch64__) || defined(__sparcv9)
+#  define STACK_BIAS 2047
+# else
+#  define STACK_BIAS 0
 # endif
 # if SANITIZER_SOLARIS
-#  define mc_gregs gregs
-# endif
-  *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
-  *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
-  stk_ptr = (uptr *) (*sp + 2047);
-  *bp = stk_ptr[15];
-# else
+  ucontext_t *ucontext = (ucontext_t*)context;
   *pc = ucontext->uc_mcontext.gregs[REG_PC];
-  *sp = ucontext->uc_mcontext.gregs[REG_O6];
-  stk_ptr = (uptr *) *sp;
-  *bp = stk_ptr[15];
+  *sp = ucontext->uc_mcontext.gregs[REG_O6] + STACK_BIAS;
+# else
+  // Historical BSDism here.
+  struct sigcontext *scontext = (struct sigcontext *)context;
+#  if defined(__arch64__)
+  *pc = scontext->sigc_regs.tpc;
+  *sp = scontext->sigc_regs.u_regs[14] + STACK_BIAS;
+#  else
+  *pc = scontext->si_regs.pc;
+  *sp = scontext->si_regs.u_regs[14];
+#  endif
 # endif
+  *bp = (uptr) ((uhwptr *) *sp)[14] + STACK_BIAS;
 #elif defined(__mips__)
   ucontext_t *ucontext = (ucontext_t*)context;
   *pc = ucontext->uc_mcontext.pc;
Index: sanitizer_common/sanitizer_stacktrace.cc
===================================================================
--- sanitizer_common/sanitizer_stacktrace.cc	(revision 269546)
+++ sanitizer_common/sanitizer_stacktrace.cc	(working copy)
@@ -16,10 +16,9 @@ 
 namespace __sanitizer {
 
 uptr StackTrace::GetNextInstructionPc(uptr pc) {
-#if defined(__mips__)
+#if defined(__sparc__) || defined(__mips__)
   return pc + 8;
-#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \
-    defined(__aarch64__)
+#elif defined(__powerpc__) || defined(__arm__) || defined(__aarch64__)
   return pc + 4;
 #else
   return pc + 1;
Index: sanitizer_common/sanitizer_stacktrace.h
===================================================================
--- sanitizer_common/sanitizer_stacktrace.h	(revision 269546)
+++ sanitizer_common/sanitizer_stacktrace.h	(working copy)
@@ -17,7 +17,7 @@  namespace __sanitizer {
 
 static const u32 kStackTraceMax = 256;
 
-#if defined(__sparc__) || (SANITIZER_LINUX && defined(__mips__))
+#if SANITIZER_LINUX && defined(__mips__)
 # define SANITIZER_CAN_FAST_UNWIND 0
 #elif SANITIZER_WINDOWS
 # define SANITIZER_CAN_FAST_UNWIND 0
Index: sanitizer_common/sanitizer_stacktrace_sparc.cc
===================================================================
--- sanitizer_common/sanitizer_stacktrace_sparc.cc	(revision 269546)
+++ sanitizer_common/sanitizer_stacktrace_sparc.cc	(working copy)
@@ -11,9 +11,13 @@ 
 // Implemention of fast stack unwinding for Sparc.
 //===----------------------------------------------------------------------===//
 
-// This file is ported to Sparc v8, but it should be easy to port to
-// Sparc v9.
-#if defined(__sparcv8__) || defined(__sparcv8) || defined(__sparc_v8__)
+#if defined(__sparc__)
+
+#if defined(__arch64__) || defined(__sparcv9)
+#define STACK_BIAS 2047
+#else
+#define STACK_BIAS 0
+#endif
 
 #include "sanitizer_common.h"
 #include "sanitizer_stacktrace.h"
@@ -24,34 +28,59 @@  void BufferedStackTrace::FastUnwindStack
                                          uptr stack_bottom, u32 max_depth) {
   const uptr kPageSize = GetPageSizeCached();
   CHECK_GE(max_depth, 2);
+#if defined(__GNUC__)
+  // __builtin_return_address returns the address of the call instruction
+  // on the SPARC and not the return address, so we need to compensate.
+  trace_buffer[0] = GetNextInstructionPc(pc);
+#else
   trace_buffer[0] = pc;
+#endif
   size = 1;
   if (stack_top < 4096) return;  // Sanity check for stack top.
   // Flush register windows to memory
+#if defined(__sparc_v9__) || defined(__sparcv9__) || defined(__sparcv9)
+  asm volatile("flushw" ::: "memory");
+#else
   asm volatile("ta 3" ::: "memory");
-  uhwptr *frame = (uhwptr*)bp;
+#endif
+  // On the SPARC, the return address is not in the frame, it is in a
+  // register.  There is no way to access it off of the current frame
+  // pointer, but it can be accessed off the previous frame pointer by
+  // reading the value from the register window save area.
+  uptr prev_bp = GET_CURRENT_FRAME();
+  uptr next_bp = prev_bp;
+  unsigned int i = 0;
+  while (next_bp != bp &&
+	 IsAligned(next_bp, sizeof(uhwptr)) &&
+	 i++ < 8) {
+    prev_bp = next_bp;
+    next_bp = (uptr) ((uhwptr *) next_bp)[14] + STACK_BIAS;
+  }
+  if (next_bp == bp)
+    bp = prev_bp;
   // Lowest possible address that makes sense as the next frame pointer.
   // Goes up as we walk the stack.
   uptr bottom = stack_bottom;
   // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
-  while (IsValidFrame((uptr)frame, stack_top, bottom) &&
-         IsAligned((uptr)frame, sizeof(*frame)) &&
+  while (IsValidFrame(bp, stack_top, bottom) &&
+         IsAligned(bp, sizeof(uhwptr)) &&
          size < max_depth) {
-    uhwptr pc1 = frame[15];
+    uhwptr pc1 = ((uhwptr *)bp)[15];
     // Let's assume that any pointer in the 0th page is invalid and
     // stop unwinding here.  If we're adding support for a platform
     // where this isn't true, we need to reconsider this check.
     if (pc1 < kPageSize)
       break;
     if (pc1 != pc) {
-      trace_buffer[size++] = (uptr) pc1;
+      // %o7 contains the address of the call instruction and not the
+      // return address, so we need to compensate.
+      trace_buffer[size++] = GetNextInstructionPc((uptr) pc1);
     }
-    bottom = (uptr)frame;
-    frame = (uhwptr*)frame[14];
+    bottom = bp;
+    bp = (uptr) ((uhwptr *) bp)[14] + STACK_BIAS;
   }
 }
 
 }  // namespace __sanitizer
 
-#endif  // !defined(__sparcv8__) && !defined(__sparcv8) &&
-        // !defined(__sparc_v8__)
+#endif  // !defined(__sparc__)
Index: sanitizer_common/sanitizer_unwind_linux_libcdep.cc
===================================================================
--- sanitizer_common/sanitizer_unwind_linux_libcdep.cc	(revision 269546)
+++ sanitizer_common/sanitizer_unwind_linux_libcdep.cc	(working copy)
@@ -134,7 +134,13 @@  void BufferedStackTrace::SlowUnwindStack
   if (to_pop == 0 && size > 1)
     to_pop = 1;
   PopStackFrames(to_pop);
+#if defined(__GNUC__) && defined(__sparc__)
+  // __builtin_return_address returns the address of the call instruction
+  // on the SPARC and not the return address, so we need to compensate.
+  trace_buffer[0] = GetNextInstructionPc(pc);
+#else
   trace_buffer[0] = pc;
+#endif
 }
 
 void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,