diff options
author | Sean Gillespie <segilles@microsoft.com> | 2017-10-27 14:37:54 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-27 14:37:54 -0700 |
commit | bd3424913138d10f9c4fdb6176fb471e8d1ba1d7 (patch) | |
tree | ca05fccdea9a8584194b4a4acf5b7cb1aab6800f /src/vm/gcheaputilities.cpp | |
parent | fb4af6c2908b633b200b556986815781f2139f4e (diff) | |
download | coreclr-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.cpp | 198 |
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 |