add_definitions(-DFEATURE_HIJACK)
endif(NOT CMAKE_SYSTEM_NAME STREQUAL NetBSD)
add_definitions(-DFEATURE_ICASTABLE)
-if (WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386))
+if (WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64))
add_definitions(-DFEATURE_INTEROP_DEBUGGING)
-endif (WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386))
+endif (WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64))
if(FEATURE_INTERPRETER)
add_definitions(-DFEATURE_INTERPRETER)
endif(FEATURE_INTERPRETER)
{
// There should have been an int3 there already. Since we already put it in there,
// we should be able to safely read it out.
+#if defined(DBG_TARGET_ARM) || defined(DBG_TARGET_ARM64)
+ PRD_TYPE opcodeTest = 0;
+#elif defined(DBG_TARGET_AMD64) || defined(DBG_TARGET_X86)
BYTE opcodeTest = 0;
+#else
+ PORTABILITY_ASSERT("NYI: Architecture specific opcode type to read");
+#endif
HRESULT hr = SafeReadStruct(PTR_TO_CORDB_ADDRESS(address), &opcodeTest);
SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
#if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64)
const BYTE patch = CORDbg_BREAK_INSTRUCTION;
BYTE opcode;
+#elif defined(DBG_TARGET_ARM64)
+ const PRD_TYPE patch = CORDbg_BREAK_INSTRUCTION;
+ PRD_TYPE opcode;
+#else
+ PORTABILITY_ASSERT("NYI: CordbProcess::SetUnmanagedBreakpoint, interop debugging NYI on this platform");
+ hr = E_NOTIMPL;
+ goto ErrExit;
+#endif
// Make sure args are good
if ((buffer == NULL) || (bufsize < sizeof(patch)) || (bufLen == NULL))
goto ErrExit;
// It's all successful, so now update our out-params & internal bookkeaping.
- opcode = (BYTE) p->opcode;
+#if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64)
+ opcode = (BYTE)p->opcode;
buffer[0] = opcode;
+#elif defined(DBG_TARGET_ARM64)
+ opcode = p->opcode;
+ memcpy_s(buffer, bufsize, &opcode, sizeof(opcode));
+#else
+ PORTABILITY_ASSERT("NYI: CordbProcess::SetUnmanagedBreakpoint, interop debugging NYI on this platform");
+#endif
*bufLen = sizeof(opcode);
p->pAddress = CORDB_ADDRESS_TO_PTR(address);
p->opcode = opcode;
_ASSERTE(SUCCEEDED(hr));
-#elif defined(DBG_TARGET_WIN64)
- PORTABILITY_ASSERT("NYI: CordbProcess::SetUnmanagedBreakpoint, interop debugging NYI on this platform");
- hr = E_NOTIMPL;
- goto ErrExit;
-#else
- hr = E_NOTIMPL;
- goto ErrExit;
-#endif // DBG_TARGET_X8_
-
ErrExit:
// If we failed, then free the patch
tempDebugContext.ContextFlags = DT_CONTEXT_FULL;
DbiGetThreadContext(pUnmanagedThread->m_handle, &tempDebugContext);
CordbUnmanagedThread::LogContext(&tempDebugContext);
+#if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64)
+ const ULONG_PTR breakpointOpcodeSize = 1;
+#elif defined(DBG_TARGET_ARM64)
+ const ULONG_PTR breakpointOpcodeSize = 4;
+#else
+ const ULONG_PTR breakpointOpcodeSize = 1;
+ PORTABILITY_ASSERT("NYI: Breakpoint size offset for this platform");
+#endif
_ASSERTE(CORDbgGetIP(&tempDebugContext) == pEvent->u.Exception.ExceptionRecord.ExceptionAddress ||
- (DWORD)CORDbgGetIP(&tempDebugContext) == ((DWORD)pEvent->u.Exception.ExceptionRecord.ExceptionAddress)+1);
+ (DWORD)CORDbgGetIP(&tempDebugContext) == ((DWORD)pEvent->u.Exception.ExceptionRecord.ExceptionAddress)+breakpointOpcodeSize);
}
#endif
#define WINNT_TLS_OFFSET_X86 0xe10 // TLS[0] at fs:[WINNT_TLS_OFFSET]
#define WINNT_TLS_OFFSET_AMD64 0x1480
#define WINNT_TLS_OFFSET_ARM 0xe10
+#define WINNT_TLS_OFFSET_ARM64 0x1480
#define WINNT5_TLSEXPANSIONPTR_OFFSET_X86 0xf94 // TLS[64] at [fs:[WINNT5_TLSEXPANSIONPTR_OFFSET]]
#define WINNT5_TLSEXPANSIONPTR_OFFSET_AMD64 0x1780
#define WINNT5_TLSEXPANSIONPTR_OFFSET_ARM 0xf94
+#define WINNT5_TLSEXPANSIONPTR_OFFSET_ARM64 0x1780
HRESULT CordbUnmanagedThread::LoadTLSArrayPtr(void)
{
DBG_ADDR(pContext->Rip),
DBG_ADDR(pContext->Rsp),
pContext->EFlags)); // EFlags is still 32bits on AMD64
+#elif defined(DBG_TARGET_ARM64)
+ LOG((LF_CORDB, LL_INFO10000,
+ "CUT::LC: Pc=" FMT_ADDR ", Sp=" FMT_ADDR ", Lr=" FMT_ADDR ", Cpsr=" FMT_ADDR "\n",
+ DBG_ADDR(pContext->Pc),
+ DBG_ADDR(pContext->Sp),
+ DBG_ADDR(pContext->Lr),
+ DBG_ADDR(pContext->Cpsr)));
#else // DBG_TARGET_X86
PORTABILITY_ASSERT("LogContext needs a PC and stack pointer.");
#endif // DBG_TARGET_X86
GetProcess()->GetDAC()->Hijack(VMPTR_Thread::NullPtr(),
GetOSTid(),
pExceptionRecord,
- (CONTEXT*) GetHijackCtx(),
- sizeof(CONTEXT),
+ (T_CONTEXT*) GetHijackCtx(),
+ sizeof(T_CONTEXT),
reason,
NULL,
&LSContextAddr);
return HRESULT_FROM_WIN32(GetLastError());
}
-#if defined(DBG_TARGET_AMD64)
+#if defined(DBG_TARGET_AMD64) || defined(DBG_TARGET_ARM64)
// On X86 Debugger::GenericHijackFunc() ensures the stack is walkable
// by simply using the EBP chain, therefore we can execute the hijack
pThread->m_vmThreadToken,
dwThreadId,
pRecord,
- (CONTEXT*) GetHijackCtx(),
- sizeof(CONTEXT),
+ (T_CONTEXT*) GetHijackCtx(),
+ sizeof(T_CONTEXT),
EHijackReason::kGenericHijack,
NULL,
NULL);
}
// else (non-threadstore threads) fallthrough
-#endif // DBG_TARGET_AMD64
+#endif // DBG_TARGET_AMD64 || defined(DBG_TARGET_ARM64)
// Remember that we've hijacked the guy.
SetState(CUTS_GenericHijacked);
m_raiseExceptionExceptionFlags = (DWORD)m_raiseExceptionEntryContext.Rdx;
m_raiseExceptionNumberParameters = (DWORD)m_raiseExceptionEntryContext.R8;
pExceptionInformation = (REMOTE_PTR)m_raiseExceptionEntryContext.R9;
+#elif defined(DBG_TARGET_ARM64)
+ m_raiseExceptionExceptionCode = (DWORD)m_raiseExceptionEntryContext.X0;
+ m_raiseExceptionExceptionFlags = (DWORD)m_raiseExceptionEntryContext.X1;
+ m_raiseExceptionNumberParameters = (DWORD)m_raiseExceptionEntryContext.X2;
+ pExceptionInformation = (REMOTE_PTR)m_raiseExceptionEntryContext.X3;
#elif defined(DBG_TARGET_X86)
hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+4), &m_raiseExceptionExceptionCode);
if(FAILED(hr))
{
#if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64)
const BYTE patch = CORDbg_BREAK_INSTRUCTION;
- HRESULT hr = pProcess->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pRemoteAddress), &patch);
- SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
+#elif defined(DBG_TARGET_ARM64)
+ const PRD_TYPE patch = CORDbg_BREAK_INSTRUCTION;
#else
+ const BYTE patch = 0;
PORTABILITY_ASSERT("NYI: ApplyRemotePatch for this platform");
#endif
+ HRESULT hr = pProcess->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pRemoteAddress), &patch);
+ SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
return S_OK;
}
#if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64)
// Read out opcode. 1 byte on x86
BYTE opcode;
+#elif defined(DBG_TARGET_ARM64)
+ // Read out opcode. 4 bytes on arm64
+ PRD_TYPE opcode;
+#else
+ BYTE opcode;
+ PORTABILITY_ASSERT("NYI: ApplyRemotePatch for this platform");
+#endif
HRESULT hr = pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pRemoteAddress), &opcode);
if (FAILED(hr))
}
*pOpcode = (PRD_TYPE) opcode;
-#else
- PORTABILITY_ASSERT("NYI: ApplyRemotePatch for this platform");
-#endif
ApplyRemotePatch(pProcess, pRemoteAddress);
return S_OK;
}
#if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64)
// Replace the BP w/ the opcode.
BYTE opcode2 = (BYTE) opcode;
+#elif defined(DBG_TARGET_ARM64)
+ // 4 bytes on arm64
+ PRD_TYPE opcode2 = opcode;
+#else
+ PRD_TYPE opcode2 = opcode;
+ PORTABILITY_ASSERT("NYI: RemoveRemotePatch for this platform");
+#endif
pProcess->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pRemoteAddress), &opcode2);
// This may fail because the module has been unloaded. In which case, the patch is also
// gone so it makes sense to return success.
-#else
- PORTABILITY_ASSERT("NYI: RemoveRemotePatch for this platform");
-#endif
return S_OK;
}
#endif // FEATURE_INTEROP_DEBUGGING
ExceptionHijackEnd
NESTED_END ExceptionHijack
+;
+; Flares for interop debugging.
+; Flares are exceptions (breakpoints) at well known addresses which the RS
+; listens for when interop debugging.
+;
+
+ LEAF_ENTRY SignalHijackStartedFlare
+ EMIT_BREAKPOINT
+ ; make sure that the basic block is unique
+ add x0, x1, x1
+ ret lr
+ LEAF_END
+
+ LEAF_ENTRY ExceptionForRuntimeHandoffStartFlare
+ EMIT_BREAKPOINT
+ ; make sure that the basic block is unique
+ add x0, x2, x2
+ ret lr
+ LEAF_END
+
+ LEAF_ENTRY ExceptionForRuntimeHandoffCompleteFlare
+ EMIT_BREAKPOINT
+ ; make sure that the basic block is unique
+ add x0, x3, x3
+ ret lr
+ LEAF_END
+
+ LEAF_ENTRY SignalHijackCompleteFlare
+ EMIT_BREAKPOINT
+ ; make sure that the basic block is unique
+ add x0, x4, x4
+ ret lr
+ LEAF_END
+
+ LEAF_ENTRY ExceptionNotForRuntimeFlare
+ EMIT_BREAKPOINT
+ ; make sure that the basic block is unique
+ add x0, x5, x5
+ ret lr
+ LEAF_END
+
+ LEAF_ENTRY NotifyRightSideOfSyncCompleteFlare
+ EMIT_BREAKPOINT
+ ; make sure that the basic block is unique
+ add x0, x6, x6
+ ret lr
+ LEAF_END
+
; must be at end of file
END
CONTRACT_VIOLATION(ThrowsViolation);
- LOG((LF_CORDB|LF_ENC,LL_INFO1000,"DC:DPOSS at 0x%x trigger:0x%x\n", address, which));
+ LOG((LF_CORDB|LF_ENC,LL_INFO1000,"DC:DPOSS at 0x%p trigger:0x%x\n", address, which));
// We should only have an exception if some managed thread was running.
// Thus we should never be here when we're stopped.
SPEW(fprintf(stderr, "0x%x D::FCHF: code=0x%08x, addr=0x%08x, Eip=0x%08x, Esp=0x%08x, EFlags=0x%08x\n",
tid, pExceptionRecord->ExceptionCode, pExceptionRecord->ExceptionAddress, pContext->Eip, pContext->Esp,
pContext->EFlags));
+#elif defined(_TARGET_ARM64_)
+ SPEW(fprintf(stderr, "0x%x D::FCHF: code=0x%08x, addr=0x%08x, Pc=0x%p, Sp=0x%p, EFlags=0x%08x\n",
+ tid, pExceptionRecord->ExceptionCode, pExceptionRecord->ExceptionAddress, pContext->Pc, pContext->Sp,
+ pContext->EFlags));
#endif
// This memory is used as IPC during the hijack. We will place a pointer to this in
}
}
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
void GenericHijackFuncHelper()
{
#if DOSPEW
{
WRAPPER_NO_CONTRACT;
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+#if defined(FEATURE_INTEROP_DEBUGGING)
SignalHijackStartedFlare();
#else
_ASSERTE(!"@todo - port the flares to the platform your running on.");
{
WRAPPER_NO_CONTRACT;
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+#if defined(FEATURE_INTEROP_DEBUGGING)
ExceptionForRuntimeHandoffStartFlare();
#else
_ASSERTE(!"@todo - port the flares to the platform your running on.");
{
WRAPPER_NO_CONTRACT;
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+#if defined(FEATURE_INTEROP_DEBUGGING)
ExceptionForRuntimeHandoffCompleteFlare();
#else
_ASSERTE(!"@todo - port the flares to the platform your running on.");
{
WRAPPER_NO_CONTRACT;
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+#if defined(FEATURE_INTEROP_DEBUGGING)
SignalHijackCompleteFlare();
#else
_ASSERTE(!"@todo - port the flares to the platform your running on.");
{
WRAPPER_NO_CONTRACT;
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+#if defined(FEATURE_INTEROP_DEBUGGING)
ExceptionNotForRuntimeFlare();
#else
_ASSERTE(!"@todo - port the flares to the platform your running on.");
{
WRAPPER_NO_CONTRACT;
STRESS_LOG0(LF_CORDB, LL_INFO100000, "D::NRSOSC: Sending flare...\n");
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+#if defined(FEATURE_INTEROP_DEBUGGING)
NotifyRightSideOfSyncCompleteFlare();
#else
_ASSERTE(!"@todo - port the flares to the platform your running on.");
pRD->volatileCurrContextPointers.R12 = &(pDE->m_context.R12);
SyncRegDisplayToCurrentContext(pRD);
-#else
+
+#elif defined(_TARGET_ARM64_)
+ pRD->IsCallerContextValid = FALSE;
+ pRD->IsCallerSPValid = FALSE; // Don't add usage of this flag. This is only temporary.
+
+ memcpy(pRD->pCurrentContext, &(pDE->m_context), sizeof(T_CONTEXT));
+
+ pRD->pCurrentContextPointers->X19 = &(pDE->m_context.X19);
+ pRD->pCurrentContextPointers->X20 = &(pDE->m_context.X20);
+ pRD->pCurrentContextPointers->X21 = &(pDE->m_context.X21);
+ pRD->pCurrentContextPointers->X22 = &(pDE->m_context.X22);
+ pRD->pCurrentContextPointers->X23 = &(pDE->m_context.X23);
+ pRD->pCurrentContextPointers->X24 = &(pDE->m_context.X24);
+ pRD->pCurrentContextPointers->X25 = &(pDE->m_context.X25);
+ pRD->pCurrentContextPointers->X26 = &(pDE->m_context.X26);
+ pRD->pCurrentContextPointers->X27 = &(pDE->m_context.X27);
+ pRD->pCurrentContextPointers->X28 = &(pDE->m_context.X28);
+ pRD->pCurrentContextPointers->Lr = &(pDE->m_context.Lr);
+ pRD->pCurrentContextPointers->Fp = &(pDE->m_context.Fp);
+
+ pRD->volatileCurrContextPointers.X0 = &(pDE->m_context.X0);
+ pRD->volatileCurrContextPointers.X1 = &(pDE->m_context.X1);
+ pRD->volatileCurrContextPointers.X2 = &(pDE->m_context.X2);
+ pRD->volatileCurrContextPointers.X3 = &(pDE->m_context.X3);
+ pRD->volatileCurrContextPointers.X4 = &(pDE->m_context.X4);
+ pRD->volatileCurrContextPointers.X5 = &(pDE->m_context.X5);
+ pRD->volatileCurrContextPointers.X6 = &(pDE->m_context.X6);
+ pRD->volatileCurrContextPointers.X7 = &(pDE->m_context.X7);
+ pRD->volatileCurrContextPointers.X8 = &(pDE->m_context.X8);
+ pRD->volatileCurrContextPointers.X9 = &(pDE->m_context.X9);
+ pRD->volatileCurrContextPointers.X10 = &(pDE->m_context.X10);
+ pRD->volatileCurrContextPointers.X11 = &(pDE->m_context.X11);
+ pRD->volatileCurrContextPointers.X12 = &(pDE->m_context.X12);
+ pRD->volatileCurrContextPointers.X13 = &(pDE->m_context.X13);
+ pRD->volatileCurrContextPointers.X14 = &(pDE->m_context.X14);
+ pRD->volatileCurrContextPointers.X15 = &(pDE->m_context.X15);
+ pRD->volatileCurrContextPointers.X16 = &(pDE->m_context.X16);
+ pRD->volatileCurrContextPointers.X17 = &(pDE->m_context.X17);
+
+ SyncRegDisplayToCurrentContext(pRD);
+#else
PORTABILITY_ASSERT("FuncEvalFrame::UpdateRegDisplay is not implemented on this platform.");
#endif
}
{
LIMITED_METHOD_CONTRACT;
+#if defined(DBG_TARGET_ARM64)
+ pContext->Pc -= CORDbg_BREAK_INSTRUCTION_SIZE;
+#else
// @ARMTODO: ARM appears to leave the PC at the start of the breakpoint (at least according to Windbg,
// which may be adjusting the view).
+#endif
return;
}
dbgHandle = info.compCompHnd->getJustMyCodeHandle(info.compMethodHnd, &pDbgHandle);
}
-#ifdef _TARGET_ARM64_
- // TODO-ARM64-NYI: don't do just-my-code
- dbgHandle = nullptr;
- pDbgHandle = nullptr;
-#endif // _TARGET_ARM64_
-
noway_assert(!dbgHandle || !pDbgHandle);
if (dbgHandle || pDbgHandle)
SUPPORTS_DAC;
#ifdef DACCESS_COMPILE
- _ASSERTE(!"ARM64:NYI");
return SK_BREAKPOINT; // Dac always uses the slower lookup
#else