callsignalhandlerwrapper: improve unwinding (dotnet/coreclr#13566)
authorKonstantin Baladurin <k.baladurin@partner.samsung.com>
Wed, 30 Aug 2017 00:01:32 +0000 (03:01 +0300)
committerJan Vorlicek <janvorli@microsoft.com>
Wed, 30 Aug 2017 00:01:32 +0000 (02:01 +0200)
* Fix free_stack macro for ARM

free_stack shouldn't contain unwinder annotations for stack adjustment

* callsignalhandlerwrapper: improve unwinding

For linux: make CallSignalHandlerWrapper's frame sigtramp frame
for gdb and lldb:
- Save all registers on stack
- Add sigreturn syscall after call of signal_handler_worker

It provides ability for gdb and lldb unwind frame with invalid pc
(due to jump to invalid addresss).

For non linux:
- Save r11 on stack as it also can be used as frame pointer
- Set instruction set flag (thumb / arm) for saved pc. It is
  necessary for gdb because it uses lr's lsb to determine
  function mode

Commit migrated from https://github.com/dotnet/coreclr/commit/79c97d9d008e7ffc5208e0932c878357b3013ebd

src/coreclr/src/pal/inc/unixasmmacrosarm.inc
src/coreclr/src/pal/src/arch/arm/callsignalhandlerwrapper.S
src/coreclr/src/pal/src/arch/arm/signalhandlerhelper.cpp

index 76ea5eb..e4a3658 100644 (file)
@@ -72,7 +72,6 @@ C_FUNC(\Name\()_End):
 
 .macro free_stack Size
         add sp, sp, (\Size)
-        .pad #-(\Size)
 .endm
 
 .macro POP_CALLEE_SAVED_REGISTERS
index a8aa9b4..b9398d6 100644 (file)
@@ -18,13 +18,27 @@ C_FUNC(SignalHandlerWorkerReturnOffset\Alignment):
 // address set to SignalHandlerWorkerReturn during SIGSEGV handling.
 // It enables the unwinder to unwind stack from the handling code to the actual failure site.
 NESTED_ENTRY CallSignalHandlerWrapper\Alignment, _TEXT, NoHandler
-    alloc_stack     (8 + \Alignment) // red zone + alignment
-    PROLOG_PUSH  "{r7, lr}"
+#ifndef __linux__
+__StackAllocationSize = (8 + 4 + \Alignment) // red zone + alignment
+    alloc_stack __StackAllocationSize
+    PROLOG_PUSH "{r7, r11, lr}"
     bl      EXTERNAL_C_FUNC(signal_handler_worker)
 LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment):
-    EPILOG_POP "{r7, lr}"
-    free_stack (8 + \Alignment)    
+    EPILOG_POP "{r7, r11, lr}"
+    free_stack __StackAllocationSize
     bx      lr
+#else
+    // This unwind information is needed for lldb gdb doesn't use it and tries
+    // to read all registers from $sp + 12
+    .save {r0-r15}
+    .pad #12
+    bl      EXTERNAL_C_FUNC(signal_handler_worker)
+LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment):
+    // Following instruction are needed to say gdb that this frame is SIGTRAMP_FRAME
+    // and it can restore all registers from stack
+    mov.w r7, #119
+    svc 0
+#endif
 NESTED_END CallSignalHandlerWrapper\Alignment, _TEXT
 
 .endm
index e1ad460..4efecfc 100644 (file)
@@ -45,11 +45,22 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context,
     // preserve 8 bytes long red zone and align stack pointer
     size_t* sp = (size_t*)ALIGN_DOWN(faultSp - 8, 8);
 
+#ifndef __linux__
+    size_t cpsr = (size_t)MCREG_Cpsr(ucontext->uc_mcontext);
+
     // Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction
-    // pushed LR
-    *--sp = (size_t)MCREG_Pc(ucontext->uc_mcontext);
+    // align
+    --sp;
+    // pushed LR with correct mode bit
+    *--sp = (size_t)MCREG_Pc(ucontext->uc_mcontext) | ((cpsr & (1 << 5)) >> 5);
     // pushed frame pointer
+    *--sp = (size_t)MCREG_R11(ucontext->uc_mcontext);
     *--sp = (size_t)MCREG_R7(ucontext->uc_mcontext); 
+#else
+    size_t size = ALIGN_UP(sizeof(ucontext->uc_mcontext), 8);
+    sp -= size / sizeof(size_t);
+    *(sigcontext *)sp = ucontext->uc_mcontext;
+#endif
 
     // Switch the current context to the signal_handler_worker and the original stack
     CONTEXT context2;