diff options
author | Aleksey Kliger (λgeek) <alklig@microsoft.com> | 2019-08-02 21:06:44 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-02 21:06:44 +0300 |
commit | 2f2f6c104b2a0115a7fb1ab24673b587c27e4365 (patch) | |
tree | 5e3a48d133a7003df176f83c80287e13a2aeef1b /netcore | |
parent | 0b8a0e09d3385281519f496f364da7e2c454a58f (diff) |
[netcore] Implement referenced assembly resolving and ALC.GetLoadContext (#15946)
* [netcore] Implement GetLoadContext
* [netcore] Implement ALC-aware referenced assembly resolution.
The process is described in
https://docs.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext?view=netcore-3.0#usage-in-the-runtime
We try four things until one of them returns a non-null assembly.
1. Check if the assembly is already loaded in the ALC of the requesting
assembly.
2. If the ALC is not the default ALC, invoke the Load override.
3. Try the default ALC (TPA - trusted platform assemblies) loading.
4. Invoke the Resolving event of the original ALC.
The implementation borrows some managed code from
https://github.com/dotnet/coreclr/blob/8ba2e15201361402acd0ae9710bd37d50785cdfa/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs#L84-L187
It would be nice if that code was shared between the runtimes.
* [runtime] Implement mono_assembly_name_culture_is_neutral
* [netcore] Add mono_alc_invoke_resolve_using_resolve_satellite
* [netcore] Use ResolveSatelliteAssembly for satellite assembly refs
* [netcore] Some AssemblyLoadContextTest tests now pass
System.Runtime.Loader.Tests.AssemblyLoadContextTest.PublicConstructor_Default
System.Runtime.Loader.Tests.AssemblyLoadContextTest.GetAssemblyNameTest_NullParameter
System.Runtime.Loader.Tests.AssemblyLoadContextTest.LoadFromAssemblyName_AssemblyNotFound
System.Runtime.Loader.Tests.AssemblyLoadContextTest.PublicConstructor_Theory (with isCollectible: False)
System.Runtime.Loader.Tests.AssemblyLoadContextTest.GetAssemblyNameTest_ValidAssembly
System.Runtime.Loader.Tests.AssemblyLoadContextTest.GetLoadContextTest_SystemPrivateCorelibAssembly
System.Runtime.Loader.Tests.AssemblyLoadContextTest.DefaultAssemblyLoadContext_Properties
System.Runtime.Loader.Tests.AssemblyLoadContextTest.GetAssemblyNameTest_AssemblyNotFound
System.Runtime.Loader.Tests.AssemblyLoadContextTest.GetLoadContextTest_ValidTrustedPlatformAssembly
Diffstat (limited to 'netcore')
4 files changed, 187 insertions, 3 deletions
diff --git a/netcore/CoreFX.issues.rsp b/netcore/CoreFX.issues.rsp index 2dd42924db1..9d2ce86b7df 100644 --- a/netcore/CoreFX.issues.rsp +++ b/netcore/CoreFX.issues.rsp @@ -950,10 +950,19 @@ # relies on AssemblyLoadContext -nomethod System.Runtime.Loader.Tests.SatelliteAssembliesTests.SatelliteLoadsCorrectly --nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.* +-nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.GetLoadContextTest_ValidUserAssembly +-nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.LoadAssemblyByPath_ValidUserAssembly +-nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.Unsupported_FixedAddressValueType +-nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.LoadAssemblyByStream_ValidUserAssembly +-nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.LoadFromAssemblyName_ValidTrustedPlatformAssembly -nomethod System.Runtime.Loader.Tests.ContextualReflectionTest.* -nomethod System.Runtime.Loader.Tests.SatelliteAssembliesTests.describeLib +# relies on collectible AssemblyLoadContext +-nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.Unload_* +-nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.Finalizer_CollectibleWithNoAssemblyLoaded +-nomethod System.Runtime.Loader.Tests.AssemblyLoadContextTest.PublicConstructor_Theory + #################################################################### ## System.Reflection.MetadataLoadContext.Tests #################################################################### diff --git a/netcore/System.Private.CoreLib/src/LinkerDescriptor/System.Private.CoreLib.xml b/netcore/System.Private.CoreLib/src/LinkerDescriptor/System.Private.CoreLib.xml index 5772c67fcc5..8ab70113be9 100644 --- a/netcore/System.Private.CoreLib/src/LinkerDescriptor/System.Private.CoreLib.xml +++ b/netcore/System.Private.CoreLib/src/LinkerDescriptor/System.Private.CoreLib.xml @@ -19,6 +19,16 @@ <!-- appdomain.c mono_object_new_checked in mono_domain_create_appdomain_checked --> <method signature="System.Void .ctor()" /> </type> + + <!-- assembly-load-context.c: --> + <type fullname="System.Runtime.Loader.AssemblyLoadContext"> + <!-- assembly-load-context.c: mono_alc_invoke_resolve_using_load --> + <method name="MonoResolveUsingLoad" /> + <!-- assembly-load-context.c: mono_alc_invoke_resolve_using_resolving_event --> + <method name="MonoResolveUsingResolvingEvent" /> + <!-- assembly-load-context.c: mono_alc_invoke_resolve_using_resolve_satellite --> + <method name="MonoResolveUsingResolveSatelliteAssembly" /> + </type> <!-- marshal.c: emit_marshal_custom (should not be used on devices) <type fullname="System.ApplicationException" /> diff --git a/netcore/System.Private.CoreLib/src/System.Reflection/RuntimeAssembly.cs b/netcore/System.Private.CoreLib/src/System.Reflection/RuntimeAssembly.cs index 7c7c205bf9f..9a264739e36 100644 --- a/netcore/System.Private.CoreLib/src/System.Reflection/RuntimeAssembly.cs +++ b/netcore/System.Private.CoreLib/src/System.Reflection/RuntimeAssembly.cs @@ -441,5 +441,11 @@ namespace System.Reflection [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern object GetFilesInternal (String name, bool getResourceModules); + + internal string? GetSimpleName () + { + // TODO: Make this cheaper and faster + return GetName ().Name; + } } } diff --git a/netcore/System.Private.CoreLib/src/System.Runtime.Loader/AssemblyLoadContext.cs b/netcore/System.Private.CoreLib/src/System.Runtime.Loader/AssemblyLoadContext.cs index 539e4e4da5b..7c1d7a489ce 100644 --- a/netcore/System.Private.CoreLib/src/System.Runtime.Loader/AssemblyLoadContext.cs +++ b/netcore/System.Private.CoreLib/src/System.Runtime.Loader/AssemblyLoadContext.cs @@ -5,6 +5,7 @@ using System.IO; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; namespace System.Runtime.Loader @@ -58,9 +59,35 @@ namespace System.Runtime.Loader return InternalGetLoadedAssemblies (); } - public static AssemblyLoadContext GetLoadContext (Assembly assembly) + [MethodImplAttribute (MethodImplOptions.InternalCall)] + extern static IntPtr GetLoadContextForAssembly (RuntimeAssembly rtAsm); + + // Returns the load context in which the specified assembly has been loaded + public static AssemblyLoadContext? GetLoadContext (Assembly assembly) { - throw new NotImplementedException (); + if (assembly == null) + throw new ArgumentNullException (nameof (assembly)); + + AssemblyLoadContext? loadContextForAssembly = null; + + RuntimeAssembly? rtAsm = assembly as RuntimeAssembly; + + // We only support looking up load context for runtime assemblies. + if (rtAsm != null) { + RuntimeAssembly runtimeAssembly = rtAsm; + IntPtr ptrAssemblyLoadContext = GetLoadContextForAssembly (runtimeAssembly); + if (ptrAssemblyLoadContext == IntPtr.Zero) + { + // If the load context is returned null, then the assembly was bound using the TPA binder + // and we shall return reference to the active "Default" binder - which could be the TPA binder + // or an overridden CLRPrivBinderAssemblyLoadContext instance. + loadContextForAssembly = AssemblyLoadContext.Default; + } else { + loadContextForAssembly = (AssemblyLoadContext) (GCHandle.FromIntPtr (ptrAssemblyLoadContext).Target)!; + } + } + + return loadContextForAssembly; } public void SetProfileOptimizationRoot (string directoryPath) @@ -87,5 +114,137 @@ namespace System.Runtime.Loader { return AssemblyResolve (null, new ResolveEventArgs (name)); } + + // Invoked by Mono to resolve using the load method. + private static Assembly? MonoResolveUsingLoad (IntPtr gchALC, string assemblyName) + { + return Resolve (gchALC, new AssemblyName (assemblyName)); + } + + // Invoked by Mono to resolve using the Resolving event after + // trying the Load overried and TPA load context without + // success. + private static Assembly? MonoResolveUsingResolvingEvent (IntPtr gchALC, string assemblyName) + { + return ResolveUsingResolvingEvent (gchALC, new AssemblyName (assemblyName)); + } + + // Invoked by Mono to resolve using the Resolving event after + // trying the Load overried and TPA load context without + // success. + private static Assembly? MonoResolveUsingResolveSatelliteAssembly (IntPtr gchALC, string assemblyName) + { + return ResolveSatelliteAssembly (gchALC, new AssemblyName (assemblyName)); + } + + +#region Copied from AssemblyLoadContext.CoreCLR.cs + // WISH: let's share this code + + // This method is invoked by the VM when using the host-provided assembly load context + // implementation. + private static Assembly? Resolve(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName) + { + AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target)!; + + return context.ResolveUsingLoad(assemblyName); + } + + // This method is invoked by the VM to resolve an assembly reference using the Resolving event + // after trying assembly resolution via Load override and TPA load context without success. + private static Assembly? ResolveUsingResolvingEvent(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName) + { + AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target)!; + + // Invoke the AssemblyResolve event callbacks if wired up + return context.ResolveUsingEvent(assemblyName); + } + + // This method is invoked by the VM to resolve a satellite assembly reference + // after trying assembly resolution via Load override without success. + private static Assembly? ResolveSatelliteAssembly(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName) + { + AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target)!; + + // Invoke the ResolveSatelliteAssembly method + return context.ResolveSatelliteAssembly(assemblyName); + } + + private Assembly? GetFirstResolvedAssembly(AssemblyName assemblyName) + { + Assembly? resolvedAssembly = null; + + Func<AssemblyLoadContext, AssemblyName, Assembly>? assemblyResolveHandler = _resolving; + + if (assemblyResolveHandler != null) + { + // Loop through the event subscribers and return the first non-null Assembly instance + foreach (Func<AssemblyLoadContext, AssemblyName, Assembly> handler in assemblyResolveHandler.GetInvocationList()) + { + resolvedAssembly = handler(this, assemblyName); + if (resolvedAssembly != null) + { + return resolvedAssembly; + } + } + } + + return null; + } + + private Assembly ValidateAssemblyNameWithSimpleName(Assembly assembly, string? requestedSimpleName) + { + // Get the name of the loaded assembly + string? loadedSimpleName = null; + + // Derived type's Load implementation is expected to use one of the LoadFrom* methods to get the assembly + // which is a RuntimeAssembly instance. However, since Assembly type can be used build any other artifact (e.g. AssemblyBuilder), + // we need to check for RuntimeAssembly. + RuntimeAssembly? rtLoadedAssembly = assembly as RuntimeAssembly; + if (rtLoadedAssembly != null) + { + loadedSimpleName = rtLoadedAssembly.GetSimpleName(); + } + + // The simple names should match at the very least + if (string.IsNullOrEmpty(requestedSimpleName)) + { + throw new ArgumentException(SR.ArgumentNull_AssemblyNameName); + } + if (string.IsNullOrEmpty(loadedSimpleName) || !requestedSimpleName.Equals(loadedSimpleName, StringComparison.InvariantCultureIgnoreCase)) + { + throw new InvalidOperationException(SR.Argument_CustomAssemblyLoadContextRequestedNameMismatch); + } + + return assembly; + } + + private Assembly? ResolveUsingLoad(AssemblyName assemblyName) + { + string? simpleName = assemblyName.Name; + Assembly? assembly = Load(assemblyName); + + if (assembly != null) + { + assembly = ValidateAssemblyNameWithSimpleName(assembly, simpleName); + } + + return assembly; + } + + private Assembly? ResolveUsingEvent(AssemblyName assemblyName) + { + string? simpleName = assemblyName.Name; + + // Invoke the AssemblyResolve event callbacks if wired up + Assembly? assembly = GetFirstResolvedAssembly(assemblyName); + if (assembly != null) + { + assembly = ValidateAssemblyNameWithSimpleName(assembly, simpleName); + } + + return assembly; + } +#endregion } } |