[Local GC] Scaffolding for loading a standalone GC (#11242)
authorSean Gillespie <segilles@microsoft.com>
Wed, 17 May 2017 22:07:34 +0000 (15:07 -0700)
committerSean Gillespie <segilles@microsoft.com>
Thu, 1 Jun 2017 17:19:59 +0000 (10:19 -0700)
* 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

26 files changed:
CMakeLists.txt
build.cmd
build.sh
src/CMakeLists.txt
src/dlls/mscordac/CMakeLists.txt
src/dlls/mscoree/coreclr/CMakeLists.txt
src/dlls/mscoree/mscorwks_unixexports.src
src/gc/CMakeLists.txt
src/gc/dac/CMakeLists.txt [deleted file]
src/gc/gc.h
src/gc/gccommon.cpp
src/gc/gcenv.ee.standalone.inl
src/gc/gcinterface.ee.h
src/gc/gcinterface.h
src/gc/sample/GCSample.cpp
src/gc/sample/gcenv.h
src/gc/unix/gcenv.unix.cpp
src/gc/wks/CMakeLists.txt [deleted file]
src/inc/clrconfigvalues.h
src/vm/CMakeLists.txt
src/vm/ceemain.cpp
src/vm/gcenv.ee.common.cpp [new file with mode: 0644]
src/vm/gcenv.ee.cpp
src/vm/gcenv.ee.h
src/vm/gcenv.ee.standalone.cpp [new file with mode: 0644]
src/vm/gcenv.ee.static.cpp [new file with mode: 0644]

index f55b54c..f6e0987 100644 (file)
@@ -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")
index b3706ba..67f4396 100644 (file)
--- 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)
index 976e18b..4577e94 100755 (executable)
--- 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
index b761b6a..9359580 100644 (file)
@@ -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)
index afe5bea..82582f4 100644 (file)
@@ -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
 )
 
index 7a4617f..81aaad4 100644 (file)
@@ -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
index 271ff8a..73ffc0c 100644 (file)
@@ -104,3 +104,6 @@ GlobalMemoryStatusEx
 VirtualQuery
 WideCharToMultiByte
 WriteFile
+
+; Function for initializing a standalone GC
+InitializeGarbageCollector
index 59c18ff..f55c1a9 100644 (file)
@@ -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 (file)
index 1f1c9eb..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include(${CLR_DIR}/dac.cmake)
-add_library_clr(gc_dac STATIC ${GC_SOURCES_DAC})
index 07ae6c9..2da0cb3 100644 (file)
@@ -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
index 4950809..d7c5722 100644 (file)
@@ -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
index f6954fc..31c3455 100644 (file)
@@ -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__
index 7b868e7..8290572 100644 (file)
@@ -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:
index 552a8ca..425745b 100644 (file)
@@ -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
index 0a771b7..43cb238 100644 (file)
@@ -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[])
 {
     //
index 4505f1a..14f60d8 100644 (file)
@@ -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
index bca0dfe..cebc515 100644 (file)
@@ -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 (file)
index fcb95a3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-add_library_clr(gc_wks STATIC ${GC_SOURCES_WKS})
index c4722bc..81e9fae 100644 (file)
@@ -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
index 3895f71..60e7ea4 100644 (file)
@@ -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})
index de6059a..617b022 100644 (file)
@@ -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 (file)
index 0000000..ca73490
--- /dev/null
@@ -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
index 63c4ffe..8be0b29 100644 (file)
  *
  */
 
-#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
index 9f7df14..6c917a5 100644 (file)
@@ -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 (file)
index 0000000..5ba2aca
--- /dev/null
@@ -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 (file)
index 0000000..240e325
--- /dev/null
@@ -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