summaryrefslogtreecommitdiff
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
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
-rw-r--r--CMakeLists.txt4
-rw-r--r--build.cmd17
-rwxr-xr-xbuild.sh4
-rw-r--r--clrfeatures.cmake8
-rw-r--r--src/gc/CMakeLists.txt26
-rw-r--r--src/gc/env/gcenv.base.h12
-rw-r--r--src/gc/gccommon.cpp103
-rw-r--r--src/gc/gcinterface.h43
-rw-r--r--src/gc/gcload.cpp120
-rw-r--r--src/gc/sample/CMakeLists.txt3
-rw-r--r--src/gc/sample/GCSample.cpp4
-rw-r--r--src/inc/clrconfigvalues.h3
-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
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")
diff --git a/build.cmd b/build.cmd
index 3a0de8c808..f2649f09db 100644
--- a/build.cmd
+++ b/build.cmd
@@ -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.
diff --git a/build.sh b/build.sh
index a098d5e9ad..865f0feebf 100755
--- a/build.sh
+++ b/build.sh
@@ -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;