summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Gillespie <segilles@microsoft.com>2017-05-17 15:07:34 -0700
committerSean Gillespie <segilles@microsoft.com>2017-06-01 10:19:59 -0700
commit1a183684b1ecf63ece8a2fd80173f083c0deea52 (patch)
tree907b31b417732768b28e80adfb4d920497f0d3f0
parentdde63bc1aa39aabae77fb89aad583483965c523e (diff)
downloadcoreclr-1a183684b1ecf63ece8a2fd80173f083c0deea52.tar.gz
coreclr-1a183684b1ecf63ece8a2fd80173f083c0deea52.tar.bz2
coreclr-1a183684b1ecf63ece8a2fd80173f083c0deea52.zip
[Local GC] Scaffolding for loading a standalone GC (#11242)
* Configure the build system to build a CoreCLR capable of loading a standalone GC * Proto-implementation of dynamic GC loading * Build the GC with the VM's CMakeLists when doing a non-standalone build of the GC * [Local GC] Introduce a new feature define, FEATURE_STANDALONE_GC_ONLY, to be used by the CI to explicitly test local GC dynamic loading code paths * Fix the FEATURE_STANDALONE_GC_ONLY build for unix linkers * Rebase against master * Code review feedback: use the existing Unix exports file
-rw-r--r--CMakeLists.txt4
-rw-r--r--build.cmd8
-rwxr-xr-xbuild.sh2
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/dlls/mscordac/CMakeLists.txt1
-rw-r--r--src/dlls/mscoree/coreclr/CMakeLists.txt6
-rw-r--r--src/dlls/mscoree/mscorwks_unixexports.src3
-rw-r--r--src/gc/CMakeLists.txt42
-rw-r--r--src/gc/dac/CMakeLists.txt2
-rw-r--r--src/gc/gc.h7
-rw-r--r--src/gc/gccommon.cpp31
-rw-r--r--src/gc/gcenv.ee.standalone.inl9
-rw-r--r--src/gc/gcinterface.ee.h4
-rw-r--r--src/gc/gcinterface.h19
-rw-r--r--src/gc/sample/GCSample.cpp2
-rw-r--r--src/gc/sample/gcenv.h6
-rw-r--r--src/gc/unix/gcenv.unix.cpp6
-rw-r--r--src/gc/wks/CMakeLists.txt1
-rw-r--r--src/inc/clrconfigvalues.h2
-rw-r--r--src/vm/CMakeLists.txt55
-rw-r--r--src/vm/ceemain.cpp123
-rw-r--r--src/vm/gcenv.ee.common.cpp394
-rw-r--r--src/vm/gcenv.ee.cpp415
-rw-r--r--src/vm/gcenv.ee.h5
-rw-r--r--src/vm/gcenv.ee.standalone.cpp30
-rw-r--r--src/vm/gcenv.ee.static.cpp25
26 files changed, 712 insertions, 494 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f55b54c498..f6e0987ebe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -585,6 +585,10 @@ if(FEATURE_STANDALONE_GC)
endif(CLR_CMAKE_PLATFORM_UNIX)
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 b3706baefa..67f43960dc 100644
--- a/build.cmd
+++ b/build.cmd
@@ -76,6 +76,7 @@ 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 __IbcTuning=
@@ -139,7 +140,12 @@ if /i "%1" == "usenmakemakefiles" (set __NMakeMakefiles=1&set __ConfigureOnly=
if /i "%1" == "pgoinstrument" (set __PgoInstrument=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 processedArgs=!processedArgs! %1&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)
diff --git a/build.sh b/build.sh
index 976e18bda6..4577e94d2b 100755
--- a/build.sh
+++ b/build.sh
@@ -800,7 +800,7 @@ while :; do
fi
;;
buildstandalonegc)
- __cmakeargs="-DFEATURE_STANDALONE_GC=1"
+ __cmakeargs="-DFEATURE_STANDALONE_GC=1 -DFEATURE_STANDALONE_GC_ONLY=1"
;;
msbuildonunsupportedplatform)
__msbuildonunsupportedplatform=1
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b761b6a82d..9359580468 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -149,7 +149,9 @@ add_subdirectory(utilcode)
add_subdirectory(gcinfo)
add_subdirectory(coreclr)
add_subdirectory(jit)
-add_subdirectory(gc)
+if(FEATURE_STANDALONE_GC)
+ add_subdirectory(gc)
+endif(FEATURE_STANDALONE_GC)
add_subdirectory(vm)
add_subdirectory(md)
add_subdirectory(debug)
diff --git a/src/dlls/mscordac/CMakeLists.txt b/src/dlls/mscordac/CMakeLists.txt
index afe5bea7d0..82582f4def 100644
--- a/src/dlls/mscordac/CMakeLists.txt
+++ b/src/dlls/mscordac/CMakeLists.txt
@@ -89,7 +89,6 @@ set(COREDAC_LIBRARIES
strongname_dac
utilcode_dac
unwinder_dac
- gc_dac
${END_LIBRARY_GROUP} # End group of libraries that have circular references
)
diff --git a/src/dlls/mscoree/coreclr/CMakeLists.txt b/src/dlls/mscoree/coreclr/CMakeLists.txt
index 7a4617fc52..81aaad45f0 100644
--- a/src/dlls/mscoree/coreclr/CMakeLists.txt
+++ b/src/dlls/mscoree/coreclr/CMakeLists.txt
@@ -73,6 +73,10 @@ if(FEATURE_MERGE_JIT_AND_ENGINE)
set(CLRJIT_STATIC clrjit_static)
endif(FEATURE_MERGE_JIT_AND_ENGINE)
+if(FEATURE_STANDALONE_GC_ONLY)
+ set(STANDALONE_GC gc_standalone)
+endif(FEATURE_STANDALONE_GC_ONLY)
+
# IMPORTANT! Please do not rearrange the order of the libraries. The linker on Linux is
# order dependent and changing the order can result in undefined symbols in the shared
# library.
@@ -83,7 +87,7 @@ set(CORECLR_LIBRARIES
debug-pal
${LIB_UNWINDER}
cee_wks
- gc_wks
+ ${STANDALONE_GC}
${END_LIBRARY_GROUP} # End group of libraries that have circular references
mdcompiler_wks
mdruntime_wks
diff --git a/src/dlls/mscoree/mscorwks_unixexports.src b/src/dlls/mscoree/mscorwks_unixexports.src
index 271ff8ae7c..73ffc0c1c5 100644
--- a/src/dlls/mscoree/mscorwks_unixexports.src
+++ b/src/dlls/mscoree/mscorwks_unixexports.src
@@ -104,3 +104,6 @@ GlobalMemoryStatusEx
VirtualQuery
WideCharToMultiByte
WriteFile
+
+; Function for initializing a standalone GC
+InitializeGarbageCollector
diff --git a/src/gc/CMakeLists.txt b/src/gc/CMakeLists.txt
index 59c18ffd87..f55c1a9a6f 100644
--- a/src/gc/CMakeLists.txt
+++ b/src/gc/CMakeLists.txt
@@ -1,24 +1,16 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
-include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(BEFORE ${CLR_DIR}/src/vm)
include_directories(BEFORE ${CLR_DIR}/src/vm/${ARCH_SOURCES_DIR})
+add_definitions(-DBUILD_AS_STANDALONE)
+
if(CLR_CMAKE_PLATFORM_UNIX)
add_compile_options(-fPIC)
endif(CLR_CMAKE_PLATFORM_UNIX)
-if(CMAKE_CONFIGURATION_TYPES)
- foreach (Config DEBUG CHECKED)
- 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)
- add_definitions(-DWRITE_BARRIER_CHECK=1)
- endif(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
-endif(CMAKE_CONFIGURATION_TYPES)
-
-set( GC_SOURCES_DAC_AND_WKS_COMMON
+set( GC_SOURCES
gccommon.cpp
gcscan.cpp
gcsvr.cpp
@@ -27,28 +19,18 @@ set( GC_SOURCES_DAC_AND_WKS_COMMON
handletablecore.cpp
handletablescan.cpp
objecthandle.cpp
- softwarewritewatch.cpp)
-
-set( GC_SOURCES_WKS
- ${GC_SOURCES_DAC_AND_WKS_COMMON}
+ softwarewritewatch.cpp
gchandletable.cpp
gceesvr.cpp
gceewks.cpp
handletablecache.cpp)
-set( GC_SOURCES_DAC
- ${GC_SOURCES_DAC_AND_WKS_COMMON})
-
-if(FEATURE_STANDALONE_GC)
- if(NOT CLR_CMAKE_PLATFORM_UNIX)
- set ( GC_SOURCES_WKS
- ${GC_SOURCES_WKS}
- windows/gcenv.windows.cpp)
- endif(NOT CLR_CMAKE_PLATFORM_UNIX)
-endif(FEATURE_STANDALONE_GC)
+if(NOT CLR_CMAKE_PLATFORM_UNIX)
+set ( GC_SOURCES
+ ${GC_SOURCES}
+ windows/gcenv.windows.cpp)
+endif(NOT CLR_CMAKE_PLATFORM_UNIX)
-convert_to_absolute_path(GC_SOURCES_WKS ${GC_SOURCES_WKS})
-convert_to_absolute_path(GC_SOURCES_DAC ${GC_SOURCES_DAC})
+convert_to_absolute_path(GC_SOURCES ${GC_SOURCES})
-add_subdirectory(wks)
-add_subdirectory(dac)
+add_library_clr(gc_standalone STATIC ${GC_SOURCES})
diff --git a/src/gc/dac/CMakeLists.txt b/src/gc/dac/CMakeLists.txt
deleted file mode 100644
index 1f1c9ebe5c..0000000000
--- a/src/gc/dac/CMakeLists.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-include(${CLR_DIR}/dac.cmake)
-add_library_clr(gc_dac STATIC ${GC_SOURCES_DAC})
diff --git a/src/gc/gc.h b/src/gc/gc.h
index 07ae6c916c..2da0cb3966 100644
--- a/src/gc/gc.h
+++ b/src/gc/gc.h
@@ -27,16 +27,17 @@ Module Name:
#include "gcinterface.h"
#include "env/gcenv.os.h"
-#include "env/gcenv.ee.h"
-#ifdef FEATURE_STANDALONE_GC
+#ifdef BUILD_AS_STANDALONE
#include "gcenv.ee.standalone.inl"
// GCStress does not currently work with Standalone GC
#ifdef STRESS_HEAP
#undef STRESS_HEAP
#endif // STRESS_HEAP
-#endif // FEATURE_STANDALONE_GC
+#else
+#include "env/gcenv.ee.h"
+#endif // BUILD_AS_STANDALONE
/*
* Promotion Function Prototypes
diff --git a/src/gc/gccommon.cpp b/src/gc/gccommon.cpp
index 4950809cda..d7c572260a 100644
--- a/src/gc/gccommon.cpp
+++ b/src/gc/gccommon.cpp
@@ -17,9 +17,9 @@
IGCHeapInternal* g_theGCHeap;
IGCHandleManager* g_theGCHandleManager;
-#ifdef FEATURE_STANDALONE_GC
+#ifdef BUILD_AS_STANDALONE
IGCToCLR* g_theGCToCLR;
-#endif // FEATURE_STANDALONE_GC
+#endif // BUILD_AS_STANDALONE
#ifdef GC_CONFIG_DRIVEN
size_t gc_global_mechanisms[MAX_GLOBAL_GC_MECHANISMS_COUNT];
@@ -143,7 +143,30 @@ namespace SVR
extern void PopulateDacVars(GcDacVars* dacVars);
}
-bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleManager** gcHandleManager, GcDacVars* gcDacVars)
+//------------------------------------------------------------------
+// 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;
@@ -184,7 +207,7 @@ bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleMa
g_theGCHeap = heap;
-#ifdef FEATURE_STANDALONE_GC
+#ifdef BUILD_AS_STANDALONE
assert(clrToGC != nullptr);
g_theGCToCLR = clrToGC;
#else
diff --git a/src/gc/gcenv.ee.standalone.inl b/src/gc/gcenv.ee.standalone.inl
index f6954fc476..31c3455d7b 100644
--- a/src/gc/gcenv.ee.standalone.inl
+++ b/src/gc/gcenv.ee.standalone.inl
@@ -5,12 +5,17 @@
#ifndef __GCTOENV_EE_STANDALONE_INL__
#define __GCTOENV_EE_STANDALONE_INL__
-#include "env/gcenv.ee.h"
+#include "gcinterface.h"
// The singular interface instance. All calls in GCToEEInterface
// will be fowarded to this interface instance.
extern IGCToCLR* g_theGCToCLR;
+namespace
+{
+
+#include "env/gcenv.ee.h"
+
// A note about this:
// In general, we don't want to pretend to be smarter than the compiler
// and force it to inline things. However, inlining is here is required
@@ -238,4 +243,6 @@ ALWAYS_INLINE MethodTable* GCToEEInterface::GetFreeObjectMethodTable()
}
#undef ALWAYS_INLINE
+} // anonymous namespace
+
#endif // __GCTOENV_EE_STANDALONE_INL__
diff --git a/src/gc/gcinterface.ee.h b/src/gc/gcinterface.ee.h
index 7b868e780e..8290572cd4 100644
--- a/src/gc/gcinterface.ee.h
+++ b/src/gc/gcinterface.ee.h
@@ -9,9 +9,9 @@
// of the execution engine. Everything that the GC does that requires the EE
// to be informed or that requires EE action must go through this interface.
//
-// When FEATURE_STANDALONE_GC is defined, this class is named IGCToCLR and is
+// When BUILD_AS_STANDALONE is defined, this class is named IGCToCLR and is
// an abstract class. The EE will provide a class that fulfills this interface,
-// and the GC will dispatch virtually on it to call into the EE. When FEATURE_STANDALONE_GC
+// and the GC will dispatch virtually on it to call into the EE. When BUILD_AS_STANDALONE
// is not defined, this class is named GCToEEInterface and the GC will dispatch statically on it.
class IGCToCLR {
public:
diff --git a/src/gc/gcinterface.h b/src/gc/gcinterface.h
index 552a8caec8..425745b0cb 100644
--- a/src/gc/gcinterface.h
+++ b/src/gc/gcinterface.h
@@ -171,15 +171,24 @@ class Object;
class IGCHeap;
class IGCHandleManager;
-// Initializes the garbage collector. Should only be called
-// once, during EE startup. Returns true if the initialization
-// was successful, false otherwise.
-bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleManager** gcHandleTable, GcDacVars* gcDacVars);
+// 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"
// The runtime needs to know whether we're using workstation or server GC
// long before the GCHeap is created. This function sets the type of
// heap that will be created, before InitializeGarbageCollector is called
-// and the heap is actually recated.
+// and the heap is actually created.
void InitializeHeapType(bool bServerHeap);
#ifdef WRITE_BARRIER_CHECK
diff --git a/src/gc/sample/GCSample.cpp b/src/gc/sample/GCSample.cpp
index 0a771b7e91..43cb23878e 100644
--- a/src/gc/sample/GCSample.cpp
+++ b/src/gc/sample/GCSample.cpp
@@ -107,6 +107,8 @@ void WriteBarrier(Object ** dst, Object * ref)
ErectWriteBarrier(dst, ref);
}
+extern "C" bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleManager** gcHandleManager, GcDacVars* gcDacVars);
+
int __cdecl main(int argc, char* argv[])
{
//
diff --git a/src/gc/sample/gcenv.h b/src/gc/sample/gcenv.h
index 4505f1af30..14f60d8c6e 100644
--- a/src/gc/sample/gcenv.h
+++ b/src/gc/sample/gcenv.h
@@ -4,9 +4,9 @@
// The sample is to be kept simple, so building the sample
// in tandem with a standalone GC is currently not supported.
-#ifdef FEATURE_STANDALONE_GC
-#undef FEATURE_STANDALONE_GC
-#endif // FEATURE_STANDALONE_GC
+#ifdef BUILD_AS_STANDALONE
+#undef BUILD_AS_STANDALONE
+#endif // BUILD_AS_STANDALONE
#if defined(_DEBUG)
#ifndef _DEBUG_IMPL
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
index bca0dfedf2..cebc515345 100644
--- a/src/gc/unix/gcenv.unix.cpp
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -32,9 +32,9 @@ static_assert(sizeof(uint64_t) == 8, "unsigned long isn't 8 bytes");
#include "gcenv.base.h"
#include "gcenv.os.h"
-#ifndef FEATURE_STANDALONE_GC
- #error "A GC-private implementation of GCToOSInterface should only be used with FEATURE_STANDALONE_GC"
-#endif // FEATURE_STANDALONE_GC
+#ifndef BUILD_AS_STANDALONE
+ #error "A GC-private implementation of GCToOSInterface should only be used with BUILD_AS_STANDALONE"
+#endif // BUILD_AS_STANDALONE
#if HAVE_SYS_TIME_H
#include <sys/time.h>
diff --git a/src/gc/wks/CMakeLists.txt b/src/gc/wks/CMakeLists.txt
deleted file mode 100644
index fcb95a385e..0000000000
--- a/src/gc/wks/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-add_library_clr(gc_wks STATIC ${GC_SOURCES_WKS})
diff --git a/src/inc/clrconfigvalues.h b/src/inc/clrconfigvalues.h
index c4722bc44a..81e9fae052 100644
--- a/src/inc/clrconfigvalues.h
+++ b/src/inc/clrconfigvalues.h
@@ -346,6 +346,8 @@ 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"), "")
//
// IBC
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index 3895f710b0..60e7ea4c59 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -13,11 +13,15 @@ add_definitions(-D_UNICODE)
if(CMAKE_CONFIGURATION_TYPES) # multi-configuration generator?
foreach (Config DEBUG CHECKED)
- set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:${Config}>:WRITE_BARRIER_CHECK=1>)
+ 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)
endforeach (Config)
else()
if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
- add_definitions(-DWRITE_BARRIER_CHECK=1)
+ if(NOT FEATURE_STANDALONE_GC_ONLY)
+ add_definitions(-DWRITE_BARRIER_CHECK=1)
+ endif(NOT FEATURE_STANDALONE_GC_ONLY)
endif(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
endif(CMAKE_CONFIGURATION_TYPES)
@@ -117,6 +121,17 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
zapsig.cpp
)
+set( GC_SOURCES_DAC_AND_WKS_COMMON
+ ../gc/gccommon.cpp
+ ../gc/gcscan.cpp
+ ../gc/gcsvr.cpp
+ ../gc/gcwks.cpp
+ ../gc/handletable.cpp
+ ../gc/handletablecore.cpp
+ ../gc/handletablescan.cpp
+ ../gc/objecthandle.cpp
+ ../gc/softwarewritewatch.cpp)
+
if(FEATURE_READYTORUN)
list(APPEND VM_SOURCES_DAC_AND_WKS_COMMON
readytoruninfo.cpp
@@ -129,6 +144,9 @@ set(VM_SOURCES_DAC
threaddebugblockinginfo.cpp
)
+set(GC_SOURCES_DAC
+ ${GC_SOURCES_DAC_AND_WKS_COMMON})
+
set(VM_SOURCES_WKS
${VM_SOURCES_DAC_AND_WKS_COMMON}
appdomainnative.cpp
@@ -177,7 +195,9 @@ set(VM_SOURCES_WKS
finalizerthread.cpp
frameworkexceptionloader.cpp
gccover.cpp
- gcenv.ee.cpp
+ gcenv.ee.static.cpp
+ gcenv.ee.common.cpp
+ gcenv.os.cpp
gchelpers.cpp
genmeth.cpp
hosting.cpp
@@ -239,12 +259,25 @@ set(VM_SOURCES_WKS
${VM_SOURCES_GDBJIT}
)
+set(GC_SOURCES_WKS
+ ${GC_SOURCES_DAC_AND_WKS_COMMON}
+ ../gc/gchandletable.cpp
+ ../gc/gceesvr.cpp
+ ../gc/gceewks.cpp
+ ../gc/handletablecache.cpp)
+
if(FEATURE_EVENT_TRACE)
list(APPEND VM_SOURCES_WKS
eventtrace.cpp
)
endif(FEATURE_EVENT_TRACE)
+if(FEATURE_STANDALONE_GC)
+ list(APPEND VM_SOURCES_WKS
+ gcenv.ee.standalone.cpp
+ )
+endif(FEATURE_STANDALONE_GC)
+
if(NOT FEATURE_STANDALONE_GC)
list(APPEND VM_SOURCES_WKS
gcenv.os.cpp
@@ -472,6 +505,22 @@ 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)
+
+# The DAC does need GC sources in order to link correctly, even if
+# it's not used.
+list(APPEND VM_SOURCES_DAC
+ ${GC_SOURCES_DAC}
+)
+
convert_to_absolute_path(VM_SOURCES_WKS ${VM_SOURCES_WKS})
convert_to_absolute_path(VM_SOURCES_WKS_ARCH_ASM ${VM_SOURCES_WKS_ARCH_ASM})
convert_to_absolute_path(VM_SOURCES_DAC ${VM_SOURCES_DAC})
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
index de6059a6ec..617b022516 100644
--- a/src/vm/ceemain.cpp
+++ b/src/vm/ceemain.cpp
@@ -2440,6 +2440,101 @@ 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
+
+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;
+}
+
+
void InitializeGarbageCollector()
{
CONTRACTL{
@@ -2463,25 +2558,19 @@ void InitializeGarbageCollector()
g_pFreeObjectMethodTable->SetComponentSize(1);
#ifdef FEATURE_STANDALONE_GC
- IGCToCLR* gcToClr = new (nothrow) GCToEEInterface();
- if (!gcToClr)
- ThrowOutOfMemory();
-#else
- IGCToCLR* gcToClr = nullptr;
-#endif
-
- IGCHandleManager *pGcHandleManager;
-
- IGCHeap *pGCHeap;
- if (!InitializeGarbageCollector(gcToClr, &pGCHeap, &pGcHandleManager, &g_gc_dac_vars))
+ if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCUseStandalone)
+#ifdef FEATURE_STANDALONE_GC_ONLY
+ || true
+#endif // FEATURE_STANDALONE_GC_ONLY
+ )
{
- ThrowOutOfMemory();
+ LoadGarbageCollector();
+ }
+ else
+#endif // FEATURE_STANDALONE_GC
+ {
+ LoadStaticGarbageCollector();
}
-
- assert(pGCHeap != nullptr);
- g_pGCHeap = pGCHeap;
- g_pGCHandleManager = pGcHandleManager;
- g_gcDacGlobals = &g_gc_dac_vars;
// Apparently the Windows linker removes global variables if they are never
// read from, which is a problem for g_gcDacGlobals since it's expected that
diff --git a/src/vm/gcenv.ee.common.cpp b/src/vm/gcenv.ee.common.cpp
new file mode 100644
index 0000000000..ca7349091f
--- /dev/null
+++ b/src/vm/gcenv.ee.common.cpp
@@ -0,0 +1,394 @@
+// 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.
+
+#include "common.h"
+#include "gcenv.h"
+
+#if defined(WIN64EXCEPTIONS)
+
+struct FindFirstInterruptiblePointState
+{
+ unsigned offs;
+ unsigned endOffs;
+ unsigned returnOffs;
+};
+
+bool FindFirstInterruptiblePointStateCB(
+ UINT32 startOffset,
+ UINT32 stopOffset,
+ LPVOID hCallback)
+{
+ FindFirstInterruptiblePointState* pState = (FindFirstInterruptiblePointState*)hCallback;
+
+ _ASSERTE(startOffset < stopOffset);
+ _ASSERTE(pState->offs < pState->endOffs);
+
+ if (stopOffset <= pState->offs)
+ {
+ // The range ends before the requested offset.
+ return false;
+ }
+
+ // The offset is in the range.
+ if (startOffset <= pState->offs &&
+ pState->offs < stopOffset)
+ {
+ pState->returnOffs = pState->offs;
+ return true;
+ }
+
+ // The range is completely after the desired offset. We use the range start offset, if
+ // it comes before the given endOffs. We assume that the callback is called with ranges
+ // in increasing order, so earlier ones are reported before later ones. That is, if we
+ // get to this case, it will be the closest interruptible range after the requested
+ // offset.
+
+ _ASSERTE(pState->offs < startOffset);
+ if (startOffset < pState->endOffs)
+ {
+ pState->returnOffs = startOffset;
+ return true;
+ }
+
+ return false;
+}
+
+// Find the first interruptible point in the range [offs .. endOffs) (the beginning of the range is inclusive,
+// the end is exclusive). Return -1 if no such point exists.
+unsigned FindFirstInterruptiblePoint(CrawlFrame* pCF, unsigned offs, unsigned endOffs)
+{
+#ifdef USE_GC_INFO_DECODER
+ GCInfoToken gcInfoToken = pCF->GetGCInfoToken();
+ GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_FOR_RANGES_CALLBACK);
+
+ FindFirstInterruptiblePointState state;
+ state.offs = offs;
+ state.endOffs = endOffs;
+ state.returnOffs = -1;
+
+ gcInfoDecoder.EnumerateInterruptibleRanges(&FindFirstInterruptiblePointStateCB, &state);
+
+ return state.returnOffs;
+#else
+ PORTABILITY_ASSERT("FindFirstInterruptiblePoint");
+ return -1;
+#endif // USE_GC_INFO_DECODER
+}
+
+#endif // WIN64EXCEPTIONS
+
+//-----------------------------------------------------------------------------
+// Determine whether we should report the generic parameter context
+//
+// This is meant to detect the situation where a ThreadAbortException is raised
+// in the prolog of a managed method, before the location for the generics
+// context has been initialized; when such a TAE is raised, we are open to a
+// race with the GC (e.g. while creating the managed object for the TAE).
+// The GC would cause a stack walk, and if we report the stack location for
+// the generic param context at this time we'd crash.
+// The long term solution is to avoid raising TAEs in any non-GC safe points,
+// and to additionally ensure that we do not expose the runtime to TAE
+// starvation.
+inline bool SafeToReportGenericParamContext(CrawlFrame* pCF)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (!pCF->IsFrameless() || !(pCF->IsActiveFrame() || pCF->IsInterrupted()))
+ {
+ return true;
+ }
+
+#ifndef USE_GC_INFO_DECODER
+
+ ICodeManager * pEECM = pCF->GetCodeManager();
+ if (pEECM != NULL && pEECM->IsInPrologOrEpilog(pCF->GetRelOffset(), pCF->GetGCInfoToken(), NULL))
+ {
+ return false;
+ }
+
+#else // USE_GC_INFO_DECODER
+
+ GcInfoDecoder gcInfoDecoder(pCF->GetGCInfoToken(),
+ DECODE_PROLOG_LENGTH);
+ UINT32 prologLength = gcInfoDecoder.GetPrologSize();
+ if (pCF->GetRelOffset() < prologLength)
+ {
+ return false;
+ }
+
+#endif // USE_GC_INFO_DECODER
+
+ return true;
+}
+
+/*
+ * GcEnumObject()
+ *
+ * This is the JIT compiler (or any remote code manager)
+ * GC enumeration callback
+ */
+
+void GcEnumObject(LPVOID pData, OBJECTREF *pObj, uint32_t flags)
+{
+ Object ** ppObj = (Object **)pObj;
+ GCCONTEXT * pCtx = (GCCONTEXT *) pData;
+
+ // Since we may be asynchronously walking another thread's stack,
+ // check (frequently) for stack-buffer-overrun corruptions after
+ // any long operation
+ if (pCtx->cf != NULL)
+ pCtx->cf->CheckGSCookies();
+
+ //
+ // Sanity check that the flags contain only these three values
+ //
+ assert((flags & ~(GC_CALL_INTERIOR|GC_CALL_PINNED|GC_CALL_CHECK_APP_DOMAIN)) == 0);
+
+ // for interior pointers, we optimize the case in which
+ // it points into the current threads stack area
+ //
+ if (flags & GC_CALL_INTERIOR)
+ PromoteCarefully(pCtx->f, ppObj, pCtx->sc, flags);
+ else
+ (pCtx->f)(ppObj, pCtx->sc, flags);
+}
+
+//-----------------------------------------------------------------------------
+void GcReportLoaderAllocator(promote_func* fn, ScanContext* sc, LoaderAllocator *pLoaderAllocator)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ if (pLoaderAllocator != NULL && pLoaderAllocator->IsCollectible())
+ {
+ Object *refCollectionObject = OBJECTREFToObject(pLoaderAllocator->GetExposedObject());
+
+#ifdef _DEBUG
+ Object *oldObj = refCollectionObject;
+#endif
+
+ _ASSERTE(refCollectionObject != NULL);
+ fn(&refCollectionObject, sc, CHECK_APP_DOMAIN);
+
+ // We are reporting the location of a local variable, assert it doesn't change.
+ _ASSERTE(oldObj == refCollectionObject);
+ }
+}
+
+//-----------------------------------------------------------------------------
+StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData)
+{
+ //
+ // KEEP IN SYNC WITH DacStackReferenceWalker::Callback in debug\daccess\daccess.cpp
+ //
+
+ Frame *pFrame;
+ GCCONTEXT *gcctx = (GCCONTEXT*) pData;
+
+#if CHECK_APP_DOMAIN_LEAKS
+ gcctx->sc->pCurrentDomain = pCF->GetAppDomain();
+#endif //CHECK_APP_DOMAIN_LEAKS
+
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+ if (g_fEnableARM)
+ {
+ gcctx->sc->pCurrentDomain = pCF->GetAppDomain();
+ }
+#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+ MethodDesc *pMD = pCF->GetFunction();
+
+#ifdef GC_PROFILING
+ gcctx->sc->pMD = pMD;
+#endif //GC_PROFILING
+
+ // Clear it on exit so that we never have a stale CrawlFrame
+ ResetPointerHolder<CrawlFrame*> rph(&gcctx->cf);
+ // put it somewhere so that GcEnumObject can get to it.
+ gcctx->cf = pCF;
+
+ bool fReportGCReferences = true;
+#if defined(WIN64EXCEPTIONS)
+ // We may have unwound this crawlFrame and thus, shouldn't report the invalid
+ // references it may contain.
+ fReportGCReferences = pCF->ShouldCrawlframeReportGCReferences();
+#endif // defined(WIN64EXCEPTIONS)
+
+ if (fReportGCReferences)
+ {
+ if (pCF->IsFrameless())
+ {
+ ICodeManager * pCM = pCF->GetCodeManager();
+ _ASSERTE(pCM != NULL);
+
+ unsigned flags = pCF->GetCodeManagerFlags();
+
+ #ifdef _TARGET_X86_
+ STRESS_LOG3(LF_GCROOTS, LL_INFO1000, "Scanning Frameless method %pM EIP = %p &EIP = %p\n",
+ pMD, GetControlPC(pCF->GetRegisterSet()), pCF->GetRegisterSet()->PCTAddr);
+ #else
+ STRESS_LOG2(LF_GCROOTS, LL_INFO1000, "Scanning Frameless method %pM ControlPC = %p\n",
+ pMD, GetControlPC(pCF->GetRegisterSet()));
+ #endif
+
+ _ASSERTE(pMD != 0);
+
+ #ifdef _DEBUG
+ LOG((LF_GCROOTS, LL_INFO1000, "Scanning Frame for method %s:%s\n",
+ pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
+ #endif // _DEBUG
+
+ DWORD relOffsetOverride = NO_OVERRIDE_OFFSET;
+#if defined(WIN64EXCEPTIONS) && defined(USE_GC_INFO_DECODER)
+ if (pCF->ShouldParentToFuncletUseUnwindTargetLocationForGCReporting())
+ {
+ GCInfoToken gcInfoToken = pCF->GetGCInfoToken();
+ GcInfoDecoder _gcInfoDecoder(
+ gcInfoToken,
+ DECODE_CODE_LENGTH
+ );
+
+ if(_gcInfoDecoder.WantsReportOnlyLeaf())
+ {
+ // We're in a special case of unwinding from a funclet, and resuming execution in
+ // another catch funclet associated with same parent function. We need to report roots.
+ // Reporting at the original throw site gives incorrect liveness information. We choose to
+ // report the liveness information at the first interruptible instruction of the catch funclet
+ // that we are going to execute. We also only report stack slots, since no registers can be
+ // live at the first instruction of a handler, except the catch object, which the VM protects
+ // specially. If the catch funclet has not interruptible point, we fall back and just report
+ // what we used to: at the original throw instruction. This might lead to bad GC behavior
+ // if the liveness is not correct.
+ const EE_ILEXCEPTION_CLAUSE& ehClauseForCatch = pCF->GetEHClauseForCatch();
+ relOffsetOverride = FindFirstInterruptiblePoint(pCF, ehClauseForCatch.HandlerStartPC,
+ ehClauseForCatch.HandlerEndPC);
+ _ASSERTE(relOffsetOverride != NO_OVERRIDE_OFFSET);
+
+ STRESS_LOG3(LF_GCROOTS, LL_INFO1000, "Setting override offset = %u for method %pM ControlPC = %p\n",
+ relOffsetOverride, pMD, GetControlPC(pCF->GetRegisterSet()));
+ }
+
+ }
+#endif // WIN64EXCEPTIONS && USE_GC_INFO_DECODER
+
+ pCM->EnumGcRefs(pCF->GetRegisterSet(),
+ pCF->GetCodeInfo(),
+ flags,
+ GcEnumObject,
+ pData,
+ relOffsetOverride);
+
+ }
+ else
+ {
+ Frame * pFrame = pCF->GetFrame();
+
+ STRESS_LOG3(LF_GCROOTS, LL_INFO1000,
+ "Scanning ExplicitFrame %p AssocMethod = %pM frameVTable = %pV\n",
+ pFrame, pFrame->GetFunction(), *((void**) pFrame));
+ pFrame->GcScanRoots( gcctx->f, gcctx->sc);
+ }
+ }
+
+
+ // If we're executing a LCG dynamic method then we must promote the associated resolver to ensure it
+ // doesn't get collected and yank the method code out from under us).
+
+ // Be careful to only promote the reference -- we can also be called to relocate the reference and
+ // that can lead to all sorts of problems since we could be racing for the relocation with the long
+ // weak handle we recover the reference from. Promoting the reference is enough, the handle in the
+ // reference will be relocated properly as long as we keep it alive till the end of the collection
+ // as long as the reference is actually maintained by the long weak handle.
+ if (pMD && gcctx->sc->promotion)
+ {
+ BOOL fMaybeCollectibleMethod = TRUE;
+
+ // If this is a frameless method then the jitmanager can answer the question of whether
+ // or not this is LCG simply by looking at the heap where the code lives, however there
+ // is also the prestub case where we need to explicitly look at the MD for stuff that isn't
+ // ngen'd
+ if (pCF->IsFrameless())
+ {
+ fMaybeCollectibleMethod = ExecutionManager::IsCollectibleMethod(pCF->GetMethodToken());
+ }
+
+ if (fMaybeCollectibleMethod && pMD->IsLCGMethod())
+ {
+ Object *refResolver = OBJECTREFToObject(pMD->AsDynamicMethodDesc()->GetLCGMethodResolver()->GetManagedResolver());
+#ifdef _DEBUG
+ Object *oldObj = refResolver;
+#endif
+ _ASSERTE(refResolver != NULL);
+ (*gcctx->f)(&refResolver, gcctx->sc, CHECK_APP_DOMAIN);
+ _ASSERTE(!pMD->IsSharedByGenericInstantiations());
+
+ // We are reporting the location of a local variable, assert it doesn't change.
+ _ASSERTE(oldObj == refResolver);
+ }
+ else
+ {
+ if (fMaybeCollectibleMethod)
+ {
+ GcReportLoaderAllocator(gcctx->f, gcctx->sc, pMD->GetLoaderAllocator());
+ }
+
+ if (fReportGCReferences)
+ {
+ GenericParamContextType paramContextType = GENERIC_PARAM_CONTEXT_NONE;
+
+ if (pCF->IsFrameless())
+ {
+ // We need to grab the Context Type here because there are cases where the MethodDesc
+ // is shared, and thus indicates there should be an instantion argument, but the JIT
+ // was still allowed to optimize it away and we won't grab it below because we're not
+ // reporting any references from this frame.
+ paramContextType = pCF->GetCodeManager()->GetParamContextType(pCF->GetRegisterSet(), pCF->GetCodeInfo());
+ }
+ else
+ {
+ if (pMD->RequiresInstMethodDescArg())
+ paramContextType = GENERIC_PARAM_CONTEXT_METHODDESC;
+ else if (pMD->RequiresInstMethodTableArg())
+ paramContextType = GENERIC_PARAM_CONTEXT_METHODTABLE;
+ }
+
+ if (SafeToReportGenericParamContext(pCF))
+ {
+ // Handle the case where the method is a static shared generic method and we need to keep the type
+ // of the generic parameters alive
+ if (paramContextType == GENERIC_PARAM_CONTEXT_METHODDESC)
+ {
+ MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(pCF->GetParamTypeArg());
+ _ASSERTE((pMDReal != NULL) || !pCF->IsFrameless());
+ if (pMDReal != NULL)
+ {
+ GcReportLoaderAllocator(gcctx->f, gcctx->sc, pMDReal->GetLoaderAllocator());
+ }
+ }
+ else if (paramContextType == GENERIC_PARAM_CONTEXT_METHODTABLE)
+ {
+ MethodTable *pMTReal = dac_cast<PTR_MethodTable>(pCF->GetParamTypeArg());
+ _ASSERTE((pMTReal != NULL) || !pCF->IsFrameless());
+ if (pMTReal != NULL)
+ {
+ GcReportLoaderAllocator(gcctx->f, gcctx->sc, pMTReal->GetLoaderAllocator());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Since we may be asynchronously walking another thread's stack,
+ // check (frequently) for stack-buffer-overrun corruptions after
+ // any long operation
+ pCF->CheckGSCookies();
+
+ return SWA_CONTINUE;
+} \ No newline at end of file
diff --git a/src/vm/gcenv.ee.cpp b/src/vm/gcenv.ee.cpp
index 63c4ffea10..8be0b29d58 100644
--- a/src/vm/gcenv.ee.cpp
+++ b/src/vm/gcenv.ee.cpp
@@ -11,33 +11,6 @@
*
*/
-#include "common.h"
-
-#include "gcenv.h"
-
-#ifdef FEATURE_STANDALONE_GC
-#include "gcenv.ee.h"
-#else
-#include "../gc/env/gcenv.ee.h"
-#endif // FEATURE_STANDALONE_GC
-
-#include "threadsuspend.h"
-
-#ifdef FEATURE_COMINTEROP
-#include "runtimecallablewrapper.h"
-#include "rcwwalker.h"
-#include "comcallablewrapper.h"
-#endif // FEATURE_COMINTEROP
-
-// the method table for the WeakReference class
-extern MethodTable* pWeakReferenceMT;
-
-// The canonical method table for WeakReference<T>
-extern MethodTable* pWeakReferenceOfTCanonMT;
-
-// Finalizes a weak reference directly.
-extern void FinalizeWeakReference(Object* obj);
-
void GCToEEInterface::SuspendEE(SUSPEND_REASON reason)
{
WRAPPER_NO_CONTRACT;
@@ -57,394 +30,6 @@ void GCToEEInterface::RestartEE(bool bFinishedGC)
ThreadSuspend::RestartEE(bFinishedGC, TRUE);
}
-/*
- * GcEnumObject()
- *
- * This is the JIT compiler (or any remote code manager)
- * GC enumeration callback
- */
-
-void GcEnumObject(LPVOID pData, OBJECTREF *pObj, uint32_t flags)
-{
- Object ** ppObj = (Object **)pObj;
- GCCONTEXT * pCtx = (GCCONTEXT *) pData;
-
- // Since we may be asynchronously walking another thread's stack,
- // check (frequently) for stack-buffer-overrun corruptions after
- // any long operation
- if (pCtx->cf != NULL)
- pCtx->cf->CheckGSCookies();
-
- //
- // Sanity check that the flags contain only these three values
- //
- assert((flags & ~(GC_CALL_INTERIOR|GC_CALL_PINNED|GC_CALL_CHECK_APP_DOMAIN)) == 0);
-
- // for interior pointers, we optimize the case in which
- // it points into the current threads stack area
- //
- if (flags & GC_CALL_INTERIOR)
- PromoteCarefully(pCtx->f, ppObj, pCtx->sc, flags);
- else
- (pCtx->f)(ppObj, pCtx->sc, flags);
-}
-
-//-----------------------------------------------------------------------------
-void GcReportLoaderAllocator(promote_func* fn, ScanContext* sc, LoaderAllocator *pLoaderAllocator)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- SO_TOLERANT;
- MODE_COOPERATIVE;
- }
- CONTRACTL_END;
-
- if (pLoaderAllocator != NULL && pLoaderAllocator->IsCollectible())
- {
- Object *refCollectionObject = OBJECTREFToObject(pLoaderAllocator->GetExposedObject());
-
-#ifdef _DEBUG
- Object *oldObj = refCollectionObject;
-#endif
-
- _ASSERTE(refCollectionObject != NULL);
- fn(&refCollectionObject, sc, CHECK_APP_DOMAIN);
-
- // We are reporting the location of a local variable, assert it doesn't change.
- _ASSERTE(oldObj == refCollectionObject);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Determine whether we should report the generic parameter context
-//
-// This is meant to detect the situation where a ThreadAbortException is raised
-// in the prolog of a managed method, before the location for the generics
-// context has been initialized; when such a TAE is raised, we are open to a
-// race with the GC (e.g. while creating the managed object for the TAE).
-// The GC would cause a stack walk, and if we report the stack location for
-// the generic param context at this time we'd crash.
-// The long term solution is to avoid raising TAEs in any non-GC safe points,
-// and to additionally ensure that we do not expose the runtime to TAE
-// starvation.
-inline bool SafeToReportGenericParamContext(CrawlFrame* pCF)
-{
- LIMITED_METHOD_CONTRACT;
- if (!pCF->IsFrameless() || !(pCF->IsActiveFrame() || pCF->IsInterrupted()))
- {
- return true;
- }
-
-#ifndef USE_GC_INFO_DECODER
-
- ICodeManager * pEECM = pCF->GetCodeManager();
- if (pEECM != NULL && pEECM->IsInPrologOrEpilog(pCF->GetRelOffset(), pCF->GetGCInfoToken(), NULL))
- {
- return false;
- }
-
-#else // USE_GC_INFO_DECODER
-
- GcInfoDecoder gcInfoDecoder(pCF->GetGCInfoToken(),
- DECODE_PROLOG_LENGTH);
- UINT32 prologLength = gcInfoDecoder.GetPrologSize();
- if (pCF->GetRelOffset() < prologLength)
- {
- return false;
- }
-
-#endif // USE_GC_INFO_DECODER
-
- return true;
-}
-
-#if defined(WIN64EXCEPTIONS)
-
-struct FindFirstInterruptiblePointState
-{
- unsigned offs;
- unsigned endOffs;
- unsigned returnOffs;
-};
-
-bool FindFirstInterruptiblePointStateCB(
- UINT32 startOffset,
- UINT32 stopOffset,
- LPVOID hCallback)
-{
- FindFirstInterruptiblePointState* pState = (FindFirstInterruptiblePointState*)hCallback;
-
- _ASSERTE(startOffset < stopOffset);
- _ASSERTE(pState->offs < pState->endOffs);
-
- if (stopOffset <= pState->offs)
- {
- // The range ends before the requested offset.
- return false;
- }
-
- // The offset is in the range.
- if (startOffset <= pState->offs &&
- pState->offs < stopOffset)
- {
- pState->returnOffs = pState->offs;
- return true;
- }
-
- // The range is completely after the desired offset. We use the range start offset, if
- // it comes before the given endOffs. We assume that the callback is called with ranges
- // in increasing order, so earlier ones are reported before later ones. That is, if we
- // get to this case, it will be the closest interruptible range after the requested
- // offset.
-
- _ASSERTE(pState->offs < startOffset);
- if (startOffset < pState->endOffs)
- {
- pState->returnOffs = startOffset;
- return true;
- }
-
- return false;
-}
-
-// Find the first interruptible point in the range [offs .. endOffs) (the beginning of the range is inclusive,
-// the end is exclusive). Return -1 if no such point exists.
-unsigned FindFirstInterruptiblePoint(CrawlFrame* pCF, unsigned offs, unsigned endOffs)
-{
-#ifdef USE_GC_INFO_DECODER
- GCInfoToken gcInfoToken = pCF->GetGCInfoToken();
- GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_FOR_RANGES_CALLBACK);
-
- FindFirstInterruptiblePointState state;
- state.offs = offs;
- state.endOffs = endOffs;
- state.returnOffs = -1;
-
- gcInfoDecoder.EnumerateInterruptibleRanges(&FindFirstInterruptiblePointStateCB, &state);
-
- return state.returnOffs;
-#else
- PORTABILITY_ASSERT("FindFirstInterruptiblePoint");
- return -1;
-#endif // USE_GC_INFO_DECODER
-}
-
-#endif // WIN64EXCEPTIONS
-
-//-----------------------------------------------------------------------------
-StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData)
-{
- //
- // KEEP IN SYNC WITH DacStackReferenceWalker::Callback in debug\daccess\daccess.cpp
- //
-
- Frame *pFrame;
- GCCONTEXT *gcctx = (GCCONTEXT*) pData;
-
-#if CHECK_APP_DOMAIN_LEAKS
- gcctx->sc->pCurrentDomain = pCF->GetAppDomain();
-#endif //CHECK_APP_DOMAIN_LEAKS
-
-#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
- if (g_fEnableARM)
- {
- gcctx->sc->pCurrentDomain = pCF->GetAppDomain();
- }
-#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
-
- MethodDesc *pMD = pCF->GetFunction();
-
-#ifdef GC_PROFILING
- gcctx->sc->pMD = pMD;
-#endif //GC_PROFILING
-
- // Clear it on exit so that we never have a stale CrawlFrame
- ResetPointerHolder<CrawlFrame*> rph(&gcctx->cf);
- // put it somewhere so that GcEnumObject can get to it.
- gcctx->cf = pCF;
-
- bool fReportGCReferences = true;
-#if defined(WIN64EXCEPTIONS)
- // We may have unwound this crawlFrame and thus, shouldn't report the invalid
- // references it may contain.
- fReportGCReferences = pCF->ShouldCrawlframeReportGCReferences();
-#endif // defined(WIN64EXCEPTIONS)
-
- if (fReportGCReferences)
- {
- if (pCF->IsFrameless())
- {
- ICodeManager * pCM = pCF->GetCodeManager();
- _ASSERTE(pCM != NULL);
-
- unsigned flags = pCF->GetCodeManagerFlags();
-
- #ifdef _TARGET_X86_
- STRESS_LOG3(LF_GCROOTS, LL_INFO1000, "Scanning Frameless method %pM EIP = %p &EIP = %p\n",
- pMD, GetControlPC(pCF->GetRegisterSet()), pCF->GetRegisterSet()->PCTAddr);
- #else
- STRESS_LOG2(LF_GCROOTS, LL_INFO1000, "Scanning Frameless method %pM ControlPC = %p\n",
- pMD, GetControlPC(pCF->GetRegisterSet()));
- #endif
-
- _ASSERTE(pMD != 0);
-
- #ifdef _DEBUG
- LOG((LF_GCROOTS, LL_INFO1000, "Scanning Frame for method %s:%s\n",
- pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
- #endif // _DEBUG
-
- DWORD relOffsetOverride = NO_OVERRIDE_OFFSET;
-#if defined(WIN64EXCEPTIONS) && defined(USE_GC_INFO_DECODER)
- if (pCF->ShouldParentToFuncletUseUnwindTargetLocationForGCReporting())
- {
- GCInfoToken gcInfoToken = pCF->GetGCInfoToken();
- GcInfoDecoder _gcInfoDecoder(
- gcInfoToken,
- DECODE_CODE_LENGTH
- );
-
- if(_gcInfoDecoder.WantsReportOnlyLeaf())
- {
- // We're in a special case of unwinding from a funclet, and resuming execution in
- // another catch funclet associated with same parent function. We need to report roots.
- // Reporting at the original throw site gives incorrect liveness information. We choose to
- // report the liveness information at the first interruptible instruction of the catch funclet
- // that we are going to execute. We also only report stack slots, since no registers can be
- // live at the first instruction of a handler, except the catch object, which the VM protects
- // specially. If the catch funclet has not interruptible point, we fall back and just report
- // what we used to: at the original throw instruction. This might lead to bad GC behavior
- // if the liveness is not correct.
- const EE_ILEXCEPTION_CLAUSE& ehClauseForCatch = pCF->GetEHClauseForCatch();
- relOffsetOverride = FindFirstInterruptiblePoint(pCF, ehClauseForCatch.HandlerStartPC,
- ehClauseForCatch.HandlerEndPC);
- _ASSERTE(relOffsetOverride != NO_OVERRIDE_OFFSET);
-
- STRESS_LOG3(LF_GCROOTS, LL_INFO1000, "Setting override offset = %u for method %pM ControlPC = %p\n",
- relOffsetOverride, pMD, GetControlPC(pCF->GetRegisterSet()));
- }
-
- }
-#endif // WIN64EXCEPTIONS && USE_GC_INFO_DECODER
-
- pCM->EnumGcRefs(pCF->GetRegisterSet(),
- pCF->GetCodeInfo(),
- flags,
- GcEnumObject,
- pData,
- relOffsetOverride);
-
- }
- else
- {
- Frame * pFrame = pCF->GetFrame();
-
- STRESS_LOG3(LF_GCROOTS, LL_INFO1000,
- "Scanning ExplicitFrame %p AssocMethod = %pM frameVTable = %pV\n",
- pFrame, pFrame->GetFunction(), *((void**) pFrame));
- pFrame->GcScanRoots( gcctx->f, gcctx->sc);
- }
- }
-
-
- // If we're executing a LCG dynamic method then we must promote the associated resolver to ensure it
- // doesn't get collected and yank the method code out from under us).
-
- // Be careful to only promote the reference -- we can also be called to relocate the reference and
- // that can lead to all sorts of problems since we could be racing for the relocation with the long
- // weak handle we recover the reference from. Promoting the reference is enough, the handle in the
- // reference will be relocated properly as long as we keep it alive till the end of the collection
- // as long as the reference is actually maintained by the long weak handle.
- if (pMD && gcctx->sc->promotion)
- {
- BOOL fMaybeCollectibleMethod = TRUE;
-
- // If this is a frameless method then the jitmanager can answer the question of whether
- // or not this is LCG simply by looking at the heap where the code lives, however there
- // is also the prestub case where we need to explicitly look at the MD for stuff that isn't
- // ngen'd
- if (pCF->IsFrameless())
- {
- fMaybeCollectibleMethod = ExecutionManager::IsCollectibleMethod(pCF->GetMethodToken());
- }
-
- if (fMaybeCollectibleMethod && pMD->IsLCGMethod())
- {
- Object *refResolver = OBJECTREFToObject(pMD->AsDynamicMethodDesc()->GetLCGMethodResolver()->GetManagedResolver());
-#ifdef _DEBUG
- Object *oldObj = refResolver;
-#endif
- _ASSERTE(refResolver != NULL);
- (*gcctx->f)(&refResolver, gcctx->sc, CHECK_APP_DOMAIN);
- _ASSERTE(!pMD->IsSharedByGenericInstantiations());
-
- // We are reporting the location of a local variable, assert it doesn't change.
- _ASSERTE(oldObj == refResolver);
- }
- else
- {
- if (fMaybeCollectibleMethod)
- {
- GcReportLoaderAllocator(gcctx->f, gcctx->sc, pMD->GetLoaderAllocator());
- }
-
- if (fReportGCReferences)
- {
- GenericParamContextType paramContextType = GENERIC_PARAM_CONTEXT_NONE;
-
- if (pCF->IsFrameless())
- {
- // We need to grab the Context Type here because there are cases where the MethodDesc
- // is shared, and thus indicates there should be an instantion argument, but the JIT
- // was still allowed to optimize it away and we won't grab it below because we're not
- // reporting any references from this frame.
- paramContextType = pCF->GetCodeManager()->GetParamContextType(pCF->GetRegisterSet(), pCF->GetCodeInfo());
- }
- else
- {
- if (pMD->RequiresInstMethodDescArg())
- paramContextType = GENERIC_PARAM_CONTEXT_METHODDESC;
- else if (pMD->RequiresInstMethodTableArg())
- paramContextType = GENERIC_PARAM_CONTEXT_METHODTABLE;
- }
-
- if (SafeToReportGenericParamContext(pCF))
- {
- // Handle the case where the method is a static shared generic method and we need to keep the type
- // of the generic parameters alive
- if (paramContextType == GENERIC_PARAM_CONTEXT_METHODDESC)
- {
- MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(pCF->GetParamTypeArg());
- _ASSERTE((pMDReal != NULL) || !pCF->IsFrameless());
- if (pMDReal != NULL)
- {
- GcReportLoaderAllocator(gcctx->f, gcctx->sc, pMDReal->GetLoaderAllocator());
- }
- }
- else if (paramContextType == GENERIC_PARAM_CONTEXT_METHODTABLE)
- {
- MethodTable *pMTReal = dac_cast<PTR_MethodTable>(pCF->GetParamTypeArg());
- _ASSERTE((pMTReal != NULL) || !pCF->IsFrameless());
- if (pMTReal != NULL)
- {
- GcReportLoaderAllocator(gcctx->f, gcctx->sc, pMTReal->GetLoaderAllocator());
- }
- }
- }
- }
- }
- }
-
- // Since we may be asynchronously walking another thread's stack,
- // check (frequently) for stack-buffer-overrun corruptions after
- // any long operation
- pCF->CheckGSCookies();
-
- return SWA_CONTINUE;
-}
-
VOID GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
{
CONTRACTL
diff --git a/src/vm/gcenv.ee.h b/src/vm/gcenv.ee.h
index 9f7df14d22..6c917a5a2b 100644
--- a/src/vm/gcenv.ee.h
+++ b/src/vm/gcenv.ee.h
@@ -9,6 +9,9 @@
#ifdef FEATURE_STANDALONE_GC
+namespace standalone
+{
+
class GCToEEInterface : public IGCToCLR {
public:
GCToEEInterface() = default;
@@ -51,6 +54,8 @@ public:
MethodTable* GetFreeObjectMethodTable();
};
+} // namespace standalone
+
#endif // FEATURE_STANDALONE_GC
#endif // _GCENV_EE_H_
diff --git a/src/vm/gcenv.ee.standalone.cpp b/src/vm/gcenv.ee.standalone.cpp
new file mode 100644
index 0000000000..5ba2aca812
--- /dev/null
+++ b/src/vm/gcenv.ee.standalone.cpp
@@ -0,0 +1,30 @@
+// 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.
+
+#include "common.h"
+#include "gcenv.h"
+#include "gcenv.ee.h"
+#include "threadsuspend.h"
+
+#ifdef FEATURE_COMINTEROP
+#include "runtimecallablewrapper.h"
+#include "rcwwalker.h"
+#include "comcallablewrapper.h"
+#endif // FEATURE_COMINTEROP
+
+// the method table for the WeakReference class
+extern MethodTable* pWeakReferenceMT;
+
+// The canonical method table for WeakReference<T>
+extern MethodTable* pWeakReferenceOfTCanonMT;
+
+// Finalizes a weak reference directly.
+extern void FinalizeWeakReference(Object* obj);
+
+namespace standalone
+{
+
+#include "gcenv.ee.cpp"
+
+} // namespace standalone \ No newline at end of file
diff --git a/src/vm/gcenv.ee.static.cpp b/src/vm/gcenv.ee.static.cpp
new file mode 100644
index 0000000000..240e325a9e
--- /dev/null
+++ b/src/vm/gcenv.ee.static.cpp
@@ -0,0 +1,25 @@
+// 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.
+
+#include "common.h"
+#include "gcenv.h"
+#include "../gc/env/gcenv.ee.h"
+#include "threadsuspend.h"
+
+#ifdef FEATURE_COMINTEROP
+#include "runtimecallablewrapper.h"
+#include "rcwwalker.h"
+#include "comcallablewrapper.h"
+#endif // FEATURE_COMINTEROP
+
+// the method table for the WeakReference class
+extern MethodTable* pWeakReferenceMT;
+
+// The canonical method table for WeakReference<T>
+extern MethodTable* pWeakReferenceOfTCanonMT;
+
+// Finalizes a weak reference directly.
+extern void FinalizeWeakReference(Object* obj);
+
+#include "gcenv.ee.cpp" \ No newline at end of file