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 | |
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
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | build.cmd | 17 | ||||
-rwxr-xr-x | build.sh | 4 | ||||
-rw-r--r-- | clrfeatures.cmake | 8 | ||||
-rw-r--r-- | src/gc/CMakeLists.txt | 26 | ||||
-rw-r--r-- | src/gc/env/gcenv.base.h | 12 | ||||
-rw-r--r-- | src/gc/gccommon.cpp | 103 | ||||
-rw-r--r-- | src/gc/gcinterface.h | 43 | ||||
-rw-r--r-- | src/gc/gcload.cpp | 120 | ||||
-rw-r--r-- | src/gc/sample/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/gc/sample/GCSample.cpp | 4 | ||||
-rw-r--r-- | src/inc/clrconfigvalues.h | 3 | ||||
-rw-r--r-- | src/vm/CMakeLists.txt | 28 | ||||
-rw-r--r-- | src/vm/ceemain.cpp | 115 | ||||
-rw-r--r-- | src/vm/gcheaputilities.cpp | 198 | ||||
-rw-r--r-- | src/vm/gcheaputilities.h | 5 |
16 files changed, 404 insertions, 289 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b4a9a4a95..a48ef765cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -599,10 +599,6 @@ if(FEATURE_STANDALONE_GC) add_subdirectory(src/gc) endif(FEATURE_STANDALONE_GC) -if(FEATURE_STANDALONE_GC_ONLY) - add_definitions(-DFEATURE_STANDALONE_GC_ONLY) -endif(FEATURE_STANDALONE_GC_ONLY) - if (CLR_CMAKE_PLATFORM_UNIX) include_directories("src/pal/inc") include_directories("src/pal/inc/rt") @@ -93,8 +93,6 @@ set __BuildArchArm64=0 set __BuildTypeDebug=0 set __BuildTypeChecked=0 set __BuildTypeRelease=0 -set __BuildStandaloneGC="-DFEATURE_STANDALONE_GC=0" -set __BuildStandaloneGCOnly="-DFEATURE_STANDALONE_GC_ONLY=0" set __PgoInstrument=0 set __PgoOptimize=1 @@ -177,12 +175,6 @@ if /i "%1" == "-enforcepgo" (set __EnforcePgo=1&set processedArgs=!proc if /i "%1" == "-nopgooptimize" (set __PgoOptimize=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-ibcinstrument" (set __IbcTuning=/Tuning&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-toolset_dir" (set __ToolsetDir=%2&set __PassThroughArgs=%__PassThroughArgs% %2&set processedArgs=!processedArgs! %1 %2&shift&shift&goto Arg_Loop) -if /i "%1" == "-buildstandalonegc" ( - set __BuildStandaloneGC="-DFEATURE_STANDALONE_GC=1" - set __BuildStandaloneGCOnly="-DFEATURE_STANDALONE_GC_ONLY=1" - set processedArgs=!processedArgs! %1 - shift&goto Arg_Loop -) REM TODO these are deprecated remove them eventually REM don't add more, use the - syntax instead @@ -205,12 +197,6 @@ if /i "%1" == "nopgooptimize" (set __PgoOptimize=0&set processedArgs=!proc if /i "%1" == "enforcepgo" (set __EnforcePgo=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "ibcinstrument" (set __IbcTuning=/Tuning&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "toolset_dir" (set __ToolsetDir=%2&set __PassThroughArgs=%__PassThroughArgs% %2&set processedArgs=!processedArgs! %1 %2&shift&shift&goto Arg_Loop) -if /i "%1" == "buildstandalonegc" ( - set __BuildStandaloneGC="-DFEATURE_STANDALONE_GC=1" - set __BuildStandaloneGCOnly="-DFEATURE_STANDALONE_GC_ONLY=1" - set processedArgs=!processedArgs! %1 - shift&goto Arg_Loop -) @REM The following can be deleted once the CI system that passes it is updated to not pass it. if /i "%1" == "altjitcrossgen" (set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) @@ -405,7 +391,7 @@ if %__BuildNative% EQU 1 ( pushd "%__IntermediatesDir%" set __ExtraCmakeArgs=!___SDKVersion! "-DCLR_CMAKE_TARGET_OS=%__BuildOs%" "-DCLR_CMAKE_PACKAGES_DIR=%__PackagesDir%" "-DCLR_CMAKE_PGO_INSTRUMENT=%__PgoInstrument%" "-DCLR_CMAKE_OPTDATA_VERSION=%__PgoOptDataVersion%" "-DCLR_CMAKE_PGO_OPTIMIZE=%__PgoOptimize%" - call "%__SourceDir%\pal\tools\gen-buildsys-win.bat" "%__ProjectDir%" %__VSVersion% %__BuildArch% %__BuildStandaloneGC% %__BuildStandaloneGCOnly% !__ExtraCmakeArgs! + call "%__SourceDir%\pal\tools\gen-buildsys-win.bat" "%__ProjectDir%" %__VSVersion% %__BuildArch% !__ExtraCmakeArgs! @if defined _echo @echo on popd @@ -805,7 +791,6 @@ echo -skipnative: skip building native components ^(default: native components a echo -skiptests: skip building tests ^(default: tests are built^). echo -skipbuildpackages: skip building nuget packages ^(default: packages are built^). echo -skiprestoreoptdata: skip restoring optimization data used by profile-based optimizations. -echo -buildstandalonegc: builds the GC in a standalone mode. echo -skiprestore: skip restoring packages ^(default: packages are restored during build^). echo -disableoss: Disable Open Source Signing for System.Private.CoreLib. echo -priority=^<N^> : specify a set of test that will be built and run, with priority N. @@ -50,7 +50,6 @@ usage() echo "-ignorewarnings - do not treat warnings as errors" echo "-cmakeargs - user-settable additional arguments passed to CMake." echo "-bindir - output directory (defaults to $__ProjectRoot/bin)" - echo "-buildstandalonegc - builds the GC in a standalone mode. Can't be used with \"cmakeargs\"." echo "-msbuildonunsupportedplatform - build managed binaries even if distro is not officially supported." echo "-numproc - set the number of build processes." exit 1 @@ -859,9 +858,6 @@ while :; do exit 1 fi ;; - buildstandalonegc|-buildstandalonegc) - __cmakeargs="$__cmakeargs -DFEATURE_STANDALONE_GC=1 -DFEATURE_STANDALONE_GC_ONLY=1" - ;; msbuildonunsupportedplatform|-msbuildonunsupportedplatform) __msbuildonunsupportedplatform=1 ;; diff --git a/clrfeatures.cmake b/clrfeatures.cmake index 2c22a9dce1..b4d7bad2dd 100644 --- a/clrfeatures.cmake +++ b/clrfeatures.cmake @@ -2,11 +2,6 @@ if(CLR_CMAKE_TARGET_TIZEN_LINUX) set(FEATURE_GDBJIT_LANGID_CS 1) endif() -if(FEATURE_STANDALONE_GC_ONLY) - set(FEATURE_EVENT_TRACE 0) - set(FEATURE_APPDOMAIN_RESOURCE_MONITORING 0) -endif() - if(NOT DEFINED FEATURE_EVENT_TRACE) if (WIN32) set(FEATURE_EVENT_TRACE 1) @@ -46,3 +41,6 @@ if(NOT DEFINED FEATURE_APPDOMAIN_RESOURCE_MONITORING) set(FEATURE_APPDOMAIN_RESOURCE_MONITORING 1) endif(NOT DEFINED FEATURE_APPDOMAIN_RESOURCE_MONITORING) +if(NOT DEFINED FEATURE_STANDALONE_GC) + set(FEATURE_STANDALONE_GC 1) +endif(NOT DEFINED FEATURE_STANDALONE_GC) diff --git a/src/gc/CMakeLists.txt b/src/gc/CMakeLists.txt index 21eb66070a..3240074b9b 100644 --- a/src/gc/CMakeLists.txt +++ b/src/gc/CMakeLists.txt @@ -1,5 +1,22 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Local GC meta-issue: https://github.com/dotnet/coreclr/issues/11518 + +# https://github.com/dotnet/coreclr/issues/11514 +remove_definitions(-DFEATURE_EVENT_TRACE=1) + +# https://github.com/dotnet/coreclr/issues/11517 +remove_definitions(-DFEATURE_APPDOMAIN_RESOURCE_MONITORING) + +# https://github.com/dotnet/coreclr/issues/11516 +remove_definitions(-DSTRESS_HEAP) + +# https://github.com/dotnet/coreclr/issues/11519 +remove_definitions(-DWRITE_BARRIER_CHECK) + +# https://github.com/dotnet/coreclr/issues/14701 +add_definitions(-DFEATURE_REDHAWK) + set( GC_SOURCES gcconfig.cpp gccommon.cpp @@ -14,6 +31,7 @@ set( GC_SOURCES gchandletable.cpp gceesvr.cpp gceewks.cpp + gcload.cpp handletablecache.cpp) if(CLR_CMAKE_PLATFORM_UNIX) @@ -40,13 +58,13 @@ endif(WIN32) convert_to_absolute_path(GC_SOURCES ${GC_SOURCES}) -add_library_clr(gc SHARED ${GC_SOURCES}) -target_link_libraries(gc ${GC_LINK_LIBRARIES}) -install_clr(gc) +add_library_clr(clrgc SHARED ${GC_SOURCES}) +target_link_libraries(clrgc ${GC_LINK_LIBRARIES}) +install_clr(clrgc) if(CLR_CMAKE_PLATFORM_UNIX) add_compile_options(-fPIC) - # dprintf causes many warnings + # dprintf causes many warnings (https://github.com/dotnet/coreclr/issues/13367) add_compile_options(-Wno-format) endif(CLR_CMAKE_PLATFORM_UNIX) diff --git a/src/gc/env/gcenv.base.h b/src/gc/env/gcenv.base.h index 6c878c1c21..734b46fd3d 100644 --- a/src/gc/env/gcenv.base.h +++ b/src/gc/env/gcenv.base.h @@ -11,8 +11,6 @@ #include <intrin.h> #endif // _MSC_VER -#define FEATURE_REDHAWK 1 - #define REDHAWK_PALIMPORT extern "C" #define REDHAWK_PALAPI __stdcall @@ -208,6 +206,16 @@ typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(void* lpThreadParameter); #endif // defined(__i386__) || defined(__x86_64__) +#ifdef __aarch64__ + #define YieldProcessor() asm volatile ("yield") + #define MemoryBarrier __sync_synchronize +#endif // __aarch64__ + +#ifdef __arm__ + #define YieldProcessor() + #define MemoryBarrier __sync_synchronize +#endif // __arm__ + #endif // _MSC_VER #ifdef _MSC_VER diff --git a/src/gc/gccommon.cpp b/src/gc/gccommon.cpp index 92c0e7b7b5..54873ffa4a 100644 --- a/src/gc/gccommon.cpp +++ b/src/gc/gccommon.cpp @@ -114,107 +114,4 @@ void record_changed_seg (uint8_t* start, uint8_t* end, } } -namespace WKS -{ - extern void PopulateDacVars(GcDacVars* dacVars); -} - -namespace SVR -{ - extern void PopulateDacVars(GcDacVars* dacVars); -} - -extern void PopulateHandleTableDacVars(GcDacVars* dacVars); - -//------------------------------------------------------------------ -// Externally-facing GC symbols, used to initialize the GC -// ----------------------------------------------------------------- - -#ifdef _MSC_VER -#define DLLEXPORT __declspec(dllexport) -#else -#define DLLEXPORT __attribute__ ((visibility ("default"))) -#endif // _MSC_VER - -#ifdef BUILD_AS_STANDALONE -#define GC_API extern "C" DLLEXPORT -#else -#define GC_API extern "C" -#endif // BUILD_AS_STANDALONE - -GC_API -bool -InitializeGarbageCollector( - /* In */ IGCToCLR* clrToGC, - /* Out */ IGCHeap** gcHeap, - /* Out */ IGCHandleManager** gcHandleManager, - /* Out */ GcDacVars* gcDacVars - ) -{ - LIMITED_METHOD_CONTRACT; - - IGCHeapInternal* heap; - - assert(gcDacVars != nullptr); - assert(gcHeap != nullptr); - assert(gcHandleManager != nullptr); - -#ifdef BUILD_AS_STANDALONE - assert(clrToGC != nullptr); - g_theGCToCLR = clrToGC; -#else - UNREFERENCED_PARAMETER(clrToGC); - assert(clrToGC == nullptr); -#endif - - // Initialize GCConfig before anything else - initialization of our - // various components may want to query the current configuration. - GCConfig::Initialize(); - if (!GCToOSInterface::Initialize()) - { - return false; - } - - IGCHandleManager* handleManager = CreateGCHandleManager(); - if (handleManager == nullptr) - { - return false; - } - -#ifdef FEATURE_SVR_GC - if (GCConfig::GetServerGC()) - { -#ifdef WRITE_BARRIER_CHECK - g_GCShadow = 0; - g_GCShadowEnd = 0; -#endif // WRITE_BARRIER_CHECK - - g_gc_heap_type = GC_HEAP_SVR; - heap = SVR::CreateGCHeap(); - SVR::PopulateDacVars(gcDacVars); - } - else - { - g_gc_heap_type = GC_HEAP_WKS; - heap = WKS::CreateGCHeap(); - WKS::PopulateDacVars(gcDacVars); - } -#else - g_gc_heap_type = GC_HEAP_WKS; - heap = WKS::CreateGCHeap(); - WKS::PopulateDacVars(gcDacVars); -#endif - - PopulateHandleTableDacVars(gcDacVars); - if (heap == nullptr) - { - return false; - } - - g_theGCHeap = heap; - *gcHandleManager = handleManager; - *gcHeap = heap; - return true; -} - #endif // !DACCESS_COMPILE diff --git a/src/gc/gcinterface.h b/src/gc/gcinterface.h index 5bec7212e8..ef5c896667 100644 --- a/src/gc/gcinterface.h +++ b/src/gc/gcinterface.h @@ -5,6 +5,15 @@ #ifndef _GC_INTERFACE_H_ #define _GC_INTERFACE_H_ +// The major version of the GC/EE interface. Breaking changes to this interface +// require bumps in the major version number. +#define GC_INTERFACE_MAJOR_VERSION 1 + +// The minor version of the GC/EE interface. Non-breaking changes are required +// to bump the minor version number. GCs and EEs with minor version number +// mismatches can still interopate correctly, with some care. +#define GC_INTERFACE_MINOR_VERSION 1 + struct ScanContext; struct gc_alloc_context; class CrawlFrame; @@ -174,20 +183,6 @@ class Object; class IGCHeap; class IGCHandleManager; -// 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. -typedef bool (*InitializeGarbageCollectorFunction)( - /* In */ IGCToCLR*, - /* Out */ IGCHeap**, - /* Out */ IGCHandleManager**, - /* Out */ GcDacVars* -); - -// The name of the function that initializes the garbage collector, -// to be used as an argument to GetProcAddress. -#define INITIALIZE_GC_FUNCTION_NAME "InitializeGarbageCollector" - #ifdef WRITE_BARRIER_CHECK //always defined, but should be 0 in Server GC extern uint8_t* g_GCShadow; @@ -871,4 +866,24 @@ struct ScanContext } }; +// These types are used as part of the loader protocol between the EE +// and the GC. +struct VersionInfo { + uint32_t MajorVersion; + uint32_t MinorVersion; + uint32_t BuildVersion; + const char* Name; +}; + +typedef void (*GC_VersionInfoFunction)( + /* Out */ VersionInfo* +); + +typedef HRESULT (*GC_InitializeFunction)( + /* In */ IGCToCLR*, + /* Out */ IGCHeap**, + /* Out */ IGCHandleManager**, + /* Out */ GcDacVars* +); + #endif // _GC_INTERFACE_H_ diff --git a/src/gc/gcload.cpp b/src/gc/gcload.cpp new file mode 100644 index 0000000000..21eedb250f --- /dev/null +++ b/src/gc/gcload.cpp @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/* + * gcload.cpp + * + * Code for loading and initializing the GC. The code in this file + * is used in the startup path of both a standalone and non-standalone GC. + */ + +#include "common.h" +#include "gcenv.h" +#include "gc.h" + +#ifdef _MSC_VER +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT __attribute__ ((visibility ("default"))) +#endif // _MSC_VER + +#define GC_EXPORT extern "C" DLLEXPORT + +// These symbols are defined in gc.cpp and populate the GcDacVars +// structure with the addresses of DAC variables within the GC. +namespace WKS +{ + extern void PopulateDacVars(GcDacVars* dacVars); +} + +namespace SVR +{ + extern void PopulateDacVars(GcDacVars* dacVars); +} + +// This symbol populates GcDacVars with handle table dacvars. +extern void PopulateHandleTableDacVars(GcDacVars* dacVars); + +GC_EXPORT +void +GC_VersionInfo(/* Out */ VersionInfo* info) +{ + info->MajorVersion = GC_INTERFACE_MAJOR_VERSION; + info->MinorVersion = GC_INTERFACE_MINOR_VERSION; + info->BuildVersion = 0; + info->Name = "CoreCLR GC"; +} + +GC_EXPORT +HRESULT +GC_Initialize( + /* In */ IGCToCLR* clrToGC, + /* Out */ IGCHeap** gcHeap, + /* Out */ IGCHandleManager** gcHandleManager, + /* Out */ GcDacVars* gcDacVars +) +{ + IGCHeapInternal* heap; + + assert(gcDacVars != nullptr); + assert(gcHeap != nullptr); + assert(gcHandleManager != nullptr); + +#ifdef BUILD_AS_STANDALONE + assert(clrToGC != nullptr); + g_theGCToCLR = clrToGC; +#else + UNREFERENCED_PARAMETER(clrToGC); + assert(clrToGC == nullptr); +#endif + + // Initialize GCConfig before anything else - initialization of our + // various components may want to query the current configuration. + GCConfig::Initialize(); + if (!GCToOSInterface::Initialize()) + { + return E_FAIL; + } + + IGCHandleManager* handleManager = CreateGCHandleManager(); + if (handleManager == nullptr) + { + return E_OUTOFMEMORY; + } + +#ifdef FEATURE_SVR_GC + if (GCConfig::GetServerGC()) + { +#ifdef WRITE_BARRIER_CHECK + g_GCShadow = 0; + g_GCShadowEnd = 0; +#endif // WRITE_BARRIER_CHECK + + g_gc_heap_type = GC_HEAP_SVR; + heap = SVR::CreateGCHeap(); + SVR::PopulateDacVars(gcDacVars); + } + else + { + g_gc_heap_type = GC_HEAP_WKS; + heap = WKS::CreateGCHeap(); + WKS::PopulateDacVars(gcDacVars); + } +#else + g_gc_heap_type = GC_HEAP_WKS; + heap = WKS::CreateGCHeap(); + WKS::PopulateDacVars(gcDacVars); +#endif + + PopulateHandleTableDacVars(gcDacVars); + if (heap == nullptr) + { + return E_OUTOFMEMORY; + } + + g_theGCHeap = heap; + *gcHandleManager = handleManager; + *gcHeap = heap; + return S_OK; +} diff --git a/src/gc/sample/CMakeLists.txt b/src/gc/sample/CMakeLists.txt index 42f097a6e3..6f8aa615d7 100644 --- a/src/gc/sample/CMakeLists.txt +++ b/src/gc/sample/CMakeLists.txt @@ -5,6 +5,8 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(..) include_directories(../env) +add_definitions(-DFEATURE_REDHAWK) + set(SOURCES GCSample.cpp gcenv.ee.cpp @@ -14,6 +16,7 @@ set(SOURCES ../gchandletable.cpp ../gcscan.cpp ../gcwks.cpp + ../gcload.cpp ../handletable.cpp ../handletablecache.cpp ../handletablecore.cpp diff --git a/src/gc/sample/GCSample.cpp b/src/gc/sample/GCSample.cpp index 62eec6698f..4248d92256 100644 --- a/src/gc/sample/GCSample.cpp +++ b/src/gc/sample/GCSample.cpp @@ -107,7 +107,7 @@ void WriteBarrier(Object ** dst, Object * ref) ErectWriteBarrier(dst, ref); } -extern "C" bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleManager** gcHandleManager, GcDacVars* gcDacVars); +extern "C" HRESULT GC_Initialize(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleManager** gcHandleManager, GcDacVars* gcDacVars); int __cdecl main(int argc, char* argv[]) { @@ -133,7 +133,7 @@ int __cdecl main(int argc, char* argv[]) GcDacVars dacVars; IGCHeap *pGCHeap; IGCHandleManager *pGCHandleManager; - if (!InitializeGarbageCollector(nullptr, &pGCHeap, &pGCHandleManager, &dacVars)) + if (GC_Initialize(nullptr, &pGCHeap, &pGCHandleManager, &dacVars) != S_OK) { return -1; } diff --git a/src/inc/clrconfigvalues.h b/src/inc/clrconfigvalues.h index 2b8026e283..e5cbaaa4a2 100644 --- a/src/inc/clrconfigvalues.h +++ b/src/inc/clrconfigvalues.h @@ -335,8 +335,7 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCNumaAware, W("GCNumaAware"), 1, "Specifie RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCCpuGroup, W("GCCpuGroup"), 0, "Specifies if to enable GC to support CPU groups") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCHeapCount, W("GCHeapCount"), 0, "") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCNoAffinitize, W("GCNoAffinitize"), 0, "") -RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCUseStandalone, W("GCUseStandalone"), 0, "") -RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCStandaloneLocation, W("GCStandaloneLocation"), "") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "") // // IBC 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; |