summaryrefslogtreecommitdiff
path: root/packaging/0001-Linux-ARM-Fix-managed-breakpoints-13316.patch
blob: 0e5ba17ce10d17421325a9da5639ea89f0d74e65 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
From a181764876c53aab4e1842ad0af3a3d56d8142c7 Mon Sep 17 00:00:00 2001
From: Igor Kulaychuk <igor.kulaychuk@gmail.com>
Date: Tue, 15 Aug 2017 00:30:56 +0300
Subject: [PATCH 1/5] [Linux/ARM] Fix managed breakpoints (#13316)

* [Linux/ARM] Fix managed breakpoints

This commit introduces the following changes in order to enable
ICorDebug-based debuggers to use breakpoints on ARM Linux:

* Use 0xde01 as breakpoint instruction on ARM Linux.
  ARM reference recommends to use 0xdefe as a breakpoint instruction,
  but Linux kernel generates SIGILL for this instruction.
  The 0xde01 instruction causes the kernel to generate SIGTRAP.

* Fix SIGTRAP handling on ARM Linux.
  Unlike x86, when SIGTRAP happens on ARM Linux, the PC points
  at the break instruction. But the rest of the code expects
  that it points to an instruction after the break,
  so we adjust the PC at the start of HandleHardwareException().

* Enable ARM single stepping for PAL.
  Handle single stepping for PAL path the same way as for non-PAL path.
  Also enable ArmSingleStepper executable buffer by allocating it
  from system global loader executable heap.

* Hande ARM single step only when debugger is attached, fix comments and code style

* Pass existing Thread object to HandleArmSingleStep
---
 src/debug/inc/arm/primitives.h  |  4 ++++
 src/vm/arm/armsinglestepper.cpp | 12 ++++++++++--
 src/vm/arm/cgencpu.h            |  4 ++++
 src/vm/armsinglestepper.h       |  4 ++++
 src/vm/exceptionhandling.cpp    | 35 +++++++++++++++++++++++++++++++++++
 5 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/src/debug/inc/arm/primitives.h b/src/debug/inc/arm/primitives.h
index 0bac542..1cceeff 100644
--- a/src/debug/inc/arm/primitives.h
+++ b/src/debug/inc/arm/primitives.h
@@ -30,7 +30,11 @@ typedef DPTR(CORDB_ADDRESS_TYPE)    PTR_CORDB_ADDRESS_TYPE;
 #define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
 
 #define CORDbg_BREAK_INSTRUCTION_SIZE 2
+#ifdef __linux__
+#define CORDbg_BREAK_INSTRUCTION (USHORT)0xde01
+#else
 #define CORDbg_BREAK_INSTRUCTION (USHORT)0xdefe
+#endif
 
 inline CORDB_ADDRESS GetPatchEndAddr(CORDB_ADDRESS patchAddr)
 {
diff --git a/src/vm/arm/armsinglestepper.cpp b/src/vm/arm/armsinglestepper.cpp
index e000959..bfe8824 100644
--- a/src/vm/arm/armsinglestepper.cpp
+++ b/src/vm/arm/armsinglestepper.cpp
@@ -97,17 +97,25 @@ ArmSingleStepper::ArmSingleStepper()
 
 ArmSingleStepper::~ArmSingleStepper()
 {
-#if !defined(DACCESS_COMPILE) && !defined(FEATURE_PAL)
+#if !defined(DACCESS_COMPILE)
+#ifdef FEATURE_PAL
+    SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(m_rgCode, kMaxCodeBuffer * sizeof(WORD));
+#else
     DeleteExecutable(m_rgCode);
 #endif
+#endif
 }
 
 void ArmSingleStepper::Init()
 {
-#if !defined(DACCESS_COMPILE) && !defined(FEATURE_PAL)
+#if !defined(DACCESS_COMPILE)
     if (m_rgCode == NULL)
     {
+#ifdef FEATURE_PAL
+        m_rgCode = (WORD *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(kMaxCodeBuffer * sizeof(WORD)));
+#else
         m_rgCode = new (executable) WORD[kMaxCodeBuffer];
+#endif
     }
 #endif
 }
diff --git a/src/vm/arm/cgencpu.h b/src/vm/arm/cgencpu.h
index 6f128f6..2a369d8 100644
--- a/src/vm/arm/cgencpu.h
+++ b/src/vm/arm/cgencpu.h
@@ -566,7 +566,11 @@ public:
         // a reasonable breakpoint substitute (it's what DebugBreak uses). Bkpt #0, on the other hand, always
         // seems to flow directly to the kernel debugger (even if we ignore it there it doesn't seem to be
         // picked up by the user mode debugger).
+#ifdef __linux__
+        Emit16(0xde01);
+#else
         Emit16(0xdefe);
+#endif
     }
 
     void ThumbEmitMovConstant(ThumbReg dest, int constant)
diff --git a/src/vm/armsinglestepper.h b/src/vm/armsinglestepper.h
index 53a1019..8893525 100644
--- a/src/vm/armsinglestepper.h
+++ b/src/vm/armsinglestepper.h
@@ -88,7 +88,11 @@ private:
         kMaxCodeBuffer = 2 + 3 + 1, // WORD slots in our redirect buffer (2 for current instruction, 3 for
                                     // breakpoint instructions used to pad out slots in an IT block and one
                                     // for the final breakpoint)
+#ifdef __linux__
+        kBreakpointOp = 0xde01,     // Opcode for the breakpoint instruction used on ARM Linux
+#else
         kBreakpointOp = 0xdefe,     // Opcode for the breakpoint instruction used on CoreARM
+#endif
     };
 
     // Bit numbers of the condition flags in the CPSR.
diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp
index 2802f73..7ed4375 100644
--- a/src/vm/exceptionhandling.cpp
+++ b/src/vm/exceptionhandling.cpp
@@ -5176,6 +5176,38 @@ BOOL IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD e
         IsIPInMarkedJitHelper(controlPc));
 }
 
+#ifdef _TARGET_ARM_
+static inline BOOL HandleArmSingleStep(PCONTEXT pContext, PEXCEPTION_RECORD pExceptionRecord, Thread *pThread)
+{
+#ifdef __linux__
+    // On ARM Linux exception point to the break instruction,
+    // but the rest of the code expects that it points to an instruction after the break
+    if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
+    {
+        SetIP(pContext, GetIP(pContext) + CORDbg_BREAK_INSTRUCTION_SIZE);
+        pExceptionRecord->ExceptionAddress = (void *)GetIP(pContext);
+    }
+#endif
+    // On ARM we don't have any reliable hardware support for single stepping so it is emulated in software.
+    // The implementation will end up throwing an EXCEPTION_BREAKPOINT rather than an EXCEPTION_SINGLE_STEP
+    // and leaves other aspects of the thread context in an invalid state. Therefore we use this opportunity
+    // to fixup the state before any other part of the system uses it (we do it here since only the debugger
+    // uses single step functionality).
+
+    // First ask the emulation itself whether this exception occurred while single stepping was enabled. If so
+    // it will fix up the context to be consistent again and return true. If so and the exception was
+    // EXCEPTION_BREAKPOINT then we translate it to EXCEPTION_SINGLE_STEP (otherwise we leave it be, e.g. the
+    // instruction stepped caused an access violation).
+    if (pThread->HandleSingleStep(pContext, pExceptionRecord->ExceptionCode) && (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT))
+    {
+        pExceptionRecord->ExceptionCode = EXCEPTION_SINGLE_STEP;
+        pExceptionRecord->ExceptionAddress = (void *)GetIP(pContext);
+        return TRUE;
+    }
+    return FALSE;
+}
+#endif // _TARGET_ARM_
+
 BOOL HandleHardwareException(PAL_SEHException* ex)
 {
     _ASSERTE(IsSafeToHandleHardwareException(ex->GetContextRecord(), ex->GetExceptionRecord()));
@@ -5239,6 +5271,9 @@ BOOL HandleHardwareException(PAL_SEHException* ex)
         Thread *pThread = GetThread();
         if (pThread != NULL && g_pDebugInterface != NULL)
         {
+#ifdef _TARGET_ARM_
+            HandleArmSingleStep(ex->GetContextRecord(), ex->GetExceptionRecord(), pThread);
+#endif
             if (ex->GetExceptionRecord()->ExceptionCode == STATUS_BREAKPOINT)
             {
                 // If this is breakpoint context, it is set up to point to an instruction after the break instruction.
-- 
2.7.4