summaryrefslogtreecommitdiff
path: root/src/vm
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm')
-rw-r--r--src/vm/CMakeLists.txt28
-rw-r--r--src/vm/ceemain.cpp115
-rw-r--r--src/vm/gcheaputilities.cpp198
-rw-r--r--src/vm/gcheaputilities.h5
4 files changed, 213 insertions, 133 deletions
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index 0aed676d94..8b9219dee3 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -10,15 +10,11 @@ add_definitions(-D_UNICODE)
if(CMAKE_CONFIGURATION_TYPES) # multi-configuration generator?
foreach (Config DEBUG CHECKED)
- if(NOT FEATURE_STANDALONE_GC_ONLY)
- set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:${Config}>:WRITE_BARRIER_CHECK=1>)
- endif(NOT FEATURE_STANDALONE_GC_ONLY)
+ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:${Config}>:WRITE_BARRIER_CHECK=1>)
endforeach (Config)
else()
if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
- if(NOT FEATURE_STANDALONE_GC_ONLY)
- add_definitions(-DWRITE_BARRIER_CHECK=1)
- endif(NOT FEATURE_STANDALONE_GC_ONLY)
+ add_definitions(-DWRITE_BARRIER_CHECK=1)
endif(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
endif(CMAKE_CONFIGURATION_TYPES)
@@ -200,6 +196,7 @@ set(VM_SOURCES_WKS
gccover.cpp
gcenv.ee.static.cpp
gcenv.ee.common.cpp
+ gcenv.os.cpp
gchelpers.cpp
genmeth.cpp
hosting.cpp
@@ -270,6 +267,7 @@ set(GC_SOURCES_WKS
../gc/gchandletable.cpp
../gc/gceesvr.cpp
../gc/gceewks.cpp
+ ../gc/gcload.cpp
../gc/softwarewritewatch.cpp
../gc/handletablecache.cpp)
@@ -285,12 +283,6 @@ if(FEATURE_STANDALONE_GC)
)
endif(FEATURE_STANDALONE_GC)
-if(NOT FEATURE_STANDALONE_GC)
- list(APPEND VM_SOURCES_WKS
- gcenv.os.cpp
- )
-endif(NOT FEATURE_STANDALONE_GC)
-
if(WIN32)
set(VM_SOURCES_DAC_AND_WKS_WIN32
@@ -511,15 +503,9 @@ list(APPEND VM_SOURCES_DAC
${VM_SOURCES_DAC_AND_WKS_ARCH}
)
-# The default option for FEATURE_STANDALONE_GC builds a standalone
-# and non-standalone GC, linking the non-standalone GC into coreclr.dll.
-# For testing purposes, FEATURE_STANDALONE_GC_ONLY instead only builds and
-# links the non-standalone GC into coreclr.dll.
-if (NOT FEATURE_STANDALONE_GC_ONLY)
- list(APPEND VM_SOURCES_WKS
- ${GC_SOURCES_WKS}
- )
-endif(NOT FEATURE_STANDALONE_GC_ONLY)
+list(APPEND VM_SOURCES_WKS
+ ${GC_SOURCES_WKS}
+)
# The DAC does need GC sources in order to link correctly, even if
# it's not used.
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
index 9dbe2b9dff..f3b72e01e2 100644
--- a/src/vm/ceemain.cpp
+++ b/src/vm/ceemain.cpp
@@ -175,7 +175,6 @@
#include "finalizerthread.h"
#include "threadsuspend.h"
#include "disassembler.h"
-#include "gcenv.ee.h"
#ifndef FEATURE_PAL
#include "dwreport.h"
@@ -2447,103 +2446,6 @@ BOOL ExecuteDLL_ReturnOrThrow(HRESULT hr, BOOL fFromThunk)
// Initialize the Garbage Collector
//
-// Prototype for the function that initialzes the garbage collector.
-// Should only be called once: here, during EE startup.
-// Returns true if the initialization was successful, false otherwise.
-//
-// When using a standalone GC, this function is loaded dynamically using
-// GetProcAddress.
-extern "C" bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleManager** gcHandleManager, GcDacVars* gcDacVars);
-
-#ifdef FEATURE_STANDALONE_GC
-
-void LoadGarbageCollector()
-{
- CONTRACTL {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- } CONTRACTL_END;
-
- TCHAR *standaloneGc = nullptr;
- CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStandaloneLocation, &standaloneGc);
- HMODULE hMod;
- if (!standaloneGc)
- {
-#ifdef FEATURE_STANDALONE_GC_ONLY
- // if the user has set GCUseStandalone but has not given us a standalone location,
- // try and load the initialization symbol from the current module.
- hMod = GetModuleInst();
-#else
- ThrowHR(E_FAIL);
-#endif // FEATURE_STANDALONE_GC_ONLY
- }
- else
- {
- hMod = CLRLoadLibrary(standaloneGc);
- }
-
- if (!hMod)
- {
- ThrowHR(E_FAIL);
- }
-
- InitializeGarbageCollectorFunction igcf = (InitializeGarbageCollectorFunction)GetProcAddress(hMod, INITIALIZE_GC_FUNCTION_NAME);
- if (!igcf)
- {
- ThrowHR(E_FAIL);
- }
-
- // at this point we are committing to using the standalone GC
- // given to us.
- IGCToCLR* gcToClr = new (nothrow) standalone::GCToEEInterface();
- if (!gcToClr)
- {
- ThrowOutOfMemory();
- }
-
- IGCHandleManager *pGcHandleManager;
- IGCHeap *pGCHeap;
- if (!igcf(gcToClr, &pGCHeap, &pGcHandleManager, &g_gc_dac_vars))
- {
- ThrowOutOfMemory();
- }
-
- assert(pGCHeap != nullptr);
- assert(pGcHandleManager != nullptr);
- g_pGCHeap = pGCHeap;
- g_pGCHandleManager = pGcHandleManager;
- g_gcDacGlobals = &g_gc_dac_vars;
-}
-
-#endif // FEATURE_STANDALONE_GC
-
-#ifndef FEATURE_STANDALONE_GC_ONLY
-void LoadStaticGarbageCollector()
-{
- CONTRACTL{
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- } CONTRACTL_END;
-
- IGCHandleManager *pGcHandleManager;
- IGCHeap *pGCHeap;
-
- if (!InitializeGarbageCollector(nullptr, &pGCHeap, &pGcHandleManager, &g_gc_dac_vars))
- {
- ThrowOutOfMemory();
- }
-
- assert(pGCHeap != nullptr);
- assert(pGcHandleManager != nullptr);
- g_pGCHeap = pGCHeap;
- g_pGCHandleManager = pGcHandleManager;
- g_gcDacGlobals = &g_gc_dac_vars;
-}
-#endif // FEATURE_STANDALONE_GC_ONLY
-
-
void InitializeGarbageCollector()
{
CONTRACTL{
@@ -2566,21 +2468,10 @@ void InitializeGarbageCollector()
g_pFreeObjectMethodTable->SetBaseSize(ObjSizeOf (ArrayBase));
g_pFreeObjectMethodTable->SetComponentSize(1);
-#ifdef FEATURE_STANDALONE_GC
- if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCUseStandalone)
-#ifdef FEATURE_STANDALONE_GC_ONLY
- || true
-#endif // FEATURE_STANDALONE_GC_ONLY
- )
- {
- LoadGarbageCollector();
- }
- else
-#endif // FEATURE_STANDALONE_GC
+ hr = GCHeapUtilities::LoadAndInitialize();
+ if (hr != S_OK)
{
-#ifndef FEATURE_STANDALONE_GC_ONLY
- LoadStaticGarbageCollector();
-#endif // FEATURE_STANDALONE_GC_ONLY
+ ThrowHR(hr);
}
// Apparently the Windows linker removes global variables if they are never
diff --git a/src/vm/gcheaputilities.cpp b/src/vm/gcheaputilities.cpp
index 3a6a42fd9d..90836a0fd2 100644
--- a/src/vm/gcheaputilities.cpp
+++ b/src/vm/gcheaputilities.cpp
@@ -4,6 +4,7 @@
#include "common.h"
#include "gcheaputilities.h"
+#include "gcenv.ee.h"
#include "appdomain.hpp"
@@ -36,3 +37,200 @@ bool g_sw_ww_enabled_for_gc_heap = false;
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
gc_alloc_context g_global_alloc_context = {};
+
+enum GC_LOAD_STATUS {
+ GC_LOAD_STATUS_BEFORE_START,
+ GC_LOAD_STATUS_START,
+ GC_LOAD_STATUS_DONE_LOAD,
+ GC_LOAD_STATUS_GET_VERSIONINFO,
+ GC_LOAD_STATUS_CALL_VERSIONINFO,
+ GC_LOAD_STATUS_DONE_VERSION_CHECK,
+ GC_LOAD_STATUS_GET_INITIALIZE,
+ GC_LOAD_STATUS_LOAD_COMPLETE
+};
+
+// Load status of the GC. If GC loading fails, the value of this
+// global indicates where the failure occured.
+GC_LOAD_STATUS g_gc_load_status = GC_LOAD_STATUS_BEFORE_START;
+
+// The version of the GC that we have loaded.
+VersionInfo g_gc_version_info;
+
+// GC entrypoints for the the linked-in GC. These symbols are invoked
+// directly if we are not using a standalone GC.
+extern "C" void GC_VersionInfo(/* Out */ VersionInfo* info);
+extern "C" HRESULT GC_Initialize(
+ /* In */ IGCToCLR* clrToGC,
+ /* Out */ IGCHeap** gcHeap,
+ /* Out */ IGCHandleManager** gcHandleManager,
+ /* Out */ GcDacVars* gcDacVars
+);
+
+#ifndef DACCESS_COMPILE
+
+namespace
+{
+
+// Loads and initializes a standalone GC, given the path to the GC
+// that we should load. Returns S_OK on success and the failed HRESULT
+// on failure.
+//
+// See Documentation/design-docs/standalone-gc-loading.md for details
+// on the loading protocol in use here.
+HRESULT LoadAndInitializeGC(LPWSTR standaloneGcLocation)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifndef FEATURE_STANDALONE_GC
+ LOG((LF_GC, LL_FATALERROR, "EE not built with the ability to load standalone GCs"));
+ return E_FAIL;
+#else
+ LOG((LF_GC, LL_INFO100, "Loading standalone GC from path %S\n", standaloneGcLocation));
+ HMODULE hMod = CLRLoadLibrary(standaloneGcLocation);
+ if (!hMod)
+ {
+ HRESULT err = GetLastError();
+ LOG((LF_GC, LL_FATALERROR, "Load of %S failed\n", standaloneGcLocation));
+ return err;
+ }
+
+ // a standalone GC dispatches virtually on GCToEEInterface, so we must instantiate
+ // a class for the GC to use.
+ IGCToCLR* gcToClr = new (nothrow) standalone::GCToEEInterface();
+ if (!gcToClr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ g_gc_load_status = GC_LOAD_STATUS_DONE_LOAD;
+ GC_VersionInfoFunction versionInfo = (GC_VersionInfoFunction)GetProcAddress(hMod, "GC_VersionInfo");
+ if (!versionInfo)
+ {
+ HRESULT err = GetLastError();
+ LOG((LF_GC, LL_FATALERROR, "Load of `GC_VersionInfo` from standalone GC failed\n"));
+ return err;
+ }
+
+ g_gc_load_status = GC_LOAD_STATUS_GET_VERSIONINFO;
+ versionInfo(&g_gc_version_info);
+ g_gc_load_status = GC_LOAD_STATUS_CALL_VERSIONINFO;
+
+ if (g_gc_version_info.MajorVersion != GC_INTERFACE_MAJOR_VERSION)
+ {
+ LOG((LF_GC, LL_FATALERROR, "Loaded GC has incompatible major version number (expected %d, got %d)\n",
+ GC_INTERFACE_MAJOR_VERSION, g_gc_version_info.MajorVersion));
+ return E_FAIL;
+ }
+
+ if (g_gc_version_info.MinorVersion < GC_INTERFACE_MINOR_VERSION)
+ {
+ LOG((LF_GC, LL_INFO100, "Loaded GC has lower minor version number (%d) than EE was compiled against (%d)\n",
+ g_gc_version_info.MinorVersion, GC_INTERFACE_MINOR_VERSION));
+ }
+
+ LOG((LF_GC, LL_INFO100, "Loaded GC identifying itself with name `%s`\n", g_gc_version_info.Name));
+ g_gc_load_status = GC_LOAD_STATUS_DONE_VERSION_CHECK;
+ GC_InitializeFunction initFunc = (GC_InitializeFunction)GetProcAddress(hMod, "GC_Initialize");
+ if (!initFunc)
+ {
+ HRESULT err = GetLastError();
+ LOG((LF_GC, LL_FATALERROR, "Load of `GC_Initialize` from standalone GC failed\n"));
+ return err;
+ }
+
+ g_gc_load_status = GC_LOAD_STATUS_GET_INITIALIZE;
+ IGCHeap* heap;
+ IGCHandleManager* manager;
+ HRESULT initResult = initFunc(gcToClr, &heap, &manager, &g_gc_dac_vars);
+ if (initResult == S_OK)
+ {
+ g_pGCHeap = heap;
+ g_pGCHandleManager = manager;
+ g_gcDacGlobals = &g_gc_dac_vars;
+ g_gc_load_status = GC_LOAD_STATUS_LOAD_COMPLETE;
+ LOG((LF_GC, LL_INFO100, "GC load successful\n"));
+ }
+ else
+ {
+ LOG((LF_GC, LL_FATALERROR, "GC initialization failed with HR = 0x%X\n", initResult));
+ }
+
+ return initResult;
+#endif // FEATURE_STANDALONE_GC
+}
+
+// Initializes a non-standalone GC. The protocol for initializing a non-standalone GC
+// is similar to loading a standalone one, except that the GC_VersionInfo and
+// GC_Initialize symbols are linked to directory and thus don't need to be loaded.
+//
+// The major and minor versions are still checked in debug builds - it must be the case
+// that the GC and EE agree on a shared version number because they are built from
+// the same sources.
+HRESULT InitializeDefaultGC()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ LOG((LF_GC, LL_INFO100, "Standalone GC location not provided, using provided GC\n"));
+
+ g_gc_load_status = GC_LOAD_STATUS_DONE_LOAD;
+ VersionInfo info;
+ GC_VersionInfo(&g_gc_version_info);
+ g_gc_load_status = GC_LOAD_STATUS_CALL_VERSIONINFO;
+
+ // the default GC builds with the rest of the EE. By definition, it must have been
+ // built with the same interface version.
+ assert(g_gc_version_info.MajorVersion == GC_INTERFACE_MAJOR_VERSION);
+ assert(g_gc_version_info.MinorVersion == GC_INTERFACE_MINOR_VERSION);
+ g_gc_load_status = GC_LOAD_STATUS_DONE_VERSION_CHECK;
+
+ IGCHeap* heap;
+ IGCHandleManager* manager;
+ HRESULT initResult = GC_Initialize(nullptr, &heap, &manager, &g_gc_dac_vars);
+ if (initResult == S_OK)
+ {
+ g_pGCHeap = heap;
+ g_pGCHandleManager = manager;
+ g_gcDacGlobals = &g_gc_dac_vars;
+ g_gc_load_status = GC_LOAD_STATUS_LOAD_COMPLETE;
+ LOG((LF_GC, LL_INFO100, "GC load successful\n"));
+ }
+ else
+ {
+ LOG((LF_GC, LL_FATALERROR, "GC initialization failed with HR = 0x%X\n", initResult));
+ }
+
+
+ return initResult;
+}
+
+} // anonymous namespace
+
+// Loads (if necessary) and initializes the GC. If using a standalone GC,
+// it loads the library containing it and dynamically loads the GC entry point.
+// If using a non-standalone GC, it invokes the GC entry point directly.
+HRESULT GCHeapUtilities::LoadAndInitialize()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // we should only call this once on startup. Attempting to load a GC
+ // twice is an error.
+ assert(g_pGCHeap == nullptr);
+
+ // we should not have attempted to load a GC already. Attempting a
+ // load after the first load already failed is an error.
+ assert(g_gc_load_status == GC_LOAD_STATUS_BEFORE_START);
+ g_gc_load_status = GC_LOAD_STATUS_START;
+
+ LPWSTR standaloneGcLocation = nullptr;
+ CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCName, &standaloneGcLocation);
+ if (!standaloneGcLocation)
+ {
+ return InitializeDefaultGC();
+ }
+ else
+ {
+ return LoadAndInitializeGC(standaloneGcLocation);
+ }
+}
+
+#endif // DACCESS_COMPILE
diff --git a/src/vm/gcheaputilities.h b/src/vm/gcheaputilities.h
index e4812ce8c0..b4bd5164af 100644
--- a/src/vm/gcheaputilities.h
+++ b/src/vm/gcheaputilities.h
@@ -196,6 +196,11 @@ public:
}
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+#ifndef DACCESS_COMPILE
+ // Loads (if using a standalone GC) and initializes the GC.
+ static HRESULT LoadAndInitialize();
+#endif // DACCESS_COMPILE
+
private:
// This class should never be instantiated.
GCHeapUtilities() = delete;