From a3bb8a5fae1e41d2bb139781f9ef5d7b111990df Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 10 Mar 2022 14:48:55 -0800 Subject: [PATCH 1/6] Load C++/CLI DLLs in default ALC --- .../System.Private.CoreLib.csproj | 3 +- ...moryAssemblyLoader.PlatformNotSupported.cs | 23 +++++++++++++++ .../InteropServices/InMemoryAssemblyLoader.cs | 28 ++++++++++++------- .../NativeHosting/Ijwhost.cs | 4 +-- 4 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.PlatformNotSupported.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 342668146a873..02161f81b0ff0 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -117,7 +117,6 @@ - @@ -285,10 +284,12 @@ + + Common\Interop\Windows\OleAut32\Interop.VariantClear.cs diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.PlatformNotSupported.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.PlatformNotSupported.cs new file mode 100644 index 0000000000000..1a0881cd414fe --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.PlatformNotSupported.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Loader; + +namespace Internal.Runtime.InteropServices +{ + /// + /// This class enables the .NET IJW host to load an in-memory module as a .NET assembly + /// + public static class InMemoryAssemblyLoader + { + /// + /// Loads into an isolated AssemblyLoadContext an assembly that has already been loaded into memory by the OS loader as a native module. + /// + /// The native module handle for the assembly. + /// The path to the assembly (as a pointer to a UTF-16 C string). + public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assemblyPath) + => throw new PlatformNotSupportedException(); + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs index 4456ce1f9ae14..3e000e79e7363 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs @@ -2,30 +2,30 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Runtime.Loader; +using System.Runtime.Versioning; namespace Internal.Runtime.InteropServices { /// /// This class enables the .NET IJW host to load an in-memory module as a .NET assembly /// + [SupportedOSPlatform("windows")] public static class InMemoryAssemblyLoader { -#if TARGET_WINDOWS private static bool IsSupported { get; } = InitializeIsSupported(); - private static bool InitializeIsSupported() => AppContext.TryGetSwitch("System.Runtime.InteropServices.EnableCppCLIHostActivation", out bool isSupported) ? isSupported : true; -#endif /// /// Loads into an isolated AssemblyLoadContext an assembly that has already been loaded into memory by the OS loader as a native module. /// /// The native module handle for the assembly. /// The path to the assembly (as a pointer to a UTF-16 C string). + [RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")] public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assemblyPath) { -#if TARGET_WINDOWS if (!IsSupported) throw new NotSupportedException("This API is not enabled in trimmed scenarios. see https://aka.ms/dotnet-illink/nativehost for more details"); @@ -35,15 +35,23 @@ public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assem throw new ArgumentOutOfRangeException(nameof(assemblyPath)); } - // We don't cache the ALCs here since each IJW assembly will call this method at most once + // We don't cache the resolvers here since each IJW assembly will call this method at most once // (the load process rewrites the stubs that call here to call the actual methods they're supposed to) + var resolver = new AssemblyDependencyResolver(assemblyPathString); + AssemblyLoadContext.Default.Resolving += + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The trimmer warning is on the method that adds this handler")] + (context, assemblyName) => + { + string? assemblyPath = resolver.ResolveAssemblyToPath(assemblyName); #pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml - AssemblyLoadContext context = new IsolatedComponentLoadContext(assemblyPathString); + return assemblyPath != null + ? context.LoadFromAssemblyPath(assemblyPath) + : null; #pragma warning restore IL2026 - context.LoadFromInMemoryModule(moduleHandle); -#else - throw new PlatformNotSupportedException(); -#endif + }; + + AssemblyLoadContext.Default.LoadFromInMemoryModule(moduleHandle); } } } diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs index 46eb6c6585a3d..99b22c870eba0 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs @@ -35,7 +35,7 @@ public void LoadLibrary() result.Should().Pass() .And.HaveStdOutContaining("[C++/CLI] NativeEntryPoint: calling managed class") - .And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"IsolatedComponentLoadContext"); + .And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"Default\" System.Runtime.Loader.DefaultAssemblyLoadContext"); } [Theory] @@ -57,7 +57,7 @@ public void ManagedHost(bool selfContained) result.Should().Pass() .And.HaveStdOutContaining("[C++/CLI] NativeEntryPoint: calling managed class") - .And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"IsolatedComponentLoadContext") + .And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"Default\" System.Runtime.Loader.DefaultAssemblyLoadContext") .And.HaveStdErrContaining($"Executing as a {(selfContained ? "self-contained" : "framework-dependent")} app"); } From 61ec88743cbe1887cf76370eaf64753251739deb Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 11 Mar 2022 15:38:26 -0800 Subject: [PATCH 2/6] Use get_function_pointer --- .../src/ILLink/ILLink.Descriptors.Shared.xml | 7 -- .../src/ILLink/ILLink.Descriptors.Windows.xml | 8 +++ .../InteropServices/InMemoryAssemblyLoader.cs | 67 +++++++++++++------ src/native/corehost/ijwhost/ijwhost.cpp | 31 ++++++--- src/native/corehost/ijwhost/ijwhost.h | 2 +- src/native/corehost/ijwhost/ijwthunk.cpp | 8 +-- 6 files changed, 82 insertions(+), 41 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml index 1b4e3a1bd2acd..e3682b253e3a7 100644 --- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml +++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml @@ -21,13 +21,6 @@ - - - - - diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml index 52ab4af9bef74..7fcb26e79540d 100644 --- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml +++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml @@ -10,5 +10,13 @@ + + + + + + diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs index 3e000e79e7363..aeba4771f2cb0 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs @@ -19,7 +19,8 @@ public static class InMemoryAssemblyLoader private static bool InitializeIsSupported() => AppContext.TryGetSwitch("System.Runtime.InteropServices.EnableCppCLIHostActivation", out bool isSupported) ? isSupported : true; /// - /// Loads into an isolated AssemblyLoadContext an assembly that has already been loaded into memory by the OS loader as a native module. + /// Loads an assembly that has already been loaded into memory by the OS loader as a native module + /// into an isolated AssemblyLoadContext. /// /// The native module handle for the assembly. /// The path to the assembly (as a pointer to a UTF-16 C string). @@ -29,29 +30,57 @@ public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assem if (!IsSupported) throw new NotSupportedException("This API is not enabled in trimmed scenarios. see https://aka.ms/dotnet-illink/nativehost for more details"); + LoadInMemoryAssemblyInContextImpl(moduleHandle, assemblyPath); + } + + /// + /// Loads into an assembly that has already been loaded into memory by the OS loader as a native module + /// into the specified load context. + /// + /// The native module handle for the assembly. + /// The path to the assembly (as a pointer to a UTF-16 C string). + /// Load context (currently must be IntPtr.Zero) + [UnmanagedCallersOnly] + [RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")] + internal static unsafe void LoadInMemoryAssemblyInContext(IntPtr moduleHandle, IntPtr assemblyPath, IntPtr loadContext) + { + if (!IsSupported) + throw new NotSupportedException("This API is not enabled in trimmed scenarios. see https://aka.ms/dotnet-illink/nativehost for more details"); + + if (loadContext != IntPtr.Zero) + throw new ArgumentOutOfRangeException(nameof(loadContext)); + + LoadInMemoryAssemblyInContextImpl(moduleHandle, assemblyPath, AssemblyLoadContext.Default); + } + + [RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")] + private static void LoadInMemoryAssemblyInContextImpl(IntPtr moduleHandle, IntPtr assemblyPath, AssemblyLoadContext? alc = null) + { string? assemblyPathString = Marshal.PtrToStringUni(assemblyPath); if (assemblyPathString == null) - { throw new ArgumentOutOfRangeException(nameof(assemblyPath)); - } - // We don't cache the resolvers here since each IJW assembly will call this method at most once + // We don't cache the ALCs or resolvers here since each IJW assembly will call this method at most once // (the load process rewrites the stubs that call here to call the actual methods they're supposed to) - var resolver = new AssemblyDependencyResolver(assemblyPathString); - AssemblyLoadContext.Default.Resolving += - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The trimmer warning is on the method that adds this handler")] - (context, assemblyName) => - { - string? assemblyPath = resolver.ResolveAssemblyToPath(assemblyName); -#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml - return assemblyPath != null - ? context.LoadFromAssemblyPath(assemblyPath) - : null; -#pragma warning restore IL2026 - }; - - AssemblyLoadContext.Default.LoadFromInMemoryModule(moduleHandle); + if (alc is null) + { + alc = new IsolatedComponentLoadContext(assemblyPathString); + } + else if (alc == AssemblyLoadContext.Default) + { + var resolver = new AssemblyDependencyResolver(assemblyPathString); + AssemblyLoadContext.Default.Resolving += + [RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")] + (context, assemblyName) => + { + string? assemblyPath = resolver.ResolveAssemblyToPath(assemblyName); + return assemblyPath != null + ? context.LoadFromAssemblyPath(assemblyPath) + : null; + }; + } + + alc.LoadFromInMemoryModule(moduleHandle); } } } diff --git a/src/native/corehost/ijwhost/ijwhost.cpp b/src/native/corehost/ijwhost/ijwhost.cpp index 85020baba02db..af147278a55d1 100644 --- a/src/native/corehost/ijwhost/ijwhost.cpp +++ b/src/native/corehost/ijwhost/ijwhost.cpp @@ -2,15 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "ijwhost.h" -#include "hostfxr.h" -#include "fxr_resolver.h" -#include "pal.h" -#include "trace.h" -#include "error_codes.h" -#include "utils.h" +#include +#include +#include +#include +#include +#include +#include #include "bootstrap_thunk.h" - #if defined(_WIN32) // IJW entry points are defined without the __declspec(dllexport) attribute. // The issue here is that the MSVC compiler links to the exact name _CorDllMain instead of their stdcall-managled names. @@ -22,8 +22,9 @@ pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate) { - return load_fxr_and_get_delegate( - hostfxr_delegate_type::hdt_load_in_memory_assembly, + get_function_pointer_fn get_function_pointer; + int status = load_fxr_and_get_delegate( + hostfxr_delegate_type::hdt_get_function_pointer, [handle](const pal::string_t& host_path, pal::string_t* config_path_out) { pal::string_t mod_path; @@ -39,8 +40,18 @@ pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_m return StatusCode::Success; }, - delegate + &get_function_pointer ); + if (status != StatusCode::Success) + return status; + + return get_function_pointer( + _X("Internal.Runtime.InteropServices.InMemoryAssemblyLoader"), + _X("LoadInMemoryAssemblyInContext"), + UNMANAGEDCALLERSONLY_METHOD, + nullptr, // load context + nullptr, // reserved + (void**)delegate); } IJW_API BOOL STDMETHODCALLTYPE _CorDllMain(HINSTANCE hInst, diff --git a/src/native/corehost/ijwhost/ijwhost.h b/src/native/corehost/ijwhost/ijwhost.h index 78a242f0dbd42..c3698a4d4acb6 100644 --- a/src/native/corehost/ijwhost/ijwhost.h +++ b/src/native/corehost/ijwhost/ijwhost.h @@ -13,7 +13,7 @@ bool patch_vtable_entries(PEDecoder& decoder); void release_bootstrap_thunks(PEDecoder& decoder); bool are_thunks_installed_for_module(HMODULE instance); -using load_in_memory_assembly_fn = void(STDMETHODCALLTYPE*)(pal::dll_t handle, const pal::char_t* path); +using load_in_memory_assembly_fn = void(STDMETHODCALLTYPE*)(pal::dll_t handle, const pal::char_t* path, void* load_context); pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate); diff --git a/src/native/corehost/ijwhost/ijwthunk.cpp b/src/native/corehost/ijwhost/ijwthunk.cpp index 91a462dfb102e..f58d0a15b6a2d 100644 --- a/src/native/corehost/ijwhost/ijwthunk.cpp +++ b/src/native/corehost/ijwhost/ijwthunk.cpp @@ -34,7 +34,7 @@ namespace HANDLE g_heapHandle; bool patch_vtable_entries(PEDecoder& pe) -{ +{ size_t numFixupRecords; IMAGE_COR_VTABLEFIXUP* pFixupTable = pe.GetVTableFixups(&numFixupRecords); @@ -68,7 +68,7 @@ bool patch_vtable_entries(PEDecoder& pe) } trace::setup(); - + error_writer_scope_t writer_scope(swallow_trace); size_t currentThunk = 0; @@ -115,7 +115,7 @@ extern "C" std::uintptr_t __stdcall start_runtime_and_get_target_address(std::ui { trace::setup(); error_writer_scope_t writer_scope(swallow_trace); - + bootstrap_thunk *pThunk = bootstrap_thunk::get_thunk_from_cookie(cookie); load_in_memory_assembly_fn loadInMemoryAssembly; pal::dll_t moduleHandle = pThunk->get_dll_handle(); @@ -145,7 +145,7 @@ extern "C" std::uintptr_t __stdcall start_runtime_and_get_target_address(std::ui #pragma warning (pop) } - loadInMemoryAssembly(moduleHandle, app_path.c_str()); + loadInMemoryAssembly(moduleHandle, app_path.c_str(), nullptr); std::uintptr_t thunkAddress = *(pThunk->get_slot_address()); From 2ccb5ba5e45dc5c4996968b4e78eb5ff7e0696d2 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 15 Mar 2022 15:16:21 -0700 Subject: [PATCH 3/6] PR feedback --- .../Runtime/InteropServices/InMemoryAssemblyLoader.cs | 6 ++++-- src/native/corehost/ijwhost/ijwhost.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs index aeba4771f2cb0..0c8cf444634f2 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs @@ -24,13 +24,14 @@ public static class InMemoryAssemblyLoader /// /// The native module handle for the assembly. /// The path to the assembly (as a pointer to a UTF-16 C string). - [RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")] public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assemblyPath) { if (!IsSupported) throw new NotSupportedException("This API is not enabled in trimmed scenarios. see https://aka.ms/dotnet-illink/nativehost for more details"); +#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml LoadInMemoryAssemblyInContextImpl(moduleHandle, assemblyPath); +#pragma warning restore IL2026 } /// @@ -41,7 +42,8 @@ public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assem /// The path to the assembly (as a pointer to a UTF-16 C string). /// Load context (currently must be IntPtr.Zero) [UnmanagedCallersOnly] - [RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The same C++/CLI feature switch applies to LoadInMemoryAssembly and this function. We rely on the warning from LoadInMemoryAssembly.")] internal static unsafe void LoadInMemoryAssemblyInContext(IntPtr moduleHandle, IntPtr assemblyPath, IntPtr loadContext) { if (!IsSupported) diff --git a/src/native/corehost/ijwhost/ijwhost.cpp b/src/native/corehost/ijwhost/ijwhost.cpp index af147278a55d1..ab4f047ee8123 100644 --- a/src/native/corehost/ijwhost/ijwhost.cpp +++ b/src/native/corehost/ijwhost/ijwhost.cpp @@ -46,7 +46,7 @@ pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_m return status; return get_function_pointer( - _X("Internal.Runtime.InteropServices.InMemoryAssemblyLoader"), + _X("Internal.Runtime.InteropServices.InMemoryAssemblyLoader, System.Private.CoreLib"), _X("LoadInMemoryAssemblyInContext"), UNMANAGEDCALLERSONLY_METHOD, nullptr, // load context From 73d6bd67677eccf5687f64936b78d88f203eee6a Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 16 Mar 2022 19:58:12 -0700 Subject: [PATCH 4/6] Root GetFunctionPointer. Add hook for erroring when disabled --- .../System.Private.CoreLib.csproj | 1 + .../src/ILLink/ILLink.Descriptors.Windows.xml | 8 +++++- .../ComponentActivator.CoreCLR.cs | 26 +++++++++++++++++++ .../InteropServices/InMemoryAssemblyLoader.cs | 6 ++--- .../src/ILLink/ILLink.Descriptors.Shared.xml | 10 +++++-- .../InteropServices/ComponentActivator.cs | 7 ++++- .../src/Resources/Strings.resx | 3 +++ src/native/corehost/ijwhost/ijwthunk.cpp | 2 +- 8 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 02161f81b0ff0..adf2908c581ab 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -117,6 +117,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml index 7fcb26e79540d..b9c183060e08d 100644 --- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml +++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml @@ -11,11 +11,17 @@ - + + + + + + diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs new file mode 100644 index 0000000000000..a864db1ed941c --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.InteropServices +{ + public static partial class ComponentActivator + { + // This hook for when GetFunctionPointer is called when the feature is disabled allows us to + // provide error messages for known hosting scenarios such as C++/CLI. + private static void OnDisabledGetFunctionPointerCall(IntPtr typeNameNative, IntPtr methodNameNative) + { + if (!OperatingSystem.IsWindows()) + return; + + // Check for the exact type and method name used by ijwhost - see src/native/corehoste/ijwhost/ijwhost.cpp + if (Marshal.PtrToStringUni(methodNameNative) == "LoadInMemoryAssemblyInContext" + && Marshal.PtrToStringUni(typeNameNative) == $"Internal.Runtime.InteropServices.{nameof(InMemoryAssemblyLoader)}, {CoreLib.Name}") + { + throw new NotSupportedException(SR.NotSupported_CppCli); + } + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs index 0c8cf444634f2..fa7414da21d53 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs @@ -27,7 +27,7 @@ public static class InMemoryAssemblyLoader public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assemblyPath) { if (!IsSupported) - throw new NotSupportedException("This API is not enabled in trimmed scenarios. see https://aka.ms/dotnet-illink/nativehost for more details"); + throw new NotSupportedException(SR.NotSupported_CppCli); #pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml LoadInMemoryAssemblyInContextImpl(moduleHandle, assemblyPath); @@ -44,10 +44,10 @@ public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assem [UnmanagedCallersOnly] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The same C++/CLI feature switch applies to LoadInMemoryAssembly and this function. We rely on the warning from LoadInMemoryAssembly.")] - internal static unsafe void LoadInMemoryAssemblyInContext(IntPtr moduleHandle, IntPtr assemblyPath, IntPtr loadContext) + public static unsafe void LoadInMemoryAssemblyInContext(IntPtr moduleHandle, IntPtr assemblyPath, IntPtr loadContext) { if (!IsSupported) - throw new NotSupportedException("This API is not enabled in trimmed scenarios. see https://aka.ms/dotnet-illink/nativehost for more details"); + throw new NotSupportedException(SR.NotSupported_CppCli); if (loadContext != IntPtr.Zero) throw new ArgumentOutOfRangeException(nameof(loadContext)); diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml index 3def281c2bcab..27d64e4c5bf8f 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml @@ -16,10 +16,16 @@ --> + + + + + + - + @@ -69,7 +75,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs b/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs index 97f8c64008371..6e813379f4694 100644 --- a/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs +++ b/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs @@ -11,7 +11,7 @@ namespace Internal.Runtime.InteropServices { - public static class ComponentActivator + public static partial class ComponentActivator { private const string TrimIncompatibleWarningMessage = "Native hosting is not trim compatible and this warning will be seen if trimming is enabled."; private const string NativeAOTIncompatibleWarningMessage = "The native code for the method requested might not be available at runtime."; @@ -115,7 +115,12 @@ public static unsafe int GetFunctionPointer(IntPtr typeNameNative, IntPtr functionHandle) { if (!IsSupported) + { +#if CORECLR + OnDisabledGetFunctionPointerCall(typeNameNative, methodNameNative); +#endif return HostFeatureDisabled; + } try { diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 0896ffcf6cc99..d3505fea8c957 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3727,6 +3727,9 @@ Built-in COM has been disabled via a feature switch. See https://aka.ms/dotnet-illink/com for more information. + + C++/CLI activation has been disabled via a feature switch. See https://aka.ms/dotnet-illink/nativehost for more information. + Queue empty. diff --git a/src/native/corehost/ijwhost/ijwthunk.cpp b/src/native/corehost/ijwhost/ijwthunk.cpp index f58d0a15b6a2d..eeddf10fd931c 100644 --- a/src/native/corehost/ijwhost/ijwthunk.cpp +++ b/src/native/corehost/ijwhost/ijwthunk.cpp @@ -128,7 +128,7 @@ extern "C" std::uintptr_t __stdcall start_runtime_and_get_target_address(std::ui // As we were taken here via an entry point with arbitrary signature, // there's no way of returning the error code so we just throw it. - trace::error(_X("Failed to start the .NET runtime. Error code %d"), status); + trace::error(_X("Failed to start the .NET runtime. Error code: %#x"), status); #pragma warning (push) #pragma warning (disable: 4297) From d703fa95c5b770382aa0b0b08a75eeb6c0ccac55 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 17 Mar 2022 09:09:43 -0700 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Vitek Karas <10670590+vitek-karas@users.noreply.github.com> Co-authored-by: Aaron Robinson --- .../src/ILLink/ILLink.Descriptors.Windows.xml | 2 +- .../Runtime/InteropServices/ComponentActivator.CoreCLR.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml index b9c183060e08d..7ddddc6f48af8 100644 --- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml +++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml @@ -13,7 +13,7 @@ + but their bodies will be mostly trimmed away by the related feature switch --> diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs index a864db1ed941c..3e89bca82e0f2 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs @@ -15,7 +15,7 @@ private static void OnDisabledGetFunctionPointerCall(IntPtr typeNameNative, IntP if (!OperatingSystem.IsWindows()) return; - // Check for the exact type and method name used by ijwhost - see src/native/corehoste/ijwhost/ijwhost.cpp + // Check for the exact type and method name used by ijwhost - see src/native/corehost/ijwhost/ijwhost.cpp if (Marshal.PtrToStringUni(methodNameNative) == "LoadInMemoryAssemblyInContext" && Marshal.PtrToStringUni(typeNameNative) == $"Internal.Runtime.InteropServices.{nameof(InMemoryAssemblyLoader)}, {CoreLib.Name}") { From bb8d6ae8be9e16b99e4d45a3fa6b22ff9a107690 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 17 Mar 2022 10:15:18 -0700 Subject: [PATCH 6/6] Add try-catch --- .../Runtime/InteropServices/ComponentActivator.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs b/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs index 6e813379f4694..118afb963cfa6 100644 --- a/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs +++ b/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs @@ -117,7 +117,19 @@ public static unsafe int GetFunctionPointer(IntPtr typeNameNative, if (!IsSupported) { #if CORECLR - OnDisabledGetFunctionPointerCall(typeNameNative, methodNameNative); + try + { + OnDisabledGetFunctionPointerCall(typeNameNative, methodNameNative); + } + catch (Exception e) + { + // The callback can intentionally throw NotSupportedException to provide errors to consumers, + // so we let that one through. Any other exceptions must not be leaked out. + if (e is NotSupportedException) + throw; + + return e.HResult; + } #endif return HostFeatureDisabled; }