BOOL fIsStub = FALSE;
+#if defined(FEATURE_PAL)
+ // Currently IsIPInModule() is not implemented in the PAL. Rather than skipping the check, we should
+ // either E_NOTIMPL this API or implement IsIPInModule() in the PAL. Since ICDProcess::IsTransitionStub()
+ // is only called by VS in mixed-mode debugging scenarios, and mixed-mode debugging is not supported on
+ // Mac, there is really no incentive to implement this API on Mac at this point.
+ ThrowHR(E_NOTIMPL);
+
+#else // !FEATURE_PAL
+
TADDR ip = (TADDR)address;
if (ip == NULL)
fIsStub = IsIPInModule(m_globalBase, ip);
}
+#endif // FEATURE_PAL
+
return fIsStub;
}
if (pFilterContext == NULL)
{
// If the filter context is NULL, then we use the true context of the thread.
+
+#ifdef FEATURE_PAL
+ // GetThreadContext() is currently not implemented in PAL because we have no way to convert a thread ID to a
+ // thread handle. The function to do the conversion is OpenThread(), which is not implemented in PAL.
+ // Instead, we just zero out the seed CONTEXT for the stackwalk. This tells the stackwalker to
+ // start the stackwalk with the first explicit frame. This won't work when we do native debugging,
+ // but that won't happen on the Linux/Mac since they don't support native debugging.
+ ZeroMemory(pContextBuffer, sizeof(*pContextBuffer));
+#else // FEATURE_PAL
pContextBuffer->ContextFlags = CONTEXT_ALL;
-
IfFailThrow(m_pTarget->GetThreadContext(pThread->GetOSThreadId(),
pContextBuffer->ContextFlags,
sizeof(*pContextBuffer),
reinterpret_cast<BYTE *>(pContextBuffer)));
+#endif // FEATURE_PAL
}
else
{
// Its possible that the debugger would still load the NGEN image sometime in the future and we will miss a sharing
// opportunity. Its an acceptable loss from an imperfect heuristic.
if (NULL == WszGetModuleHandle(szFullPathName))
+#endif
{
szFullPathName = NULL;
fDebuggerLoadingNgen = false;
}
-#endif
+
}
// If we don't have or decided not to load the NGEN image, check to see if IL image is available
DWORD dwOpenFlags,
bool validateFileInfo)
{
+#ifdef FEATURE_PAL
+ // TODO: Some intricate details of file mapping don't work on Linux as on Windows.
+ // We have to revisit this and try to fix it for POSIX system.
+ return E_FAIL;
+#else
if (validateFileInfo)
{
// Check that we've got the right file to target.
}
return hr;
+#endif // FEATURE_PAL
}
//---------------------------------------------------------------------------------------
ReleaseHolder<ISymUnmanagedBinder> pBinder;
if (symFormat == IDacDbiInterface::kSymbolFormatPDB)
{
+#ifndef FEATURE_PAL
// PDB format - use diasymreader.dll with COM activation
InlineSString<_MAX_PATH> ssBuf;
IfFailThrow(FakeCoCreateInstanceEx(CLSID_CorSymBinder_SxS,
IID_ISymUnmanagedBinder,
(void**)&pBinder,
NULL));
+#else
+ IfFailThrow(FakeCoCreateInstance(CLSID_CorSymBinder_SxS,
+ IID_ISymUnmanagedBinder,
+ (void**)&pBinder));
+#endif
}
else if (symFormat == IDacDbiInterface::kSymbolFormatILDB)
{
ret = pCanary->GetUserAddr();
#endif
+#ifdef FEATURE_PAL
+ // We don't have executable heap in PAL, but we still need to allocate
+ // executable memory, that's why have change protection level for
+ // each allocation.
+ // TODO: We need to look how JIT solves this problem.
+ DWORD unusedFlags;
+ if (!VirtualProtect(ret, size, PAGE_EXECUTE_READWRITE, &unusedFlags))
+ {
+ _ASSERTE(!"VirtualProtect failed to make this memory executable");
+ }
+#endif // FEATURE_PAL
+
return ret;
}
g_hardwareExceptionHandler(&exception);
- ASSERT("HandleHardwareException has returned, it should not.\n");
+ if (pointers->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT ||
+ pointers->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP)
+ {
+
+#if HAVE_MACH_EXCEPTIONS
+ RtlRestoreContext(&exception.ContextRecord, &exception.ExceptionRecord);
+#elif __LINUX__
+ // Here we shamelessly exploit undocumented behavior of Linux signal handlers:
+ //
+ // When signal handler exits it completely restores context saved in ucontext,
+ // if any changes are applied to it by the signal handler they will also be
+ // restored and affect further course of execution.
+ //
+ // We'd love to avoid using this assumption, but unfortunately setcontext()
+ // (which is used in PAL implementation of RtlRestoreContext) doesn't restore eflags
+ // and loads zero into RAX register. It might be fine for exception handling, but unacceptable
+ // for debugger. That's why for debugger's signals (breakpoint and singlestep) we
+ // actually allow HardwareExceptionHandler to return and change context on exit.
+
+ // TODO: We should just implement setcontext helper in assembly and call it from RtlRestoreContext
+ // on Linux. setcontext() is not actually that hard to implement.
+
+ CONTEXTToNativeContext(&exception.ContextRecord, ucontext);
+ return;
+#else // HAVE_MACH_EXCEPTIONS
+ #error not yet implemented
+#endif // HAVE_MACH_EXCEPTIONS
+ }
+ else
+ {
+ ASSERT("HardwareExceptionHandler has returned, it should not.");
+ }
}
else
{
// This is a DEBUG routing to verify that a memory region complies with executable requirements
BOOL DbgIsExecutable(LPVOID lpMem, SIZE_T length)
{
-#if defined(CROSSGEN_COMPILE)
+#if defined(CROSSGEN_COMPILE) || defined(FEATURE_PAL)
// No NX support on PAL or for crossgen compilations.
return TRUE;
#else // !defined(CROSSGEN_COMPILE)
VOID PALAPI HandleHardwareException(PAL_SEHException* ex)
{
- if (ex->ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT)
+ if (ex->ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT && ex->ExceptionRecord.ExceptionCode != STATUS_SINGLE_STEP)
{
// A hardware exception is handled only if it happened in a jitted code or
// in one of the JIT helper functions (JIT_MemSet, ...)
UNREACHABLE();
}
- _ASSERTE(!"HandleHardwareException: Hardware exception happened out of managed code");
+ _ASSERTE(!"HandleHardwareException: Hardware exception happened out of managed code");
+ }
+ else
+ {
+ // This is a breakpoint or single step stop, we report it to the debugger.
+ Thread *pThread = GetThread();
+ if (pThread != NULL && g_pDebugInterface != NULL)
+ {
+ if (ex->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
+ {
+ // If this is breakpoint context is set up to point to an instuction after the break instuction.
+ // But debugger expectes to see context that points to the break instruction, that's why we correct it.
+ SetIP(&ex->ContextRecord, GetIP(&ex->ContextRecord) - CORDbg_BREAK_INSTRUCTION_SIZE);
+ ex->ExceptionRecord.ExceptionAddress = (void *)GetIP(&ex->ContextRecord);
+ }
+
+ if (g_pDebugInterface->FirstChanceNativeException(&ex->ExceptionRecord,
+ &ex->ContextRecord,
+ ex->ExceptionRecord.ExceptionCode,
+ pThread))
+ {
+ return;
+ // Ideally we'd like to put
+ // RtlRestoreContext(&ex->ContextRecord, &ex->ExceptionRecord);
+ // here, but RtlRestoreContext is not completely reliable for debugging on Linux (doesn't restore EFlags)
+ // that's why we'll just return. Read more in signal.cpp/common_signal_handler .
+ }
+ }
}
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
}