From 4426d6a643c313d734143aa601669c03719c3525 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 11 Nov 2022 23:48:16 +0100 Subject: [PATCH 1/7] CoreCLR initialization failures logging 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. --- src/coreclr/dlls/mscoree/exports.cpp | 33 +++ 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 | 2 +- src/coreclr/gc/gcenv.ee.standalone.inl | 10 +- src/coreclr/gc/gcinterface.ee.h | 7 + src/coreclr/gc/gcinterface.h | 6 +- src/coreclr/gc/gcload.cpp | 2 +- src/coreclr/gc/sample/gcenv.ee.cpp | 4 + src/coreclr/gc/unix/gcenv.unix.cpp | 4 + src/coreclr/hosts/corerun/corerun.cpp | 23 +- src/coreclr/hosts/inc/coreclrhost.h | 18 ++ src/coreclr/nativeaot/Runtime/gcrhenv.cpp | 4 + src/coreclr/vm/appdomain.cpp | 232 ++++++++++-------- src/coreclr/vm/ceemain.cpp | 30 ++- src/coreclr/vm/codeman.cpp | 10 + src/coreclr/vm/common.h | 2 + src/coreclr/vm/gcenv.ee.cpp | 5 + src/coreclr/vm/gcenv.ee.h | 2 + src/native/corehost/coreclr_resolver.h | 7 + src/native/corehost/hostpolicy/coreclr.cpp | 20 ++ .../standalone/coreclr_resolver.cpp | 2 + 24 files changed, 328 insertions(+), 119 deletions(-) diff --git a/src/coreclr/dlls/mscoree/exports.cpp b/src/coreclr/dlls/mscoree/exports.cpp index e8ee88275df8e..f01c01d5face8 100644 --- a/src/coreclr/dlls/mscoree/exports.cpp +++ b/src/coreclr/dlls/mscoree/exports.cpp @@ -158,6 +158,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 +452,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 987f67bc36aff..0ac421b63e071 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 ebf0556e7a870..a35a59c095604 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 3a7c049d4af88..2f4eecc3501fa 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 aa596a10a7c4d..e2e161ac8966b 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -13575,13 +13575,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; } @@ -13591,7 +13595,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); @@ -13606,13 +13610,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()); @@ -13739,6 +13746,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 @@ -13862,6 +13870,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; } @@ -45537,6 +45546,7 @@ HRESULT GCHeap::Initialize() if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE)) { + GCToEEInterface::LogErrorToHost("Creation of WaitForGCEvent failed"); return E_FAIL; } @@ -45618,9 +45628,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 413075246fd5c..2dca1e19281ea 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 -IGCToCLR* g_theGCToCLR; +IGCToCLR2* 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 e52c42efcb470..78bf2ee7daff0 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 IGCToCLR* g_theGCToCLR; +extern IGCToCLR2* g_theGCToCLR; // GC version that the current runtime supports extern VersionInfo g_runtimeSupportedVersion; @@ -314,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 e2019c8a1adbe..e1a53bd244d24 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 7e8c1b5665fac..cb706134679f0 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 c33125ea86414..5d5c0b32f4d51 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 = clrToGC; + g_theGCToCLR = (IGCToCLR2*)clrToGC; #else UNREFERENCED_PARAMETER(clrToGC); assert(clrToGC == nullptr); diff --git a/src/coreclr/gc/sample/gcenv.ee.cpp b/src/coreclr/gc/sample/gcenv.ee.cpp index 84274f2341c3e..ac6d80bf03467 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/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 7ad9a38a75e51..fddd4cff2107f 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -361,6 +361,7 @@ bool GCToOSInterface::Initialize() int cpuCount = sysconf(SYSCONF_GET_NUMPROCS); if (cpuCount == -1) { + GCToEEInterface::LogErrorToHost("Cannot get CPU count"); return false; } @@ -385,6 +386,7 @@ bool GCToOSInterface::Initialize() if (g_helperPage == MAP_FAILED) { + GCToEEInterface::LogErrorToHost("Failed to allocate g_helperPage"); return false; } @@ -398,12 +400,14 @@ bool GCToOSInterface::Initialize() if (status != 0) { + GCToEEInterface::LogErrorToHost("Failed to mlock g_helperPage"); return false; } status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL); if (status != 0) { + GCToEEInterface::LogErrorToHost("Failed to initialize g_flushProcessWriteBuffersMutex"); munlock(g_helperPage, OS_PAGE_SIZE); return false; } diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 60b5c5522d185..e503a0f84c9f3 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -147,11 +147,11 @@ static string_t build_tpa(const string_t& core_root, const string_t& core_librar return tpa_list.str(); } -static bool try_get_export(pal::mod_t mod, const char* symbol, void** fptr) +static bool try_get_export(pal::mod_t mod, const char* symbol, void** fptr, bool isOptional = false) { assert(mod != nullptr && symbol != nullptr && fptr != nullptr); *fptr = pal::get_module_symbol(mod, symbol); - if (*fptr != nullptr) + if ((*fptr != nullptr) || isOptional) return true; pal::fprintf(stderr, W("Export '%s' not found.\n"), symbol); @@ -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 + try_get_export(coreclr_mod, "coreclr_set_error_writer", (void**)&coreclr_set_error_writer_func, true /* isOptional */); + // 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 46a3119d628de..01eeac600a224 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 56864387123e6..9454d54b8cffd 100644 --- a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp +++ b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp @@ -1357,6 +1357,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 74c74041a49d6..1fc56ff4f39b9 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1265,144 +1265,166 @@ 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); - g_pWeakReferenceClass = CoreLibBinder::GetClass(CLASS__WEAKREFERENCE); - g_pWeakReferenceOfTClass = CoreLibBinder::GetClass(CLASS__WEAKREFERENCEGENERIC); + g_pWeakReferenceClass = CoreLibBinder::GetClass(CLASS__WEAKREFERENCE); + g_pWeakReferenceOfTClass = CoreLibBinder::GetClass(CLASS__WEAKREFERENCEGENERIC); -#ifdef FEATURE_COMINTEROP - if (g_pConfig->IsBuiltInCOMSupported()) - { - g_pBaseCOMObject = CoreLibBinder::GetClass(CLASS__COM_OBJECT); - } - else - { - g_pBaseCOMObject = NULL; - } -#endif + #ifdef FEATURE_COMINTEROP + if (g_pConfig->IsBuiltInCOMSupported()) + { + g_pBaseCOMObject = CoreLibBinder::GetClass(CLASS__COM_OBJECT); + } + else + { + g_pBaseCOMObject = NULL; + } + #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 00411af2d843b..c7bd77bb60c35 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -210,6 +210,7 @@ #endif // FEATURE_GDBJIT #include "genanalysis.h" +#include "nativelibrary.h" static int GetThreadUICultureId(_Out_ LocaleIDValue* pLocale); // TODO: This shouldn't use the LCID. We should rely on name instead @@ -876,6 +877,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 @@ -931,9 +937,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); @@ -947,6 +950,21 @@ void EEStartupHelper() g_MiniMetaDataBuffMaxSize, MEM_COMMIT, PAGE_READWRITE); #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS +#ifdef TARGET_UNIX + // 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 g_fEEStarted = TRUE; g_EEStartupStatus = S_OK; @@ -968,9 +986,6 @@ void EEStartupHelper() { SystemDomain::SystemModule()->ExpandAll(); } - - // Perform CoreLib consistency check if requested - g_CoreLib.CheckExtended(); #endif // _DEBUG @@ -978,6 +993,7 @@ ErrExit: ; } EX_CATCH { + hr = GET_EXCEPTION()->GetHR(); } EX_END_CATCH(RethrowTerminalExceptionsWithInitCheck) @@ -1628,8 +1644,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/codeman.cpp b/src/coreclr/vm/codeman.cpp index a81a2bd9f663f..d57fcfd5f66dc 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -2022,6 +2022,7 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName DEBUGARG(LPCWSTR pwzJitPath) { pJitLoadData->jld_hr = E_FAIL; LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: pwzJitName is null")); + LogErrorToHost("LoadAndInitializeJIT: pwzJitName is null"); return; } @@ -2049,9 +2050,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); + LogErrorToHost("LoadAndInitializeJIT: invalid characters in %s", utf8JitName); } } + MAKE_UTF8PTR_FROMWIDE_NOTHROW(utf8JitName, pwzJitName); + if (SUCCEEDED(hr)) { pJitLoadData->jld_status = JIT_LOAD_STATUS_DONE_LOAD; @@ -2105,21 +2110,25 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName DEBUGARG(LPCWSTR pwzJitPath) { // Mismatched version ID. Fail the load. LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: mismatched JIT version identifier in %S\n", pwzJitName)); + LogErrorToHost("LoadAndInitializeJIT: mismatched JIT version identifier in %s", utf8JitName); } } else { LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: failed to get ICorJitCompiler in %S\n", pwzJitName)); + LogErrorToHost("LoadAndInitializeJIT: failed to get ICorJitCompiler in %s", utf8JitName); } } else { LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: failed to find 'getJit' entrypoint in %S\n", pwzJitName)); + LogErrorToHost("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)); + LogErrorToHost("LoadAndInitializeJIT: LoadAndInitializeJIT: caught an exception trying to initialize %s", utf8JitName); } EX_END_CATCH(SwallowAllExceptions) } @@ -2127,6 +2136,7 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName DEBUGARG(LPCWSTR pwzJitPath) { pJitLoadData->jld_hr = hr; LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: failed to load %S, hr=0x%08x\n", pwzJitName, hr)); + LogErrorToHost("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 902f7cad6e385..7e5d8300f621c 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -390,6 +390,8 @@ extern DummyGlobalContract ___contract; #undef FPO_ON #endif +void LogErrorToHost(const char* format, ...); + #endif // !_common_h_ diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 677f67bef5229..1ecb498794c4f 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -1737,3 +1737,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 6403f2637b817..c431cb8245a4c 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/native/corehost/coreclr_resolver.h b/src/native/corehost/coreclr_resolver.h index dc7ad889ff848..81d6403c2879d 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 88f2933c1c7e6..2d5a353156840 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 8248118a4253f..b040c3e854627 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 From bd8dc22bca04913518e0f3754b19e1bfcc52abf9 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 24 Nov 2022 16:26:40 +0100 Subject: [PATCH 2/7] Reflect PR feedback and a bit of cleanup * revert the try_get_export to not to have the isOptional argument * Unify the error logging in JIT loading to single variadic macro * Also fixes Unix build, the code in gcenv.unix.cpp cannot use the GCToEEInterface --- src/coreclr/gc/gcload.cpp | 1 + src/coreclr/gc/unix/gcenv.unix.cpp | 4 ---- src/coreclr/hosts/corerun/corerun.cpp | 6 +++--- src/coreclr/vm/codeman.cpp | 25 +++++++++++-------------- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/coreclr/gc/gcload.cpp b/src/coreclr/gc/gcload.cpp index 5d5c0b32f4d51..20c469f2baf84 100644 --- a/src/coreclr/gc/gcload.cpp +++ b/src/coreclr/gc/gcload.cpp @@ -88,6 +88,7 @@ GC_Initialize( if (!GCToOSInterface::Initialize()) { + GCToEEInterface::LogErrorToHost("Failed to initialize GCToOSInterface"); return E_FAIL; } #endif diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index fddd4cff2107f..7ad9a38a75e51 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -361,7 +361,6 @@ bool GCToOSInterface::Initialize() int cpuCount = sysconf(SYSCONF_GET_NUMPROCS); if (cpuCount == -1) { - GCToEEInterface::LogErrorToHost("Cannot get CPU count"); return false; } @@ -386,7 +385,6 @@ bool GCToOSInterface::Initialize() if (g_helperPage == MAP_FAILED) { - GCToEEInterface::LogErrorToHost("Failed to allocate g_helperPage"); return false; } @@ -400,14 +398,12 @@ bool GCToOSInterface::Initialize() if (status != 0) { - GCToEEInterface::LogErrorToHost("Failed to mlock g_helperPage"); return false; } status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL); if (status != 0) { - GCToEEInterface::LogErrorToHost("Failed to initialize g_flushProcessWriteBuffersMutex"); munlock(g_helperPage, OS_PAGE_SIZE); return false; } diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index e503a0f84c9f3..49f549de5eb66 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -147,11 +147,11 @@ static string_t build_tpa(const string_t& core_root, const string_t& core_librar return tpa_list.str(); } -static bool try_get_export(pal::mod_t mod, const char* symbol, void** fptr, bool isOptional = false) +static bool try_get_export(pal::mod_t mod, const char* symbol, void** fptr) { assert(mod != nullptr && symbol != nullptr && fptr != nullptr); *fptr = pal::get_module_symbol(mod, symbol); - if ((*fptr != nullptr) || isOptional) + if (*fptr != nullptr) return true; pal::fprintf(stderr, W("Export '%s' not found.\n"), symbol); @@ -297,7 +297,7 @@ static int run(const configuration& config) } // The coreclr_set_error_writer is optional - try_get_export(coreclr_mod, "coreclr_set_error_writer", (void**)&coreclr_set_error_writer_func, true /* isOptional */); + (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()); diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index d57fcfd5f66dc..1928561fbe985 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1969,6 +1969,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.) // @@ -2021,8 +2025,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")); - LogErrorToHost("LoadAndInitializeJIT: pwzJitName is null"); + LogJITInitializationError("LoadAndInitializeJIT: pwzJitName is null"); return; } @@ -2049,9 +2052,8 @@ 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); - LogErrorToHost("LoadAndInitializeJIT: invalid characters in %s", utf8JitName); + LogJITInitializationError("LoadAndInitializeJIT: invalid characters in %s", utf8JitName); } } @@ -2109,34 +2111,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)); - LogErrorToHost("LoadAndInitializeJIT: mismatched JIT version identifier in %s", utf8JitName); + LogJITInitializationError("LoadAndInitializeJIT: mismatched JIT version identifier in %s", utf8JitName); } } else { - LOG((LF_JIT, LL_FATALERROR, "LoadAndInitializeJIT: failed to get ICorJitCompiler in %S\n", pwzJitName)); - LogErrorToHost("LoadAndInitializeJIT: failed to get ICorJitCompiler in %s", utf8JitName); + 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)); - LogErrorToHost("LoadAndInitializeJIT: failed to find 'getJit' entrypoint in %s", utf8JitName); + 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)); - LogErrorToHost("LoadAndInitializeJIT: LoadAndInitializeJIT: caught an exception trying to initialize %s", utf8JitName); + 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)); - LogErrorToHost("LoadAndInitializeJIT: failed to load %s, hr=0x%08X", utf8JitName, hr); + LogJITInitializationError("LoadAndInitializeJIT: failed to load %s, hr=0x%08X", utf8JitName, hr); } } From 7e330b5008de53d1fdcf645c744fd6acb8cace5b Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 24 Nov 2022 18:51:24 +0100 Subject: [PATCH 3/7] Add single file host detection --- src/coreclr/vm/ceemain.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index c7bd77bb60c35..1a469387c21de 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -951,19 +951,27 @@ void EEStartupHelper() #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS #ifdef TARGET_UNIX - // Preload the libSystem.Native.so/dylib to detect possible problems with loading it early - EX_TRY + // 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. { - 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()); + SString currentModulePathName; + if (GetClrModulePathName(currentModulePathName) && currentModulePathName.EndsWith(SString(SString::Ascii, PAL_SHLIB_SUFFIX))) + { + // 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; + } } - EX_END_HOOK; #endif // TARGET_UNIX g_fEEStarted = TRUE; From 45e27b1955421b3319567c715571fbadbbf14fe6 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 25 Nov 2022 13:15:10 -0800 Subject: [PATCH 4/7] Fix libSystem.Native check location --- src/coreclr/vm/ceemain.cpp | 25 ------------------------- src/coreclr/vm/corhost.cpp | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 1a469387c21de..668f0183ba6c5 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -210,7 +210,6 @@ #endif // FEATURE_GDBJIT #include "genanalysis.h" -#include "nativelibrary.h" static int GetThreadUICultureId(_Out_ LocaleIDValue* pLocale); // TODO: This shouldn't use the LCID. We should rely on name instead @@ -950,30 +949,6 @@ void EEStartupHelper() g_MiniMetaDataBuffMaxSize, MEM_COMMIT, PAGE_READWRITE); #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS -#ifdef TARGET_UNIX - // 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. - { - SString currentModulePathName; - if (GetClrModulePathName(currentModulePathName) && currentModulePathName.EndsWith(SString(SString::Ascii, PAL_SHLIB_SUFFIX))) - { - // 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 - g_fEEStarted = TRUE; g_EEStartupStatus = S_OK; hr = S_OK; diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp index 72fe94761c1db..37eb53728a2e9 100644 --- a/src/coreclr/vm/corhost.cpp +++ b/src/coreclr/vm/corhost.cpp @@ -36,6 +36,8 @@ #include "dwreport.h" #endif // !TARGET_UNIX +#include "nativelibrary.h" + #ifndef DACCESS_COMPILE extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading); @@ -626,6 +628,24 @@ HRESULT CorHost2::CreateAppDomainWithManager( sAppPaths)); } +#if defined(TARGET_UNIX) && !defined(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 && !CORECLR_EMBEDDED + *pAppDomainID=DefaultADID; m_fAppDomainCreated = TRUE; From 6f1d39a2f982744905d1503c9df9c875fd587a8b Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 29 Nov 2022 01:46:44 -0800 Subject: [PATCH 5/7] Add the coreclr_set_error_writer API to Mono --- src/mono/mono/mini/main-core.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/mono/mono/mini/main-core.c b/src/mono/mono/mini/main-core.c index 66e85d713c947..74d6cbcf1d447 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 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 +} From 970af28665f04ef8eff7128a92dc3e5f9e9910a0 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 29 Nov 2022 06:36:42 -0800 Subject: [PATCH 6/7] Fix Mono x86 missing calling convention spec --- src/mono/mono/mini/main-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/mini/main-core.c b/src/mono/mono/mini/main-core.c index 74d6cbcf1d447..d9e26269e68a9 100644 --- a/src/mono/mono/mini/main-core.c +++ b/src/mono/mono/mini/main-core.c @@ -44,7 +44,7 @@ MONO_API int STDAPICALLTYPE coreclr_create_delegate (void* hostHandle, unsigned const char* entryPointAssemblyName, const char* entryPointTypeName, const char* entryPointMethodName, void** delegate); -MONO_API int coreclr_set_error_writer(coreclr_error_writer_callback_fn error_writer); +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 From 1985979af4968a245ea063386b94427cba263d24 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 6 Dec 2022 00:20:31 +0100 Subject: [PATCH 7/7] Fix error code returned when the S.P.C. is not found --- src/coreclr/binder/utils.cpp | 94 ++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/src/coreclr/binder/utils.cpp b/src/coreclr/binder/utils.cpp index 44d44dc9c0a7b..edd1e0026b74d 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; } }