diff options
author | Steve MacLean <Steve.MacLean@microsoft.com> | 2019-07-03 22:29:07 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-03 22:29:07 -0400 |
commit | 613f9f233abde4159a85aa8130b7fcb57dc7a4ef (patch) | |
tree | 09aed307f82583475ef52a7e2448c46da8c640db /src/debug | |
parent | ae3430d8ce50c9e954192e89871aff783da375e2 (diff) | |
download | coreclr-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.cpp | 40 | ||||
-rw-r--r-- | src/debug/ee/controller.cpp | 48 | ||||
-rw-r--r-- | src/debug/ee/debugger.cpp | 2 | ||||
-rw-r--r-- | src/debug/ee/funceval.cpp | 6 |
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 |