From bda611c19ff5f3f5551beea301f17ada25e1413a Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 5 Jan 2023 21:08:13 +0100 Subject: [PATCH 1/2] [Release/7.0] CoreCLR initialization failures logging Port of #78484, #78790, #80104 and #80294 This change adds detecting and logging of failures during coreclr initialization. For logging, it uses a new host API `coreclr_set_error_writer` to register a callback to report the errors to the host. The hosts have support for optional usage of this API so that they can work with older runtime versions as well. The logging checks and reports failures with: * System.Private.CoreLib.dll * GC initialization * JIT initialization * libSystem.Native.so/dylib on Unix The logging messages should allow customers to self-diagnose the issues causing the failures. This change also adds backport of support for standalone GC back compatibility that is a prerequisite for it. --- src/coreclr/binder/utils.cpp | 94 ++++---- src/coreclr/dlls/mscoree/exports.cpp | 40 +++- src/coreclr/dlls/mscoree/mscorwks_ntdef.src | 1 + .../dlls/mscoree/mscorwks_unixexports.src | 1 + src/coreclr/gc/env/gcenv.ee.h | 2 + src/coreclr/gc/gc.cpp | 20 +- src/coreclr/gc/gccommon.cpp | 3 +- src/coreclr/gc/gcenv.ee.standalone.inl | 13 +- src/coreclr/gc/gcinterface.ee.h | 7 + src/coreclr/gc/gcinterface.h | 6 +- src/coreclr/gc/gcload.cpp | 12 +- src/coreclr/gc/sample/gcenv.ee.cpp | 4 + src/coreclr/hosts/corerun/corerun.cpp | 19 ++ src/coreclr/hosts/inc/coreclrhost.h | 18 ++ src/coreclr/nativeaot/Runtime/gcrhenv.cpp | 4 + src/coreclr/vm/appdomain.cpp | 208 ++++++++++-------- src/coreclr/vm/ceemain.cpp | 15 +- src/coreclr/vm/ceemain.h | 5 + src/coreclr/vm/codeman.cpp | 21 +- src/coreclr/vm/common.h | 2 + src/coreclr/vm/corhost.cpp | 23 ++ src/coreclr/vm/gcenv.ee.cpp | 5 + src/coreclr/vm/gcenv.ee.h | 2 + src/coreclr/vm/gcheaputilities.cpp | 10 +- src/mono/mono/mini/main-core.c | 23 ++ src/native/corehost/coreclr_resolver.h | 7 + src/native/corehost/hostpolicy/coreclr.cpp | 20 ++ .../standalone/coreclr_resolver.cpp | 2 + .../hostpolicy/static/coreclr_resolver.cpp | 4 + 29 files changed, 427 insertions(+), 164 deletions(-) diff --git a/src/coreclr/binder/utils.cpp b/src/coreclr/binder/utils.cpp index 44d44dc9c0a7be..edd1e0026b74d8 100644 --- a/src/coreclr/binder/utils.cpp +++ b/src/coreclr/binder/utils.cpp @@ -150,56 +150,68 @@ namespace BINDER_SPACE isNativeImage = false; HRESULT pathResult = S_OK; - IF_FAIL_GO(pathResult = GetNextPath(paths, startPos, outPath)); - if (pathResult == S_FALSE) + while(true) { - return S_FALSE; - } - - if (Path::IsRelative(outPath)) - { - GO_WITH_HRESULT(E_INVALIDARG); - } - - { - // Find the beginning of the simple name - SString::CIterator iSimpleNameStart = outPath.End(); - - if (!outPath.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W)) - { - iSimpleNameStart = outPath.Begin(); - } - else + IF_FAIL_GO(pathResult = GetNextPath(paths, startPos, outPath)); + if (pathResult == S_FALSE) { - // Advance past the directory separator to the first character of the file name - iSimpleNameStart++; + return S_FALSE; } - if (iSimpleNameStart == outPath.End()) + if (Path::IsRelative(outPath)) { GO_WITH_HRESULT(E_INVALIDARG); } - const SString sNiDll(SString::Literal, W(".ni.dll")); - const SString sNiExe(SString::Literal, W(".ni.exe")); - const SString sDll(SString::Literal, W(".dll")); - const SString sExe(SString::Literal, W(".exe")); - - if (!dllOnly && (outPath.EndsWithCaseInsensitive(sNiDll) || - outPath.EndsWithCaseInsensitive(sNiExe))) - { - simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 7); - isNativeImage = true; - } - else if (outPath.EndsWithCaseInsensitive(sDll) || - (!dllOnly && outPath.EndsWithCaseInsensitive(sExe))) - { - simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 4); - } - else { - // Invalid filename - GO_WITH_HRESULT(E_INVALIDARG); + // Find the beginning of the simple name + SString::CIterator iSimpleNameStart = outPath.End(); + + if (!outPath.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W)) + { + iSimpleNameStart = outPath.Begin(); + } + else + { + // Advance past the directory separator to the first character of the file name + iSimpleNameStart++; + } + + if (iSimpleNameStart == outPath.End()) + { + GO_WITH_HRESULT(E_INVALIDARG); + } + + const SString sNiDll(SString::Literal, W(".ni.dll")); + const SString sNiExe(SString::Literal, W(".ni.exe")); + const SString sDll(SString::Literal, W(".dll")); + const SString sExe(SString::Literal, W(".exe")); + + if (dllOnly && (outPath.EndsWithCaseInsensitive(sExe) || + outPath.EndsWithCaseInsensitive(sNiExe))) + { + // Skip exe files when the caller requested only dlls + continue; + } + + if (outPath.EndsWithCaseInsensitive(sNiDll) || + outPath.EndsWithCaseInsensitive(sNiExe)) + { + simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 7); + isNativeImage = true; + } + else if (outPath.EndsWithCaseInsensitive(sDll) || + outPath.EndsWithCaseInsensitive(sExe)) + { + simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 4); + } + else + { + // Invalid filename + GO_WITH_HRESULT(E_INVALIDARG); + } + + break; } } diff --git a/src/coreclr/dlls/mscoree/exports.cpp b/src/coreclr/dlls/mscoree/exports.cpp index e8ee88275df8eb..ca22f3ad324b73 100644 --- a/src/coreclr/dlls/mscoree/exports.cpp +++ b/src/coreclr/dlls/mscoree/exports.cpp @@ -14,6 +14,7 @@ #include #include #include +#include "../../vm/ceemain.h" #ifdef FEATURE_GDBJIT #include "../../vm/gdbjithelpers.h" #endif // FEATURE_GDBJIT @@ -25,12 +26,6 @@ // Holder for const wide strings typedef NewArrayHolder ConstWStringHolder; -// Specifies whether coreclr is embedded or standalone -extern bool g_coreclr_embedded; - -// Specifies whether hostpolicy is embedded in executable or standalone -extern bool g_hostpolicy_embedded; - // Holder for array of wide strings class ConstWStringArrayHolder : public NewArrayHolder { @@ -158,6 +153,26 @@ static void ConvertConfigPropertiesToUnicode( *propertyValuesWRef = propertyValuesW; } +coreclr_error_writer_callback_fn g_errorWriter = nullptr; + +// +// Set callback for writing error logging +// +// Parameters: +// errorWriter - callback that will be called for each line of the error info +// - passing in NULL removes a callback that was previously set +// +// Returns: +// S_OK +// +extern "C" +DLLEXPORT +int coreclr_set_error_writer(coreclr_error_writer_callback_fn error_writer) +{ + g_errorWriter = error_writer; + return S_OK; +} + #ifdef FEATURE_GDBJIT GetInfoForMethodDelegate getInfoForMethodDelegate = NULL; extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const char*, const char*, void**); @@ -432,3 +447,16 @@ int coreclr_execute_assembly( return hr; } + +void LogErrorToHost(const char* format, ...) +{ + if (g_errorWriter != NULL) + { + char messageBuffer[1024]; + va_list args; + va_start(args, format); + _vsnprintf_s(messageBuffer, ARRAY_SIZE(messageBuffer), _TRUNCATE, format, args); + g_errorWriter(messageBuffer); + va_end(args); + } +} diff --git a/src/coreclr/dlls/mscoree/mscorwks_ntdef.src b/src/coreclr/dlls/mscoree/mscorwks_ntdef.src index 987f67bc36aff4..0ac421b63e0718 100644 --- a/src/coreclr/dlls/mscoree/mscorwks_ntdef.src +++ b/src/coreclr/dlls/mscoree/mscorwks_ntdef.src @@ -22,6 +22,7 @@ EXPORTS coreclr_create_delegate coreclr_execute_assembly coreclr_initialize + coreclr_set_error_writer coreclr_shutdown coreclr_shutdown_2 diff --git a/src/coreclr/dlls/mscoree/mscorwks_unixexports.src b/src/coreclr/dlls/mscoree/mscorwks_unixexports.src index ebf0556e7a870d..a35a59c095604a 100644 --- a/src/coreclr/dlls/mscoree/mscorwks_unixexports.src +++ b/src/coreclr/dlls/mscoree/mscorwks_unixexports.src @@ -2,6 +2,7 @@ coreclr_create_delegate coreclr_execute_assembly coreclr_initialize +coreclr_set_error_writer coreclr_shutdown coreclr_shutdown_2 diff --git a/src/coreclr/gc/env/gcenv.ee.h b/src/coreclr/gc/env/gcenv.ee.h index 3a7c049d4af880..2f4eecc3501fae 100644 --- a/src/coreclr/gc/env/gcenv.ee.h +++ b/src/coreclr/gc/env/gcenv.ee.h @@ -94,6 +94,8 @@ class GCToEEInterface static uint32_t GetCurrentProcessCpuCount(); static void DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved); + + static void LogErrorToHost(const char *message); }; #endif // __GCENV_EE_H__ diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 5d0af20f569e69..ba3b718bfbebb7 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -13509,13 +13509,17 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, gc_log = CreateLogFile(GCConfig::GetLogFile(), false); if (gc_log == NULL) + { + GCToEEInterface::LogErrorToHost("Cannot create log file"); return E_FAIL; + } // GCLogFileSize in MBs. gc_log_file_size = static_cast(GCConfig::GetLogFileSize()); if (gc_log_file_size <= 0 || gc_log_file_size > 500) { + GCToEEInterface::LogErrorToHost("Invalid log file size (valid size needs to be larger than 0 and smaller than 500)"); fclose (gc_log); return E_FAIL; } @@ -13525,7 +13529,7 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, if (!gc_log_buffer) { fclose(gc_log); - return E_FAIL; + return E_OUTOFMEMORY; } memset (gc_log_buffer, '*', gc_log_buffer_size); @@ -13540,13 +13544,16 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, gc_config_log = CreateLogFile(GCConfig::GetConfigLogFile(), true); if (gc_config_log == NULL) + { + GCToEEInterface::LogErrorToHost("Cannot create log file"); return E_FAIL; + } gc_config_log_buffer = new (nothrow) uint8_t [gc_config_log_buffer_size]; if (!gc_config_log_buffer) { fclose(gc_config_log); - return E_FAIL; + return E_OUTOFMEMORY; } compact_ratio = static_cast(GCConfig::GetCompactRatio()); @@ -13673,6 +13680,7 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, else { assert (!"cannot use regions without specifying the range!!!"); + GCToEEInterface::LogErrorToHost("Cannot use regions without specifying the range (using DOTNET_GCRegionRange)"); return E_FAIL; } #else //USE_REGIONS @@ -13786,6 +13794,7 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, if (!init_semi_shared()) { + GCToEEInterface::LogErrorToHost("PER_HEAP_ISOLATED data members initialization failed"); hres = E_FAIL; } @@ -45376,6 +45385,7 @@ HRESULT GCHeap::Initialize() if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE)) { + GCToEEInterface::LogErrorToHost("Creation of WaitForGCEvent failed"); return E_FAIL; } @@ -45457,9 +45467,15 @@ HRESULT GCHeap::Initialize() int hb_info_size_per_node = hb_info_size_per_proc * procs_per_numa_node; uint8_t* numa_mem = (uint8_t*)GCToOSInterface::VirtualReserve (hb_info_size_per_node, 0, 0, numa_node_index); if (!numa_mem) + { + GCToEEInterface::LogErrorToHost("Reservation of numa_mem failed"); return E_FAIL; + } if (!GCToOSInterface::VirtualCommit (numa_mem, hb_info_size_per_node, numa_node_index)) + { + GCToEEInterface::LogErrorToHost("Commit of numa_mem failed"); return E_FAIL; + } heap_balance_info_proc* hb_info_procs = (heap_balance_info_proc*)numa_mem; hb_info_numa_nodes[numa_node_index].hb_info_procs = hb_info_procs; diff --git a/src/coreclr/gc/gccommon.cpp b/src/coreclr/gc/gccommon.cpp index 27eb8f935c3372..813a9a2e0389a7 100644 --- a/src/coreclr/gc/gccommon.cpp +++ b/src/coreclr/gc/gccommon.cpp @@ -17,7 +17,8 @@ IGCHeapInternal* g_theGCHeap; IGCHandleManager* g_theGCHandleManager; #ifdef BUILD_AS_STANDALONE -IGCToCLR* g_theGCToCLR; +IGCToCLR2* g_theGCToCLR; +VersionInfo g_runtimeSupportedVersion; #endif // BUILD_AS_STANDALONE #ifdef GC_CONFIG_DRIVEN diff --git a/src/coreclr/gc/gcenv.ee.standalone.inl b/src/coreclr/gc/gcenv.ee.standalone.inl index 83b5406e76eed4..78bf2ee7daff00 100644 --- a/src/coreclr/gc/gcenv.ee.standalone.inl +++ b/src/coreclr/gc/gcenv.ee.standalone.inl @@ -9,7 +9,10 @@ // The singular interface instance. All calls in GCToEEInterface // will be forwarded to this interface instance. -extern IGCToCLR* g_theGCToCLR; +extern IGCToCLR2* g_theGCToCLR; + +// GC version that the current runtime supports +extern VersionInfo g_runtimeSupportedVersion; struct StressLogMsg; @@ -311,4 +314,12 @@ inline void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStar g_theGCToCLR->DiagAddNewRegion(generation, rangeStart, rangeEnd, rangeEndReserved); } +inline void GCToEEInterface::LogErrorToHost(const char *message) +{ + if (g_runtimeSupportedVersion.MajorVersion >= GC_INTERFACE2_MAJOR_VERSION) + { + g_theGCToCLR->LogErrorToHost(message); + } +} + #endif // __GCTOENV_EE_STANDALONE_INL__ diff --git a/src/coreclr/gc/gcinterface.ee.h b/src/coreclr/gc/gcinterface.ee.h index e2019c8a1adbe6..e1a53bd244d240 100644 --- a/src/coreclr/gc/gcinterface.ee.h +++ b/src/coreclr/gc/gcinterface.ee.h @@ -448,4 +448,11 @@ class IGCToCLR { void DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved) = 0; }; +class IGCToCLR2 : public IGCToCLR { +public: + + virtual + void LogErrorToHost(const char *message) = 0; +}; + #endif // _GCINTERFACE_EE_H_ diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index 023f257d10fa58..0c31ec283433d6 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -6,13 +6,17 @@ // The major version of the GC/EE interface. Breaking changes to this interface // require bumps in the major version number. -#define GC_INTERFACE_MAJOR_VERSION 5 +#define GC_INTERFACE_MAJOR_VERSION 6 // The minor version of the GC/EE interface. Non-breaking changes are required // to bump the minor version number. GCs and EEs with minor version number // mismatches can still interopate correctly, with some care. #define GC_INTERFACE_MINOR_VERSION 1 +// The major version of the GC/EE interface. Breaking changes to this interface +// require bumps in the major version number. +#define GC_INTERFACE2_MAJOR_VERSION 6 + struct ScanContext; struct gc_alloc_context; class CrawlFrame; diff --git a/src/coreclr/gc/gcload.cpp b/src/coreclr/gc/gcload.cpp index d12c09d603b809..20c469f2baf844 100644 --- a/src/coreclr/gc/gcload.cpp +++ b/src/coreclr/gc/gcload.cpp @@ -43,8 +43,15 @@ extern void PopulateHandleTableDacVars(GcDacVars* dacVars); GC_EXPORT void -GC_VersionInfo(/* Out */ VersionInfo* info) +GC_VersionInfo(/* InOut */ VersionInfo* info) { +#ifdef BUILD_AS_STANDALONE + // On entry, the info argument contains the interface version that the runtime supports. + // It is later used to enable backwards compatibility between the GC and the runtime. + // For example, GC would only call functions on g_theGCToCLR interface that the runtime + // supports. + g_runtimeSupportedVersion = *info; +#endif info->MajorVersion = GC_INTERFACE_MAJOR_VERSION; info->MinorVersion = GC_INTERFACE_MINOR_VERSION; info->BuildVersion = 0; @@ -68,7 +75,7 @@ GC_Initialize( #ifdef BUILD_AS_STANDALONE assert(clrToGC != nullptr); - g_theGCToCLR = clrToGC; + g_theGCToCLR = (IGCToCLR2*)clrToGC; #else UNREFERENCED_PARAMETER(clrToGC); assert(clrToGC == nullptr); @@ -81,6 +88,7 @@ GC_Initialize( if (!GCToOSInterface::Initialize()) { + GCToEEInterface::LogErrorToHost("Failed to initialize GCToOSInterface"); return E_FAIL; } #endif diff --git a/src/coreclr/gc/sample/gcenv.ee.cpp b/src/coreclr/gc/sample/gcenv.ee.cpp index 84274f2341c3e4..ac6d80bf034677 100644 --- a/src/coreclr/gc/sample/gcenv.ee.cpp +++ b/src/coreclr/gc/sample/gcenv.ee.cpp @@ -358,3 +358,7 @@ uint32_t GCToEEInterface::GetCurrentProcessCpuCount() void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved) { } + +void GCToEEInterface::LogErrorToHost(const char *message) +{ +} diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 60b5c5522d1857..49f549de5eb663 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -205,6 +205,11 @@ class logger_t final static void* CurrentClrInstance; static unsigned int CurrentAppDomainId; +static void log_error_info(const char* line) +{ + std::fprintf(stderr, "%s\n", line); +} + static int run(const configuration& config) { platform_specific_actions actions; @@ -282,6 +287,7 @@ static int run(const configuration& config) // Get CoreCLR exports coreclr_initialize_ptr coreclr_init_func = nullptr; coreclr_execute_assembly_ptr coreclr_execute_func = nullptr; + coreclr_set_error_writer_ptr coreclr_set_error_writer_func = nullptr; coreclr_shutdown_2_ptr coreclr_shutdown2_func = nullptr; if (!try_get_export(coreclr_mod, "coreclr_initialize", (void**)&coreclr_init_func) || !try_get_export(coreclr_mod, "coreclr_execute_assembly", (void**)&coreclr_execute_func) @@ -290,6 +296,9 @@ static int run(const configuration& config) return -1; } + // The coreclr_set_error_writer is optional + (void)try_get_export(coreclr_mod, "coreclr_set_error_writer", (void**)&coreclr_set_error_writer_func); + // Construct CoreCLR properties. pal::string_utf8_t tpa_list_utf8 = pal::convert_to_utf8(tpa_list.c_str()); pal::string_utf8_t app_path_utf8 = pal::convert_to_utf8(app_path.c_str()); @@ -344,6 +353,11 @@ static int run(const configuration& config) propertyCount, propertyKeys.data(), propertyValues.data(), entry_assembly_utf8.c_str(), config.entry_assembly_argc, argv_utf8.get() }; + if (coreclr_set_error_writer_func != nullptr) + { + coreclr_set_error_writer_func(log_error_info); + } + int result; result = coreclr_init_func( exe_path_utf8.c_str(), @@ -361,6 +375,11 @@ static int run(const configuration& config) return -1; } + if (coreclr_set_error_writer_func != nullptr) + { + coreclr_set_error_writer_func(nullptr); + } + int exit_code; { actions.before_execute_assembly(config.entry_assembly_fullpath); diff --git a/src/coreclr/hosts/inc/coreclrhost.h b/src/coreclr/hosts/inc/coreclrhost.h index 46a3119d628de0..01eeac600a2240 100644 --- a/src/coreclr/hosts/inc/coreclrhost.h +++ b/src/coreclr/hosts/inc/coreclrhost.h @@ -47,6 +47,24 @@ CORECLR_HOSTING_API(coreclr_initialize, void** hostHandle, unsigned int* domainId); +// +// Type of the callback function that can be set by the coreclr_set_error_writer +// +typedef void (*coreclr_error_writer_callback_fn) (const char *message); + +// +// Set callback for writing error logging +// +// Parameters: +// errorWriter - callback that will be called for each line of the error info +// - passing in NULL removes a callback that was previously set +// +// Returns: +// S_OK +// +CORECLR_HOSTING_API(coreclr_set_error_writer, + coreclr_error_writer_callback_fn errorWriter); + // // Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host. // diff --git a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp index e75eb1c66be27e..99337cfbcc55b6 100644 --- a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp +++ b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp @@ -1349,6 +1349,10 @@ bool GCToEEInterface::GetIntConfigValue(const char* privateKey, const char* publ return true; } +void GCToEEInterface::LogErrorToHost(const char *message) +{ +} + bool GCToEEInterface::GetStringConfigValue(const char* privateKey, const char* publicKey, const char** value) { UNREFERENCED_PARAMETER(privateKey); diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 1ca1fe8cad79c6..924c09165b1013 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1267,108 +1267,110 @@ void SystemDomain::LoadBaseSystemClasses() ETWOnStartup(LdSysBases_V1, LdSysBasesEnd_V1); - m_pSystemPEAssembly = PEAssembly::OpenSystem(); + EX_TRY + { + m_pSystemPEAssembly = PEAssembly::OpenSystem(); - // Only partially load the system assembly. Other parts of the code will want to access - // the globals in this function before finishing the load. - m_pSystemAssembly = DefaultDomain()->LoadDomainAssembly(NULL, m_pSystemPEAssembly, FILE_LOAD_POST_LOADLIBRARY)->GetAssembly(); + // Only partially load the system assembly. Other parts of the code will want to access + // the globals in this function before finishing the load. + m_pSystemAssembly = DefaultDomain()->LoadDomainAssembly(NULL, m_pSystemPEAssembly, FILE_LOAD_POST_LOADLIBRARY)->GetAssembly(); - // Set up binder for CoreLib - CoreLibBinder::AttachModule(m_pSystemAssembly->GetModule()); + // Set up binder for CoreLib + CoreLibBinder::AttachModule(m_pSystemAssembly->GetModule()); - // Load Object - g_pObjectClass = CoreLibBinder::GetClass(CLASS__OBJECT); + // Load Object + g_pObjectClass = CoreLibBinder::GetClass(CLASS__OBJECT); - // Now that ObjectClass is loaded, we can set up - // the system for finalizers. There is no point in deferring this, since we need - // to know this before we allocate our first object. - g_pObjectFinalizerMD = CoreLibBinder::GetMethod(METHOD__OBJECT__FINALIZE); + // Now that ObjectClass is loaded, we can set up + // the system for finalizers. There is no point in deferring this, since we need + // to know this before we allocate our first object. + g_pObjectFinalizerMD = CoreLibBinder::GetMethod(METHOD__OBJECT__FINALIZE); - g_pCanonMethodTableClass = CoreLibBinder::GetClass(CLASS____CANON); + g_pCanonMethodTableClass = CoreLibBinder::GetClass(CLASS____CANON); - // NOTE: !!!IMPORTANT!!! ValueType and Enum MUST be loaded one immediately after - // the other, because we have coded MethodTable::IsChildValueType - // in such a way that it depends on this behaviour. - // Load the ValueType class - g_pValueTypeClass = CoreLibBinder::GetClass(CLASS__VALUE_TYPE); + // NOTE: !!!IMPORTANT!!! ValueType and Enum MUST be loaded one immediately after + // the other, because we have coded MethodTable::IsChildValueType + // in such a way that it depends on this behaviour. + // Load the ValueType class + g_pValueTypeClass = CoreLibBinder::GetClass(CLASS__VALUE_TYPE); - // Load the enum class - g_pEnumClass = CoreLibBinder::GetClass(CLASS__ENUM); - _ASSERTE(!g_pEnumClass->IsValueType()); + // Load the enum class + g_pEnumClass = CoreLibBinder::GetClass(CLASS__ENUM); + _ASSERTE(!g_pEnumClass->IsValueType()); - // Load System.RuntimeType - g_pRuntimeTypeClass = CoreLibBinder::GetClass(CLASS__CLASS); - _ASSERTE(g_pRuntimeTypeClass->IsFullyLoaded()); + // Load System.RuntimeType + g_pRuntimeTypeClass = CoreLibBinder::GetClass(CLASS__CLASS); + _ASSERTE(g_pRuntimeTypeClass->IsFullyLoaded()); - // Load Array class - g_pArrayClass = CoreLibBinder::GetClass(CLASS__ARRAY); + // Load Array class + g_pArrayClass = CoreLibBinder::GetClass(CLASS__ARRAY); - // Calling a method on IList for an array requires redirection to a method on - // the SZArrayHelper class. Retrieving such methods means calling - // GetActualImplementationForArrayGenericIListMethod, which calls FetchMethod for - // the corresponding method on SZArrayHelper. This basically results in a class - // load due to a method call, which the debugger cannot handle, so we pre-load - // the SZArrayHelper class here. - g_pSZArrayHelperClass = CoreLibBinder::GetClass(CLASS__SZARRAYHELPER); + // Calling a method on IList for an array requires redirection to a method on + // the SZArrayHelper class. Retrieving such methods means calling + // GetActualImplementationForArrayGenericIListMethod, which calls FetchMethod for + // the corresponding method on SZArrayHelper. This basically results in a class + // load due to a method call, which the debugger cannot handle, so we pre-load + // the SZArrayHelper class here. + g_pSZArrayHelperClass = CoreLibBinder::GetClass(CLASS__SZARRAYHELPER); - // Load Nullable class - g_pNullableClass = CoreLibBinder::GetClass(CLASS__NULLABLE); + // Load Nullable class + g_pNullableClass = CoreLibBinder::GetClass(CLASS__NULLABLE); - // Load the Object array class. - g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass)); + // Load the Object array class. + g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass)); - // We have delayed allocation of CoreLib's static handles until we load the object class - CoreLibBinder::GetModule()->AllocateRegularStaticHandles(DefaultDomain()); + // We have delayed allocation of CoreLib's static handles until we load the object class + CoreLibBinder::GetModule()->AllocateRegularStaticHandles(DefaultDomain()); - // Boolean has to be loaded first to break cycle in IComparisonOperations and IEqualityOperators - CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_BOOLEAN); + // Boolean has to be loaded first to break cycle in IComparisonOperations and IEqualityOperators + CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_BOOLEAN); - // Int32 has to be loaded next to break cycle in IShiftOperators - CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_I4); + // Int32 has to be loaded next to break cycle in IShiftOperators + CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_I4); - // Make sure all primitive types are loaded - for (int et = ELEMENT_TYPE_VOID; et <= ELEMENT_TYPE_R8; et++) - CoreLibBinder::LoadPrimitiveType((CorElementType)et); + // Make sure all primitive types are loaded + for (int et = ELEMENT_TYPE_VOID; et <= ELEMENT_TYPE_R8; et++) + CoreLibBinder::LoadPrimitiveType((CorElementType)et); - CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_I); - CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_U); + CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_I); + CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_U); - g_TypedReferenceMT = CoreLibBinder::GetClass(CLASS__TYPED_REFERENCE); + g_TypedReferenceMT = CoreLibBinder::GetClass(CLASS__TYPED_REFERENCE); - // unfortunately, the following cannot be delay loaded since the jit - // uses it to compute method attributes within a function that cannot - // handle Complus exception and the following call goes through a path - // where a complus exception can be thrown. It is unfortunate, because - // we know that the delegate class and multidelegate class are always - // guaranteed to be found. - g_pDelegateClass = CoreLibBinder::GetClass(CLASS__DELEGATE); - g_pMulticastDelegateClass = CoreLibBinder::GetClass(CLASS__MULTICAST_DELEGATE); + // unfortunately, the following cannot be delay loaded since the jit + // uses it to compute method attributes within a function that cannot + // handle Complus exception and the following call goes through a path + // where a complus exception can be thrown. It is unfortunate, because + // we know that the delegate class and multidelegate class are always + // guaranteed to be found. + g_pDelegateClass = CoreLibBinder::GetClass(CLASS__DELEGATE); + g_pMulticastDelegateClass = CoreLibBinder::GetClass(CLASS__MULTICAST_DELEGATE); - // further loading of nonprimitive types may need casting support. - // initialize cast cache here. - CastCache::Initialize(); - ECall::PopulateManagedCastHelpers(); + // further loading of nonprimitive types may need casting support. + // initialize cast cache here. + CastCache::Initialize(); + ECall::PopulateManagedCastHelpers(); - // used by IsImplicitInterfaceOfSZArray - CoreLibBinder::GetClass(CLASS__IENUMERABLEGENERIC); - CoreLibBinder::GetClass(CLASS__ICOLLECTIONGENERIC); - CoreLibBinder::GetClass(CLASS__ILISTGENERIC); - CoreLibBinder::GetClass(CLASS__IREADONLYCOLLECTIONGENERIC); - CoreLibBinder::GetClass(CLASS__IREADONLYLISTGENERIC); + // used by IsImplicitInterfaceOfSZArray + CoreLibBinder::GetClass(CLASS__IENUMERABLEGENERIC); + CoreLibBinder::GetClass(CLASS__ICOLLECTIONGENERIC); + CoreLibBinder::GetClass(CLASS__ILISTGENERIC); + CoreLibBinder::GetClass(CLASS__IREADONLYCOLLECTIONGENERIC); + CoreLibBinder::GetClass(CLASS__IREADONLYLISTGENERIC); - // Load String - g_pStringClass = CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_STRING); + // Load String + g_pStringClass = CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_STRING); - ECall::PopulateManagedStringConstructors(); + ECall::PopulateManagedStringConstructors(); - g_pExceptionClass = CoreLibBinder::GetClass(CLASS__EXCEPTION); - g_pOutOfMemoryExceptionClass = CoreLibBinder::GetException(kOutOfMemoryException); - g_pStackOverflowExceptionClass = CoreLibBinder::GetException(kStackOverflowException); - g_pExecutionEngineExceptionClass = CoreLibBinder::GetException(kExecutionEngineException); - g_pThreadAbortExceptionClass = CoreLibBinder::GetException(kThreadAbortException); + g_pExceptionClass = CoreLibBinder::GetClass(CLASS__EXCEPTION); + g_pOutOfMemoryExceptionClass = CoreLibBinder::GetException(kOutOfMemoryException); + g_pStackOverflowExceptionClass = CoreLibBinder::GetException(kStackOverflowException); + g_pExecutionEngineExceptionClass = CoreLibBinder::GetException(kExecutionEngineException); + g_pThreadAbortExceptionClass = CoreLibBinder::GetException(kThreadAbortException); - g_pThreadClass = CoreLibBinder::GetClass(CLASS__THREAD); + g_pThreadClass = CoreLibBinder::GetClass(CLASS__THREAD); #ifdef FEATURE_COMINTEROP if (g_pConfig->IsBuiltInCOMSupported()) @@ -1381,27 +1383,47 @@ void SystemDomain::LoadBaseSystemClasses() } #endif - g_pIDynamicInterfaceCastableInterface = CoreLibBinder::GetClass(CLASS__IDYNAMICINTERFACECASTABLE); + g_pIDynamicInterfaceCastableInterface = CoreLibBinder::GetClass(CLASS__IDYNAMICINTERFACECASTABLE); -#ifdef FEATURE_ICASTABLE - g_pICastableInterface = CoreLibBinder::GetClass(CLASS__ICASTABLE); -#endif // FEATURE_ICASTABLE + #ifdef FEATURE_ICASTABLE + g_pICastableInterface = CoreLibBinder::GetClass(CLASS__ICASTABLE); + #endif // FEATURE_ICASTABLE - // Make sure that FCall mapping for Monitor.Enter is initialized. We need it in case Monitor.Enter is used only as JIT helper. - // For more details, see comment in code:JITutil_MonEnterWorker around "__me = GetEEFuncEntryPointMacro(JIT_MonEnter)". - ECall::GetFCallImpl(CoreLibBinder::GetMethod(METHOD__MONITOR__ENTER)); + // Make sure that FCall mapping for Monitor.Enter is initialized. We need it in case Monitor.Enter is used only as JIT helper. + // For more details, see comment in code:JITutil_MonEnterWorker around "__me = GetEEFuncEntryPointMacro(JIT_MonEnter)". + ECall::GetFCallImpl(CoreLibBinder::GetMethod(METHOD__MONITOR__ENTER)); -#ifdef PROFILING_SUPPORTED - // Note that g_profControlBlock.fBaseSystemClassesLoaded must be set to TRUE only after - // all base system classes are loaded. Profilers are not allowed to call any type-loading - // APIs until g_profControlBlock.fBaseSystemClassesLoaded is TRUE. It is important that - // all base system classes need to be loaded before profilers can trigger the type loading. - g_profControlBlock.fBaseSystemClassesLoaded = TRUE; -#endif // PROFILING_SUPPORTED + #ifdef PROFILING_SUPPORTED + // Note that g_profControlBlock.fBaseSystemClassesLoaded must be set to TRUE only after + // all base system classes are loaded. Profilers are not allowed to call any type-loading + // APIs until g_profControlBlock.fBaseSystemClassesLoaded is TRUE. It is important that + // all base system classes need to be loaded before profilers can trigger the type loading. + g_profControlBlock.fBaseSystemClassesLoaded = TRUE; + #endif // PROFILING_SUPPORTED -#if defined(_DEBUG) - g_CoreLib.Check(); -#endif + // Perform any once-only SafeHandle initialization. + SafeHandle::Init(); + + #if defined(_DEBUG) + g_CoreLib.Check(); + g_CoreLib.CheckExtended(); + #endif // _DEBUG + } + EX_HOOK + { + Exception *ex = GET_EXCEPTION(); + + LogErrorToHost("Failed to load System.Private.CoreLib.dll (error code 0x%08X)", ex->GetHR()); + MAKE_UTF8PTR_FROMWIDE_NOTHROW(filePathUtf8, SystemDomain::System()->BaseLibrary()) + if (filePathUtf8 != NULL) + { + LogErrorToHost("Path: %s", filePathUtf8); + } + SString err; + ex->GetMessage(err); + LogErrorToHost("Error message: %s", err.GetUTF8()); + } + EX_END_HOOK; } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 4094b4ff28db83..257a16e4144004 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -890,6 +890,11 @@ void EEStartupHelper() // requires write barriers to have been set up on x86, which happens as part // of InitJITHelpers1. hr = g_pGCHeap->Initialize(); + if (FAILED(hr)) + { + LogErrorToHost("GC heap initialization failed with error 0x%08X", hr); + } + IfFailGo(hr); #ifdef FEATURE_PERFTRACING @@ -945,9 +950,6 @@ void EEStartupHelper() StackSampler::Init(); #endif - // Perform any once-only SafeHandle initialization. - SafeHandle::Init(); - #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS // retrieve configured max size for the mini-metadata buffer (defaults to 64KB) g_MiniMetaDataBuffMaxSize = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MiniMdBufferCapacity); @@ -961,7 +963,6 @@ void EEStartupHelper() g_MiniMetaDataBuffMaxSize, MEM_COMMIT, PAGE_READWRITE); #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS - g_fEEStarted = TRUE; g_EEStartupStatus = S_OK; hr = S_OK; @@ -982,9 +983,6 @@ void EEStartupHelper() { SystemDomain::SystemModule()->ExpandAll(); } - - // Perform CoreLib consistency check if requested - g_CoreLib.CheckExtended(); #endif // _DEBUG @@ -992,6 +990,7 @@ ErrExit: ; } EX_CATCH { + hr = GET_EXCEPTION()->GetHR(); } EX_END_CATCH(RethrowTerminalExceptionsWithInitCheck) @@ -1642,8 +1641,10 @@ void InitializeGarbageCollector() g_pFreeObjectMethodTable->SetComponentSize(1); hr = GCHeapUtilities::LoadAndInitialize(); + if (hr != S_OK) { + LogErrorToHost("GC initialization failed with error 0x%08X", hr); ThrowHR(hr); } diff --git a/src/coreclr/vm/ceemain.h b/src/coreclr/vm/ceemain.h index 688f41c6deb1be..1404a5a04237ff 100644 --- a/src/coreclr/vm/ceemain.h +++ b/src/coreclr/vm/ceemain.h @@ -54,5 +54,10 @@ INT32 GetLatchedExitCode (void); // Stronger than IsGCHeapInitialized BOOL IsGarbageCollectorFullyInitialized(); +// Specifies whether coreclr is embedded or standalone +extern bool g_coreclr_embedded; + +// Specifies whether hostpolicy is embedded in executable or standalone +extern bool g_hostpolicy_embedded; #endif diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 6c2e15fe7a82fa..d8cdd083b46771 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1832,6 +1832,10 @@ static bool ValidateJitName(LPCWSTR pwzJitName) CORINFO_OS getClrVmOs(); +#define LogJITInitializationError(...) \ + LOG((LF_JIT, LL_FATALERROR, __VA_ARGS__)); \ + LogErrorToHost(__VA_ARGS__); + // LoadAndInitializeJIT: load the JIT dll into the process, and initialize it (call the UtilCode initialization function, // check the JIT-EE interface GUID, etc.) // @@ -1884,7 +1888,7 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName DEBUGARG(LPCWSTR pwzJitPath) if (pwzJitName == nullptr) { pJitLoadData->jld_hr = E_FAIL; - LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: pwzJitName is null")); + LogJITInitializationError("LoadAndInitializeJIT: pwzJitName is null"); return; } @@ -1911,10 +1915,13 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName DEBUGARG(LPCWSTR pwzJitPath) } else { - LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: invalid characters in %S\n", pwzJitName)); + MAKE_UTF8PTR_FROMWIDE_NOTHROW(utf8JitName, pwzJitName); + LogJITInitializationError("LoadAndInitializeJIT: invalid characters in %s", utf8JitName); } } + MAKE_UTF8PTR_FROMWIDE_NOTHROW(utf8JitName, pwzJitName); + if (SUCCEEDED(hr)) { pJitLoadData->jld_status = JIT_LOAD_STATUS_DONE_LOAD; @@ -1967,29 +1974,29 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName DEBUGARG(LPCWSTR pwzJitPath) else { // Mismatched version ID. Fail the load. - LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: mismatched JIT version identifier in %S\n", pwzJitName)); + LogJITInitializationError("LoadAndInitializeJIT: mismatched JIT version identifier in %s", utf8JitName); } } else { - LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: failed to get ICorJitCompiler in %S\n", pwzJitName)); + LogJITInitializationError("LoadAndInitializeJIT: failed to get ICorJitCompiler in %s", utf8JitName); } } else { - LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: failed to find 'getJit' entrypoint in %S\n", pwzJitName)); + LogJITInitializationError("LoadAndInitializeJIT: failed to find 'getJit' entrypoint in %s", utf8JitName); } } EX_CATCH { - LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: caught an exception trying to initialize %S\n", pwzJitName)); + LogJITInitializationError("LoadAndInitializeJIT: LoadAndInitializeJIT: caught an exception trying to initialize %s", utf8JitName); } EX_END_CATCH(SwallowAllExceptions) } else { pJitLoadData->jld_hr = hr; - LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: failed to load %S, hr=0x%08x\n", pwzJitName, hr)); + LogJITInitializationError("LoadAndInitializeJIT: failed to load %s, hr=0x%08X", utf8JitName, hr); } } diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index 6430970b87b6e1..074518eab0cc29 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -417,6 +417,8 @@ extern DummyGlobalContract ___contract; #undef FPO_ON #endif +void LogErrorToHost(const char* format, ...); + #endif // !_common_h_ diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp index e1babc7f6228e4..bd283b7076fda3 100644 --- a/src/coreclr/vm/corhost.cpp +++ b/src/coreclr/vm/corhost.cpp @@ -37,6 +37,8 @@ #include "dwreport.h" #endif // !TARGET_UNIX +#include "nativelibrary.h" + #ifndef DACCESS_COMPILE extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading); @@ -659,6 +661,27 @@ HRESULT CorHost2::CreateAppDomainWithManager( sAppPaths)); } +#if defined(TARGET_UNIX) + if (!g_coreclr_embedded) + { + // Check if the current code is executing in the single file host or in libcoreclr.so. The libSystem.Native is linked + // into the single file host, so we need to check only when this code is in libcoreclr.so. + // Preload the libSystem.Native.so/dylib to detect possible problems with loading it early + EX_TRY + { + NativeLibrary::LoadLibraryByName(W("libSystem.Native"), SystemDomain::SystemAssembly(), FALSE, 0, TRUE); + } + EX_HOOK + { + Exception *ex = GET_EXCEPTION(); + SString err; + ex->GetMessage(err); + LogErrorToHost("Error message: %s", err.GetUTF8()); + } + EX_END_HOOK; + } +#endif // TARGET_UNIX + *pAppDomainID=DefaultADID; m_fAppDomainCreated = TRUE; diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 364a08c4bd5407..848e7a32950f81 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -1785,3 +1785,8 @@ void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStart, uint { ProfilerAddNewRegion(generation, rangeStart, rangeEnd, rangeEndReserved); } + +void GCToEEInterface::LogErrorToHost(const char *message) +{ + ::LogErrorToHost("GC: %s", message); +} diff --git a/src/coreclr/vm/gcenv.ee.h b/src/coreclr/vm/gcenv.ee.h index 6403f2637b817f..c431cb8245a4cc 100644 --- a/src/coreclr/vm/gcenv.ee.h +++ b/src/coreclr/vm/gcenv.ee.h @@ -87,6 +87,8 @@ class GCToEEInterface : public IGCToCLR { uint32_t GetCurrentProcessCpuCount(); void DiagAddNewRegion(int generation, BYTE * rangeStart, BYTE * rangeEnd, BYTE * rangeEndReserved); + + void LogErrorToHost(const char *message); }; } // namespace standalone diff --git a/src/coreclr/vm/gcheaputilities.cpp b/src/coreclr/vm/gcheaputilities.cpp index 817179eb2f017f..56630efedc4bd7 100644 --- a/src/coreclr/vm/gcheaputilities.cpp +++ b/src/coreclr/vm/gcheaputilities.cpp @@ -214,17 +214,21 @@ HRESULT LoadAndInitializeGC(LPCWSTR standaloneGcLocation) } g_gc_load_status = GC_LOAD_STATUS_GET_VERSIONINFO; + g_gc_version_info.MajorVersion = GC_INTERFACE_MAJOR_VERSION; + g_gc_version_info.MinorVersion = GC_INTERFACE_MINOR_VERSION; + g_gc_version_info.BuildVersion = 0; versionInfo(&g_gc_version_info); g_gc_load_status = GC_LOAD_STATUS_CALL_VERSIONINFO; - if (g_gc_version_info.MajorVersion != GC_INTERFACE_MAJOR_VERSION) + if (g_gc_version_info.MajorVersion < GC_INTERFACE_MAJOR_VERSION) { - LOG((LF_GC, LL_FATALERROR, "Loaded GC has incompatible major version number (expected %d, got %d)\n", + LOG((LF_GC, LL_FATALERROR, "Loaded GC has incompatible major version number (expected at least %d, got %d)\n", GC_INTERFACE_MAJOR_VERSION, g_gc_version_info.MajorVersion)); return E_FAIL; } - if (g_gc_version_info.MinorVersion < GC_INTERFACE_MINOR_VERSION) + if ((g_gc_version_info.MajorVersion == GC_INTERFACE_MAJOR_VERSION) && + (g_gc_version_info.MinorVersion < GC_INTERFACE_MINOR_VERSION)) { LOG((LF_GC, LL_INFO100, "Loaded GC has lower minor version number (%d) than EE was compiled against (%d)\n", g_gc_version_info.MinorVersion, GC_INTERFACE_MINOR_VERSION)); diff --git a/src/mono/mono/mini/main-core.c b/src/mono/mono/mini/main-core.c index 66e85d713c9473..d9e26269e68a94 100644 --- a/src/mono/mono/mini/main-core.c +++ b/src/mono/mono/mini/main-core.c @@ -20,10 +20,16 @@ #pragma comment(linker, "/export:coreclr_execute_assembly=_coreclr_execute_assembly@24") #pragma comment(linker, "/export:coreclr_shutdown_2=_coreclr_shutdown_2@12") #pragma comment(linker, "/export:coreclr_create_delegate=_coreclr_create_delegate@24") +#pragma comment(linker, "/export:coreclr_set_error_writer=_coreclr_set_error_writer@4") #undef MONO_API #define MONO_API MONO_EXTERN_C #endif +// +// Type of the callback function that can be set by the coreclr_set_error_writer +// +typedef void (*coreclr_error_writer_callback_fn) (const char *message); + MONO_API int STDAPICALLTYPE coreclr_initialize (const char* exePath, const char* appDomainFriendlyName, int propertyCount, const char** propertyKeys, const char** propertyValues, void** hostHandle, unsigned int* domainId); @@ -38,6 +44,8 @@ MONO_API int STDAPICALLTYPE coreclr_create_delegate (void* hostHandle, unsigned const char* entryPointAssemblyName, const char* entryPointTypeName, const char* entryPointMethodName, void** delegate); +MONO_API int STDAPICALLTYPE coreclr_set_error_writer(coreclr_error_writer_callback_fn error_writer); + // // Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain // @@ -117,3 +125,18 @@ int STDAPICALLTYPE coreclr_create_delegate (void* hostHandle, unsigned int domai { return monovm_create_delegate (entryPointAssemblyName, entryPointTypeName, entryPointMethodName, delegate); } + +// +// Set callback for writing error logging +// +// Parameters: +// errorWriter - callback that will be called for each line of the error info +// - passing in NULL removes a callback that was previously set +// +// Returns: +// S_OK +// +int STDAPICALLTYPE coreclr_set_error_writer(coreclr_error_writer_callback_fn error_writer) +{ + return 0; // S_OK +} diff --git a/src/native/corehost/coreclr_resolver.h b/src/native/corehost/coreclr_resolver.h index dc7ad889ff8480..81d6403c2879d6 100644 --- a/src/native/corehost/coreclr_resolver.h +++ b/src/native/corehost/coreclr_resolver.h @@ -9,6 +9,8 @@ using host_handle_t = void*; +typedef void (*coreclr_error_writer_callback_fn)(const char* line); + // Prototype of the coreclr_initialize function from coreclr.dll using coreclr_initialize_fn = pal::hresult_t(STDMETHODCALLTYPE*)( const char* exePath, @@ -19,6 +21,10 @@ using coreclr_initialize_fn = pal::hresult_t(STDMETHODCALLTYPE*)( host_handle_t* hostHandle, unsigned int* domainId); +// Prototype of the coreclr_set_error_writer function from coreclr.dll +using coreclr_set_error_writer_fn = pal::hresult_t(STDMETHODCALLTYPE*)( + coreclr_error_writer_callback_fn callBack); + // Prototype of the coreclr_shutdown function from coreclr.dll using coreclr_shutdown_fn = pal::hresult_t(STDMETHODCALLTYPE*)( host_handle_t hostHandle, @@ -46,6 +52,7 @@ using coreclr_create_delegate_fn = pal::hresult_t(STDMETHODCALLTYPE*)( struct coreclr_resolver_contract_t { pal::dll_t coreclr; + coreclr_set_error_writer_fn coreclr_set_error_writer; coreclr_shutdown_fn coreclr_shutdown; coreclr_initialize_fn coreclr_initialize; coreclr_execute_assembly_fn coreclr_execute_assembly; diff --git a/src/native/corehost/hostpolicy/coreclr.cpp b/src/native/corehost/hostpolicy/coreclr.cpp index 88f2933c1c7e6d..2d5a353156840e 100644 --- a/src/native/corehost/hostpolicy/coreclr.cpp +++ b/src/native/corehost/hostpolicy/coreclr.cpp @@ -18,6 +18,13 @@ namespace coreclr_resolver_t::resolve_coreclr(libcoreclr_path, coreclr_contract); return true; } + + void log_error(const char* line) + { + pal::string_t lineStr; + pal::clr_palstring(line, &lineStr); + trace::error(_X("%s"), lineStr.c_str()); + } } pal::hresult_t coreclr_t::create( @@ -54,6 +61,14 @@ pal::hresult_t coreclr_t::create( }; properties.enumerate(callback); + // Can't use propagate_error_writer_t here because of the difference in encoding on Windows + // coreclr error writer always gets UTF8 string, but error writers in hostfxr/hostpolicy will use UTF16 on Windows + // and UTF8 everywhere else. + if (coreclr_contract.coreclr_set_error_writer != nullptr) + { + coreclr_contract.coreclr_set_error_writer(log_error); + } + pal::hresult_t hr; hr = coreclr_contract.coreclr_initialize( exe_path, @@ -64,6 +79,11 @@ pal::hresult_t coreclr_t::create( &host_handle, &domain_id); + if (coreclr_contract.coreclr_set_error_writer != nullptr) + { + coreclr_contract.coreclr_set_error_writer(nullptr); + } + if (!SUCCEEDED(hr)) return hr; diff --git a/src/native/corehost/hostpolicy/standalone/coreclr_resolver.cpp b/src/native/corehost/hostpolicy/standalone/coreclr_resolver.cpp index 8248118a4253f8..b040c3e8546278 100644 --- a/src/native/corehost/hostpolicy/standalone/coreclr_resolver.cpp +++ b/src/native/corehost/hostpolicy/standalone/coreclr_resolver.cpp @@ -19,10 +19,12 @@ bool coreclr_resolver_t::resolve_coreclr(const pal::string_t& libcoreclr_path, c } coreclr_resolver_contract.coreclr_initialize = reinterpret_cast(pal::get_symbol(coreclr_resolver_contract.coreclr, "coreclr_initialize")); + coreclr_resolver_contract.coreclr_set_error_writer = reinterpret_cast(pal::get_symbol(coreclr_resolver_contract.coreclr, "coreclr_set_error_writer")); coreclr_resolver_contract.coreclr_shutdown = reinterpret_cast(pal::get_symbol(coreclr_resolver_contract.coreclr, "coreclr_shutdown_2")); coreclr_resolver_contract.coreclr_execute_assembly = reinterpret_cast(pal::get_symbol(coreclr_resolver_contract.coreclr, "coreclr_execute_assembly")); coreclr_resolver_contract.coreclr_create_delegate = reinterpret_cast(pal::get_symbol(coreclr_resolver_contract.coreclr, "coreclr_create_delegate")); + // Only the coreclr_set_error_writer is optional assert(coreclr_resolver_contract.coreclr_initialize != nullptr && coreclr_resolver_contract.coreclr_shutdown != nullptr && coreclr_resolver_contract.coreclr_execute_assembly != nullptr diff --git a/src/native/corehost/hostpolicy/static/coreclr_resolver.cpp b/src/native/corehost/hostpolicy/static/coreclr_resolver.cpp index bc3f9138e96bf0..fcad10ffd95972 100644 --- a/src/native/corehost/hostpolicy/static/coreclr_resolver.cpp +++ b/src/native/corehost/hostpolicy/static/coreclr_resolver.cpp @@ -39,6 +39,9 @@ extern "C" const char* entryPointTypeName, const char* entryPointMethodName, void** delegate); + + pal::hresult_t STDMETHODCALLTYPE coreclr_set_error_writer( + coreclr_error_writer_callback_fn error_writer); } @@ -49,6 +52,7 @@ bool coreclr_resolver_t::resolve_coreclr(const pal::string_t& libcoreclr_path, c coreclr_resolver_contract.coreclr_shutdown = reinterpret_cast(coreclr_shutdown_2); coreclr_resolver_contract.coreclr_execute_assembly = reinterpret_cast(coreclr_execute_assembly); coreclr_resolver_contract.coreclr_create_delegate = reinterpret_cast(coreclr_create_delegate); + coreclr_resolver_contract.coreclr_set_error_writer = reinterpret_cast(coreclr_set_error_writer); return true; } From 1f70fc362f1dcb5a8f6e02b502869860d2c910b1 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 26 Jan 2023 10:37:18 +0100 Subject: [PATCH 2/2] Fix GC interfaces versioning (#81188) * Fix GC interfaces versioning The change that introduced GC interfaces versioning had a bug preventing it from using .NET 8 GC being used with .NET 7 runtime. The GC_VersionInfo return value should have been the minimum supported version, not the current one. But more importantly there was also some confusion on what interface the GC_INTERFACE_MAJOR_VERSION represents. While the change considered it to be a version of the IGCToCLR interface, it really means the version of the IGCHeap interface. This change creates a separate version, EE_INTERFACE_MAJOR_VERSION for versioning o the IGCToCLR interface to rectify that. @Maoni0 also didn't like the way of creating a new version of IGCToCLR interface for each major version change, so I am changing it to single IGCToCLR interface. --- src/coreclr/gc/gccommon.cpp | 2 +- src/coreclr/gc/gcenv.ee.standalone.inl | 4 ++-- src/coreclr/gc/gcinterface.ee.h | 5 +---- src/coreclr/gc/gcinterface.h | 12 ++++++------ src/coreclr/gc/gcload.cpp | 2 +- src/coreclr/vm/gcheaputilities.cpp | 4 ++-- 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/coreclr/gc/gccommon.cpp b/src/coreclr/gc/gccommon.cpp index 813a9a2e0389a7..0d29e32c7b494a 100644 --- a/src/coreclr/gc/gccommon.cpp +++ b/src/coreclr/gc/gccommon.cpp @@ -17,7 +17,7 @@ IGCHeapInternal* g_theGCHeap; IGCHandleManager* g_theGCHandleManager; #ifdef BUILD_AS_STANDALONE -IGCToCLR2* g_theGCToCLR; +IGCToCLR* g_theGCToCLR; VersionInfo g_runtimeSupportedVersion; #endif // BUILD_AS_STANDALONE diff --git a/src/coreclr/gc/gcenv.ee.standalone.inl b/src/coreclr/gc/gcenv.ee.standalone.inl index 78bf2ee7daff00..a1478a116e7b72 100644 --- a/src/coreclr/gc/gcenv.ee.standalone.inl +++ b/src/coreclr/gc/gcenv.ee.standalone.inl @@ -9,7 +9,7 @@ // The singular interface instance. All calls in GCToEEInterface // will be forwarded to this interface instance. -extern IGCToCLR2* g_theGCToCLR; +extern IGCToCLR* g_theGCToCLR; // GC version that the current runtime supports extern VersionInfo g_runtimeSupportedVersion; @@ -316,7 +316,7 @@ inline void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStar inline void GCToEEInterface::LogErrorToHost(const char *message) { - if (g_runtimeSupportedVersion.MajorVersion >= GC_INTERFACE2_MAJOR_VERSION) + if (g_runtimeSupportedVersion.MajorVersion >= 1) { g_theGCToCLR->LogErrorToHost(message); } diff --git a/src/coreclr/gc/gcinterface.ee.h b/src/coreclr/gc/gcinterface.ee.h index e1a53bd244d240..5a67bf4e0dd7e7 100644 --- a/src/coreclr/gc/gcinterface.ee.h +++ b/src/coreclr/gc/gcinterface.ee.h @@ -446,11 +446,8 @@ class IGCToCLR { virtual void DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved) = 0; -}; - -class IGCToCLR2 : public IGCToCLR { -public: + // The following method is available only with EE_INTERFACE_MAJOR_VERSION >= 1 virtual void LogErrorToHost(const char *message) = 0; }; diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index 0c31ec283433d6..365a619bbb1d8d 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -4,18 +4,18 @@ #ifndef _GC_INTERFACE_H_ #define _GC_INTERFACE_H_ -// The major version of the GC/EE interface. Breaking changes to this interface +// The major version of the IGCHeap interface. Breaking changes to this interface // require bumps in the major version number. -#define GC_INTERFACE_MAJOR_VERSION 6 +#define GC_INTERFACE_MAJOR_VERSION 5 -// The minor version of the GC/EE interface. Non-breaking changes are required +// The minor version of the IGCHeap interface. Non-breaking changes are required // to bump the minor version number. GCs and EEs with minor version number -// mismatches can still interopate correctly, with some care. +// mismatches can still interoperate correctly, with some care. #define GC_INTERFACE_MINOR_VERSION 1 -// The major version of the GC/EE interface. Breaking changes to this interface +// The major version of the IGCToCLR interface. Breaking changes to this interface // require bumps in the major version number. -#define GC_INTERFACE2_MAJOR_VERSION 6 +#define EE_INTERFACE_MAJOR_VERSION 1 struct ScanContext; struct gc_alloc_context; diff --git a/src/coreclr/gc/gcload.cpp b/src/coreclr/gc/gcload.cpp index 20c469f2baf844..cd3a0b43a6d82d 100644 --- a/src/coreclr/gc/gcload.cpp +++ b/src/coreclr/gc/gcload.cpp @@ -75,7 +75,7 @@ GC_Initialize( #ifdef BUILD_AS_STANDALONE assert(clrToGC != nullptr); - g_theGCToCLR = (IGCToCLR2*)clrToGC; + g_theGCToCLR = clrToGC; #else UNREFERENCED_PARAMETER(clrToGC); assert(clrToGC == nullptr); diff --git a/src/coreclr/vm/gcheaputilities.cpp b/src/coreclr/vm/gcheaputilities.cpp index 56630efedc4bd7..6f354370e2e25e 100644 --- a/src/coreclr/vm/gcheaputilities.cpp +++ b/src/coreclr/vm/gcheaputilities.cpp @@ -214,8 +214,8 @@ HRESULT LoadAndInitializeGC(LPCWSTR standaloneGcLocation) } g_gc_load_status = GC_LOAD_STATUS_GET_VERSIONINFO; - g_gc_version_info.MajorVersion = GC_INTERFACE_MAJOR_VERSION; - g_gc_version_info.MinorVersion = GC_INTERFACE_MINOR_VERSION; + g_gc_version_info.MajorVersion = EE_INTERFACE_MAJOR_VERSION; + g_gc_version_info.MinorVersion = 0; g_gc_version_info.BuildVersion = 0; versionInfo(&g_gc_version_info); g_gc_load_status = GC_LOAD_STATUS_CALL_VERSIONINFO;