summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAditya Mandaleeka <adityam@microsoft.com>2016-01-13 16:38:34 -0800
committerAditya Mandaleeka <adityam@microsoft.com>2016-01-15 19:47:59 -0800
commit07eb08f918846f0e7bcb5fc5eda0d0865a910a98 (patch)
treebcb4fe106ae4a486c69f737ee6817efd24a53764
parentacdee68f193eca1cb0f9f5c9244f412707df7b90 (diff)
downloadcoreclr-07eb08f918846f0e7bcb5fc5eda0d0865a910a98.tar.gz
coreclr-07eb08f918846f0e7bcb5fc5eda0d0865a910a98.tar.bz2
coreclr-07eb08f918846f0e7bcb5fc5eda0d0865a910a98.zip
Add support for GCStress 0xC.
-rw-r--r--src/debug/ee/debugger.cpp8
-rw-r--r--src/debug/ee/debugger.h7
-rw-r--r--src/inc/switches.h2
-rw-r--r--src/pal/inc/pal.h7
-rw-r--r--src/pal/src/exception/machexception.cpp5
-rw-r--r--src/pal/src/exception/seh.cpp21
-rw-r--r--src/pal/src/exception/signal.cpp2
-rw-r--r--src/pal/src/thread/context.cpp16
-rw-r--r--src/vm/CMakeLists.txt1
-rw-r--r--src/vm/amd64/cgencpu.h2
-rw-r--r--src/vm/ceemain.cpp22
-rw-r--r--src/vm/disassembler.cpp308
-rw-r--r--src/vm/disassembler.h122
-rw-r--r--src/vm/excep.cpp19
-rw-r--r--src/vm/excep.h1
-rw-r--r--src/vm/exceptionhandling.cpp12
-rw-r--r--src/vm/gccover.cpp113
-rw-r--r--src/vm/jitinterface.cpp18
-rw-r--r--src/vm/jitinterface.h1
19 files changed, 612 insertions, 75 deletions
diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp
index 6279ed0499..435369c9e0 100644
--- a/src/debug/ee/debugger.cpp
+++ b/src/debug/ee/debugger.cpp
@@ -1046,7 +1046,13 @@ MemoryRange Debugger::s_hijackFunction[kMaxHijackFunctions] =
GetMemoryRangeForFunction(RedirectedHandledJITCaseForUserSuspend_Stub,
RedirectedHandledJITCaseForUserSuspend_StubEnd),
GetMemoryRangeForFunction(RedirectedHandledJITCaseForYieldTask_Stub,
- RedirectedHandledJITCaseForYieldTask_StubEnd)};
+ RedirectedHandledJITCaseForYieldTask_StubEnd)
+#ifdef HAVE_GCCOVER
+ ,
+ GetMemoryRangeForFunction(RedirectedHandledJITCaseForGCStress_Stub,
+ RedirectedHandledJITCaseForGCStress_StubEnd)
+#endif // HAVE_GCCOVER
+ };
#endif // FEATURE_HIJACK && !PLATFORM_UNIX
// Save the necessary information for the debugger to recognize an IP in one of the thread redirection
diff --git a/src/debug/ee/debugger.h b/src/debug/ee/debugger.h
index d14c83c87a..13cc5be14b 100644
--- a/src/debug/ee/debugger.h
+++ b/src/debug/ee/debugger.h
@@ -2884,6 +2884,9 @@ private:
kRedirectedForDbgThreadControl,
kRedirectedForUserSuspend,
kRedirectedForYieldTask,
+#ifdef HAVE_GCCOVER
+ kRedirectedForGCStress,
+#endif // HAVE_GCCOVER
kMaxHijackFunctions,
};
@@ -2972,6 +2975,10 @@ void RedirectedHandledJITCaseForUserSuspend_StubEnd();
void RedirectedHandledJITCaseForYieldTask_Stub();
void RedirectedHandledJITCaseForYieldTask_StubEnd();
+#ifdef HAVE_GCCOVER
+void RedirectedHandledJITCaseForGCStress_Stub();
+void RedirectedHandledJITCaseForGCStress_StubEnd();
+#endif // HAVE_GCCOVER
};
diff --git a/src/inc/switches.h b/src/inc/switches.h
index 27f528c1d3..ddf3c5b6ac 100644
--- a/src/inc/switches.h
+++ b/src/inc/switches.h
@@ -141,7 +141,7 @@
#endif
// GCCoverage has a dependency on msvcdisXXX.dll, which is not available for CoreSystem. Hence, it is disabled for CoreSystem builds.
-#if defined(STRESS_HEAP) && defined(_DEBUG) && defined(FEATURE_HIJACK) && !(defined(FEATURE_CORESYSTEM) && (defined(_TARGET_X86_) || defined(_TARGET_AMD64_)))
+#if defined(STRESS_HEAP) && defined(_DEBUG) && defined(FEATURE_HIJACK) && (!defined(FEATURE_CORECLR) || (defined(WIN32) || defined(__LINUX__)) && defined(_TARGET_AMD64_))
#define HAVE_GCCOVER
#endif
diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h
index 5324b3bdde..cca0d995ab 100644
--- a/src/pal/inc/pal.h
+++ b/src/pal/inc/pal.h
@@ -6763,6 +6763,7 @@ public:
};
typedef VOID (PALAPI *PHARDWARE_EXCEPTION_HANDLER)(PAL_SEHException* ex);
+typedef DWORD (PALAPI *PGET_GCMARKER_EXCEPTION_CODE)(LPVOID ip);
PALIMPORT
VOID
@@ -6770,6 +6771,12 @@ PALAPI
PAL_SetHardwareExceptionHandler(
IN PHARDWARE_EXCEPTION_HANDLER exceptionHandler);
+PALIMPORT
+VOID
+PALAPI
+PAL_SetGetGcMarkerExceptionCode(
+ IN PGET_GCMARKER_EXCEPTION_CODE getGcMarkerExceptionCode);
+
//
// This holder is used to indicate that a hardware
// exception should be raised as a C++ exception
diff --git a/src/pal/src/exception/machexception.cpp b/src/pal/src/exception/machexception.cpp
index 4cd19d7be9..854b8b3967 100644
--- a/src/pal/src/exception/machexception.cpp
+++ b/src/pal/src/exception/machexception.cpp
@@ -612,8 +612,9 @@ static DWORD exception_from_trap_code(
return EXCEPTION_ACCESS_VIOLATION;
- // Instruction failed. Illegal or undefined instruction or operand.
- case EXC_BAD_INSTRUCTION :
+ // Instruction failed. Illegal, privileged, or undefined instruction or operand.
+ case EXC_BAD_INSTRUCTION:
+ // TODO: Identify privileged instruction. Need to get the thread state and read the machine code. May be better to do this in the place that calls SEHProcessException, similar to how it's done on Linux.
return EXCEPTION_ILLEGAL_INSTRUCTION;
// Arithmetic exception; exact nature of exception is in subcode field.
diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp
index 7acb3dce1e..fa445bc3d3 100644
--- a/src/pal/src/exception/seh.cpp
+++ b/src/pal/src/exception/seh.cpp
@@ -57,6 +57,7 @@ const UINT RESERVED_SEH_BIT = 0x800000;
/* Internal variables definitions **********************************************/
PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = NULL;
+PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = NULL;
/* Internal function definitions **********************************************/
@@ -135,6 +136,26 @@ PAL_SetHardwareExceptionHandler(
/*++
Function:
+ PAL_SetGetGcMarkerExceptionCode
+
+ Register a function that determines if the specified IP has code that is a GC marker for GCCover.
+
+Parameters:
+ getGcMarkerExceptionCode - the function to register
+
+Return value:
+ None
+--*/
+VOID
+PALAPI
+PAL_SetGetGcMarkerExceptionCode(
+ IN PGET_GCMARKER_EXCEPTION_CODE getGcMarkerExceptionCode)
+{
+ g_getGcMarkerExceptionCode = getGcMarkerExceptionCode;
+}
+
+/*++
+Function:
SEHProcessException
Build the PAL exception and sent it to any handler registered.
diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp
index aa70c22fff..131433f077 100644
--- a/src/pal/src/exception/signal.cpp
+++ b/src/pal/src/exception/signal.cpp
@@ -626,7 +626,7 @@ static void common_signal_handler(PEXCEPTION_POINTERS pointers, int code,
// Fill context record with required information. from pal.h :
// On non-Win32 platforms, the CONTEXT pointer in the
// PEXCEPTION_POINTERS will contain at least the CONTEXT_CONTROL registers.
- CONTEXTFromNativeContext(ucontext, &context, CONTEXT_CONTROL | CONTEXT_INTEGER);
+ CONTEXTFromNativeContext(ucontext, &context, CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT);
pointers->ContextRecord = &context;
diff --git a/src/pal/src/thread/context.cpp b/src/pal/src/thread/context.cpp
index dfb1c4baf1..f15cdd0270 100644
--- a/src/pal/src/thread/context.cpp
+++ b/src/pal/src/thread/context.cpp
@@ -32,6 +32,8 @@ Abstract:
SET_DEFAULT_DEBUG_CHANNEL(DEBUG);
+extern PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode;
+
// in context2.S
extern void CONTEXT_CaptureContext(LPCONTEXT lpContext);
@@ -603,6 +605,19 @@ DWORD CONTEXTGetExceptionCodeForSignal(const siginfo_t *siginfo,
case SEGV_MAPERR: // Address not mapped to object
case SEGV_ACCERR: // Invalid permissions for mapped object
return EXCEPTION_ACCESS_VIOLATION;
+ case SI_KERNEL:
+ {
+ // Identify privileged instructions that are not identified as such by the system
+ if (g_getGcMarkerExceptionCode != nullptr)
+ {
+ DWORD exceptionCode = g_getGcMarkerExceptionCode(GetNativeContextPC(context));
+ if (exceptionCode != 0)
+ {
+ return exceptionCode;
+ }
+ }
+ // fall through
+ }
default:
break;
}
@@ -639,6 +654,7 @@ DWORD CONTEXTGetExceptionCodeForSignal(const siginfo_t *siginfo,
default:
break;
}
+
ASSERT("Got unknown signal number %d with code %d\n",
siginfo->si_signo, siginfo->si_code);
return EXCEPTION_ILLEGAL_INSTRUCTION;
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index 964c987532..4c26eb4bbe 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -74,6 +74,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
debughelp.cpp
debuginfostore.cpp
decodemd.cpp
+ disassembler.cpp
dllimport.cpp
domainfile.cpp
dynamicmethod.cpp
diff --git a/src/vm/amd64/cgencpu.h b/src/vm/amd64/cgencpu.h
index de64b1600b..42d450ec6b 100644
--- a/src/vm/amd64/cgencpu.h
+++ b/src/vm/amd64/cgencpu.h
@@ -82,7 +82,9 @@ EXTERN_C void FastCallFinalizeWorker(Object *obj, PCODE funcPtr);
#define INSTRFMT_K64SMALL
#define INSTRFMT_K64
+#ifndef FEATURE_PAL
#define USE_REDIRECT_FOR_GCSTRESS
+#endif // FEATURE_PAL
//
// REX prefix byte
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
index c9cd1897a7..305fbf1c96 100644
--- a/src/vm/ceemain.cpp
+++ b/src/vm/ceemain.cpp
@@ -195,6 +195,7 @@
#include "eemessagebox.h"
#include "finalizerthread.h"
#include "threadsuspend.h"
+#include "disassembler.h"
#ifndef FEATURE_PAL
#include "dwreport.h"
@@ -983,6 +984,23 @@ void EEStartupHelper(COINITIEE fFlags)
}
#endif
+#if USE_DISASSEMBLER
+ if ((g_pConfig->GetGCStressLevel() & (EEConfig::GCSTRESS_INSTR_JIT | EEConfig::GCSTRESS_INSTR_NGEN)) != 0)
+ {
+ Disassembler::StaticInitialize();
+ if (!Disassembler::IsAvailable())
+ {
+#ifdef HAVE_GCCOVER
+#ifdef _DEBUG
+ printf("External disassembler is not available. Disabling GCStress for GCSTRESS_INSTR_JIT and GCSTRESS_INSTR_NGEN.\n");
+#endif // _DEBUG
+ g_pConfig->SetGCStressLevel(
+ g_pConfig->GetGCStressLevel() & ~(EEConfig::GCSTRESS_INSTR_JIT | EEConfig::GCSTRESS_INSTR_NGEN));
+#endif // HAVE_GCCOVER
+ }
+ }
+#endif // USE_DISASSEMBLER
+
// Monitors, Crsts, and SimpleRWLocks all use the same spin heuristics
// Cache the (potentially user-overridden) values now so they are accessible from asm routines
InitializeSpinConstants();
@@ -2167,6 +2185,10 @@ part2:
// 2) Only when the runtime is processing DLL_PROCESS_DETACH.
CLRRemoveVectoredHandlers();
+#if USE_DISASSEMBLER
+ Disassembler::StaticClose();
+#endif // USE_DISASSEMBLER
+
#ifdef _DEBUG
if (_DbgBreakCount)
_ASSERTE(!"EE Shutting down after an assert");
diff --git a/src/vm/disassembler.cpp b/src/vm/disassembler.cpp
new file mode 100644
index 0000000000..a65410a1b7
--- /dev/null
+++ b/src/vm/disassembler.cpp
@@ -0,0 +1,308 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "common.h"
+
+#include "disassembler.h"
+#include "dllimport.h"
+
+#if USE_DISASSEMBLER
+
+// TODO: Which contracts should be used where? Currently, everything is using LIMITED_METHOD_CONTRACT.
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+HMODULE Disassembler::s_libraryHandle = nullptr;
+
+Disassembler::CorDisasm *(*Disassembler::External_InitDisasm)(enum TargetArch Target) = nullptr;
+SIZE_T(*Disassembler::External_DisasmInstruction)(const Disassembler::CorDisasm *Disasm, size_t Address,
+ const uint8_t *Bytes, size_t Maxlength,
+ bool PrintAssembly) = nullptr;
+void(*Disassembler::External_FinishDisasm)(const Disassembler::CorDisasm *Disasm) = nullptr;
+#endif // USE_COREDISTOOLS_DISASSEMBLER
+
+Disassembler::ExternalDisassembler *Disassembler::s_availableExternalDisassembler = nullptr;
+
+#if defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
+// static
+bool Disassembler::IsRexPrefix(UINT8 potentialRexByte)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef _TARGET_AMD64_
+ return (potentialRexByte & 0xf0) == REX_PREFIX_BASE;
+#else // !_TARGET_AMD64_
+ return false;
+#endif // _TARGET_AMD64_
+}
+
+// static
+UINT8 Disassembler::DecodeModFromModRm(UINT8 modRm)
+{
+ LIMITED_METHOD_CONTRACT;
+ return modRm >> 6;
+}
+
+// static
+UINT8 Disassembler::DecodeRegOrOpCodeFromModRm(UINT8 modRm)
+{
+ LIMITED_METHOD_CONTRACT;
+ return (modRm >> 3) & 0x7;
+}
+
+// static
+UINT8 Disassembler::DecodeRmFromModRm(UINT8 modRm)
+{
+ LIMITED_METHOD_CONTRACT;
+ return modRm & 0x7;
+}
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
+
+// static
+bool Disassembler::IsAvailable()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ return s_libraryHandle != nullptr;
+#else // !USE_COREDISTOOLS_DISASSEMBLER
+ return true;
+#endif // USE_COREDISTOOLS_DISASSEMBLER
+}
+
+// static
+void Disassembler::StaticInitialize()
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(!IsAvailable());
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ // TODO: The 'coredistools' library will eventually be part of a NuGet package, need to be able to load
+ // that using appropriate search paths
+ LPCWSTR libraryName = MAKEDLLNAME(W("coredistools"));
+ HMODULE libraryHandle = CLRLoadLibrary(libraryName);
+ do
+ {
+ if (libraryHandle == nullptr)
+ {
+ #ifdef _DEBUG
+ wprintf(W("LoadLibrary failed for '%s': error %u\n"), libraryName, GetLastError());
+ #endif // _DEBUG
+ break;
+ }
+
+ External_InitDisasm =
+ reinterpret_cast<decltype(External_InitDisasm)>(GetProcAddress(libraryHandle, "InitDisasm"));
+ if (External_InitDisasm == nullptr)
+ {
+ #ifdef _DEBUG
+ wprintf(
+ W("GetProcAddress failed for library '%s', function 'InitDisasm': error %u\n"),
+ libraryName,
+ GetLastError());
+ #endif // _DEBUG
+ break;
+ }
+
+ External_DisasmInstruction =
+ reinterpret_cast<decltype(External_DisasmInstruction)>(GetProcAddress(libraryHandle, "DisasmInstruction"));
+ if (External_DisasmInstruction == nullptr)
+ {
+ #ifdef _DEBUG
+ wprintf(
+ W("GetProcAddress failed for library '%s', function 'DisasmInstruction': error %u\n"),
+ libraryName,
+ GetLastError());
+ #endif // _DEBUG
+ break;
+ }
+
+ External_FinishDisasm =
+ reinterpret_cast<decltype(External_FinishDisasm)>(GetProcAddress(libraryHandle, "FinishDisasm"));
+ if (External_FinishDisasm == nullptr)
+ {
+ #ifdef _DEBUG
+ wprintf(
+ W("GetProcAddress failed for library '%s', function 'FinishDisasm': error %u\n"),
+ libraryName,
+ GetLastError());
+ #endif // _DEBUG
+ break;
+ }
+
+ // Set this last to indicate successful load of the library and all exports
+ s_libraryHandle = libraryHandle;
+ _ASSERTE(IsAvailable());
+ return;
+ } while (false);
+
+ CLRFreeLibrary(libraryHandle);
+ _ASSERTE(!IsAvailable());
+#endif // USE_COREDISTOOLS_DISASSEMBLER
+}
+
+// static
+void Disassembler::StaticClose()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (!IsAvailable())
+ {
+ return;
+ }
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ CLRFreeLibrary(s_libraryHandle);
+ s_libraryHandle = nullptr;
+#endif
+}
+
+Disassembler::Disassembler()
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(IsAvailable());
+
+ // TODO: Is it ok to save and reuse an instance of the LLVM-based disassembler? It may later be used from a different
+ // thread, and it may be deleted from a different thread than the one from which it was created.
+
+ // Try to get an external disassembler that is already available for use before creating one
+ ExternalDisassembler *externalDisassembler =
+ FastInterlockExchangePointer(&s_availableExternalDisassembler, static_cast<ExternalDisassembler *>(nullptr));
+ if (externalDisassembler == nullptr)
+ {
+ #if USE_COREDISTOOLS_DISASSEMBLER
+ // First parameter:
+ // - Empty string for the current architecture
+ // - A string of the form "x86_64-pc-win32"
+ externalDisassembler = External_InitDisasm(Target_Host);
+ #elif USE_MSVC_DISASSEMBLER
+ #ifdef _TARGET_X86_
+ externalDisassembler = ExternalDisassembler::PdisNew(ExternalDisassembler::distX86);
+ #elif defined(_TARGET_AMD64_)
+ externalDisassembler = ExternalDisassembler::PdisNew(ExternalDisassembler::distX8664);
+ #endif // defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+ #endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ }
+
+ _ASSERTE(externalDisassembler != nullptr);
+ m_externalDisassembler = externalDisassembler;
+}
+
+Disassembler::~Disassembler()
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(IsAvailable());
+
+ // Save the external disassembler for future use. We only save one instance, so delete a previously saved one.
+ ExternalDisassembler *externalDisassemblerToDelete =
+ FastInterlockExchangePointer(&s_availableExternalDisassembler, m_externalDisassembler);
+ if (externalDisassemblerToDelete == nullptr)
+ {
+ return;
+ }
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ External_FinishDisasm(externalDisassemblerToDelete);
+#elif USE_MSVC_DISASSEMBLER
+ delete externalDisassemblerToDelete;
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+}
+
+SIZE_T Disassembler::DisassembleInstruction(const UINT8 *code, SIZE_T codeLength, InstructionType *instructionTypeRef) const
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(IsAvailable());
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ SIZE_T instructionLength = External_DisasmInstruction(m_externalDisassembler, reinterpret_cast<SIZE_T>(code), code, codeLength, false /* PrintAssembly */);
+#elif USE_MSVC_DISASSEMBLER
+ SIZE_T instructionLength =
+ m_externalDisassembler->CbDisassemble(reinterpret_cast<ExternalDisassembler::ADDR>(code), code, codeLength);
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ _ASSERTE(instructionLength <= codeLength);
+
+ if (instructionTypeRef != nullptr)
+ {
+ if (instructionLength == 0)
+ {
+ *instructionTypeRef = InstructionType::Unknown;
+ }
+ else
+ {
+ #if USE_COREDISTOOLS_DISASSEMBLER
+ *instructionTypeRef = DetermineInstructionType(code, instructionLength);
+ #elif USE_MSVC_DISASSEMBLER
+ *instructionTypeRef = DetermineInstructionType(m_externalDisassembler->Trmt());
+ #endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ }
+ }
+
+ return instructionLength;
+}
+
+// static
+InstructionType Disassembler::DetermineInstructionType(
+#if USE_COREDISTOOLS_DISASSEMBLER
+ const UINT8 *instructionCode, SIZE_T instructionCodeLength
+#elif USE_MSVC_DISASSEMBLER
+ ExternalDisassembler::TRMT terminationType
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ )
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ _ASSERTE(instructionCodeLength != 0);
+
+ SIZE_T i = 0;
+ if (Disassembler::IsRexPrefix(instructionCode[i]))
+ {
+ ++i;
+ }
+
+ switch (instructionCode[i])
+ {
+ case 0xe8: // call near rel
+ #ifdef _TARGET_X86_
+ case 0x9a: // call far ptr
+ #endif // _TARGET_X86_
+ return InstructionType::Call_DirectUnconditional;
+
+ case 0xff:
+ ++i;
+ if (i >= instructionCodeLength)
+ {
+ break;
+ }
+
+ switch (Disassembler::DecodeRegOrOpCodeFromModRm(instructionCode[i]))
+ {
+ case 2: // call near r/m
+ case 3: // call far m
+ return InstructionType::Call_IndirectUnconditional;
+
+ case 4: // jmp near r/m
+ case 5: // jmp far m
+ return InstructionType::Branch_IndirectUnconditional;
+ }
+ break;
+ }
+#elif USE_MSVC_DISASSEMBLER
+ switch (terminationType)
+ {
+ case ExternalDisassembler::trmtCall:
+ return InstructionType::Call_DirectUnconditional;
+
+ case ExternalDisassembler::trmtCallInd:
+ return InstructionType::Call_IndirectUnconditional;
+
+ case ExternalDisassembler::trmtBraInd:
+ return InstructionType::Branch_IndirectUnconditional;
+ }
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+
+ return InstructionType::Unknown;
+}
+
+#endif // USE_DISASSEMBLER
diff --git a/src/vm/disassembler.h b/src/vm/disassembler.h
new file mode 100644
index 0000000000..68ac15c93d
--- /dev/null
+++ b/src/vm/disassembler.h
@@ -0,0 +1,122 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef __DISASSEMBLER_H__
+#define __DISASSEMBLER_H__
+
+#include "switches.h"
+
+#define USE_COREDISTOOLS_DISASSEMBLER 0
+#define USE_MSVC_DISASSEMBLER 0
+#ifdef HAVE_GCCOVER
+ #ifdef FEATURE_CORECLR
+ #undef USE_COREDISTOOLS_DISASSEMBLER
+ #define USE_COREDISTOOLS_DISASSEMBLER 1
+ #elif defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
+ #undef USE_MSVC_DISASSEMBLER
+ #define USE_MSVC_DISASSEMBLER 1
+ #endif // defined(FEATURE_CORECLR) || defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
+#endif // HAVE_GCCOVER
+
+#if USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ #define USE_DISASSEMBLER 1
+#else
+ #define USE_DISASSEMBLER 0
+#endif
+
+#if USE_DISASSEMBLER
+
+#if USE_MSVC_DISASSEMBLER
+#undef free
+
+// This pragma is needed because public\vc\inc\xiosbase contains
+// a static local variable
+#pragma warning(disable : 4640)
+#include "msvcdis.h"
+#pragma warning(default : 4640)
+
+#include "disx86.h"
+
+#define free(memblock) Use_free(memblock)
+#endif // USE_MSVC_DISASSEMBLER
+
+enum class InstructionType : UINT8
+{
+ Unknown,
+ Call_DirectUnconditional,
+ Call_IndirectUnconditional,
+ Branch_IndirectUnconditional
+};
+
+class InstructionInfo;
+
+// Wraps the MSVC disassembler or the coredistools disassembler
+class Disassembler
+{
+#if USE_COREDISTOOLS_DISASSEMBLER
+private:
+ class CorDisasm;
+ typedef CorDisasm ExternalDisassembler;
+#elif USE_MSVC_DISASSEMBLER
+private:
+ typedef DIS ExternalDisassembler;
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+
+#if defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
+public:
+ static bool IsRexPrefix(UINT8 potentialRexByte);
+ static UINT8 DecodeModFromModRm(UINT8 modRm);
+ static UINT8 DecodeRegOrOpCodeFromModRm(UINT8 modRm);
+ static UINT8 DecodeRmFromModRm(UINT8 modRm);
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
+
+public:
+ static bool IsAvailable();
+ static void StaticInitialize();
+ static void StaticClose();
+
+public:
+ Disassembler();
+ ~Disassembler();
+
+public:
+ SIZE_T DisassembleInstruction(const UINT8 *code, SIZE_T codeLength, InstructionType *instructionTypeRef) const;
+ static InstructionType DetermineInstructionType(
+ #if USE_COREDISTOOLS_DISASSEMBLER
+ const UINT8 *instructionCode, SIZE_T instructionCodeLength
+ #elif USE_MSVC_DISASSEMBLER
+ ExternalDisassembler::TRMT terminationType
+ #endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ );
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+private:
+ static HMODULE s_libraryHandle;
+
+ // 'coredistools' library exports
+private:
+
+ enum TargetArch {
+ Target_Host, // Target is the same as host architecture
+ Target_X86,
+ Target_X64,
+ Target_Thumb,
+ Target_Arm64
+ };
+
+ static CorDisasm *(*External_InitDisasm)(enum TargetArch Target);
+ static SIZE_T (*External_DisasmInstruction)(const CorDisasm *Disasm, size_t Address,
+ const uint8_t *Bytes, size_t Maxlength,
+ bool PrintAssembly);
+ static void (*External_FinishDisasm)(const CorDisasm *Disasm);
+#endif // USE_COREDISTOOLS_DISASSEMBLER
+
+private:
+ static ExternalDisassembler *s_availableExternalDisassembler;
+ ExternalDisassembler *m_externalDisassembler;
+};
+
+#endif // USE_DISASSEMBLER
+#endif // __DISASSEMBLER_H__
diff --git a/src/vm/excep.cpp b/src/vm/excep.cpp
index 5259e91d1a..c20e6a63eb 100644
--- a/src/vm/excep.cpp
+++ b/src/vm/excep.cpp
@@ -7097,7 +7097,24 @@ bool IsInterceptableException(Thread *pThread)
);
}
-// Did we hit an DO_A_GC_HERE marker in JITTed code?
+// Determines whether we hit an DO_A_GC_HERE marker in JITted code, and returns the
+// appropriate exception code, or zero if the code is not a GC marker.
+DWORD GetGcMarkerExceptionCode(LPVOID ip)
+{
+#if defined(HAVE_GCCOVER) && defined(FEATURE_CORECLR)
+ WRAPPER_NO_CONTRACT;
+
+ if (GCStress<cfg_any>::IsEnabled() && IsGcCoverageInterrupt(ip))
+ {
+ return STATUS_CLR_GCCOVER_CODE;
+ }
+#else // !(defined(HAVE_GCCOVER) && defined(FEATURE_CORECLR))
+ LIMITED_METHOD_CONTRACT;
+#endif // defined(HAVE_GCCOVER) && defined(FEATURE_CORECLR)
+ return 0;
+}
+
+// Did we hit an DO_A_GC_HERE marker in JITted code?
bool IsGcMarker(DWORD exceptionCode, CONTEXT *pContext)
{
#ifdef HAVE_GCCOVER
diff --git a/src/vm/excep.h b/src/vm/excep.h
index 947d7896e1..46b379eb58 100644
--- a/src/vm/excep.h
+++ b/src/vm/excep.h
@@ -769,6 +769,7 @@ LONG NotifyDebuggerLastChance(Thread *pThread,
void CPFH_AdjustContextForThreadSuspensionRace(T_CONTEXT *pContext, Thread *pThread);
#endif // _TARGET_X86_
+DWORD GetGcMarkerExceptionCode(LPVOID ip);
bool IsGcMarker(DWORD exceptionCode, T_CONTEXT *pContext);
void InitSavedExceptionInfo();
diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp
index 4441f3b039..04d5c69ae2 100644
--- a/src/vm/exceptionhandling.cpp
+++ b/src/vm/exceptionhandling.cpp
@@ -156,6 +156,9 @@ void InitializeExceptionHandling()
#ifdef FEATURE_PAL
// Register handler of hardware exceptions like null reference in PAL
PAL_SetHardwareExceptionHandler(HandleHardwareException);
+
+ // Register handler for determining whether the specified IP has code that is a GC marker for GCCover
+ PAL_SetGetGcMarkerExceptionCode(GetGcMarkerExceptionCode);
#endif // FEATURE_PAL
}
@@ -5039,8 +5042,15 @@ VOID PALAPI HandleHardwareException(PAL_SEHException* ex)
// A hardware exception is handled only if it happened in a jitted code or
// in one of the JIT helper functions (JIT_MemSet, ...)
PCODE controlPc = GetIP(&ex->ContextRecord);
- if (ExecutionManager::IsManagedCode(controlPc) || IsIPInMarkedJitHelper(controlPc))
+ BOOL isInManagedCode = ExecutionManager::IsManagedCode(controlPc);
+ if (isInManagedCode || IsIPInMarkedJitHelper(controlPc))
{
+ if (isInManagedCode && IsGcMarker(ex->ExceptionRecord.ExceptionCode, &ex->ContextRecord))
+ {
+ RtlRestoreContext(&ex->ContextRecord, &ex->ExceptionRecord);
+ UNREACHABLE();
+ }
+
// Create frame necessary for the exception handling
FrameWithCookie<FaultingExceptionFrame> fef;
#if defined(WIN64EXCEPTIONS)
diff --git a/src/vm/gccover.cpp b/src/vm/gccover.cpp
index 8a591d9faa..fd5a6154b4 100644
--- a/src/vm/gccover.cpp
+++ b/src/vm/gccover.cpp
@@ -32,59 +32,16 @@
#include "gcinfodecoder.h"
#endif
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4244)
-#endif // _MSC_VER
-
-
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
-#undef free
-
-// This pragma is needed because public\vc\inc\xiosbase contains
-// a static local variable
-#pragma warning(disable : 4640)
-#include "msvcdis.h"
-#pragma warning(default : 4640)
-
-#include "disx86.h"
-
-#define free(memblock) Use_free(memblock)
-
- // We need a X86 instruction walker (disassembler), here are some
- // routines for caching such a disassembler in a concurrent environment.
-static DIS* g_Disasm = 0;
-
-
-static DIS* GetDisasm() {
- DIS* myDisasm = FastInterlockExchangePointer(&g_Disasm, 0);
- if (myDisasm == 0)
- {
-#ifdef _TARGET_X86_
- myDisasm = DIS::PdisNew(DIS::distX86);
-#elif defined(_TARGET_AMD64_)
- myDisasm = DIS::PdisNew(DIS::distX8664);
-#endif
- }
- _ASSERTE(myDisasm);
- return(myDisasm);
-}
-
-static void ReleaseDisasm(DIS* myDisasm) {
- myDisasm = FastInterlockExchangePointer(&g_Disasm, myDisasm);
- delete myDisasm;
-}
-
-#endif // defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
-
+#include "disassembler.h"
/****************************************************************************/
MethodDesc* AsMethodDesc(size_t addr);
static SLOT getTargetOfCall(SLOT instrPtr, PCONTEXT regs, SLOT*nextInstr);
+#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
static void replaceSafePointInstructionWithGcStressInstr(UINT32 safePointOffset, LPVOID codeStart);
static bool replaceInterruptibleRangesWithGcStressInstr (UINT32 startOffset, UINT32 stopOffset, LPVOID codeStart);
+#endif
static MethodDesc* getTargetMethodDesc(PCODE target)
{
@@ -360,8 +317,8 @@ private:
// entire funclet.
//
unsigned ofsLastInterruptible = m_pCodeManager->FindEndOfLastInterruptibleRegion(
- pCurFunclet - m_codeStart,
- m_curFuncletEnd - m_codeStart,
+ static_cast<unsigned int>(pCurFunclet - m_codeStart),
+ static_cast<unsigned int>(m_curFuncletEnd - m_codeStart),
m_pvGCInfo);
if (ofsLastInterruptible)
@@ -450,7 +407,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
#endif
cur = codeStart;
- DIS* pdis = GetDisasm();
+ Disassembler disassembler;
// When we find a direct call instruction and we are partially-interruptible
// we determine the target and place a breakpoint after the call
@@ -481,7 +438,8 @@ void GCCoverageInfo::SprinkleBreakpoints(
_ASSERTE(*cur != INTERRUPT_INSTR && *cur != INTERRUPT_INSTR_CALL);
MethodDesc* targetMD = NULL;
- size_t len = pdis->CbDisassemble(0, cur, codeEnd-cur);
+ InstructionType instructionType;
+ size_t len = disassembler.DisassembleInstruction(cur, codeEnd - cur, &instructionType);
#ifdef _TARGET_AMD64_
// REVISIT_TODO apparently the jit does not use the entire RUNTIME_FUNCTION range
@@ -504,9 +462,9 @@ void GCCoverageInfo::SprinkleBreakpoints(
_ASSERTE(len > 0);
_ASSERTE(len <= (size_t)(codeEnd-cur));
- switch(pdis->Trmt())
+ switch(instructionType)
{
- case DIS::trmtCallInd:
+ case InstructionType::Call_IndirectUnconditional:
#ifdef _TARGET_AMD64_
if(safePointDecoder.IsSafePoint((UINT32)(cur + len - codeStart + regionOffsetAdj)))
#endif
@@ -515,7 +473,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
}
break;
- case DIS::trmtCall:
+ case InstructionType::Call_DirectUnconditional:
if(fGcStressOnDirectCalls.val(CLRConfig::INTERNAL_GcStressOnDirectCalls))
{
#ifdef _TARGET_AMD64_
@@ -532,11 +490,16 @@ void GCCoverageInfo::SprinkleBreakpoints(
}
}
break;
+
#ifdef _TARGET_AMD64_
- case DIS::trmtBraInd:
+ case InstructionType::Branch_IndirectUnconditional:
fSawPossibleSwitch = true;
break;
#endif
+
+ default:
+ // Clang issues an error saying that some enum values are not handled in the switch, that's intended
+ break;
}
if (prevDirectCallTargetMD != 0)
@@ -552,7 +515,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
// up only touching the call instructions (specially so that we
// can really do the GC on the instruction just after the call).
_ASSERTE(FitsIn<DWORD>((cur - codeStart) + regionOffsetAdj));
- if (codeMan->IsGcSafe(&codeInfo, (cur - codeStart) + (DWORD)regionOffsetAdj))
+ if (codeMan->IsGcSafe(&codeInfo, static_cast<DWORD>((cur - codeStart) + regionOffsetAdj)))
*cur = INTERRUPT_INSTR;
#ifdef _TARGET_X86_
@@ -586,8 +549,6 @@ void GCCoverageInfo::SprinkleBreakpoints(
if ((regionOffsetAdj==0) && (*codeStart != INTERRUPT_INSTR))
doingEpilogChecks = false;
- ReleaseDisasm(pdis);
-
#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
//Save the method code from hotRegion
memcpy(saveAddr, (BYTE*)methodRegion.hotStartAddress, methodRegion.hotSize);
@@ -1155,6 +1116,38 @@ int GCcoverCount = 0;
void* forceStack[8];
/****************************************************************************/
+
+bool IsGcCoverageInterrupt(LPVOID ip)
+{
+ // Determine if the IP is valid for a GC marker first, before trying to dereference it to check the instruction
+
+ EECodeInfo codeInfo(reinterpret_cast<PCODE>(ip));
+ if (!codeInfo.IsValid())
+ {
+ return false;
+ }
+
+ GCCoverageInfo *gcCover = codeInfo.GetMethodDesc()->m_GcCover;
+ if (gcCover == nullptr)
+ {
+ return false;
+ }
+
+ // Now it's safe to dereference the IP to check the instruction
+ UINT8 instructionCode = *reinterpret_cast<UINT8 *>(ip);
+ switch (instructionCode)
+ {
+ case INTERRUPT_INSTR:
+ case INTERRUPT_INSTR_CALL:
+ case INTERRUPT_INSTR_PROTECT_RET:
+ return true;
+
+ default:
+ // Another thread may have already changed the code back to the original
+ return instructionCode == gcCover->savedCode[codeInfo.GetRelOffset()];
+ }
+}
+
BOOL OnGcCoverageInterrupt(PCONTEXT regs)
{
SO_NOT_MAINLINE_FUNCTION;
@@ -1673,11 +1666,5 @@ void DoGcStress (PCONTEXT regs, MethodDesc *pMD)
}
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif // _MSC_VER: warning C4244
-#endif // _TARGET_X86_ || _TARGET_AMD64_
-
#endif // HAVE_GCCOVER
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index 6ca4299f63..519ccc266c 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -14404,15 +14404,23 @@ LPVOID EECodeInfo::findNextFunclet (LPVOID pvFuncletStart, SIZE_T
while (cbCode > 0)
{
+ PRUNTIME_FUNCTION pFunctionEntry;
+ ULONGLONG uImageBase;
+#ifdef FEATURE_PAL
+ EECodeInfo codeInfo;
+ codeInfo.Init((PCODE)pvFuncletStart);
+ pFunctionEntry = codeInfo.GetFunctionEntry();
+ uImageBase = (ULONGLONG)codeInfo.GetModuleBase();
+#else // !FEATURE_PAL
//
// This is GCStress debug only - use the slow OS APIs to enumerate funclets
//
- ULONGLONG uImageBase;
- PRUNTIME_FUNCTION pFunctionEntry = (PRUNTIME_FUNCTION) RtlLookupFunctionEntry((ULONGLONG)pvFuncletStart,
- &uImageBase
- AMD64_ARG(NULL)
- );
+ pFunctionEntry = (PRUNTIME_FUNCTION) RtlLookupFunctionEntry((ULONGLONG)pvFuncletStart,
+ &uImageBase
+ AMD64_ARG(NULL)
+ );
+#endif
if (pFunctionEntry != NULL)
{
diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h
index ad34fa9a8c..0035715e34 100644
--- a/src/vm/jitinterface.h
+++ b/src/vm/jitinterface.h
@@ -1614,6 +1614,7 @@ void *GenFastGetSharedStaticBase(bool bCheckCCtor);
#ifdef HAVE_GCCOVER
void SetupGcCoverage(MethodDesc* pMD, BYTE* nativeCode);
void SetupGcCoverageForNativeImage(Module* module);
+bool IsGcCoverageInterrupt(LPVOID ip);
BOOL OnGcCoverageInterrupt(PT_CONTEXT regs);
void DoGcStress (PT_CONTEXT regs, MethodDesc *pMD);
#endif //HAVE_GCCOVER