summaryrefslogtreecommitdiff
path: root/src/vm/gcheaputilities.cpp
diff options
context:
space:
mode:
authorSean Gillespie <segilles@microsoft.com>2017-10-27 14:37:54 -0700
committerGitHub <noreply@github.com>2017-10-27 14:37:54 -0700
commitbd3424913138d10f9c4fdb6176fb471e8d1ba1d7 (patch)
treeca05fccdea9a8584194b4a4acf5b7cb1aab6800f /src/vm/gcheaputilities.cpp
parentfb4af6c2908b633b200b556986815781f2139f4e (diff)
downloadcoreclr-bd3424913138d10f9c4fdb6176fb471e8d1ba1d7.tar.gz
coreclr-bd3424913138d10f9c4fdb6176fb471e8d1ba1d7.tar.bz2
coreclr-bd3424913138d10f9c4fdb6176fb471e8d1ba1d7.zip
[Local GC] Implement loader protocol for a standalone GC (#14663)
* First cut of the load protocol * Implement for non-standalone GC * Initial working implementation * First steps towards not using GetProcAddress when not using a standalone GC * Factor out loading routines into standalone and non-standalone cases * Remove the FEATURE_STANDALONE_GC_ONLY build * Code cleanup and comments * Comments for the version numbers * Use more appropriate type for config string * add GC_LOAD_STATUS_BEFORE_START to disambiguate failures before the start of the load and failures at the beginning of the load * FEATURE_STANDALONE_GC on by default * Implement YieldProcessor and MemoryBarrier for arm and arm64 * Remove missed FEATURE_STANDALONE_GC feature check
Diffstat (limited to 'src/vm/gcheaputilities.cpp')
-rw-r--r--src/vm/gcheaputilities.cpp198
1 files changed, 198 insertions, 0 deletions
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