libphobos: Fix backtraces in Fibers on AArch64.
authorIain Buclaw <ibuclaw@gcc.gnu.org>
Mon, 26 Nov 2018 17:27:34 +0000 (17:27 +0000)
committerIain Buclaw <ibuclaw@gcc.gnu.org>
Mon, 26 Nov 2018 17:27:34 +0000 (17:27 +0000)
When throwing an Exception in the Fiber the backtrace generation
crashes.  This happens because backtrace does not func the stack bottom.
Using '.cfi_undefined x30' tells the debug info that the value in the lr
is unknown, which seems to be the nicest way to stop the unwinder.
Setting x30 to 0 is another option, however it still creates one invalid
frame in gdb, so the .cfi variant is used here instead.

Backport from upstream druntime 2.083.

Reviewed-on: https://github.com/dlang/druntime/pull/2308

From-SVN: r266470

libphobos/libdruntime/core/thread.d
libphobos/libdruntime/core/threadasm.S

index b5244711646c01cc71e25d8c0b532547c4ef9676..ff15d066a4968c4c8624eb60612b7b36dd30dfc3 100644 (file)
@@ -3582,6 +3582,15 @@ private
             version = AsmExternal;
         }
     }
+    else version (AArch64)
+    {
+        version (Posix)
+        {
+            version = AsmAArch64_Posix;
+            version = AsmExternal;
+            version = AlignFiberStackTo16Byte;
+        }
+    }
     else version (ARM)
     {
         version (Posix)
@@ -3673,7 +3682,11 @@ private
 
   // Look above the definition of 'class Fiber' for some information about the implementation of this routine
   version (AsmExternal)
-    extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc;
+  {
+      extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc;
+      version (AArch64)
+          extern (C) void fiber_trampoline() nothrow;
+  }
   else
     extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc
     {
@@ -4909,6 +4922,29 @@ private:
             pstack -= ABOVE;
             *cast(size_t*)(pstack - SZ_RA) = cast(size_t)&fiber_entryPoint;
         }
+        else version (AsmAArch64_Posix)
+        {
+            // Like others, FP registers and return address (lr) are kept
+            // below the saved stack top (tstack) to hide from GC scanning.
+            // fiber_switchContext expects newp sp to look like this:
+            //   19: x19
+            //   ...
+            //    9: x29 (fp)  <-- newp tstack
+            //    8: x30 (lr)  [&fiber_entryPoint]
+            //    7: d8
+            //   ...
+            //    0: d15
+
+            version (StackGrowsDown) {}
+            else
+                static assert(false, "Only full descending stacks supported on AArch64");
+
+            // Only need to set return address (lr).  Everything else is fine
+            // zero initialized.
+            pstack -= size_t.sizeof * 11;    // skip past x19-x29
+            push(cast(size_t) &fiber_trampoline); // see threadasm.S for docs
+            pstack += size_t.sizeof;         // adjust sp (newp) above lr
+        }
         else version (AsmARM_Posix)
         {
             /* We keep the FP registers and the return address below
index 02bd756de3cad2281235f6a38039d8dd5d701cdd..140e5f9a9e4f30a089a01c3169d9766c34d05714 100644 (file)
@@ -487,6 +487,7 @@ fiber_switchContext:
  */
         .text
         .global CSYM(fiber_switchContext)
+        .type   fiber_switchContext, %function
         .p2align  2
 CSYM(fiber_switchContext):
         stp     d15, d14, [sp, #-20*8]!
@@ -518,6 +519,29 @@ CSYM(fiber_switchContext):
         ldp     d15, d14, [sp], #20*8
         ret
 
+
+/**
+ * When generating any kind of backtrace (gdb, exception handling) for
+ * a function called in a Fiber, we need to tell the unwinder to stop
+ * at our Fiber main entry point, i.e. we need to mark the bottom of
+ * the call stack. This can be done by clearing the link register lr
+ * prior to calling fiber_entryPoint (i.e. in fiber_switchContext) or
+ * using a .cfi_undefined directive for the link register in the
+ * Fiber entry point. cfi_undefined seems to yield better results in gdb.
+ * Unfortunately we can't place it into fiber_entryPoint using inline
+ * asm, so we use this trampoline instead.
+ */
+        .text
+        .global CSYM(fiber_trampoline)
+        .p2align  2
+        .type   fiber_trampoline, %function
+CSYM(fiber_trampoline):
+        .cfi_startproc
+        .cfi_undefined x30
+        // fiber_entryPoint never returns
+        bl fiber_entryPoint
+        .cfi_endproc
+
 #elif defined(__MINGW32__)
 /************************************************************************************
  * GDC MinGW ASM BITS