summaryrefslogtreecommitdiff
path: root/src/debug
diff options
context:
space:
mode:
authorSteve MacLean <Steve.MacLean@microsoft.com>2019-07-03 22:29:07 -0400
committerGitHub <noreply@github.com>2019-07-03 22:29:07 -0400
commit613f9f233abde4159a85aa8130b7fcb57dc7a4ef (patch)
tree09aed307f82583475ef52a7e2448c46da8c640db /src/debug
parentae3430d8ce50c9e954192e89871aff783da375e2 (diff)
downloadcoreclr-613f9f233abde4159a85aa8130b7fcb57dc7a4ef.tar.gz
coreclr-613f9f233abde4159a85aa8130b7fcb57dc7a4ef.tar.bz2
coreclr-613f9f233abde4159a85aa8130b7fcb57dc7a4ef.zip
arm64singlestepper (#25512)
Add single step emulation for arm64 Linux Add a copy and rework armsinglestepper to arm64singlestepper Add arm64 emulation of all armv8 user space instructions which read or write PC. - ADR, ADRP - Branch instructions: B, BL, B.cond, BR, BLR, RET - LDR (literal) * Add FEATURE_EMULATE_SINGLESTEP * Enable for ARM64 linux * Debugging fixes Fix IsSSFlagEnabled bug Fix opcode type Fix code buffer asserts Fix CBZ typo Fix BitExtract Fix m_targetPc Minimize written instructions Fix comments Fix Bypass address truncation Fix false assert Add additional logging Use %lx to log addresses Remove stray LOG Remove stray assert
Diffstat (limited to 'src/debug')
-rw-r--r--src/debug/ee/arm64/primitives.cpp40
-rw-r--r--src/debug/ee/controller.cpp48
-rw-r--r--src/debug/ee/debugger.cpp2
-rw-r--r--src/debug/ee/funceval.cpp6
4 files changed, 70 insertions, 26 deletions
diff --git a/src/debug/ee/arm64/primitives.cpp b/src/debug/ee/arm64/primitives.cpp
index 6895f784c5..d08eef62b4 100644
--- a/src/debug/ee/arm64/primitives.cpp
+++ b/src/debug/ee/arm64/primitives.cpp
@@ -13,3 +13,43 @@ void CopyREGDISPLAY(REGDISPLAY* pDst, REGDISPLAY* pSrc)
CONTEXT tmp;
CopyRegDisplay(pSrc, pDst, &tmp);
}
+
+#ifdef FEATURE_EMULATE_SINGLESTEP
+void SetSSFlag(DT_CONTEXT *, Thread *pThread)
+{
+ _ASSERTE(pThread != NULL);
+
+ pThread->EnableSingleStep();
+}
+
+void UnsetSSFlag(DT_CONTEXT *, Thread *pThread)
+{
+ _ASSERTE(pThread != NULL);
+
+ pThread->DisableSingleStep();
+}
+
+// Check if single stepping is enabled.
+bool IsSSFlagEnabled(DT_CONTEXT *, Thread *pThread)
+{
+ _ASSERTE(pThread != NULL);
+
+ return pThread->IsSingleStepEnabled();
+}
+#else // FEATURE_EMULATE_SINGLESTEP
+void SetSSFlag(DT_CONTEXT *pContext, Thread *)
+{
+ SetSSFlag(pContext);
+}
+
+void UnsetSSFlag(DT_CONTEXT *pContext, Thread *)
+{
+ UnsetSSFlag(pContext);
+}
+
+// Check if single stepping is enabled.
+bool IsSSFlagEnabled(DT_CONTEXT *pContext, Thread *)
+{
+ return IsSSFlagEnabled(pContext);
+}
+#endif // FEATURE_EMULATE_SINGLESTEP
diff --git a/src/debug/ee/controller.cpp b/src/debug/ee/controller.cpp
index 7d75a60d0c..a82666d5cf 100644
--- a/src/debug/ee/controller.cpp
+++ b/src/debug/ee/controller.cpp
@@ -69,7 +69,7 @@ bool DebuggerControllerPatch::IsSafeForStackTrace()
}
-#ifndef _TARGET_ARM_
+#ifndef FEATURE_EMULATE_SINGLESTEP
// returns a pointer to the shared buffer. each call will AddRef() the object
// before returning it so callers only need to Release() when they're finished with it.
SharedPatchBypassBuffer* DebuggerControllerPatch::GetOrCreateSharedPatchBypassBuffer()
@@ -92,7 +92,7 @@ SharedPatchBypassBuffer* DebuggerControllerPatch::GetOrCreateSharedPatchBypassBu
return m_pSharedPatchBypassBuffer;
}
-#endif // _TARGET_ARM_
+#endif // FEATURE_EMULATE_SINGLESTEP
// @todo - remove all this splicing trash
// This Sort/Splice stuff just reorders the patches within a particular chain such
@@ -502,7 +502,7 @@ DebuggerControllerPatch *DebuggerPatchTable::AddPatchForMethodDef(DebuggerContro
{
ThrowOutOfMemory();
}
-#ifndef _TARGET_ARM_
+#ifndef FEATURE_EMULATE_SINGLESTEP
patch->Initialize();
#endif
@@ -602,7 +602,7 @@ DebuggerControllerPatch *DebuggerPatchTable::AddPatchForAddress(DebuggerControll
{
ThrowOutOfMemory();
}
-#ifndef _TARGET_ARM_
+#ifndef FEATURE_EMULATE_SINGLESTEP
patch->Initialize();
#endif
@@ -721,7 +721,7 @@ void DebuggerPatchTable::RemovePatch(DebuggerControllerPatch *patch)
{
// Since we're deleting this patch, it must not be activated (i.e. it must not have a stored opcode)
_ASSERTE( !patch->IsActivated() );
-#ifndef _TARGET_ARM_
+#ifndef FEATURE_EMULATE_SINGLESTEP
patch->DoCleanup();
#endif
@@ -3233,7 +3233,7 @@ void DebuggerController::ApplyTraceFlag(Thread *thread)
g_pEEInterface->MarkThreadForDebugStepping(thread, true);
LOG((LF_CORDB,LL_INFO1000, "DC::ApplyTraceFlag marked thread for debug stepping\n"));
- SetSSFlag(reinterpret_cast<DT_CONTEXT *>(context) ARM_ARG(thread));
+ SetSSFlag(reinterpret_cast<DT_CONTEXT *>(context) ARM_ARG(thread) ARM64_ARG(thread));
LOG((LF_CORDB,LL_INFO1000, "DC::ApplyTraceFlag Leaving, baby!\n"));
}
@@ -3271,7 +3271,7 @@ void DebuggerController::UnapplyTraceFlag(Thread *thread)
// Always need to unmark for stepping
g_pEEInterface->MarkThreadForDebugStepping(thread, false);
- UnsetSSFlag(reinterpret_cast<DT_CONTEXT *>(context) ARM_ARG(thread));
+ UnsetSSFlag(reinterpret_cast<DT_CONTEXT *>(context) ARM_ARG(thread) ARM64_ARG(thread));
}
void DebuggerController::EnableExceptionHook()
@@ -4311,7 +4311,7 @@ bool DebuggerController::DispatchNativeException(EXCEPTION_RECORD *pException,
g_pEEInterface->SetThreadFilterContext(pCurThread, NULL);
}
-#ifdef _TARGET_ARM_
+#ifdef FEATURE_EMULATE_SINGLESTEP
if (pCurThread->IsSingleStepEnabled())
pCurThread->ApplySingleStep(pContext);
#endif
@@ -4337,7 +4337,7 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
// On ARM the single-step emulation already utilizes a per-thread execution buffer similar to the scheme
// below. As a result we can skip most of the instruction parsing logic that's instead internalized into
// the single-step emulation itself.
-#ifndef _TARGET_ARM_
+#ifndef FEATURE_EMULATE_SINGLESTEP
// NOTE: in order to correctly single-step RIP-relative writes on multiple threads we need to set up
// a shared buffer with the instruction and a buffer for the RIP-relative value so that all threads
@@ -4423,7 +4423,7 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
}
#endif // _TARGET_AMD64_
-#endif // !_TARGET_ARM_
+#endif // !FEATURE_EMULATE_SINGLESTEP
// Signals our thread that the debugger will be manipulating the context
// during the patch skip operation. This effectively prevents other threads
@@ -4457,8 +4457,8 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
ARM_ONLY(_ASSERTE(!"We should always have a filter context in DebuggerPatchSkip."));
}
-#ifdef _TARGET_ARM_
- // Since we emulate all single-stepping on ARM using an instruction buffer and a breakpoint all we have to
+#ifdef FEATURE_EMULATE_SINGLESTEP
+ // Since we emulate all single-stepping on ARM/ARM64 using an instruction buffer and a breakpoint all we have to
// do here is initiate a normal single-step except that we pass the instruction to be stepped explicitly
// (calling EnableSingleStep() would infer this by looking at the PC in the context, which would pick up
// the patch we're trying to skip).
@@ -4469,18 +4469,21 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
{
ControllerLockHolder lockController;
g_pEEInterface->MarkThreadForDebugStepping(thread, true);
+
+#ifdef _TARGET_ARM_
WORD opcode2 = 0;
if (Is32BitInstruction(patch->opcode))
{
opcode2 = CORDbgGetInstruction((CORDB_ADDRESS_TYPE *)(((DWORD)patch->address) + 2));
}
+#endif // _TARGET_ARM_
- thread->BypassWithSingleStep((DWORD)patch->address, patch->opcode, opcode2);
+ thread->BypassWithSingleStep(patch->address, patch->opcode ARM_ARG(opcode2));
m_singleStep = true;
}
-#else // _TARGET_ARM_
+#else // FEATURE_EMULATE_SINGLESTEP
#ifdef _TARGET_ARM64_
patchBypass = NativeWalker::SetupOrSimulateInstructionForPatchSkip(context, m_pSharedPatchBypassBuffer, (const BYTE *)patch->address, patch->opcode);
@@ -4503,14 +4506,14 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
EnableSingleStep();
-#endif // _TARGET_ARM_
+#endif // FEATURE_EMULATE_SINGLESTEP
EnableExceptionHook();
}
DebuggerPatchSkip::~DebuggerPatchSkip()
{
-#ifndef _TARGET_ARM_
+#ifndef FEATURE_EMULATE_SINGLESTEP
_ASSERTE(m_pSharedPatchBypassBuffer);
m_pSharedPatchBypassBuffer->Release();
#endif
@@ -4518,8 +4521,8 @@ DebuggerPatchSkip::~DebuggerPatchSkip()
void DebuggerPatchSkip::DebuggerDetachClean()
{
-// Since for ARM SharedPatchBypassBuffer isn't existed, we don't have to anything here.
-#ifndef _TARGET_ARM_
+// Since for ARM/ARM64 SharedPatchBypassBuffer isn't existed, we don't have to anything here.
+#ifndef FEATURE_EMULATE_SINGLESTEP
// Fix for Bug 1176448
// When a debugger is detaching from the debuggee, we need to move the IP if it is pointing
// somewhere in PatchBypassBuffer.All managed threads are suspended during detach, so changing
@@ -4632,7 +4635,7 @@ TP_RESULT DebuggerPatchSkip::TriggerPatch(DebuggerControllerPatch *patch,
ARM_ONLY(_ASSERTE(!"Should not have called DebuggerPatchSkip::TriggerPatch."));
LOG((LF_CORDB, LL_EVERYTHING, "DPS::TP called\n"));
-#if defined(_DEBUG) && !defined(_TARGET_ARM_)
+#if defined(_DEBUG) && !defined(FEATURE_EMULATE_SINGLESTEP)
CONTEXT *context = GetManagedLiveCtx(thread);
LOG((LF_CORDB, LL_INFO1000, "DPS::TP: We've patched 0x%x (byPass:0x%x) "
@@ -4651,7 +4654,7 @@ TP_RESULT DebuggerPatchSkip::TriggerPatch(DebuggerControllerPatch *patch,
patchCheck = g_patches->GetNextPatch(patchCheck);
_ASSERTE(patchCheck == NULL);
-#endif // _DEBUG
+#endif // defined(_DEBUG) && !defined(FEATURE_EMULATE_SINGLESTEP)
DisableAll();
EnableExceptionHook();
@@ -4689,7 +4692,7 @@ TP_RESULT DebuggerPatchSkip::TriggerExceptionHook(Thread *thread, CONTEXT * cont
LOG((LF_CORDB,LL_INFO10000, "DPS::TEH: doing the patch-skip thing\n"));
-#if defined(_TARGET_ARM64_)
+#if defined(_TARGET_ARM64_) && !defined(FEATURE_EMULATE_SINGLESTEP)
if (!IsSingleStep(exception->ExceptionCode))
{
@@ -4712,7 +4715,8 @@ TP_RESULT DebuggerPatchSkip::TriggerExceptionHook(Thread *thread, CONTEXT * cont
SetIP(context, targetIp);
LOG((LF_CORDB, LL_ALWAYS, "Redirecting after Patch to 0x%p\n", GetIP(context)));
-#elif defined (_TARGET_ARM_)
+#elif defined(FEATURE_EMULATE_SINGLESTEP)
+
//Do nothing
#else
_ASSERTE(m_pSharedPatchBypassBuffer);
diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp
index 7a54b46a3c..0299cfef12 100644
--- a/src/debug/ee/debugger.cpp
+++ b/src/debug/ee/debugger.cpp
@@ -16156,7 +16156,7 @@ BOOL Debugger::IsThreadContextInvalid(Thread *pThread)
if (success)
{
// Check single-step flag
- if (IsSSFlagEnabled(reinterpret_cast<DT_CONTEXT *>(&ctx) ARM_ARG(pThread)))
+ if (IsSSFlagEnabled(reinterpret_cast<DT_CONTEXT *>(&ctx) ARM_ARG(pThread) ARM64_ARG(pThread)))
{
// Can't hijack a thread whose SS-flag is set. This could lead to races
// with the thread taking the SS-exception.
diff --git a/src/debug/ee/funceval.cpp b/src/debug/ee/funceval.cpp
index 2dbea0e487..8c6ed884b6 100644
--- a/src/debug/ee/funceval.cpp
+++ b/src/debug/ee/funceval.cpp
@@ -3757,9 +3757,9 @@ void * STDCALL FuncEvalHijackWorker(DebuggerEval *pDE)
FrameWithCookie<FuncEvalFrame> FEFrame(pDE, GetIP(&pDE->m_context), true);
FEFrame.Push();
- // On ARM the single step flag is per-thread and not per context. We need to make sure that the SS flag is cleared
+ // On ARM/ARM64 the single step flag is per-thread and not per context. We need to make sure that the SS flag is cleared
// for the funceval, and that the state is back to what it should be after the funceval completes.
-#ifdef _TARGET_ARM_
+#ifdef FEATURE_EMULATE_SINGLESTEP
bool ssEnabled = pDE->m_thread->IsSingleStepEnabled();
if (ssEnabled)
pDE->m_thread->DisableSingleStep();
@@ -3767,7 +3767,7 @@ void * STDCALL FuncEvalHijackWorker(DebuggerEval *pDE)
FuncEvalHijackRealWorker(pDE, pThread, &FEFrame);
-#ifdef _TARGET_ARM_
+#ifdef FEATURE_EMULATE_SINGLESTEP
if (ssEnabled)
pDE->m_thread->EnableSingleStep();
#endif