summaryrefslogtreecommitdiff
path: root/packaging/0001-Linux-ARM-Fix-managed-breakpoints-13316.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packaging/0001-Linux-ARM-Fix-managed-breakpoints-13316.patch')
-rw-r--r--packaging/0001-Linux-ARM-Fix-managed-breakpoints-13316.patch173
1 files changed, 173 insertions, 0 deletions
diff --git a/packaging/0001-Linux-ARM-Fix-managed-breakpoints-13316.patch b/packaging/0001-Linux-ARM-Fix-managed-breakpoints-13316.patch
new file mode 100644
index 0000000000..0e5ba17ce1
--- /dev/null
+++ b/packaging/0001-Linux-ARM-Fix-managed-breakpoints-13316.patch
@@ -0,0 +1,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
+