diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index a37abf7a0c116..45ab475cb90a6 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -215,7 +215,19 @@ + + + + + + + + + + + + @@ -334,4 +346,4 @@ - + \ No newline at end of file diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs index c95e1630a04a1..939b0fe26afac 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs @@ -312,6 +312,12 @@ internal MetadataImport(RuntimeModule module) // since the instance can be replaced during HotReload and EnC scenarios. m_metadataImport2 = GetMetadataImport(module); } + + internal MetadataImport(IntPtr pIMetaDataAssemblyImport2) + { + ArgumentNullException.ThrowIfNull(pIMetaDataAssemblyImport2); + m_metadataImport2 = pIMetaDataAssemblyImport2; + } #endregion [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MetadataImport_Enum")] @@ -610,6 +616,38 @@ public bool IsValidToken(int token) { return IsValidToken(m_metadataImport2, token); } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int GetAssemblyFromScope(IntPtr scope, out uint tkAssembly); + public uint GetAssemblyFromScope() + { + ThrowBadImageExceptionForHR(GetAssemblyFromScope(m_metadataImport2, out uint tkAssembly)); + return tkAssembly; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe int GetAssemblyProps( + IntPtr scope, + uint mda, + out byte* ppbPublicKey, + out uint pcbPublicKey, + out uint pulHashAlgId, + void** pszName, + void* pMetadata, + out uint pdwAsselblyFlags); + public unsafe void GetAssemblyProps( + uint assemblyToken, + out byte* publicKey, + out uint publicKeyLength, + out uint hashAlgId, + out string assemblyName, + void* pMetadata, + out uint asselblyFlags) + { + void* _name; + ThrowBadImageExceptionForHR(GetAssemblyProps(m_metadataImport2, assemblyToken, out publicKey, out publicKeyLength, out hashAlgId, &_name, pMetadata, out asselblyFlags)); + assemblyName = new MdUtf8String(_name).ToString(); + } #endregion private static void ThrowBadImageExceptionForHR(int hr) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/ApplicationContext.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/ApplicationContext.cs new file mode 100644 index 0000000000000..ecb621eeeae72 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/ApplicationContext.cs @@ -0,0 +1,301 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; + +namespace System.Runtime.Loader +{ + internal sealed class TPAEntry + { + public string? ILFileName; + public string? NIFileName; + } + + internal sealed class FailureCacheKey : IEquatable + { + public readonly string SimpleName; + public readonly AssemblyVersion AssemblyVersion; + + public FailureCacheKey(string simpleName, AssemblyVersion AssemblyVersion) + { + SimpleName = simpleName; + this.AssemblyVersion = AssemblyVersion; + } + + public override bool Equals(object? obj) => obj is FailureCacheKey other && Equals(other); + + public bool Equals(FailureCacheKey? other) => other != null && SimpleName == other.SimpleName && AssemblyVersion.Equals(other.AssemblyVersion); + + public override int GetHashCode() => HashCode.Combine(SimpleName, AssemblyVersion.Major, AssemblyVersion.Minor, AssemblyVersion.Build, AssemblyVersion.Revision); + } + + internal sealed class ApplicationContext + { + private volatile int _version; + + public int Version => _version; + + public Dictionary ExecutionContext { get; } = new Dictionary(); + + public Dictionary FailureCache { get; } = new Dictionary(); + + public object ContextCriticalSection { get; } = new object(); + + public List PlatformResourceRoots { get; } = new List(); + + public List AppPaths { get; } = new List(); + + public Dictionary? TrustedPlatformAssemblyMap { get; private set; } + + public void IncrementVersion() => Interlocked.Increment(ref _version); + + private static bool GetNextPath(string paths, ref int startPos, out string outPath) + { + bool wrappedWithQuotes = false; + + // Skip any leading spaces or path separators + while (startPos < paths.Length && paths[startPos] is ' ' or PathInternal.PathSeparator) + startPos++; + + if (startPos == paths.Length) + { + // No more paths in the string and we just skipped over some white space + outPath = string.Empty; + return false; + } + + // Support paths being wrapped with quotations + while (startPos < paths.Length && paths[startPos] == '\"') + { + startPos++; + wrappedWithQuotes = true; + } + + int iEnd = startPos; // Where current path ends + int iNext; // Where next path starts + + static int IndexOfInRange(ReadOnlySpan str, int start, char ch) + { + int index = str[start..].IndexOf(ch); + return index >= 0 ? index + start : index; + } + + if (wrappedWithQuotes) + { + iEnd = IndexOfInRange(paths, iEnd, '\"'); + if (iEnd != -1) + { + // Find where the next path starts - there should be a path separator right after the closing quotation mark + iNext = IndexOfInRange(paths, iEnd, PathInternal.PathSeparator); + if (iNext != -1) + { + iNext++; + } + else + { + iNext = paths.Length; + } + } + else + { + // There was no terminating quotation mark - that's bad + throw new ArgumentException(nameof(paths)); + } + } + else if ((iEnd = IndexOfInRange(paths, iEnd, PathInternal.PathSeparator)) != -1) + { + iNext = iEnd + 1; + } + else + { + iNext = iEnd = paths.Length; + } + + // Skip any trailing spaces + while (paths[iEnd - 1] == ' ') + { + iEnd--; + } + + Debug.Assert(startPos < iEnd); + + outPath = paths[startPos..iEnd]; + startPos = iNext; + return true; + } + + private static bool GetNextTPAPath(string paths, ref int startPos, bool dllOnly, out string outPath, out string simpleName, out bool isNativeImage) + { + isNativeImage = false; + + while (true) + { + if (!GetNextPath(paths, ref startPos, out outPath)) + { + simpleName = string.Empty; + return false; + } + + if (!Path.IsPathFullyQualified(outPath)) + { + throw new ArgumentException(nameof(paths)); + } + + // Find the beginning of the simple name + int iSimpleNameStart = outPath.LastIndexOf(PathInternal.DirectorySeparatorChar); + if (iSimpleNameStart == -1) + { + iSimpleNameStart = 0; + } + else + { + // Advance past the directory separator to the first character of the file name + iSimpleNameStart++; + } + + if (iSimpleNameStart == outPath.Length) + { + throw new ArgumentException(nameof(paths)); + } + + if (dllOnly && (outPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) + || outPath.EndsWith(".ni.exe", StringComparison.OrdinalIgnoreCase))) + { + // Skip exe files when the caller requested only dlls + continue; + } + + if (outPath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) + || outPath.EndsWith(".ni.exe", StringComparison.OrdinalIgnoreCase)) + { + simpleName = outPath[iSimpleNameStart..^7]; + isNativeImage = true; + } + else if (outPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) + || outPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) + { + simpleName = outPath[iSimpleNameStart..^4]; + } + else + { + // Invalid filename + throw new ArgumentException(nameof(paths)); + } + + return true; ; + } + } + + public void SetupBindingPaths(string trustedPlatformAssemblies, string platformResourceRoots, string appPaths, bool acquireLock) + { + if (acquireLock) + { + lock (ContextCriticalSection) + { + Core(trustedPlatformAssemblies, platformResourceRoots, appPaths); + } + } + else + { + Core(trustedPlatformAssemblies, platformResourceRoots, appPaths); + } + + void Core(string trustedPlatformAssemblies, string platformResourceRoots, string appPaths) + { + if (TrustedPlatformAssemblyMap != null) + { + return; + } + + // + // Parse TrustedPlatformAssemblies + // + + TrustedPlatformAssemblyMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + for (int i = 0; i < trustedPlatformAssemblies.Length;) + { + if (!GetNextTPAPath(trustedPlatformAssemblies, ref i, dllOnly: false, out string fileName, out string simpleName, out bool isNativeImage)) + { + break; + } + + if (TrustedPlatformAssemblyMap.TryGetValue(simpleName, out TPAEntry? existingEntry)) + { + // + // We want to store only the first entry matching a simple name we encounter. + // The exception is if we first store an IL reference and later in the string + // we encounter a native image. Since we don't touch IL in the presence of + // native images, we replace the IL entry with the NI. + // + if ((existingEntry.ILFileName != null && !isNativeImage) || + (existingEntry.NIFileName != null && isNativeImage)) + { + continue; + } + } + + if (isNativeImage) + { + existingEntry ??= new TPAEntry(); + existingEntry.NIFileName = fileName; + } + else + { + existingEntry ??= new TPAEntry(); + existingEntry.ILFileName = fileName; + } + + TrustedPlatformAssemblyMap[simpleName] = existingEntry; + } + + // + // Parse PlatformResourceRoots + // + + for (int i = 0; i < platformResourceRoots.Length;) + { + if (!GetNextPath(platformResourceRoots, ref i, out string pathName)) + { + break; + } + + if (!Path.IsPathFullyQualified(pathName)) + { + throw new ArgumentException(nameof(pathName)); + } + + PlatformResourceRoots.Add(pathName); + } + + // + // Parse AppPaths + // + + for (int i = 0; i < appPaths.Length;) + { + if (!GetNextPath(appPaths, ref i, out string pathName)) + { + break; + } + + + if (!Path.IsPathFullyQualified(pathName)) + { + throw new ArgumentException(nameof(pathName)); + } + + AppPaths.Add(pathName); + } + } + } + + public void AddToFailureCache(BinderAssemblyName assemblyName, int hresult) + { + FailureCache.Add(new FailureCacheKey(assemblyName.SimpleName, assemblyName.Version), hresult); + IncrementVersion(); + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/Assembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/Assembly.cs new file mode 100644 index 0000000000000..87036f58150f7 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/Assembly.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.Loader +{ + // BINDER_SPACE::Assembly represents a result of binding to an actual assembly (PEImage) + // It is basically a tuple of 1) physical assembly and 2) binder which created/owns this binding + // We also store whether it was bound using TPA list + internal sealed class BinderAssembly + { + // fields used by VM +#pragma warning disable CA1823, 414, 169 + private BinderAssemblyName m_assemblyName; + + // The assembly object is rooted by native object. + // Reference native ALC to allow managed ALC object to be collected. + private IntPtr m_binder; + private IntPtr m_peImage; + private IntPtr m_pDomainAssembly; + private bool m_isInTPA; + private bool m_isCoreLib; +#pragma warning restore CA1823, 414, 169 + + public IntPtr Binder + { + get => m_binder; + set => m_binder = value; + } + + public BinderAssemblyName AssemblyName => m_assemblyName; + + public IntPtr PEImage => m_peImage; + + public bool IsInTPA => m_isInTPA; + + public BinderAssembly(nint pPEImage, bool isInTPA) + { + // Get assembly name def from meta data import and store it for later refs access + m_assemblyName = new BinderAssemblyName(pPEImage) + { + IsDefinition = true + }; + + m_isCoreLib = AssemblyName.IsCoreLib; + + // validate architecture + if (!AssemblyBinderCommon.IsValidArchitecture(AssemblyName.ProcessorArchitecture)) + { + // Assembly image can't be executed on this platform + throw new BadImageFormatException(); + } + + m_isInTPA = isInTPA; + m_peImage = pPEImage; + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyIdentity.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyIdentity.cs new file mode 100644 index 0000000000000..b344db93d25a7 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyIdentity.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; + +namespace System.Runtime.Loader +{ + internal enum AssemblyIdentityFlags + { + IDENTITY_FLAG_EMPTY = 0x000, + IDENTITY_FLAG_SIMPLE_NAME = 0x001, + IDENTITY_FLAG_VERSION = 0x002, + IDENTITY_FLAG_PUBLIC_KEY_TOKEN = 0x004, + IDENTITY_FLAG_PUBLIC_KEY = 0x008, + IDENTITY_FLAG_CULTURE = 0x010, + IDENTITY_FLAG_PROCESSOR_ARCHITECTURE = 0x040, + IDENTITY_FLAG_RETARGETABLE = 0x080, + IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL = 0x100, + IDENTITY_FLAG_CONTENT_TYPE = 0x800, + IDENTITY_FLAG_FULL_NAME = IDENTITY_FLAG_SIMPLE_NAME | IDENTITY_FLAG_VERSION + } + + internal enum PEKind : uint + { + None = 0x00000000, + MSIL = 0x00000001, + I386 = 0x00000002, + IA64 = 0x00000003, + AMD64 = 0x00000004, + ARM = 0x00000005, + ARM64 = 0x00000006, + Invalid = 0xffffffff, + } + + internal class AssemblyIdentity + { + public string SimpleName = string.Empty; + public AssemblyVersion Version = new AssemblyVersion(); + public string CultureOrLanguage = string.Empty; + public byte[] PublicKeyOrTokenBLOB = Array.Empty(); + public PEKind ProcessorArchitecture; + public AssemblyContentType ContentType; + public AssemblyIdentityFlags IdentityFlags = AssemblyIdentityFlags.IDENTITY_FLAG_CULTURE | AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL; + + // See https://docs.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names#specifying-assembly-names + public const string NeutralCulture = "neutral"; + + public string NormalizedCulture => string.IsNullOrEmpty(CultureOrLanguage) ? NeutralCulture : CultureOrLanguage; + + public bool IsRetargetable => (IdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_RETARGETABLE) != 0; + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Binder.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Binder.cs new file mode 100644 index 0000000000000..b7cad3811e952 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Binder.cs @@ -0,0 +1,204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Runtime.Loader +{ + // System.Reflection.TypeLoading.AssemblyNameData + internal readonly unsafe struct AssemblyNameData + { + public readonly void* Name; + public readonly void* Culture; + + public readonly byte* PublicKeyOrToken; + public readonly int PublicKeyOrTokenLength; + + public readonly int MajorVersion; + public readonly int MinorVersion; + public readonly int BuildNumber; + public readonly int RevisionNumber; + + public readonly PEKind ProcessorArchitecture; + public readonly AssemblyContentType ContentType; + + public readonly AssemblyIdentityFlags IdentityFlags; + } + + public partial class AssemblyLoadContext + { + // called by vm + private protected unsafe int BindAssemblyByName(void* pAssemblyNameData, out BinderAssembly? assembly) + => BindUsingAssemblyName(new BinderAssemblyName((AssemblyNameData*)pAssemblyNameData), out assembly); + + internal ApplicationContext AppContext { get; } = new ApplicationContext(); + + // called by vm + // ensure to call with BaseDomain::LoadLockHolder in native (from native binder) + private void AddLoadedAssembly(IntPtr loadedAssembly) + { + _loadedAssemblies.Add(loadedAssembly); + + // #ifdef FEATURE_READYTORUN + DeclareLoadedAssembly(loadedAssembly); + // #endif // FEATURE_READYTORUN + } + + //# ifdef FEATURE_READYTORUN + + private static void MvidMismatchFatalError(Guid mvidActual, Guid mvidExpected, string simpleName, bool compositeComponent, string assemblyRequirementName) + { + string message; + + if (compositeComponent) + { + message = $"MVID mismatch between loaded assembly '{simpleName}' (MVID = {mvidActual}) and an assembly with the same simple name embedded in the native image '{assemblyRequirementName}' (MVID = {mvidExpected})"; + } + else + { + message = $"MVID mismatch between loaded assembly '{simpleName}' (MVID = {mvidActual}) and version of assembly '{simpleName}' expected by assembly '{assemblyRequirementName}' (MVID = {mvidExpected})"; + } + + Environment.FailFast(message); + } + + // called by vm + private unsafe void DeclareDependencyOnMvid(byte* simpleName, in Guid mvid, bool compositeComponent, byte* imageName) + => DeclareDependencyOnMvid(new MdUtf8String(simpleName).ToString(), mvid, compositeComponent, new MdUtf8String(imageName).ToString()); + + // Must be called under the LoadLock + private void DeclareDependencyOnMvid(string simpleName, Guid mvid, bool compositeComponent, string imageName) + { + // If the table is empty, then we didn't fill it with all the loaded assemblies as they were loaded. Record this detail, and fix after adding the dependency + bool addAllLoadedModules = false; + if (_assemblySimpleNameMvidCheckHash.Count == 0) + { + addAllLoadedModules = true; + } + + ref SimpleNameToExpectedMVIDAndRequiringAssembly? entry = ref CollectionsMarshal.GetValueRefOrAddDefault(_assemblySimpleNameMvidCheckHash, simpleName, out bool found); + if (!found) + { + entry = new SimpleNameToExpectedMVIDAndRequiringAssembly + { + Mvid = mvid, + CompositeComponent = compositeComponent, + AssemblyRequirementName = imageName + }; + } + else + { + Debug.Assert(entry != null); + // Elem already exists. Determine if the existing elem is another one with the same mvid, in which case just record that a dependency is in play. + // If the existing elem has a different mvid, fail. + if (entry.Mvid == mvid) + { + // Mvid matches exactly. + if (entry.AssemblyRequirementName == null) + { + entry.AssemblyRequirementName = imageName; + entry.CompositeComponent = compositeComponent; + } + else + { + MvidMismatchFatalError(entry.Mvid, mvid, simpleName, compositeComponent, imageName); + } + } + } + + if (addAllLoadedModules) + { + foreach (IntPtr assembly in _loadedAssemblies) + { + DeclareLoadedAssembly(assembly); + } + } + } + + // Must be called under the LoadLock + private unsafe void DeclareLoadedAssembly(IntPtr loadedAssembly) + { + // If table is empty, then no mvid dependencies have been declared, so we don't need to record this information + if (_assemblySimpleNameMvidCheckHash.Count == 0) + { + return; + } + + var mdImport = new MetadataImport(Assembly_GetMDImport(loadedAssembly)); + mdImport.GetScopeProps(out Guid mvid); + string simpleName = new MdUtf8String(Assembly_GetSimpleName(loadedAssembly)).ToString(); + + ref SimpleNameToExpectedMVIDAndRequiringAssembly? entry = ref CollectionsMarshal.GetValueRefOrAddDefault(_assemblySimpleNameMvidCheckHash, simpleName, out bool found); + if (!found) + { + entry = new SimpleNameToExpectedMVIDAndRequiringAssembly + { + Mvid = mvid, + CompositeComponent = false, + AssemblyRequirementName = null + }; + } + else + { + Debug.Assert(entry != null); + // Elem already exists. Determine if the existing elem is another one with the same mvid, in which case do nothing. Everything is fine here. + // If the existing elem has a different mvid, but isn't a dependency on exact mvid elem, then set the mvid to all 0. + // If the existing elem has a different mvid, and is a dependency on exact mvid elem, then we've hit a fatal error. + if (entry.Mvid == mvid) + { + // Mvid matches exactly. + } + else if (entry.AssemblyRequirementName == null) + { + // Another loaded assembly, set the stored Mvid to all zeroes to indicate that it isn't a unique mvid + entry.Mvid = Guid.Empty; + } + else + { + MvidMismatchFatalError(entry.Mvid, mvid, simpleName, entry.CompositeComponent, entry.AssemblyRequirementName); + } + } + } + //#endif // FEATURE_READYTORUN + + private sealed class SimpleNameToExpectedMVIDAndRequiringAssembly + { + // When an assembly is loaded, this Mvid value will be set to the mvid of the assembly. If there are multiple assemblies + // with different mvid's loaded with the same simple name, then the Mvid value will be set to all zeroes. + public Guid Mvid; + + // If an assembly of this simple name is not yet loaded, but a depedency on an exact mvid is registered, then this field will + // be filled in with the simple assembly name of the first assembly loaded with an mvid dependency. + public string? AssemblyRequirementName; + + // To disambiguate between component images of a composite image and requirements from a non-composite --inputbubble assembly, use this bool + public bool CompositeComponent; + } + + // Use a case senstive comparison here even though + // assembly name matching should be case insensitive. Case insensitive + // comparisons are slow and have throwing scenarios, and this hash table + // provides a best-effort match to prevent problems, not perfection + private readonly Dictionary _assemblySimpleNameMvidCheckHash = new Dictionary(); + private readonly List _loadedAssemblies = new List(); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetMDImport")] + private static partial IntPtr Assembly_GetMDImport(IntPtr pAssembly); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetSimpleNameNative")] + private static unsafe partial byte* Assembly_GetSimpleName(IntPtr pAssembly); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetExposedObject")] + internal static unsafe partial void Assembly_GetExposedObject(IntPtr pAssembly, ObjectHandleOnStack rtAssembly); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetPEImage")] + internal static partial IntPtr Assembly_GetPEImage(IntPtr pAssembly); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_SetSymbolBytes")] + internal static unsafe partial void Assembly_SetSymbolBytes(IntPtr pAssembly, byte* ptrSymbolArray, int cbSymbolArrayLength); + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.BinderCommon.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.BinderCommon.cs new file mode 100644 index 0000000000000..e7b34f4f0966e --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.BinderCommon.cs @@ -0,0 +1,1296 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Loader.Tracing; + +namespace System.Runtime.Loader +{ + internal enum CorPEKind + { + peNot = 0x00000000, // not a PE file + peILonly = 0x00000001, // flag IL_ONLY is set in COR header + pe32BitRequired = 0x00000002, // flag 32BITREQUIRED is set and 32BITPREFERRED is clear in COR header + pe32Plus = 0x00000004, // PE32+ file (64 bit) + pe32Unmanaged = 0x00000008, // PE32 without COR header + pe32BitPreferred = 0x00000010 // flags 32BITREQUIRED and 32BITPREFERRED are set in COR header + } + + internal struct BundleFileLocation + { + public long Size; + public long Offset; + public long UncompressedSize; + + public readonly bool IsValid => Offset != 0; + } + + internal static partial class AssemblyBinderCommon + { + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PEImage_BinderAcquireImport")] + internal static unsafe partial IntPtr BinderAcquireImport(IntPtr pPEImage, int* pdwPAFlags); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PEImage_BinderAcquirePEImage", StringMarshalling = StringMarshalling.Utf16)] + private static unsafe partial int BinderAcquirePEImage(string szAssemblyPath, out IntPtr ppPEImage, BundleFileLocation bundleFileLocation); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PEImage_Release")] + internal static partial void PEImage_Release(IntPtr pPEImage); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PEImage_GetMVID")] + internal static partial void PEImage_GetMVID(IntPtr pPEImage, out Guid mvid); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PEImage_GetPath", StringMarshalling = StringMarshalling.Utf16)] + internal static unsafe partial char* PEImage_GetPath(IntPtr pPEImage); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Bundle_AppIsBundle")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool AppIsBundle(); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Bundle_ProbeAppBundle", StringMarshalling = StringMarshalling.Utf16)] + private static partial void ProbeAppBundle(string path, [MarshalAs(UnmanagedType.Bool)] bool pathIsBundleRelative, out BundleFileLocation result); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Bundle_GetAppBundleBasePath")] + private static partial void GetAppBundleBasePath(StringHandleOnStack path); + + public static bool IsCompatibleAssemblyVersion(BinderAssemblyName requestedName, BinderAssemblyName foundName) + { + AssemblyVersion pRequestedVersion = requestedName.Version; + AssemblyVersion pFoundVersion = foundName.Version; + + if (!pRequestedVersion.HasMajor) + { + // An unspecified requested version component matches any value for the same component in the found version, + // regardless of lesser-order version components + return true; + } + if (!pFoundVersion.HasMajor || pRequestedVersion.Major > pFoundVersion.Major) + { + // - A specific requested version component does not match an unspecified value for the same component in + // the found version, regardless of lesser-order version components + // - Or, the requested version is greater than the found version + return false; + } + if (pRequestedVersion.Major < pFoundVersion.Major) + { + // The requested version is less than the found version + return true; + } + + if (!pRequestedVersion.HasMinor) + { + return true; + } + if (!pFoundVersion.HasMinor || pRequestedVersion.Minor > pFoundVersion.Minor) + { + return false; + } + if (pRequestedVersion.Minor < pFoundVersion.Minor) + { + return true; + } + + if (!pRequestedVersion.HasBuild) + { + return true; + } + if (!pFoundVersion.HasBuild || pRequestedVersion.Build > pFoundVersion.Build) + { + return false; + } + if (pRequestedVersion.Build < pFoundVersion.Build) + { + return true; + } + + if (!pRequestedVersion.HasRevision) + { + return true; + } + if (!pFoundVersion.HasRevision || pRequestedVersion.Revision > pFoundVersion.Revision) + { + return false; + } + return true; + } + + public static void CreateImageAssembly(IntPtr pPEImage, ref BindResult bindResult) => bindResult.SetResult(new BinderAssembly(pPEImage, isInTPA: false)); + + // defined in System.Reflection.PortableExecutable.Machine, but it's in System.Reflection.Metadata + // also defined in System.Reflection.ImageFileMachine + private const int IMAGE_FILE_MACHINE_I386 = 0x014c; // Intel 386. + private const int IMAGE_FILE_MACHINE_ARMNT = 0x01c4; // ARM Thumb-2 Little-Endian + private const int IMAGE_FILE_MACHINE_AMD64 = 0x8664; // AMD64 (K8) + private const int IMAGE_FILE_MACHINE_ARM64 = 0xAA64; // ARM64 Little-Endian + + public static unsafe PEKind TranslatePEToArchitectureType(int* pdwPAFlags) + { + CorPEKind CLRPeKind = (CorPEKind)pdwPAFlags[0]; + int dwImageType = pdwPAFlags[1]; + + if (CLRPeKind == CorPEKind.peNot) + { + // Not a PE. Shouldn't ever get here. + throw new BadImageFormatException(); + } + + if ((CLRPeKind & CorPEKind.peILonly) != 0 && (CLRPeKind & CorPEKind.pe32Plus) == 0 && + (CLRPeKind & CorPEKind.pe32BitRequired) == 0 && dwImageType == IMAGE_FILE_MACHINE_I386) + { + // Processor-agnostic (MSIL) + return PEKind.MSIL; + } + else if ((CLRPeKind & CorPEKind.pe32Plus) != 0) + { + // 64-bit + if ((CLRPeKind & CorPEKind.pe32BitRequired) != 0) + { + // Invalid + throw new BadImageFormatException(); + } + + // Regardless of whether ILONLY is set or not, the architecture + // is the machine type. + if (dwImageType == IMAGE_FILE_MACHINE_ARM64) + return PEKind.ARM64; + else if (dwImageType == IMAGE_FILE_MACHINE_AMD64) + return PEKind.AMD64; + else + { + // We don't support other architectures + throw new BadImageFormatException(); + } + } + else + { + // 32-bit, non-agnostic + if (dwImageType == IMAGE_FILE_MACHINE_I386) + return PEKind.I386; + else if (dwImageType == IMAGE_FILE_MACHINE_ARMNT) + return PEKind.ARM; + else + { + // Not supported + throw new BadImageFormatException(); + } + } + } + + // HResult + public const int RO_E_METADATA_NAME_NOT_FOUND = unchecked((int)0x8000000F); + public const int E_PATHNOTFOUND = unchecked((int)0x80070003); + public const int E_NOTREADY = unchecked((int)0x80070015); + public const int E_BADNETPATH = unchecked((int)0x80070035); + public const int E_BADNETNAME = unchecked((int)0x80070043); + public const int E_INVALID_NAME = unchecked((int)0x8007007B); + public const int E_MODNOTFOUND = unchecked((int)0x8007007E); + public const int E_DLLNOTFOUND = unchecked((int)0x80070485); + public const int E_WRONG_TARGET_NAME = unchecked((int)0x80070574); + public const int INET_E_CANNOT_CONNECT = unchecked((int)0x800C0004); + public const int INET_E_RESOURCE_NOT_FOUND = unchecked((int)0x800C0005); + public const int INET_E_OBJECT_NOT_FOUND = unchecked((int)0x800C0006); + public const int INET_E_DATA_NOT_AVAILABLE = unchecked((int)0x800C0007); + public const int INET_E_DOWNLOAD_FAILURE = unchecked((int)0x800C0008); + public const int INET_E_CONNECTION_TIMEOUT = unchecked((int)0x800C000B); + public const int INET_E_UNKNOWN_PROTOCOL = unchecked((int)0x800C000D); + public const int FUSION_E_APP_DOMAIN_LOCKED = unchecked((int)0x80131053); + public const int CLR_E_BIND_ASSEMBLY_VERSION_TOO_LOW = unchecked((int)0x80132000); + public const int CLR_E_BIND_ASSEMBLY_PUBLIC_KEY_MISMATCH = unchecked((int)0x80132001); + public const int CLR_E_BIND_ASSEMBLY_NOT_FOUND = unchecked((int)0x80132004); + public const int CLR_E_BIND_TYPE_NOT_FOUND = unchecked((int)0x80132005); + public const int CLR_E_BIND_ARCHITECTURE_MISMATCH = unchecked((int)0x80132006); + + public static int BindAssembly(AssemblyLoadContext binder, BinderAssemblyName assemblyName, bool excludeAppPaths, out BinderAssembly? result) + { + int kContextVersion = 0; + BindResult bindResult = default; + int hr = HResults.S_OK; + result = null; + ApplicationContext applicationContext = binder.AppContext; + + // Tracing happens outside the binder lock to avoid calling into managed code within the lock + using var tracer = new ResolutionAttemptedOperation(assemblyName, binder, ref hr); + + Retry: + lock (applicationContext.ContextCriticalSection) + { + hr = BindByName(applicationContext, assemblyName, false, false, excludeAppPaths, ref bindResult); + + if (hr < 0) goto Exit; + + // Remember the post-bind version + kContextVersion = applicationContext.Version; + } + + Exit: + tracer.TraceBindResult(bindResult); + + if (bindResult.Assembly != null) + { + hr = RegisterAndGetHostChosen(applicationContext, kContextVersion, bindResult, out BindResult hostBindResult); + + if (hr == HResults.S_FALSE) + { + // Another bind interfered. We need to retry the entire bind. + // This by design loops as long as needed because by construction we eventually + // will succeed or fail the bind. + bindResult = default; + goto Retry; + } + else if (hr == HResults.S_OK) + { + Debug.Assert(hostBindResult.Assembly != null); + result = hostBindResult.Assembly; + } + } + + return hr; + } + + // Skipped - the managed binder can't bootstrap CoreLib + // static Assembly? BindToSystem(string systemDirectory); + + // called by vm + private static unsafe int BindToSystemSatellite(char* systemDirectory, char* simpleName, char* cultureName, out BinderAssembly? assembly) + { + // Satellite assembly's relative path + + // append culture name + + // append satellite assembly's simple name + + // append extension + string relativePath = (string.IsNullOrEmpty(new string(cultureName)) ? new string(simpleName) : new string(cultureName)) + ".dll"; + + // Satellite assembly's path: + // * Absolute path when looking for a file on disk + // * Bundle-relative path when looking within the single-file bundle. + string sCoreLibSatellite = string.Empty; + + PathSource pathSource = PathSource.Bundle; + ProbeAppBundle(relativePath, pathIsBundleRelative: true, out BundleFileLocation bundleFileLocation); + if (!bundleFileLocation.IsValid) + { + sCoreLibSatellite = new string(systemDirectory); + pathSource = PathSource.ApplicationAssemblies; + } + + sCoreLibSatellite = Path.Combine(sCoreLibSatellite, relativePath); + + int hr = GetAssembly(sCoreLibSatellite, isInTPA: true, out assembly, default); + if (hr < 0) + { + assembly = null; + } + + AssemblyLoadContext.TracePathProbed(sCoreLibSatellite, pathSource, hr); + + return hr; + } + + private static int BindByName( + ApplicationContext applicationContext, + BinderAssemblyName assemblyName, + bool skipFailureChecking, + bool skipVersionCompatibilityCheck, + bool excludeAppPaths, + ref BindResult bindResult) + { + // Look for already cached binding failure (ignore PA, every PA will lock the context) + + if (applicationContext.FailureCache.TryGetValue(new FailureCacheKey(assemblyName.SimpleName, assemblyName.Version), out int hr)) + { + if (hr < 0) // FAILED(hr) + { + if (hr == HResults.E_FILENOTFOUND && skipFailureChecking) + { + // Ignore pre-existing transient bind error (re-bind will succeed) + applicationContext.FailureCache.Remove(new FailureCacheKey(assemblyName.SimpleName, assemblyName.Version)); + } + + return hr; // goto LogExit + } + else if (hr == HResults.S_FALSE) + { + // workaround: Special case for byte arrays. Rerun the bind to create binding log. + assemblyName.IsDefinition = true; + } + } + + if (!IsValidArchitecture(assemblyName.ProcessorArchitecture)) + { + // Assembly reference contains wrong architecture + hr = HResults.FUSION_E_INVALID_NAME; + goto Exit; + } + + hr = BindLocked(applicationContext, assemblyName, skipVersionCompatibilityCheck, excludeAppPaths, ref bindResult); + + if (hr < 0) return hr; + + if (bindResult.Assembly == null) + { + // Behavior rules are clueless now + hr = HResults.E_FILENOTFOUND; + goto Exit; + } + + Exit: + if (hr < 0) + { + if (skipFailureChecking) + { + if (hr != HResults.E_FILENOTFOUND) + { + // Cache non-transient bind error for byte-array + hr = HResults.S_FALSE; + } + else + { + // Ignore transient bind error (re-bind will succeed) + return hr; // goto LogExit; + } + } + + applicationContext.AddToFailureCache(assemblyName, hr); + } + + return hr; + } + + private static int BindLocked( + ApplicationContext applicationContext, + BinderAssemblyName assemblyName, + bool skipVersionCompatibilityCheck, + bool excludeAppPaths, + ref BindResult bindResult) + { + bool isTpaListProvided = applicationContext.TrustedPlatformAssemblyMap != null; + int hr = FindInExecutionContext(applicationContext, assemblyName, out BinderAssembly? assembly); + + // Add the attempt to the bind result on failure / not found. On success, it will be added after the version check. + if (hr < 0 || assembly == null) + bindResult.SetAttemptResult(hr, assembly, isInContext: true); + + if (hr < 0) return hr; + + if (assembly != null) + { + if (!skipVersionCompatibilityCheck) + { + // Can't give higher version than already bound + bool isCompatible = IsCompatibleAssemblyVersion(assemblyName, assembly.AssemblyName); + hr = isCompatible ? HResults.S_OK : FUSION_E_APP_DOMAIN_LOCKED; + bindResult.SetAttemptResult(hr, assembly, isInContext: true); + + // TPA binder returns FUSION_E_REF_DEF_MISMATCH for incompatible version + if (hr == FUSION_E_APP_DOMAIN_LOCKED && isTpaListProvided) // hr == FUSION_E_APP_DOMAIN_LOCKED + hr = HResults.FUSION_E_REF_DEF_MISMATCH; + } + else + { + bindResult.SetAttemptResult(hr, assembly, isInContext: true); + } + + if (hr < 0) return hr; + + bindResult.SetResult(assembly, isInContext: true); + } + else if (isTpaListProvided) + { + // BindByTpaList handles setting attempt results on the bind result + hr = BindByTpaList(applicationContext, assemblyName, excludeAppPaths, ref bindResult); + + if (hr >= 0 && bindResult.Assembly != null) // SUCCEEDED(hr) && pBindResult->HaveResult() + { + bool isCompatible = IsCompatibleAssemblyVersion(assemblyName, bindResult.Assembly.AssemblyName); + hr = isCompatible ? HResults.S_OK : FUSION_E_APP_DOMAIN_LOCKED; + bindResult.SetAttemptResult(hr, bindResult.Assembly); + + // TPA binder returns FUSION_E_REF_DEF_MISMATCH for incompatible version + if (hr == FUSION_E_APP_DOMAIN_LOCKED && isTpaListProvided) // hr == FUSION_E_APP_DOMAIN_LOCKED + hr = HResults.FUSION_E_REF_DEF_MISMATCH; + } + + if (hr < 0) + { + bindResult.SetNoResult(); + } + } + + return hr; + } + + private static int FindInExecutionContext(ApplicationContext applicationContext, BinderAssemblyName assemblyName, out BinderAssembly? assembly) + { + applicationContext.ExecutionContext.TryGetValue(assemblyName, out assembly); + + // Set any found context entry. It is up to the caller to check the returned HRESULT + // for errors due to validation + if (assembly == null) + return HResults.S_FALSE; + + if (assembly != null && assemblyName.IsDefinition + && (assembly.AssemblyName.ProcessorArchitecture != assemblyName.ProcessorArchitecture)) + { + return FUSION_E_APP_DOMAIN_LOCKED; + } + + return HResults.S_OK; + } + + // + // Tests whether a candidate assembly's name matches the requested. + // This does not do a version check. The binder applies version policy + // further up the stack once it gets a successful bind. + // + private static bool TestCandidateRefMatchesDef(BinderAssemblyName requestedAssemblyName, BinderAssemblyName boundAssemblyName, bool tpaListAssembly) + { + AssemblyNameIncludeFlags includeFlags = AssemblyNameIncludeFlags.INCLUDE_DEFAULT; + + if (!tpaListAssembly) + { + if (requestedAssemblyName.IsNeutralCulture) + { + includeFlags |= AssemblyNameIncludeFlags.EXCLUDE_CULTURE; + } + } + + if (requestedAssemblyName.ProcessorArchitecture != PEKind.None) + { + includeFlags |= AssemblyNameIncludeFlags.INCLUDE_ARCHITECTURE; + } + + return boundAssemblyName.Equals(requestedAssemblyName, includeFlags); + } + + private static int BindSatelliteResourceFromBundle(BinderAssemblyName requestedAssemblyName, string relativePath, ref BindResult bindResult) + { + int hr = HResults.S_OK; + + ProbeAppBundle(relativePath, pathIsBundleRelative: true, out BundleFileLocation bundleFileLocation); + if (!bundleFileLocation.IsValid) + { + return hr; + } + + hr = GetAssembly(relativePath, isInTPA: false, out BinderAssembly? assembly, bundleFileLocation); + + AssemblyLoadContext.TracePathProbed(relativePath, PathSource.Bundle, hr); + + // Missing files are okay and expected when probing + if (hr == HResults.E_FILENOTFOUND) + { + return HResults.S_OK; + } + + bindResult.SetAttemptResult(hr, assembly); + if (hr < 0) + return hr; + + Debug.Assert(assembly != null); + BinderAssemblyName boundAssemblyName = assembly.AssemblyName; + if (TestCandidateRefMatchesDef(requestedAssemblyName, boundAssemblyName, tpaListAssembly: false)) + { + bindResult.SetResult(assembly); + hr = HResults.S_OK; + } + else + { + hr = HResults.FUSION_E_REF_DEF_MISMATCH; + } + + bindResult.SetAttemptResult(hr, assembly); + return hr; + } + + private static int BindSatelliteResourceByProbingPaths( + List resourceRoots, + BinderAssemblyName requestedAssemblyName, + string relativePath, + ref BindResult bindResult, + PathSource pathSource) + { + foreach (string bindingPath in resourceRoots) + { + string fileName = Path.Combine(bindingPath, relativePath); + int hr = GetAssembly(fileName, isInTPA: false, out BinderAssembly? assembly); + AssemblyLoadContext.TracePathProbed(fileName, pathSource, hr); + + // Missing files are okay and expected when probing + if (hr == HResults.E_FILENOTFOUND) + { + continue; + } + + bindResult.SetAttemptResult(hr, assembly); + if (hr < 0) + return hr; + + Debug.Assert(assembly != null); + BinderAssemblyName boundAssemblyName = assembly.AssemblyName; + if (TestCandidateRefMatchesDef(requestedAssemblyName, boundAssemblyName, tpaListAssembly: false)) + { + bindResult.SetResult(assembly); + hr = HResults.S_OK; + } + else + { + hr = HResults.FUSION_E_REF_DEF_MISMATCH; + } + + bindResult.SetAttemptResult(hr, assembly); + return hr; + } + + // Up-stack expects S_OK when we don't find any candidate assemblies and no fatal error occurred (ie, no S_FALSE) + return HResults.S_OK; + } + + private static int BindSatelliteResource(ApplicationContext applicationContext, BinderAssemblyName requestedAssemblyName, ref BindResult bindResult) + { + Debug.Assert(!requestedAssemblyName.IsNeutralCulture); + + string fileName = Path.Combine(requestedAssemblyName.CultureOrLanguage, requestedAssemblyName.SimpleName) + ".dll"; + + // Satellite resource probing strategy is to look: + // * First within the single-file bundle + // * Then under each of the Platform Resource Roots + // * Then under each of the App Paths. + // + // During each search, if we find a platform resource file with matching file name, but whose ref-def didn't match, + // fall back to application resource lookup to handle case where a user creates resources with the same + // names as platform ones. + + int hr = BindSatelliteResourceFromBundle(requestedAssemblyName, fileName, ref bindResult); + + if (bindResult.Assembly != null || hr < 0) + { + return hr; + } + + hr = BindSatelliteResourceByProbingPaths(applicationContext.PlatformResourceRoots, requestedAssemblyName, fileName, ref bindResult, PathSource.PlatformResourceRoots); + + if (bindResult.Assembly != null || hr < 0) + { + return hr; + } + + hr = BindSatelliteResourceByProbingPaths(applicationContext.AppPaths, requestedAssemblyName, fileName, ref bindResult, PathSource.AppPaths); + + return hr; + } + + private static int BindAssemblyByProbingPaths(List bindingPaths, BinderAssemblyName requestedAssemblyName, out BinderAssembly? result) + { + PathSource pathSource = PathSource.AppPaths; + + // Loop through the binding paths looking for a matching assembly + foreach (string bindingPath in bindingPaths) + { + string fileNameWithoutExtension = Path.Combine(bindingPath, requestedAssemblyName.SimpleName); + + // Look for a matching dll first + string fileName = fileNameWithoutExtension + ".dll"; + + int hr = GetAssembly(fileName, isInTPA: false, out BinderAssembly? assembly); + AssemblyLoadContext.TracePathProbed(fileName, pathSource, hr); + + if (hr < 0) + { + fileName = fileNameWithoutExtension + ".exe"; + hr = GetAssembly(fileName, isInTPA: false, out assembly); + AssemblyLoadContext.TracePathProbed(fileName, pathSource, hr); + } + + // Since we're probing, file not founds are ok and we should just try another + // probing path + if (hr == HResults.COR_E_FILENOTFOUND) + { + continue; + } + + // Set any found assembly. It is up to the caller to check the returned HRESULT for errors due to validation + result = assembly; + if (hr < 0) + return hr; + + // We found a candidate. + // + // Below this point, we either establish that the ref-def matches, or + // we fail the bind. + + Debug.Assert(assembly != null); + + // Compare requested AssemblyName with that from the candidate assembly + if (!TestCandidateRefMatchesDef(requestedAssemblyName, assembly.AssemblyName, tpaListAssembly: false)) + return HResults.FUSION_E_REF_DEF_MISMATCH; + + return HResults.S_OK; + } + + result = null; + return HResults.COR_E_FILENOTFOUND; + } + + /* + * BindByTpaList is the entry-point for the custom binding algorithm in CoreCLR. + * + * The search for assemblies will proceed in the following order: + * + * If this application is a single-file bundle, the meta-data contained in the bundle + * will be probed to find the requested assembly. If the assembly is not found, + * The list of platform assemblies (TPAs) are considered next. + * + * Platform assemblies are specified as a list of files. This list is the only set of + * assemblies that we will load as platform. They can be specified as IL or NIs. + * + * Resources for platform assemblies are located by probing starting at the Platform Resource Roots, + * a set of folders configured by the host. + * + * If a requested assembly identity cannot be found in the TPA list or the resource roots, + * it is considered an application assembly. We probe for application assemblies in the + * AppPaths, a list of paths containing IL files and satellite resource folders. + * + */ + + public static int BindByTpaList(ApplicationContext applicationContext, BinderAssemblyName requestedAssemblyName, bool excludeAppPaths, ref BindResult bindResult) + { + bool fPartialMatchOnTpa = false; + + if (!requestedAssemblyName.IsNeutralCulture) + { + int hr = BindSatelliteResource(applicationContext, requestedAssemblyName, ref bindResult); + if (hr < 0) + return hr; + } + else + { + BinderAssembly? tpaAssembly = null; + + // Is assembly in the bundle? + // Single-file bundle contents take precedence over TPA. + // The list of bundled assemblies is contained in the bundle manifest, and NOT in the TPA. + // Therefore the bundle is first probed using the assembly's simple name. + // If found, the assembly is loaded from the bundle. + if (AppIsBundle()) + { + // Search Assembly.ni.dll, then Assembly.dll + // The Assembly.ni.dll paths are rare, and intended for supporting managed C++ R2R assemblies. + ReadOnlySpan candidates = ["ni.dll", ".dll"]; + + // Loop through the binding paths looking for a matching assembly + foreach (string candidate in candidates) + { + string assemblyFileName = requestedAssemblyName.SimpleName + candidate; + string? assemblyFilePath = string.Empty; + GetAppBundleBasePath(new StringHandleOnStack(ref assemblyFilePath)); + assemblyFilePath += assemblyFileName; + + ProbeAppBundle(assemblyFileName, pathIsBundleRelative: true, out BundleFileLocation bundleFileLocation); + if (bundleFileLocation.IsValid) + { + int hr = GetAssembly(assemblyFilePath, isInTPA: true, out tpaAssembly, bundleFileLocation); + + AssemblyLoadContext.TracePathProbed(assemblyFilePath, PathSource.Bundle, hr); + + if (hr != HResults.E_FILENOTFOUND) + { + // Any other error is fatal + if (hr < 0) return hr; + + Debug.Assert(tpaAssembly != null); + if (TestCandidateRefMatchesDef(requestedAssemblyName, tpaAssembly.AssemblyName, tpaListAssembly: true)) + { + // We have found the requested assembly match in the bundle with validation of the full-qualified name. + // Bind to it. + bindResult.SetResult(tpaAssembly); + return HResults.S_OK; + } + } + } + } + } + + // Is assembly on TPA list? + Debug.Assert(applicationContext.TrustedPlatformAssemblyMap != null); + if (applicationContext.TrustedPlatformAssemblyMap.TryGetValue(requestedAssemblyName.SimpleName, out TPAEntry? tpaEntry)) + { + string? tpaFileName = tpaEntry.NIFileName ?? tpaEntry.ILFileName; + Debug.Assert(tpaFileName != null); + + int hr = GetAssembly(tpaFileName, isInTPA: true, out tpaAssembly); + AssemblyLoadContext.TracePathProbed(tpaFileName, PathSource.ApplicationAssemblies, hr); + + bindResult.SetAttemptResult(hr, tpaAssembly); + + // On file not found, simply fall back to app path probing + if (hr != HResults.E_FILENOTFOUND) + { + // Any other error is fatal + if (hr < 0) return hr; + + Debug.Assert(tpaAssembly != null); + if (TestCandidateRefMatchesDef(requestedAssemblyName, tpaAssembly.AssemblyName, tpaListAssembly: true)) + { + // We have found the requested assembly match on TPA with validation of the full-qualified name. Bind to it. + bindResult.SetResult(tpaAssembly); + bindResult.SetAttemptResult(HResults.S_OK, tpaAssembly); + return HResults.S_OK; + } + else + { + // We found the assembly on TPA but it didn't match the RequestedAssembly assembly-name. In this case, lets proceed to see if we find the requested + // assembly in the App paths. + bindResult.SetAttemptResult(HResults.FUSION_E_REF_DEF_MISMATCH, tpaAssembly); + fPartialMatchOnTpa = true; + } + } + + // We either didn't find a candidate, or the ref-def failed. Either way; fall back to app path probing. + } + + if (!excludeAppPaths) + { + // Probe AppPaths + + int hr = BindAssemblyByProbingPaths(applicationContext.AppPaths, requestedAssemblyName, out BinderAssembly? assembly); + bindResult.SetAttemptResult(hr, assembly); + + if (hr != HResults.E_FILENOTFOUND) + { + if (hr < 0) return hr; + Debug.Assert(assembly != null); + + // At this point, we have found an assembly with the expected name in the App paths. If this was also found on TPA, + // make sure that the app assembly has the same fullname (excluding version) as the TPA version. If it does, then + // we should bind to the TPA assembly. If it does not, then bind to the app assembly since it has a different fullname than the + // TPA assembly. + if (fPartialMatchOnTpa) + { + Debug.Assert(tpaAssembly != null); + + if (TestCandidateRefMatchesDef(assembly.AssemblyName, tpaAssembly.AssemblyName, true)) + { + // Fullname (SimpleName+Culture+PKT) matched for TPA and app assembly - so bind to TPA instance. + bindResult.SetResult(tpaAssembly); + bindResult.SetAttemptResult(hr, tpaAssembly); + return HResults.S_OK; + } + else + { + // Fullname (SimpleName+Culture+PKT) did not match for TPA and app assembly - so bind to app instance. + bindResult.SetResult(assembly); + return HResults.S_OK; + } + } + else + { + // We didn't see this assembly on TPA - so simply bind to the app instance. + bindResult.SetResult(assembly); + return HResults.S_OK; + } + } + } + } + + // Couldn't find a matching assembly in any of the probing paths + // Return S_FALSE here. BindByName will interpret a successful HRESULT + // and lack of BindResult as a failure to find a matching assembly. + return HResults.S_FALSE; + } + + private static int GetAssembly(string assemblyPath, bool isInTPA, out BinderAssembly? assembly, BundleFileLocation bundleFileLocation = default) + { + int hr = BinderAcquirePEImage(assemblyPath, out IntPtr pPEImage, bundleFileLocation); + + try + { + if (hr < 0) + { + // Normalize file not found + + // ported from Assembly::FileNotFound in coreclr\vm\assembly.cpp + if (hr is HResults.E_FILENOTFOUND + or E_MODNOTFOUND + or E_INVALID_NAME + or HResults.CTL_E_FILENOTFOUND + or E_PATHNOTFOUND + or E_BADNETNAME + or E_BADNETPATH + or E_NOTREADY + or E_WRONG_TARGET_NAME + or INET_E_UNKNOWN_PROTOCOL + or INET_E_CONNECTION_TIMEOUT + or INET_E_CANNOT_CONNECT + or INET_E_RESOURCE_NOT_FOUND + or INET_E_OBJECT_NOT_FOUND + or INET_E_DOWNLOAD_FAILURE + or INET_E_DATA_NOT_AVAILABLE + or E_DLLNOTFOUND + or CLR_E_BIND_ASSEMBLY_VERSION_TOO_LOW + or CLR_E_BIND_ASSEMBLY_PUBLIC_KEY_MISMATCH + or CLR_E_BIND_ASSEMBLY_NOT_FOUND + or RO_E_METADATA_NAME_NOT_FOUND + or CLR_E_BIND_TYPE_NOT_FOUND) + { + hr = HResults.E_FILENOTFOUND; + } + + assembly = null; + return hr; + } + + assembly = new BinderAssembly(pPEImage, isInTPA); + pPEImage = IntPtr.Zero; + return HResults.S_OK; + } + catch (Exception e) + { + assembly = null; + return e.HResult; + } + finally + { + // SAFE_RELEASE(pPEImage); + if (pPEImage != IntPtr.Zero) + PEImage_Release(pPEImage); + } + } + + public static int Register(ApplicationContext applicationContext, ref BindResult bindResult) + { + Debug.Assert(!bindResult.IsContextBound); + Debug.Assert(bindResult.Assembly != null); + + applicationContext.IncrementVersion(); + + // Register the bindResult in the ExecutionContext only if we dont have it already. + // This method is invoked under a lock (by its caller), so we are thread safe. + int hr = FindInExecutionContext(applicationContext, bindResult.Assembly.AssemblyName, out BinderAssembly? assembly); + if (hr < 0) + return hr; + + if (assembly == null) + { + applicationContext.ExecutionContext.Add(bindResult.Assembly.AssemblyName, bindResult.Assembly); + } + else + { + // Update the BindResult with the assembly we found + bindResult.SetResult(assembly, isInContext: true); + } + + return HResults.S_OK; + } + + public static int RegisterAndGetHostChosen(ApplicationContext applicationContext, int kContextVersion, in BindResult bindResult, out BindResult hostBindResult) + { + Debug.Assert(bindResult.Assembly != null); + hostBindResult = default; + int hr = HResults.S_OK; + + if (!bindResult.IsContextBound) + { + hostBindResult = bindResult; + + // Lock the application context + lock (applicationContext.ContextCriticalSection) + { + // Only perform costly validation if other binds succeeded before us + if (kContextVersion != applicationContext.Version) + { + hr = OtherBindInterfered(applicationContext, bindResult); + if (hr < 0) return hr; + + if (hr == HResults.S_FALSE) + { + // Another bind interfered + return hr; + } + } + + // No bind interfered, we can now register + hr = Register(applicationContext, ref hostBindResult); + if (hr < 0) return hr; + } + } + else + { + // No work required. Return the input + hostBindResult = bindResult; + } + + return hr; + } + + private static int OtherBindInterfered(ApplicationContext applicationContext, BindResult bindResult) + { + Debug.Assert(bindResult.Assembly != null); + Debug.Assert(bindResult.Assembly.AssemblyName != null); + + // Look for already cached binding failure (ignore PA, every PA will lock the context) + if (!applicationContext.FailureCache.ContainsKey(new FailureCacheKey(bindResult.Assembly.AssemblyName.SimpleName, bindResult.Assembly.AssemblyName.Version))) // hr == S_OK + { + int hr = FindInExecutionContext(applicationContext, bindResult.Assembly.AssemblyName, out BinderAssembly? assembly); + if (hr >= 0 && assembly != null) + { + // We can accept this bind in the domain + return HResults.S_OK; + } + } + + // Some other bind interfered + return HResults.S_FALSE; + } + + public static int BindUsingPEImage(AssemblyLoadContext binder, BinderAssemblyName assemblyName, IntPtr pPEImage, bool excludeAppPaths, out BinderAssembly? assembly) + { + int hr = HResults.S_OK; + + int kContextVersion = 0; + BindResult bindResult = default; + + // Prepare binding data + assembly = null; + ApplicationContext applicationContext = binder.AppContext; + + // Tracing happens outside the binder lock to avoid calling into managed code within the lock + using var tracer = new ResolutionAttemptedOperation(assemblyName, binder, ref hr); + + bool mvidMismatch = false; + + try + { + Retry: + mvidMismatch = false; + + // Lock the application context + lock (applicationContext.ContextCriticalSection) + { + // Attempt uncached bind and register stream if possible + // We skip version compatibility check - so assemblies with same simple name will be reported + // as a successful bind. Below we compare MVIDs in that case instead (which is a more precise equality check). + hr = BindByName(applicationContext, assemblyName, true, true, excludeAppPaths, ref bindResult); + + if (hr == HResults.E_FILENOTFOUND) + { + // IF_FAIL_GO(CreateImageAssembly(pPEImage, &bindResult)); + try + { + bindResult.SetResult(new BinderAssembly(pPEImage, false)); + } + catch (Exception ex) + { + return ex.HResult; + } + } + else if (hr == HResults.S_OK) + { + if (bindResult.Assembly != null) + { + // Attempt was made to load an assembly that has the same name as a previously loaded one. Since same name + // does not imply the same assembly, we will need to check the MVID to confirm it is the same assembly as being + // requested. + + Guid incomingMVID; + Guid boundMVID; + + // GetMVID can throw exception + try + { + PEImage_GetMVID(pPEImage, out incomingMVID); + PEImage_GetMVID(bindResult.Assembly.PEImage, out boundMVID); + } + catch (Exception ex) + { + return ex.HResult; + } + + mvidMismatch = incomingMVID != boundMVID; + if (mvidMismatch) + { + // MVIDs do not match, so fail the load. + return HResults.COR_E_FILELOAD; + } + + // MVIDs match - request came in for the same assembly that was previously loaded. + // Let it through... + } + } + + // Remember the post-bind version of the context + kContextVersion = applicationContext.Version; + } + + if (bindResult.Assembly != null) + { + // This has to happen outside the binder lock as it can cause new binds + hr = RegisterAndGetHostChosen(applicationContext, kContextVersion, bindResult, out BindResult hostBindResult); + if (hr < 0) return hr; + + if (hr == HResults.S_FALSE) + { + // tracer.TraceBindResult(bindResult); + + // Another bind interfered. We need to retry entire bind. + // This by design loops as long as needed because by construction we eventually + // will succeed or fail the bind. + bindResult = default; + goto Retry; + } + else if (hr == HResults.S_OK) + { + assembly = hostBindResult.Assembly; + } + } + } + finally + { + tracer.TraceBindResult(bindResult, mvidMismatch); + } + + return hr; + } + + // CreateDefaultBinder + + public static bool IsValidArchitecture(PEKind architecture) + { + if (architecture is PEKind.MSIL or PEKind.None) + return true; + + PEKind processArchitecture = +#if TARGET_X86 + PEKind.I386; +#elif TARGET_AMD64 + PEKind.AMD64; +#elif TARGET_ARM + PEKind.ARM; +#elif TARGET_ARM64 + PEKind.ARM64; +#else + PEKind.MSIL; +#endif + + return architecture == processArchitecture; + } + + // AssemblySpec::InitializeAssemblyNameRef + public static unsafe AssemblyName InitializeAssemblyNameRef(BinderAssemblyName assemblyName) + { + string culture = string.Empty; + + if ((assemblyName.IdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_CULTURE) != 0) + { + culture = assemblyName.IsNeutralCulture ? string.Empty : assemblyName.CultureOrLanguage; + } + + fixed (char* pName = assemblyName.SimpleName) + fixed (char* pCulture = culture) + fixed (byte* pPublicKeyOrToken = assemblyName.PublicKeyOrTokenBLOB) + { + var nativeAssemblyNameParts = new NativeAssemblyNameParts + { + _pName = pName, + _pCultureName = pCulture, + _major = (ushort)assemblyName.Version.Major, + _minor = (ushort)assemblyName.Version.Minor, + _build = (ushort)assemblyName.Version.Build, + _revision = (ushort)assemblyName.Version.Revision, + _pPublicKeyOrToken = pPublicKeyOrToken, + _cbPublicKeyOrToken = assemblyName.PublicKeyOrTokenBLOB.Length, + }; + + if ((assemblyName.IdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY) != 0) + nativeAssemblyNameParts._flags |= System.Reflection.AssemblyNameFlags.PublicKey; + + // Architecture unused + + // Retargetable + if ((assemblyName.IdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_RETARGETABLE) != 0) + nativeAssemblyNameParts._flags |= System.Reflection.AssemblyNameFlags.Retargetable; + + // Content type unused + + return new AssemblyName(&nativeAssemblyNameParts); + } + } + } + + public partial class AssemblyLoadContext + { + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "DomainAssembly_GetPEAssembly")] + private static partial IntPtr DomainAssembly_GetPEAssembly(IntPtr pDomainAssembly); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PEAssembly_GetHostAssembly")] + private static partial IntPtr PEAssembly_GetHostAssembly(IntPtr pPEAssembly); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "DomainAssembly_EnsureReferenceBinder")] + private static partial IntPtr DomainAssembly_EnsureReferenceBinder(IntPtr pDomainAssembly, IntPtr pBinder); + + internal static int BindUsingHostAssemblyResolver( + BinderAssemblyName assemblyName, + AssemblyLoadContext? defaultBinder, + AssemblyLoadContext binder, + out BinderAssembly? loadedAssembly) + { + int hr = HResults.E_FAIL; + loadedAssembly = null; + BinderAssembly? resolvedAssembly = null; + + // body of RuntimeInvokeHostAssemblyResolver + bool fResolvedAssembly = false; + Assembly? refLoadedAssembly = null; + using var tracer = new ResolutionAttemptedOperation(assemblyName, binder, ref hr); + + // Allocate an AssemblyName managed object + // Initialize the AssemblyName object + AssemblyName refAssemblyName = AssemblyBinderCommon.InitializeAssemblyNameRef(assemblyName); + + bool isSatelliteAssemblyRequest = !assemblyName.IsNeutralCulture; + try + { + if (defaultBinder != null) + { + // Step 2 (of CustomAssemblyBinder::BindAssemblyByName) - Invoke Load method + // This is not invoked for TPA Binder since it always returns NULL. + tracer.GoToStage(Stage.AssemblyLoadContextLoad); + + refLoadedAssembly = binder.ResolveUsingLoad(refAssemblyName); + if (refLoadedAssembly != null) + { + fResolvedAssembly = true; + } + + hr = fResolvedAssembly ? HResults.S_OK : HResults.COR_E_FILENOTFOUND; + + // Step 3 (of CustomAssemblyBinder::BindAssemblyByName) + if (!fResolvedAssembly && !isSatelliteAssemblyRequest) + { + tracer.GoToStage(Stage.DefaultAssemblyLoadContextFallback); + + // If we could not resolve the assembly using Load method, then attempt fallback with TPA Binder. + // Since TPA binder cannot fallback to itself, this fallback does not happen for binds within TPA binder. + + hr = defaultBinder.BindUsingAssemblyName(assemblyName, out BinderAssembly? coreCLRFoundAssembly); + if (hr >= 0) + { + Debug.Assert(coreCLRFoundAssembly != null); + resolvedAssembly = coreCLRFoundAssembly; + fResolvedAssembly = true; + } + } + } + + if (!fResolvedAssembly && isSatelliteAssemblyRequest) + { + // Step 4 (of CustomAssemblyBinder::BindAssemblyByName) + + // Attempt to resolve it using the ResolveSatelliteAssembly method. + tracer.GoToStage(Stage.ResolveSatelliteAssembly); + + refLoadedAssembly = binder.ResolveSatelliteAssembly(refAssemblyName); + if (refLoadedAssembly != null) + { + // Set the flag indicating we found the assembly + fResolvedAssembly = true; + } + + hr = fResolvedAssembly ? HResults.S_OK : HResults.COR_E_FILENOTFOUND; + } + + if (!fResolvedAssembly) + { + // Step 5 (of CustomAssemblyBinder::BindAssemblyByName) + + // If we couldn't resolve the assembly using TPA LoadContext as well, then + // attempt to resolve it using the Resolving event. + tracer.GoToStage(Stage.AssemblyLoadContextResolvingEvent); + + refLoadedAssembly = binder.ResolveUsingEvent(refAssemblyName); + if (refLoadedAssembly != null) + { + // Set the flag indicating we found the assembly + fResolvedAssembly = true; + } + + hr = fResolvedAssembly ? HResults.S_OK : HResults.COR_E_FILENOTFOUND; + } + + if (fResolvedAssembly && resolvedAssembly == null) + { + // If we are here, assembly was successfully resolved via Load or Resolving events. + Debug.Assert(refLoadedAssembly != null); + + // We were able to get the assembly loaded. Now, get its name since the host could have + // performed the resolution using an assembly with different name. + + RuntimeAssembly? rtAssembly = + GetRuntimeAssembly(refLoadedAssembly) + ?? throw new InvalidOperationException(SR.Arg_MustBeRuntimeAssembly); + + IntPtr pDomainAssembly = rtAssembly.GetUnderlyingNativeHandle(); + IntPtr pLoadedPEAssembly = IntPtr.Zero; + bool fFailLoad = false; + if (pDomainAssembly == IntPtr.Zero) + { + // Reflection emitted assemblies will not have a domain assembly. + fFailLoad = true; + } + else + { + pLoadedPEAssembly = DomainAssembly_GetPEAssembly(pDomainAssembly); + if (PEAssembly_GetHostAssembly(pLoadedPEAssembly) == IntPtr.Zero) + { + // Reflection emitted assemblies will not have a domain assembly. + fFailLoad = true; + } + } + + // The loaded assembly's BINDER_SPACE::Assembly* is saved as HostAssembly in PEAssembly + if (fFailLoad) + { + // string name = assemblyName.GetDisplayName(AssemblyNameIncludeFlags.INCLUDE_ALL); + throw new InvalidOperationException(SR.Host_AssemblyResolver_DynamicallyEmittedAssemblies_Unsupported); + } + + // For collectible assemblies, ensure that the parent loader allocator keeps the assembly's loader allocator + // alive for all its lifetime. + if (rtAssembly.IsCollectible) + { + DomainAssembly_EnsureReferenceBinder(pDomainAssembly, binder._nativeAssemblyLoadContext); + } + + resolvedAssembly = GCHandle.FromIntPtr(PEAssembly_GetHostAssembly(pLoadedPEAssembly)).Target as BinderAssembly; + } + + if (fResolvedAssembly) + { + Debug.Assert(resolvedAssembly != null); + + // Get the BINDER_SPACE::Assembly reference to return back to. + loadedAssembly = resolvedAssembly; + hr = HResults.S_OK; + + tracer.SetFoundAssembly(resolvedAssembly); + } + else + { + hr = HResults.COR_E_FILENOTFOUND; + } + } + catch (Exception ex) + { + tracer.SetException(ex); + throw; + } + + return hr; + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs index 27debf8d2a271..008254b57b7ed 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs @@ -45,20 +45,21 @@ internal static Assembly[] GetLoadedAssemblies() internal static extern bool IsTracingEnabled(); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_TraceResolvingHandlerInvoked", StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool TraceResolvingHandlerInvoked(string assemblyName, string handlerName, string? alcName, string? resultAssemblyName, string? resultAssemblyPath); + internal static partial void TraceResolvingHandlerInvoked(string assemblyName, string handlerName, string? alcName, string? resultAssemblyName, string? resultAssemblyPath); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_TraceAssemblyResolveHandlerInvoked", StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool TraceAssemblyResolveHandlerInvoked(string assemblyName, string handlerName, string? resultAssemblyName, string? resultAssemblyPath); + internal static partial void TraceAssemblyResolveHandlerInvoked(string assemblyName, string handlerName, string? resultAssemblyName, string? resultAssemblyPath); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_TraceAssemblyLoadFromResolveHandlerInvoked", StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool TraceAssemblyLoadFromResolveHandlerInvoked(string assemblyName, [MarshalAs(UnmanagedType.Bool)] bool isTrackedAssembly, string requestingAssemblyPath, string? requestedAssemblyPath); + internal static partial void TraceAssemblyLoadFromResolveHandlerInvoked(string assemblyName, [MarshalAs(UnmanagedType.Bool)] bool isTrackedAssembly, string requestingAssemblyPath, string? requestedAssemblyPath); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_TraceSatelliteSubdirectoryPathProbed", StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool TraceSatelliteSubdirectoryPathProbed(string filePath, int hResult); + internal static void TraceSatelliteSubdirectoryPathProbed(string filePath, int hResult) => TracePathProbed(filePath, PathSource.SatelliteSubdirectory, hResult); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_TracePathProbed", StringMarshalling = StringMarshalling.Utf16)] + internal static partial void TracePathProbed(string filePath, PathSource source, int hResult); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_TraceResolutionAttempted", StringMarshalling = StringMarshalling.Utf16)] + internal static partial void TraceResolutionAttempted(string assemblyName, Tracing.Stage stage, string assemblyLoadContextName, Tracing.Result result, string resultAssemblyName, string resultAssemblyPath, string errorMsg); [RequiresUnreferencedCode("Types and members the loaded assembly depends on might be removed")] private RuntimeAssembly InternalLoadFromPath(string? assemblyPath, string? nativeImagePath) @@ -107,17 +108,6 @@ internal Assembly LoadFromInMemoryModule(IntPtr moduleHandle) } } #endif - - // 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); - } - // This method is invoked by the VM when using the host-provided assembly load context // implementation. private static IntPtr ResolveUnmanagedDll(string unmanagedDllName, IntPtr gchManagedAssemblyLoadContext) @@ -134,15 +124,6 @@ private static IntPtr ResolveUnmanagedDllUsingEvent(string unmanagedDllName, Ass return context.GetResolvedUnmanagedDll(assembly, unmanagedDllName); } - // 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); - } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetLoadContextForAssembly")] private static partial IntPtr GetLoadContextForAssembly(QCallAssembly assembly); @@ -218,13 +199,16 @@ private static void StopAssemblyLoad(ref Guid activityId) // Don't use trace to TPL event source in ActivityTracker - that event source is a singleton and its instantiation may have triggered the load. ActivityTracker.Instance.OnStop(NativeRuntimeEventSource.Log.Name, AssemblyLoadName, 0, ref activityId, useTplSource: false); } - - /// - /// Called by the runtime to make sure the default ALC is initialized - /// - private static void InitializeDefaultContext() - { - _ = Default; - } } + + // This must match the BindingPathSource value map in ClrEtwAll.man + internal enum PathSource : ushort + { + ApplicationAssemblies, + Unused, + AppPaths, + PlatformResourceRoots, + SatelliteSubdirectory, + Bundle + }; } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CustomBinder.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CustomBinder.cs new file mode 100644 index 0000000000000..96db193288ee4 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CustomBinder.cs @@ -0,0 +1,112 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Runtime.Loader +{ + public partial class AssemblyLoadContext + { + private int BindAssemblyByNameWorker(BinderAssemblyName assemblyName, out BinderAssembly? coreCLRFoundAssembly) + { + // CoreLib should be bound using BindToSystem + Debug.Assert(!assemblyName.IsCoreLib); + + int hr = AssemblyBinderCommon.BindAssembly(this, assemblyName, excludeAppPaths: false, out coreCLRFoundAssembly); + + if (hr >= 0) + { + Debug.Assert(coreCLRFoundAssembly != null); + coreCLRFoundAssembly.Binder = _nativeAssemblyLoadContext; + } + + return hr; + } + + internal virtual int BindUsingAssemblyName(BinderAssemblyName assemblyName, out BinderAssembly? assembly) + { + int hr; + BinderAssembly? coreCLRFoundAssembly; + + // When LoadContext needs to resolve an assembly reference, it will go through the following lookup order: + // + // 1) Lookup the assembly within the LoadContext itself. If assembly is found, use it. + // 2) Invoke the LoadContext's Load method implementation. If assembly is found, use it. + // 3) Lookup the assembly within DefaultBinder (except for satellite requests). If assembly is found, use it. + // 4) Invoke the LoadContext's ResolveSatelliteAssembly method (for satellite requests). If assembly is found, use it. + // 5) Invoke the LoadContext's Resolving event. If assembly is found, use it. + // 6) Raise exception. + // + // This approach enables a LoadContext to override assemblies that have been loaded in TPA context by loading + // a different (or even the same!) version. + + { + // Step 1 - Try to find the assembly within the LoadContext. + hr = BindAssemblyByNameWorker(assemblyName, out coreCLRFoundAssembly); + if (hr is HResults.E_FILENOTFOUND or AssemblyBinderCommon.FUSION_E_APP_DOMAIN_LOCKED or HResults.FUSION_E_REF_DEF_MISMATCH) + { + // If we are here, one of the following is possible: + // + // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR + // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def + // mismatch (either due to version difference or strong-name difference). + // + // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call + // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly + // that has been loaded. + + hr = BindUsingHostAssemblyResolver(assemblyName, Default, this, out coreCLRFoundAssembly); + + if (hr >= 0) + { + // We maybe returned an assembly that was bound to a different AssemblyBinder instance. + // In such a case, we will not overwrite the binder (which would be wrong since the assembly would not + // be present in the cache of the current binding context). + Debug.Assert(coreCLRFoundAssembly != null); + if (coreCLRFoundAssembly.Binder == IntPtr.Zero) + coreCLRFoundAssembly.Binder = _nativeAssemblyLoadContext; + } + } + } + + assembly = hr < 0 ? null : coreCLRFoundAssembly; + return hr; + } + + // called by vm + internal virtual int BindUsingPEImage(nint pPEImage, bool excludeAppPaths, out BinderAssembly? assembly) + { + assembly = null; + int hr; + + try + { + BinderAssembly? coreCLRFoundAssembly; + BinderAssemblyName assemblyName = new BinderAssemblyName(pPEImage); + + // Validate architecture + if (!AssemblyBinderCommon.IsValidArchitecture(assemblyName.ProcessorArchitecture)) + return AssemblyBinderCommon.CLR_E_BIND_ARCHITECTURE_MISMATCH; + + // Disallow attempt to bind to the core library. Aside from that, + // the LoadContext can load any assembly (even if it was in a different LoadContext like TPA). + if (assemblyName.IsCoreLib) + return HResults.E_FILENOTFOUND; + + hr = AssemblyBinderCommon.BindUsingPEImage(this, assemblyName, pPEImage, excludeAppPaths, out coreCLRFoundAssembly); + if (hr == HResults.S_OK) + { + Debug.Assert(coreCLRFoundAssembly != null); + coreCLRFoundAssembly.Binder = _nativeAssemblyLoadContext; + assembly = coreCLRFoundAssembly; + } + } + catch (Exception ex) + { + return ex.HResult; + } + + return hr; + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.DefaultBinder.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.DefaultBinder.cs new file mode 100644 index 0000000000000..e1c737c70b396 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.DefaultBinder.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Runtime.Loader +{ + public partial class AssemblyLoadContext + { + // called by vm + private static BinderAssembly InitializeDefault(IntPtr pCoreLibAssembly) + { + // ensure to touch Default to make it initialized + Default.AddLoadedAssembly(pCoreLibAssembly); + return new BinderAssembly(Assembly_GetPEImage(pCoreLibAssembly), true) { Binder = Default._nativeAssemblyLoadContext }; + } + + // called by VM + private static unsafe void SetupBindingPaths(char* trustedPlatformAssemblies, char* platformResourceRoots, char* appPaths) + { + Default.AppContext.SetupBindingPaths(new string(trustedPlatformAssemblies), new string(platformResourceRoots), new string(appPaths), acquireLock: true); + } + + private protected IntPtr GetNativeAssemblyLoadContext() => _nativeAssemblyLoadContext; + } + + internal partial class DefaultAssemblyLoadContext + { + // Helper functions + private int BindAssemblyByNameWorker(BinderAssemblyName assemblyName, out BinderAssembly? coreCLRFoundAssembly, bool excludeAppPaths) + { + // CoreLib should be bound using BindToSystem + Debug.Assert(!assemblyName.IsCoreLib); + + int hr = AssemblyBinderCommon.BindAssembly(this, assemblyName, excludeAppPaths, out coreCLRFoundAssembly); + + if (hr >= 0) + { + Debug.Assert(coreCLRFoundAssembly != null); + coreCLRFoundAssembly.Binder = GetNativeAssemblyLoadContext(); + } + + return hr; + } + + internal override int BindUsingAssemblyName(BinderAssemblyName assemblyName, out BinderAssembly? assembly) + { + assembly = null; + + int hr = BindAssemblyByNameWorker(assemblyName, out BinderAssembly? coreCLRFoundAssembly, excludeAppPaths: false); + + if (hr is HResults.E_FILENOTFOUND or AssemblyBinderCommon.FUSION_E_APP_DOMAIN_LOCKED or HResults.FUSION_E_REF_DEF_MISMATCH) + { + // If we are here, one of the following is possible: + // + // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR + // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def + // mismatch (either due to version difference or strong-name difference). + // + // Attempt to resolve the assembly via managed ALC instance. This can either fail the bind or return reference to an existing + // assembly that has been loaded + + // For satellite assemblies, the managed ALC has additional resolution logic (defined by the runtime) which + // should be run even if the managed default ALC has not yet been used. (For non-satellite assemblies, any + // additional logic comes through a user-defined event handler which would have initialized the managed ALC, + // so if the managed ALC is not set yet, there is no additional logic to run) + + // The logic was folded from native ALC. We are the managed ALC now and no additional initialization is required. + + hr = BindUsingHostAssemblyResolver(assemblyName, null, this, out coreCLRFoundAssembly); + + if (hr >= 0) + { + // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance. + // In such a case, we will not overwrite the binding context (which would be wrong since it would not + // be present in the cache of the current binding context). + Debug.Assert(coreCLRFoundAssembly != null); + if (coreCLRFoundAssembly.Binder == IntPtr.Zero) + coreCLRFoundAssembly.Binder = GetNativeAssemblyLoadContext(); + } + } + + if (hr >= 0) + assembly = coreCLRFoundAssembly; + + return hr; + } + + internal override int BindUsingPEImage(nint pPEImage, bool excludeAppPaths, out BinderAssembly? assembly) + { + assembly = null; + int hr; + + try + { + BinderAssembly? coreCLRFoundAssembly; + BinderAssemblyName assemblyName = new BinderAssemblyName(pPEImage); + + // Validate architecture + if (!AssemblyBinderCommon.IsValidArchitecture(assemblyName.ProcessorArchitecture)) + return AssemblyBinderCommon.CLR_E_BIND_ARCHITECTURE_MISMATCH; + + // Easy out for CoreLib + if (assemblyName.IsCoreLib) + return HResults.E_FILENOTFOUND; + + { + // Ensure we are not being asked to bind to a TPA assembly + + Debug.Assert(AppContext.TrustedPlatformAssemblyMap != null); + + if (AppContext.TrustedPlatformAssemblyMap.ContainsKey(assemblyName.SimpleName)) + { + // The simple name of the assembly being requested to be bound was found in the TPA list. + // Now, perform the actual bind to see if the assembly was really in the TPA assembly list or not. + hr = BindAssemblyByNameWorker(assemblyName, out coreCLRFoundAssembly, excludeAppPaths: true); + if (hr >= 0) + { + Debug.Assert(coreCLRFoundAssembly != null); + if (coreCLRFoundAssembly.IsInTPA) + { + assembly = coreCLRFoundAssembly; + return hr; + } + } + } + } + + hr = AssemblyBinderCommon.BindUsingPEImage(this, assemblyName, pPEImage, excludeAppPaths, out coreCLRFoundAssembly); + if (hr == HResults.S_OK) + { + Debug.Assert(coreCLRFoundAssembly != null); + coreCLRFoundAssembly.Binder = GetNativeAssemblyLoadContext(); + assembly = coreCLRFoundAssembly; + } + } + catch (Exception ex) + { + return ex.HResult; + } + + return hr; + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyName.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyName.cs new file mode 100644 index 0000000000000..c04c1e5733f88 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyName.cs @@ -0,0 +1,379 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.IO; +using System.Numerics; +using System.Reflection; + +namespace System.Runtime.Loader +{ + internal unsafe struct AssemblyMetaDataInternal + { + public ushort usMajorVersion; // Major Version. + public ushort usMinorVersion; // Minor Version. + public ushort usBuildNumber; // Build Number. + public ushort usRevisionNumber; // Revision Number. + public byte* szLocale; // Locale. + } + + internal enum CorAssemblyFlags + { + afPublicKey = 0x0001, // The assembly ref holds the full (unhashed) public key. + + afPA_None = 0x0000, // Processor Architecture unspecified + afPA_MSIL = 0x0010, // Processor Architecture: neutral (PE32) + afPA_x86 = 0x0020, // Processor Architecture: x86 (PE32) + afPA_IA64 = 0x0030, // Processor Architecture: Itanium (PE32+) + afPA_AMD64 = 0x0040, // Processor Architecture: AMD X64 (PE32+) + afPA_ARM = 0x0050, // Processor Architecture: ARM (PE32) + afPA_ARM64 = 0x0060, // Processor Architecture: ARM64 (PE32+) + afPA_NoPlatform = 0x0070, // applies to any platform but cannot run on any (e.g. reference assembly), should not have "specified" set + afPA_Specified = 0x0080, // Propagate PA flags to AssemblyRef record + afPA_Mask = 0x0070, // Bits describing the processor architecture + afPA_FullMask = 0x00F0, // Bits describing the PA incl. Specified + afPA_Shift = 0x0004, // NOT A FLAG, shift count in PA flags <--> index conversion + + afEnableJITcompileTracking = 0x8000, // From "DebuggableAttribute". + afDisableJITcompileOptimizer = 0x4000, // From "DebuggableAttribute". + afDebuggableAttributeMask = 0xc000, + + afRetargetable = 0x0100, // The assembly can be retargeted (at runtime) to an + // assembly from a different publisher. + + afContentType_Default = 0x0000, + afContentType_WindowsRuntime = 0x0200, + afContentType_Mask = 0x0E00, // Bits describing ContentType + } + + internal enum AssemblyNameIncludeFlags + { + INCLUDE_DEFAULT = 0x00, + INCLUDE_VERSION = 0x01, + INCLUDE_ARCHITECTURE = 0x02, + INCLUDE_RETARGETABLE = 0x04, + INCLUDE_CONTENT_TYPE = 0x08, + INCLUDE_PUBLIC_KEY_TOKEN = 0x10, + EXCLUDE_CULTURE = 0x20, + INCLUDE_ALL = INCLUDE_VERSION + | INCLUDE_ARCHITECTURE + | INCLUDE_RETARGETABLE + | INCLUDE_CONTENT_TYPE + | INCLUDE_PUBLIC_KEY_TOKEN, + } + + internal sealed unsafe class BinderAssemblyName : AssemblyIdentity, IEquatable + { + public bool IsDefinition; + + public BinderAssemblyName(IntPtr pPEImage) + { + int* dwPAFlags = stackalloc int[2]; + IntPtr pIMetaDataAssemblyImport = AssemblyBinderCommon.BinderAcquireImport(pPEImage, dwPAFlags); + var scope = new MetadataImport(pIMetaDataAssemblyImport); + + ProcessorArchitecture = AssemblyBinderCommon.TranslatePEToArchitectureType(dwPAFlags); + + // Get the assembly token + uint mda = scope.GetAssemblyFromScope(); + + AssemblyMetaDataInternal amd = default; + + // Get name and metadata + scope.GetAssemblyProps( + mda, + out byte* pvPublicKeyToken, + out uint dwPublicKeyToken, + out uint dwHashAlgId, + out string? assemblyName, + &amd, + out uint flags); + + CorAssemblyFlags dwRefOrDefFlags = (CorAssemblyFlags)flags; + + { + string culture = new MdUtf8String(amd.szLocale).ToString(); + int index = culture.IndexOf(';'); + if (index != -1) + { + culture = culture[..index]; + } + + CultureOrLanguage = culture; + IdentityFlags |= AssemblyIdentityFlags.IDENTITY_FLAG_CULTURE; + } + + { + const int MAX_PATH_FNAME = 260; + if (assemblyName.Length >= MAX_PATH_FNAME) + { + throw new FileLoadException + { + HResult = HResults.FUSION_E_INVALID_NAME + }; + } + + SimpleName = assemblyName; + IdentityFlags |= AssemblyIdentityFlags.IDENTITY_FLAG_SIMPLE_NAME; + } + + // See if the assembly[def] is retargetable (ie, for a generic assembly). + if ((dwRefOrDefFlags & CorAssemblyFlags.afRetargetable) != 0) + { + IdentityFlags |= AssemblyIdentityFlags.IDENTITY_FLAG_RETARGETABLE; + } + + // Set ContentType + if ((dwRefOrDefFlags & CorAssemblyFlags.afContentType_Mask) == CorAssemblyFlags.afContentType_Default) + { + ContentType = AssemblyContentType.Default; + } + else + { + // We no longer support WindowsRuntime assembly. + throw new FileLoadException + { + HResult = HResults.FUSION_E_INVALID_NAME + }; + } + + // Set the assembly version + { + Version = new AssemblyVersion + { + Major = amd.usMajorVersion, + Minor = amd.usMinorVersion, + Build = amd.usBuildNumber, + Revision = amd.usRevisionNumber + }; + + IdentityFlags |= AssemblyIdentityFlags.IDENTITY_FLAG_VERSION; + } + + // Set public key and/or public key token (if we have it) + if (dwPublicKeyToken != 0 && pvPublicKeyToken != null) + { + if ((dwRefOrDefFlags & CorAssemblyFlags.afPublicKey) != 0) + { + byte[] publicKeyToken = AssemblyNameHelpers.ComputePublicKeyToken(new ReadOnlySpan(pvPublicKeyToken, (int)dwPublicKeyToken)); + + PublicKeyOrTokenBLOB = publicKeyToken; + } + else + { + PublicKeyOrTokenBLOB = new ReadOnlySpan(pvPublicKeyToken, (int)dwPublicKeyToken).ToArray(); + } + + IdentityFlags |= AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY_TOKEN; + } + } + + public unsafe BinderAssemblyName(AssemblyNameData* data) + { + AssemblyIdentityFlags flags = data->IdentityFlags; + SimpleName = new MdUtf8String(data->Name).ToString(); + Version = new AssemblyVersion + { + Major = data->MajorVersion, + Minor = data->MinorVersion, + Build = data->BuildNumber, + Revision = data->RevisionNumber + }; + CultureOrLanguage = new MdUtf8String(data->Culture).ToString(); + + PublicKeyOrTokenBLOB = new ReadOnlySpan(data->PublicKeyOrToken, data->PublicKeyOrTokenLength).ToArray(); + if ((flags & AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY) != 0) + { + // Convert public key to token + + byte[]? publicKeyToken = AssemblyNameHelpers.ComputePublicKeyToken(PublicKeyOrTokenBLOB); + Debug.Assert(publicKeyToken != null); + + PublicKeyOrTokenBLOB = publicKeyToken; + flags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY; + flags |= AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY_TOKEN; + } + + ProcessorArchitecture = data->ProcessorArchitecture; + ContentType = data->ContentType; + IdentityFlags |= flags; + } + + // TODO: Is this simple comparison enough? + public bool IsCoreLib => string.EqualsOrdinalIgnoreCase(SimpleName, CoreLib.Name); + + public bool IsNeutralCulture => string.IsNullOrEmpty(CultureOrLanguage) || string.EqualsOrdinalIgnoreCase(CultureOrLanguage, NeutralCulture); + + public override int GetHashCode() => GetHashCode(AssemblyNameIncludeFlags.INCLUDE_DEFAULT); + + public int GetHashCode(AssemblyNameIncludeFlags dwIncludeFlags) + { + uint dwHash = 0; + AssemblyIdentityFlags dwUseIdentityFlags = IdentityFlags; + + // Prune unwanted name parts + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_VERSION) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_VERSION; + } + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_ARCHITECTURE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_PROCESSOR_ARCHITECTURE; + } + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_RETARGETABLE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_RETARGETABLE; + } + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_CONTENT_TYPE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_CONTENT_TYPE; + } + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_PUBLIC_KEY_TOKEN) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY; + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY_TOKEN; + } + if ((dwIncludeFlags & AssemblyNameIncludeFlags.EXCLUDE_CULTURE) != 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_CULTURE; + } + + static uint HashBytes(ReadOnlySpan bytes) + { + // ported from coreclr/inc/utilcode.h + uint hash = 5831; + + foreach (byte b in bytes) + { + hash = ((hash << 5) + hash) ^ b; + } + + return hash; + } + + dwHash ^= (uint)SimpleName.GetNonRandomizedHashCodeOrdinalIgnoreCase(); + dwHash = BitOperations.RotateLeft(dwHash, 4); + + if ((dwUseIdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY) != 0 || + (dwUseIdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY_TOKEN) != 0) + { + dwHash ^= HashBytes(PublicKeyOrTokenBLOB); + dwHash = BitOperations.RotateLeft(dwHash, 4); + } + + if ((dwUseIdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_VERSION) != 0) + { + + dwHash ^= (uint)Version.Major; + dwHash = BitOperations.RotateLeft(dwHash, 8); + dwHash ^= (uint)Version.Minor; + dwHash = BitOperations.RotateLeft(dwHash, 8); + dwHash ^= (uint)Version.Build; + dwHash = BitOperations.RotateLeft(dwHash, 8); + dwHash ^= (uint)Version.Revision; + dwHash = BitOperations.RotateLeft(dwHash, 8); + } + + if ((dwUseIdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_CULTURE) != 0) + { + dwHash ^= (uint)NormalizedCulture.GetNonRandomizedHashCodeOrdinalIgnoreCase(); + dwHash = BitOperations.RotateLeft(dwHash, 4); + } + + if ((dwUseIdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_RETARGETABLE) != 0) + { + dwHash ^= 1; + dwHash = BitOperations.RotateLeft(dwHash, 4); + } + + if ((dwUseIdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_PROCESSOR_ARCHITECTURE) != 0) + { + dwHash ^= (uint)ProcessorArchitecture; + dwHash = BitOperations.RotateLeft(dwHash, 4); + } + + if ((dwUseIdentityFlags & AssemblyIdentityFlags.IDENTITY_FLAG_CONTENT_TYPE) != 0) + { + dwHash ^= (uint)ContentType; + dwHash = BitOperations.RotateLeft(dwHash, 4); + } + + return (int)dwHash; + } + + public override bool Equals(object? obj) => obj is BinderAssemblyName other && Equals(other); + + public bool Equals(BinderAssemblyName? other) => Equals(other, AssemblyNameIncludeFlags.INCLUDE_DEFAULT); + + public bool Equals(AssemblyIdentity? other, AssemblyNameIncludeFlags dwIncludeFlags) + { + if (other is null) + return false; + + bool fEquals = false; + + if (ContentType == AssemblyContentType.WindowsRuntime) + { // Assembly is meaningless for WinRT, all assemblies form one joint type namespace + return ContentType == other.ContentType; + } + + if (string.Equals(SimpleName, other.SimpleName, StringComparison.OrdinalIgnoreCase) && + ContentType == other.ContentType) + { + fEquals = true; + + if ((dwIncludeFlags & AssemblyNameIncludeFlags.EXCLUDE_CULTURE) == 0) + { + fEquals = string.Equals(NormalizedCulture, other.NormalizedCulture, StringComparison.OrdinalIgnoreCase); + } + + if (fEquals && (dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_PUBLIC_KEY_TOKEN) != 0) + { + fEquals = PublicKeyOrTokenBLOB.AsSpan().SequenceEqual(other.PublicKeyOrTokenBLOB); + } + + if (fEquals && ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_ARCHITECTURE) != 0)) + { + fEquals = ProcessorArchitecture == other.ProcessorArchitecture; + } + + if (fEquals && ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_VERSION) != 0)) + { + fEquals = Version.Equals(other.Version); + } + + if (fEquals && ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_RETARGETABLE) != 0)) + { + fEquals = IsRetargetable == other.IsRetargetable; + } + } + + return fEquals; + } + + public string GetDisplayName(AssemblyNameIncludeFlags dwIncludeFlags) + { + AssemblyIdentityFlags dwUseIdentityFlags = IdentityFlags; + + // Prune unwanted name parts + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_VERSION) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_VERSION; + } + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_ARCHITECTURE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_PROCESSOR_ARCHITECTURE; + } + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_RETARGETABLE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_RETARGETABLE; + } + if ((dwIncludeFlags & AssemblyNameIncludeFlags.INCLUDE_CONTENT_TYPE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentityFlags.IDENTITY_FLAG_CONTENT_TYPE; + } + + return TextualIdentityParser.ToString(this, dwUseIdentityFlags); + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyVersion.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyVersion.cs new file mode 100644 index 0000000000000..94fb43c299055 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyVersion.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.Loader +{ + internal struct AssemblyVersion : IEquatable + { + public int Major; + public int Minor; + public int Build; + public int Revision; + + public const int Unspecified = -1; + + public AssemblyVersion() + { + Major = Unspecified; + Minor = Unspecified; + Build = Unspecified; + Revision = Unspecified; + } + + public bool HasMajor => Major != Unspecified; + + public bool HasMinor => Minor != Unspecified; + + public bool HasBuild => Build != Unspecified; + + public bool HasRevision => Revision != Unspecified; + + public bool Equals(AssemblyVersion other) => + Major == other.Major && + Minor == other.Minor && + Build == other.Build && + Revision == other.Revision; + + public override bool Equals(object? obj) + => obj is AssemblyVersion other && Equals(other); + + public override int GetHashCode() => HashCode.Combine(Major, Minor, Build, Revision); + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/BindResult.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/BindResult.cs new file mode 100644 index 0000000000000..27dffcffd617f --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/BindResult.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.Loader +{ + internal struct BindResult + { + public struct AttemptResult + { + public BinderAssembly? Assembly; + public int HResult; + public bool Attempted; + } + + public bool IsContextBound { get; private set; } + public BinderAssembly? Assembly { get; private set; } + + private AttemptResult _inContextAttempt; + private AttemptResult _applicationAssembliesResult; + + public void SetAttemptResult(int hResult, BinderAssembly? assembly, bool isInContext = false) + { + ref AttemptResult result = ref (isInContext ? ref _inContextAttempt : ref _applicationAssembliesResult); + result.HResult = hResult; + result.Assembly = assembly; + result.Attempted = true; + } + + public readonly AttemptResult GetAttemptResult(bool isInContext = false) => isInContext ? _inContextAttempt : _applicationAssembliesResult; + + public void SetResult(BinderAssembly assembly, bool isInContext = false) + { + Assembly = assembly; + IsContextBound = isInContext; + } + + public void SetNoResult() + { + Assembly = null; + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/TextualIdentityParser.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/TextualIdentityParser.cs new file mode 100644 index 0000000000000..4903bfd48e992 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/TextualIdentityParser.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Reflection; +using System.Text; + +namespace System.Runtime.Loader +{ + internal static class TextualIdentityParser + { + public static string ToString(AssemblyIdentity pAssemblyIdentity, AssemblyIdentityFlags includeFlags) + { + if (string.IsNullOrEmpty(pAssemblyIdentity.SimpleName)) + { + return string.Empty; + } + + ValueStringBuilder textualIdentity = new ValueStringBuilder(256); + + AppendStringEscaped(ref textualIdentity, pAssemblyIdentity.SimpleName); + + if ((includeFlags & AssemblyIdentityFlags.IDENTITY_FLAG_VERSION) != 0) + { + AssemblyVersion version = pAssemblyIdentity.Version; + textualIdentity.Append(", Version="); + textualIdentity.AppendSpanFormattable(version.Major); + textualIdentity.Append('.'); + textualIdentity.AppendSpanFormattable(version.Minor); + textualIdentity.Append('.'); + textualIdentity.AppendSpanFormattable(version.Build); + textualIdentity.Append('.'); + textualIdentity.AppendSpanFormattable(version.Revision); + } + + if ((includeFlags & AssemblyIdentityFlags.IDENTITY_FLAG_CULTURE) != 0) + { + textualIdentity.Append(", Culture="); + AppendStringEscaped(ref textualIdentity, pAssemblyIdentity.NormalizedCulture); + } + + if ((includeFlags & AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY) != 0) + { + textualIdentity.Append(", PublicKey="); + AppendBinary(ref textualIdentity, pAssemblyIdentity.PublicKeyOrTokenBLOB); + } + else if ((includeFlags & AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY_TOKEN) != 0) + { + textualIdentity.Append(", PublicKeyToken="); + AppendBinary(ref textualIdentity, pAssemblyIdentity.PublicKeyOrTokenBLOB); + } + else if ((includeFlags & AssemblyIdentityFlags.IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL) != 0) + { + textualIdentity.Append(", PublicKeyToken=null"); + } + + if ((includeFlags & AssemblyIdentityFlags.IDENTITY_FLAG_PROCESSOR_ARCHITECTURE) != 0) + { + textualIdentity.Append(", processorArchitecture="); + textualIdentity.Append(pAssemblyIdentity.ProcessorArchitecture switch + { + PEKind.I386 => "x86", + PEKind.IA64 => "IA64", + PEKind.AMD64 => "AMD64", + PEKind.ARM => "ARM", + PEKind.MSIL => "MSIL", + _ => throw new UnreachableException() + }); + } + + if ((includeFlags & AssemblyIdentityFlags.IDENTITY_FLAG_RETARGETABLE) != 0) + { + textualIdentity.Append(", Retargetable=Yes"); + } + + if ((includeFlags & AssemblyIdentityFlags.IDENTITY_FLAG_CONTENT_TYPE) != 0) + { + textualIdentity.Append($", ContentType={nameof(AssemblyContentType.WindowsRuntime)}"); + } + + return textualIdentity.ToString(); + } + + private static void AppendStringEscaped(ref ValueStringBuilder vsb, string input) + { + Debug.Assert(input.Length > 0); + + bool fNeedQuotes = (input[0] is '\n' or '\r' or ' ' or '\t') + || (input[^1] is '\n' or '\r' or ' ' or '\t'); + char quoteCharacter = '\"'; + + ValueStringBuilder tmpString = new ValueStringBuilder(stackalloc char[256]); + + // Fusion textual identity compat: escape all non-quote characters even if quoted + foreach (char ch in input) + { + switch (ch) + { + case '\"': + case '\'': + if (fNeedQuotes && (quoteCharacter != ch)) + { + tmpString.Append(ch); + } + else if (!fNeedQuotes) + { + fNeedQuotes = true; + quoteCharacter = (ch == '\"') ? '\'' : '\"'; + tmpString.Append(ch); + } + else + { + tmpString.Append('\\'); + tmpString.Append(ch); + } + break; + + case '=': + case ',': + case '\\': + tmpString.Append('\\'); + tmpString.Append(ch); + break; + + case (char)9: + tmpString.Append("\\t"); + break; + + case (char)10: + tmpString.Append("\\n"); + break; + + case (char)13: + tmpString.Append("\\r"); + break; + + default: + tmpString.Append(ch); + break; + } + } + + if (fNeedQuotes) + { + vsb.Append(quoteCharacter); + vsb.Append(tmpString.AsSpan()); + vsb.Append(quoteCharacter); + } + else + { + vsb.Append(tmpString.AsSpan()); + } + + tmpString.Dispose(); + } + + private static void AppendBinary(ref ValueStringBuilder vsb, ReadOnlySpan data) + { + vsb.EnsureCapacity(vsb.Length + data.Length * 2); + HexConverter.EncodeToUtf16(data, vsb.RawChars[vsb.Length..], HexConverter.Casing.Lower); + vsb.Length += data.Length * 2; + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/Tracing/ResolutionAttemptedOperation.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/Tracing/ResolutionAttemptedOperation.cs new file mode 100644 index 0000000000000..bcecf1199aca2 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/Tracing/ResolutionAttemptedOperation.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Runtime.Loader.Tracing +{ + // This must match the ResolutionAttemptedStage value map in ClrEtwAll.man + internal enum Stage : ushort + { + FindInLoadContext = 0, + AssemblyLoadContextLoad = 1, + ApplicationAssemblies = 2, + DefaultAssemblyLoadContextFallback = 3, + ResolveSatelliteAssembly = 4, + AssemblyLoadContextResolvingEvent = 5, + AppDomainAssemblyResolveEvent = 6, + NotYetStarted = 0xffff, // Used as flag to not fire event; not present in value map + }; + + // This must match the ResolutionAttemptedResult value map in ClrEtwAll.man + internal enum Result : ushort + { + Success = 0, + AssemblyNotFound = 1, + IncompatibleVersion = 2, + MismatchedAssemblyName = 3, + Failure = 4, + Exception = 5, + }; + + // An object of this class manages firing events for all the stages during a binder resolving + // attempt operation. It has minimal cost if tracing for this event is disabled. + // + // This class should be declared in the stack. As information is determined by each stage + // (e.g. an AssemblySpec is initialized), the appropriate Set*() method should be called. All + // pointers held by an object of this class must either be a nullptr, or point to a valid + // object during the time it is in scope. + // + // As the resolution progresses to different stages, the GoToStage() method should be called. + // Calling it will fire an event for the previous stage with whatever context the class had + // at that point; it is assumed that if GoToStage() is called, the previous stage failed + // (the HRESULT is read by the dtor to assess success). + // + // It holds a reference to a HRESULT (that must be live during the lifetime of this object), + // which is used to determine the success or failure of a stage either at the moment this + // class is destructed (e.g. last stage), or when moving from one stage to another. (This + // is especially useful if the HRESULT is captured from an exception handler.) + internal ref struct ResolutionAttemptedOperation + { + private ref readonly int _hr; + private Stage _stage; + private bool _tracingEnabled; + private BinderAssemblyName? _assemblyNameObject; + private BinderAssembly? _foundAssembly; + private string _assemblyName = string.Empty; + private string _assemblyLoadContextName = string.Empty; + private string? _exceptionMessage; + + public ResolutionAttemptedOperation(BinderAssemblyName? assemblyName, AssemblyLoadContext binder, ref int hResult) + { + _hr = ref hResult; + _stage = Stage.NotYetStarted; + _tracingEnabled = AssemblyLoadContext.IsTracingEnabled(); + _assemblyNameObject = assemblyName; + + if (!_tracingEnabled) + return; + + // When binding the main assembly (by code base instead of name), the assembly name will be null. In this special case, we just + // leave the assembly name empty. + if (_assemblyNameObject != null) + _assemblyName = _assemblyNameObject.GetDisplayName(AssemblyNameIncludeFlags.INCLUDE_VERSION | AssemblyNameIncludeFlags.INCLUDE_PUBLIC_KEY_TOKEN); + + _assemblyLoadContextName = (binder == AssemblyLoadContext.Default ? "Default" : binder.ToString()); + } + + public void TraceBindResult(in BindResult bindResult, bool mvidMismatch = false) + { + if (!_tracingEnabled) + return; + + string? errorMsg = null; + + // Use the error message that would be reported in the file load exception + if (mvidMismatch) + { + errorMsg = SR.Format(SR.EE_FileLoad_Error_Generic, _assemblyName, SR.Host_AssemblyResolver_AssemblyAlreadyLoadedInContext); + } + + BindResult.AttemptResult inContextAttempt = bindResult.GetAttemptResult(isInContext: true); + BindResult.AttemptResult appAssembliesAttempt = bindResult.GetAttemptResult(isInContext: false); + + if (inContextAttempt.Attempted) + { + // If there the attempt HR represents a success, but the tracked HR represents a failure (e.g. from further validation), report the failed HR + bool isLastAttempt = !appAssembliesAttempt.Attempted; + TraceStage(Stage.FindInLoadContext, + isLastAttempt && (_hr < 0) && (inContextAttempt.HResult >= 0) ? _hr : inContextAttempt.HResult, + inContextAttempt.Assembly, + mvidMismatch && isLastAttempt ? errorMsg : null); + } + + if (appAssembliesAttempt.Attempted) + { + TraceStage(Stage.ApplicationAssemblies, + (_hr < 0) && (appAssembliesAttempt.HResult >= 0) ? _hr : appAssembliesAttempt.HResult, + appAssembliesAttempt.Assembly, + mvidMismatch ? errorMsg : null); + } + } + + public void SetFoundAssembly(BinderAssembly assembly) => _foundAssembly = assembly; + + public void GoToStage(Stage stage) + { + Debug.Assert(stage != _stage); + Debug.Assert(stage != Stage.NotYetStarted); + + if (!_tracingEnabled) + return; + + // Going to a different stage should only happen if the current + // stage failed (or if the binding process wasn't yet started). + // Firing the event at this point not only helps timing each binding + // stage, but avoids keeping track of which stages were reached to + // resolve the assembly. + TraceStage(_stage, _hr, _foundAssembly); + _stage = stage; + _exceptionMessage = string.Empty; + } + + public void SetException(Exception ex) + { + if (!_tracingEnabled) + return; + + _exceptionMessage = ex.Message; + } + + // FEATURE_EVENT_TRACE + public void Dispose() + { + if (!_tracingEnabled) + return; + + TraceStage(_stage, _hr, _foundAssembly); + } + + private unsafe void TraceStage(Stage stage, int hResult, BinderAssembly? resultAssembly, string? customError = null) + { + if (!_tracingEnabled || stage == Stage.NotYetStarted) + return; + + string resultAssemblyName = string.Empty; + string resultAssemblyPath = string.Empty; + + if (resultAssembly != null) + { + resultAssemblyName = resultAssembly.AssemblyName.GetDisplayName(AssemblyNameIncludeFlags.INCLUDE_VERSION | AssemblyNameIncludeFlags.INCLUDE_PUBLIC_KEY_TOKEN); + resultAssemblyPath = new string(AssemblyBinderCommon.PEImage_GetPath(resultAssembly.PEImage)); + } + + Result result; + string errorMsg; + if (customError != null) + { + errorMsg = customError; + result = Result.Failure; + } + else if (!string.IsNullOrEmpty(_exceptionMessage)) + { + errorMsg = _exceptionMessage; + result = Result.Exception; + } + else + { + switch (hResult) + { + case HResults.S_FALSE: + case HResults.COR_E_FILENOTFOUND: + result = Result.AssemblyNotFound; + errorMsg = "Could not locate assembly"; + break; + + case AssemblyBinderCommon.FUSION_E_APP_DOMAIN_LOCKED: + result = Result.IncompatibleVersion; + errorMsg = "Requested version"; + + if (_assemblyNameObject != null) + { + AssemblyVersion version = _assemblyNameObject.Version; + errorMsg += $" {version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; + } + + if (resultAssembly != null) + { + AssemblyVersion version = resultAssembly.AssemblyName.Version; + errorMsg += $" is incompatible with found version {version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; + } + + break; + + case HResults.FUSION_E_REF_DEF_MISMATCH: + result = Result.MismatchedAssemblyName; + errorMsg = $"Requested assembly name ':{_assemblyName}' does not match found assembly name"; + + if (resultAssembly != null) + { + errorMsg += $" '{resultAssemblyName}'"; + } + + break; + + case >= 0: // SUCCEEDED(hr) + result = Result.Success; + Debug.Assert(resultAssembly != null); + errorMsg = string.Empty; + break; + + default: + result = Result.Failure; + errorMsg = $"Resolution failed with HRESULT ({_hr:x8})"; + break; + } + } + + AssemblyLoadContext.TraceResolutionAttempted( + _assemblyName, + stage, + _assemblyLoadContextName, + result, + resultAssemblyName, + resultAssemblyPath, + errorMsg); + } + } +} diff --git a/src/coreclr/binder/CMakeLists.txt b/src/coreclr/binder/CMakeLists.txt index f52dcb6bb44ef..0856a1b3985a0 100644 --- a/src/coreclr/binder/CMakeLists.txt +++ b/src/coreclr/binder/CMakeLists.txt @@ -4,24 +4,15 @@ include_directories(BEFORE "../vm") include_directories(BEFORE "inc") set(BINDER_COMMON_SOURCES - applicationcontext.cpp - assembly.cpp assemblybindercommon.cpp assemblyname.cpp bindertracing.cpp - defaultassemblybinder.cpp - failurecache.cpp textualidentityparser.cpp utils.cpp ) set(BINDER_COMMON_HEADERS - inc/applicationcontext.hpp - inc/applicationcontext.inl - inc/assembly.hpp - inc/assembly.inl inc/assemblybindercommon.hpp - inc/assemblyhashtraits.hpp inc/assemblyidentity.hpp inc/assemblyname.hpp inc/assemblyname.inl @@ -29,11 +20,7 @@ set(BINDER_COMMON_HEADERS inc/assemblyversion.inl inc/bindertypes.hpp inc/bindertracing.h - inc/bindresult.hpp - inc/bindresult.inl inc/defaultassemblybinder.h - inc/failurecache.hpp - inc/failurecachehashtraits.hpp inc/textualidentityparser.hpp inc/utils.hpp ) diff --git a/src/coreclr/binder/applicationcontext.cpp b/src/coreclr/binder/applicationcontext.cpp deleted file mode 100644 index e41b504c8feb6..0000000000000 --- a/src/coreclr/binder/applicationcontext.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// ApplicationContext.cpp -// - - -// -// Implements the ApplicationContext class -// -// ============================================================ - -#include "applicationcontext.hpp" -#include "assemblyhashtraits.hpp" -#include "stringarraylist.h" -#include "failurecache.hpp" -#include "utils.hpp" -#include "ex.h" -#include "clr/fs/path.h" -using namespace clr::fs; - -namespace BINDER_SPACE -{ - ApplicationContext::ApplicationContext() - { - m_pExecutionContext = NULL; - m_pFailureCache = NULL; - m_contextCS = NULL; - m_pTrustedPlatformAssemblyMap = nullptr; - } - - ApplicationContext::~ApplicationContext() - { - SAFE_DELETE(m_pExecutionContext); - SAFE_DELETE(m_pFailureCache); - - if (m_contextCS != NULL) - { - ClrDeleteCriticalSection(m_contextCS); - } - - if (m_pTrustedPlatformAssemblyMap != nullptr) - { - delete m_pTrustedPlatformAssemblyMap; - } - } - - HRESULT ApplicationContext::Init() - { - HRESULT hr = S_OK; - - NewHolder pExecutionContext; - - FailureCache *pFailureCache = NULL; - - // Allocate context objects - SAFE_NEW(pExecutionContext, ExecutionContext); - - SAFE_NEW(pFailureCache, FailureCache); - - m_contextCS = ClrCreateCriticalSection( - CrstFusionAppCtx, - CRST_REENTRANCY); - if (!m_contextCS) - { - SAFE_DELETE(pFailureCache); - hr = E_OUTOFMEMORY; - } - else - { - m_pExecutionContext = pExecutionContext.Extract(); - - m_pFailureCache = pFailureCache; - } - - Exit: - return hr; - } - - HRESULT ApplicationContext::SetupBindingPaths(SString &sTrustedPlatformAssemblies, - SString &sPlatformResourceRoots, - SString &sAppPaths, - BOOL fAcquireLock) - { - HRESULT hr = S_OK; - - CRITSEC_Holder contextLock(fAcquireLock ? GetCriticalSectionCookie() : NULL); - if (m_pTrustedPlatformAssemblyMap != nullptr) - { - GO_WITH_HRESULT(S_OK); - } - - // - // Parse TrustedPlatformAssemblies - // - m_pTrustedPlatformAssemblyMap = new SimpleNameToFileNameMap(); - - sTrustedPlatformAssemblies.Normalize(); - - for (SString::Iterator i = sTrustedPlatformAssemblies.Begin(); i != sTrustedPlatformAssemblies.End(); ) - { - SString fileName; - SString simpleName; - bool isNativeImage = false; - HRESULT pathResult = S_OK; - IF_FAIL_GO(pathResult = GetNextTPAPath(sTrustedPlatformAssemblies, i, /*dllOnly*/ false, fileName, simpleName, isNativeImage)); - if (pathResult == S_FALSE) - { - break; - } - - const SimpleNameToFileNameMapEntry *pExistingEntry = m_pTrustedPlatformAssemblyMap->LookupPtr(simpleName.GetUnicode()); - - if (pExistingEntry != nullptr) - { - // - // We want to store only the first entry matching a simple name we encounter. - // The exception is if we first store an IL reference and later in the string - // we encounter a native image. Since we don't touch IL in the presence of - // native images, we replace the IL entry with the NI. - // - if ((pExistingEntry->m_wszILFileName != nullptr && !isNativeImage) || - (pExistingEntry->m_wszNIFileName != nullptr && isNativeImage)) - { - continue; - } - } - - LPWSTR wszSimpleName = nullptr; - if (pExistingEntry == nullptr) - { - wszSimpleName = new WCHAR[simpleName.GetCount() + 1]; - if (wszSimpleName == nullptr) - { - GO_WITH_HRESULT(E_OUTOFMEMORY); - } - wcscpy_s(wszSimpleName, simpleName.GetCount() + 1, simpleName.GetUnicode()); - } - else - { - wszSimpleName = pExistingEntry->m_wszSimpleName; - } - - LPWSTR wszFileName = new WCHAR[fileName.GetCount() + 1]; - if (wszFileName == nullptr) - { - GO_WITH_HRESULT(E_OUTOFMEMORY); - } - wcscpy_s(wszFileName, fileName.GetCount() + 1, fileName.GetUnicode()); - - SimpleNameToFileNameMapEntry mapEntry; - mapEntry.m_wszSimpleName = wszSimpleName; - if (isNativeImage) - { - mapEntry.m_wszNIFileName = wszFileName; - mapEntry.m_wszILFileName = pExistingEntry == nullptr ? nullptr : pExistingEntry->m_wszILFileName; - } - else - { - mapEntry.m_wszILFileName = wszFileName; - mapEntry.m_wszNIFileName = pExistingEntry == nullptr ? nullptr : pExistingEntry->m_wszNIFileName; - } - - m_pTrustedPlatformAssemblyMap->AddOrReplace(mapEntry); - } - - // - // Parse PlatformResourceRoots - // - sPlatformResourceRoots.Normalize(); - for (SString::Iterator i = sPlatformResourceRoots.Begin(); i != sPlatformResourceRoots.End(); ) - { - SString pathName; - HRESULT pathResult = S_OK; - - IF_FAIL_GO(pathResult = GetNextPath(sPlatformResourceRoots, i, pathName)); - if (pathResult == S_FALSE) - { - break; - } - - if (Path::IsRelative(pathName)) - { - GO_WITH_HRESULT(E_INVALIDARG); - } - - m_platformResourceRoots.Append(pathName); - } - - // - // Parse AppPaths - // - sAppPaths.Normalize(); - for (SString::Iterator i = sAppPaths.Begin(); i != sAppPaths.End(); ) - { - SString pathName; - HRESULT pathResult = S_OK; - - IF_FAIL_GO(pathResult = GetNextPath(sAppPaths, i, pathName)); - if (pathResult == S_FALSE) - { - break; - } - - if (Path::IsRelative(pathName)) - { - GO_WITH_HRESULT(E_INVALIDARG); - } - - m_appPaths.Append(pathName); - } - - Exit: - return hr; - } - - bool ApplicationContext::IsTpaListProvided() - { - return m_pTrustedPlatformAssemblyMap != nullptr; - } -}; diff --git a/src/coreclr/binder/assembly.cpp b/src/coreclr/binder/assembly.cpp deleted file mode 100644 index 9348c7b67e4e8..0000000000000 --- a/src/coreclr/binder/assembly.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// Assembly.cpp -// - - -// -// Implements the Assembly class -// -// ============================================================ -#include "common.h" -#include "assembly.hpp" -#include "utils.hpp" -#include "assemblybindercommon.hpp" - -namespace BINDER_SPACE -{ - Assembly::Assembly() - { - m_cRef = 1; - m_pPEImage = NULL; - m_pAssemblyName = NULL; - m_isInTPA = false; - m_pBinder = NULL; - m_domainAssembly = NULL; - } - - Assembly::~Assembly() - { - SAFE_RELEASE(m_pPEImage); - SAFE_RELEASE(m_pAssemblyName); - } - - HRESULT Assembly::Init(PEImage *pPEImage, BOOL fIsInTPA) - { - HRESULT hr = S_OK; - - ReleaseHolder pAssemblyName; - SAFE_NEW(pAssemblyName, AssemblyName); - - // Get assembly name def from meta data import and store it for later refs access - IF_FAIL_GO(pAssemblyName->Init(pPEImage)); - pAssemblyName->SetIsDefinition(TRUE); - - // validate architecture - if (!AssemblyBinderCommon::IsValidArchitecture(pAssemblyName->GetArchitecture())) - { - // Assembly image can't be executed on this platform - IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); - } - - m_isInTPA = fIsInTPA; - - pPEImage->AddRef(); - m_pPEImage = pPEImage; - - // Now take ownership of assembly name - m_pAssemblyName = pAssemblyName.Extract(); - - Exit: - return hr; - } - - PEImage* Assembly::GetPEImage() - { - return m_pPEImage; - } - - LPCWSTR Assembly::GetSimpleName() - { - AssemblyName *pAsmName = GetAssemblyName(); - return (pAsmName == nullptr ? nullptr : (LPCWSTR)pAsmName->GetSimpleName()); - } -} - diff --git a/src/coreclr/binder/assemblybindercommon.cpp b/src/coreclr/binder/assemblybindercommon.cpp index e1de7af70677e..1caae65b2a3c7 100644 --- a/src/coreclr/binder/assemblybindercommon.cpp +++ b/src/coreclr/binder/assemblybindercommon.cpp @@ -15,24 +15,13 @@ #include "assemblybindercommon.hpp" #include "assemblyname.hpp" #include "assembly.hpp" -#include "applicationcontext.hpp" -#include "assemblyhashtraits.hpp" #include "bindertracing.h" -#include "bindresult.inl" -#include "failurecache.hpp" #include "utils.hpp" #include "stringarraylist.h" #include "configuration.h" #if !defined(DACCESS_COMPILE) #include "defaultassemblybinder.h" -// Helper function in the VM, invoked by the Binder, to invoke the host assembly resolver -extern HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, - BINDER_SPACE::AssemblyName *pAssemblyName, - DefaultAssemblyBinder *pDefaultBinder, - AssemblyBinder *pBinder, - BINDER_SPACE::Assembly **ppLoadedAssembly); - #endif // !defined(DACCESS_COMPILE) STDAPI BinderAcquirePEImage(LPCTSTR szAssemblyPath, @@ -41,89 +30,6 @@ STDAPI BinderAcquirePEImage(LPCTSTR szAssemblyPath, namespace BINDER_SPACE { - namespace - { - // - // This defines the assembly equivalence relation - // - bool IsCompatibleAssemblyVersion(/* in */ AssemblyName *pRequestedName, - /* in */ AssemblyName *pFoundName) - { - AssemblyVersion *pRequestedVersion = pRequestedName->GetVersion(); - AssemblyVersion *pFoundVersion = pFoundName->GetVersion(); - - if (!pRequestedVersion->HasMajor()) - { - // An unspecified requested version component matches any value for the same component in the found version, - // regardless of lesser-order version components - return true; - } - if (!pFoundVersion->HasMajor() || pRequestedVersion->GetMajor() > pFoundVersion->GetMajor()) - { - // - A specific requested version component does not match an unspecified value for the same component in - // the found version, regardless of lesser-order version components - // - Or, the requested version is greater than the found version - return false; - } - if (pRequestedVersion->GetMajor() < pFoundVersion->GetMajor()) - { - // The requested version is less than the found version - return true; - } - - if (!pRequestedVersion->HasMinor()) - { - return true; - } - if (!pFoundVersion->HasMinor() || pRequestedVersion->GetMinor() > pFoundVersion->GetMinor()) - { - return false; - } - if (pRequestedVersion->GetMinor() < pFoundVersion->GetMinor()) - { - return true; - } - - if (!pRequestedVersion->HasBuild()) - { - return true; - } - if (!pFoundVersion->HasBuild() || pRequestedVersion->GetBuild() > pFoundVersion->GetBuild()) - { - return false; - } - if (pRequestedVersion->GetBuild() < pFoundVersion->GetBuild()) - { - return true; - } - - if (!pRequestedVersion->HasRevision()) - { - return true; - } - if (!pFoundVersion->HasRevision() || pRequestedVersion->GetRevision() > pFoundVersion->GetRevision()) - { - return false; - } - return true; - } - - HRESULT CreateImageAssembly(PEImage *pPEImage, - BindResult *pBindResult) - { - HRESULT hr = S_OK; - ReleaseHolder pAssembly; - - SAFE_NEW(pAssembly, Assembly); - IF_FAIL_GO(pAssembly->Init(pPEImage, /* fIsInTPA */ FALSE )); - - pBindResult->SetResult(pAssembly); - - Exit: - return hr; - } - }; - HRESULT AssemblyBinderCommon::TranslatePEToArchitectureType(DWORD *pdwPAFlags, PEKIND *PeKind) { HRESULT hr = S_OK; @@ -188,76 +94,16 @@ namespace BINDER_SPACE Exit: return hr; } - - HRESULT AssemblyBinderCommon::BindAssembly(/* in */ AssemblyBinder *pBinder, - /* in */ AssemblyName *pAssemblyName, - /* in */ bool excludeAppPaths, - /* out */ Assembly **ppAssembly) - { - HRESULT hr = S_OK; - LONG kContextVersion = 0; - BindResult bindResult; - ApplicationContext* pApplicationContext = pBinder->GetAppContext(); - - // Tracing happens outside the binder lock to avoid calling into managed code within the lock - BinderTracing::ResolutionAttemptedOperation tracer{pAssemblyName, pBinder, 0 /*managedALC*/, hr}; - - Retry: - { - // Lock the binding application context - CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); - - _ASSERTE(pAssemblyName != NULL); - IF_FAIL_GO(BindByName(pApplicationContext, - pAssemblyName, - false, // skipFailureCaching - false, // skipVersionCompatibilityCheck - excludeAppPaths, - &bindResult)); - - // Remember the post-bind version - kContextVersion = pApplicationContext->GetVersion(); - - } // lock(pApplicationContext) - - Exit: - tracer.TraceBindResult(bindResult); - - if (bindResult.HaveResult()) - { - BindResult hostBindResult; - - hr = RegisterAndGetHostChosen(pApplicationContext, - kContextVersion, - &bindResult, - &hostBindResult); - - if (hr == S_FALSE) - { - // Another bind interfered. We need to retry the entire bind. - // This by design loops as long as needed because by construction we eventually - // will succeed or fail the bind. - bindResult.Reset(); - goto Retry; - } - else if (hr == S_OK) - { - *ppAssembly = hostBindResult.GetAssembly(TRUE /* fAddRef */); - } - } - - return hr; - } - + /* static */ HRESULT AssemblyBinderCommon::BindToSystem(SString &systemDirectory, - Assembly **ppSystemAssembly) + PEImage **ppPEImage) { HRESULT hr = S_OK; - _ASSERTE(ppSystemAssembly != NULL); + _ASSERTE(ppPEImage != NULL); - ReleaseHolder pSystemAssembly; + ReleaseHolder pSystemAssembly; // System.Private.CoreLib.dll is expected to be found at one of the following locations: // * Non-single-file app: In systemDirectory, beside coreclr.dll @@ -279,14 +125,11 @@ namespace BINDER_SPACE sCoreLib.Set(systemDirectory); CombinePath(sCoreLib, sCoreLibName, sCoreLib); - hr = AssemblyBinderCommon::GetAssembly(sCoreLib, - TRUE /* fIsInTPA */, - &pSystemAssembly, - bundleFileLocation); + hr = BinderAcquirePEImage(sCoreLib.GetUnicode(), &pSystemAssembly, bundleFileLocation); BinderTracing::PathProbed(sCoreLib, pathSource, hr); - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + if ((FAILED(hr)) && IsFileNotFound(hr)) { // Try to find corelib in the TPA StackSString sCoreLibSimpleName(CoreLibName_W); @@ -318,971 +161,21 @@ namespace BINDER_SPACE { GO_WITH_HRESULT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } - - hr = AssemblyBinderCommon::GetAssembly(sCoreLib, - TRUE /* fIsInTPA */, - &pSystemAssembly, - bundleFileLocation); + + hr = BinderAcquirePEImage(sCoreLib.GetUnicode(), &pSystemAssembly, bundleFileLocation); BinderTracing::PathProbed(sCoreLib, BinderTracing::PathSource::ApplicationAssemblies, hr); } IF_FAIL_GO(hr); - *ppSystemAssembly = pSystemAssembly.Extract(); - - Exit: - return hr; - } - - - /* static */ - HRESULT AssemblyBinderCommon::BindToSystemSatellite(SString& systemDirectory, - SString& simpleName, - SString& cultureName, - Assembly** ppSystemAssembly) - { - HRESULT hr = S_OK; - - _ASSERTE(ppSystemAssembly != NULL); - - // Satellite assembly's relative path - StackSString relativePath; - - // append culture name - if (!cultureName.IsEmpty()) - { - CombinePath(relativePath, cultureName, relativePath); - } - - // append satellite assembly's simple name - CombinePath(relativePath, simpleName, relativePath); - - // append extension - relativePath.Append(W(".dll")); - - // Satellite assembly's path: - // * Absolute path when looking for a file on disk - // * Bundle-relative path when looking within the single-file bundle. - StackSString sCoreLibSatellite; - - BinderTracing::PathSource pathSource = BinderTracing::PathSource::Bundle; - BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(relativePath, /*pathIsBundleRelative */ true); - if (!bundleFileLocation.IsValid()) - { - sCoreLibSatellite.Set(systemDirectory); - pathSource = BinderTracing::PathSource::ApplicationAssemblies; - } - CombinePath(sCoreLibSatellite, relativePath, sCoreLibSatellite); - - ReleaseHolder pSystemAssembly; - IF_FAIL_GO(AssemblyBinderCommon::GetAssembly(sCoreLibSatellite, - TRUE /* fIsInTPA */, - &pSystemAssembly, - bundleFileLocation)); - BinderTracing::PathProbed(sCoreLibSatellite, pathSource, hr); - - *ppSystemAssembly = pSystemAssembly.Extract(); - - Exit: - return hr; - } - - /* static */ - HRESULT AssemblyBinderCommon::BindByName(ApplicationContext *pApplicationContext, - AssemblyName *pAssemblyName, - bool skipFailureCaching, - bool skipVersionCompatibilityCheck, - bool excludeAppPaths, - BindResult *pBindResult) - { - HRESULT hr = S_OK; - PathString assemblyDisplayName; - - // Look for already cached binding failure (ignore PA, every PA will lock the context) - pAssemblyName->GetDisplayName(assemblyDisplayName, - AssemblyName::INCLUDE_VERSION); - - hr = pApplicationContext->GetFailureCache()->Lookup(assemblyDisplayName); - if (FAILED(hr)) - { - if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) && skipFailureCaching) - { - // Ignore pre-existing transient bind error (re-bind will succeed) - pApplicationContext->GetFailureCache()->Remove(assemblyDisplayName); - } - - goto LogExit; - } - else if (hr == S_FALSE) - { - // workaround: Special case for byte arrays. Rerun the bind to create binding log. - pAssemblyName->SetIsDefinition(TRUE); - hr = S_OK; - } - - if (!IsValidArchitecture(pAssemblyName->GetArchitecture())) - { - // Assembly reference contains wrong architecture - IF_FAIL_GO(FUSION_E_INVALID_NAME); - } - - IF_FAIL_GO(BindLocked(pApplicationContext, - pAssemblyName, - skipVersionCompatibilityCheck, - excludeAppPaths, - pBindResult)); - - if (!pBindResult->HaveResult()) - { - // Behavior rules are clueless now - IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); - } - - Exit: - if (FAILED(hr)) - { - if (skipFailureCaching) - { - if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - // Cache non-transient bind error for byte-array - hr = S_FALSE; - } - else - { - // Ignore transient bind error (re-bind will succeed) - goto LogExit; - } - } - - hr = pApplicationContext->AddToFailureCache(assemblyDisplayName, hr); - } - - LogExit: - return hr; - } - - /* static */ - HRESULT AssemblyBinderCommon::BindLocked(ApplicationContext *pApplicationContext, - AssemblyName *pAssemblyName, - bool skipVersionCompatibilityCheck, - bool excludeAppPaths, - BindResult *pBindResult) - { - HRESULT hr = S_OK; - - bool isTpaListProvided = pApplicationContext->IsTpaListProvided(); - Assembly *pAssembly = NULL; - hr = FindInExecutionContext(pApplicationContext, pAssemblyName, &pAssembly); - - // Add the attempt to the bind result on failure / not found. On success, it will be added after the version check. - if (FAILED(hr) || pAssembly == NULL) - pBindResult->SetAttemptResult(hr, pAssembly, /*isInContext*/ true); - - IF_FAIL_GO(hr); - if (pAssembly != NULL) - { - if (!skipVersionCompatibilityCheck) - { - // Can't give higher version than already bound - bool isCompatible = IsCompatibleAssemblyVersion(pAssemblyName, pAssembly->GetAssemblyName()); - hr = isCompatible ? S_OK : FUSION_E_APP_DOMAIN_LOCKED; - pBindResult->SetAttemptResult(hr, pAssembly, /*isInContext*/ true); - - // TPA binder returns FUSION_E_REF_DEF_MISMATCH for incompatible version - if (hr == FUSION_E_APP_DOMAIN_LOCKED && isTpaListProvided) - hr = FUSION_E_REF_DEF_MISMATCH; - } - else - { - pBindResult->SetAttemptResult(hr, pAssembly, /*isInContext*/ true); - } - - IF_FAIL_GO(hr); - - pBindResult->SetResult(pAssembly, /*isInContext*/ true); - } - else - if (isTpaListProvided) - { - // BindByTpaList handles setting attempt results on the bind result - hr = BindByTpaList(pApplicationContext, - pAssemblyName, - excludeAppPaths, - pBindResult); - if (SUCCEEDED(hr) && pBindResult->HaveResult()) - { - bool isCompatible = IsCompatibleAssemblyVersion(pAssemblyName, pBindResult->GetAssemblyName()); - hr = isCompatible ? S_OK : FUSION_E_APP_DOMAIN_LOCKED; - pBindResult->SetAttemptResult(hr, pBindResult->GetAssembly()); - - // TPA binder returns FUSION_E_REF_DEF_MISMATCH for incompatible version - if (hr == FUSION_E_APP_DOMAIN_LOCKED && isTpaListProvided) - hr = FUSION_E_REF_DEF_MISMATCH; - } - - if (FAILED(hr)) - { - pBindResult->SetNoResult(); - } - IF_FAIL_GO(hr); - } - Exit: - return hr; - } - - /* static */ - HRESULT AssemblyBinderCommon::FindInExecutionContext(ApplicationContext *pApplicationContext, - AssemblyName *pAssemblyName, - Assembly **ppAssembly) - { - _ASSERTE(pApplicationContext != NULL); - _ASSERTE(pAssemblyName != NULL); - _ASSERTE(ppAssembly != NULL); - - ExecutionContext *pExecutionContext = pApplicationContext->GetExecutionContext(); - Assembly *pAssembly = pExecutionContext->Lookup(pAssemblyName); - - // Set any found context entry. It is up to the caller to check the returned HRESULT - // for errors due to validation - *ppAssembly = pAssembly; - if (pAssembly == NULL) - return S_FALSE; - - AssemblyName *pContextName = pAssembly->GetAssemblyName(); - if (pAssemblyName->GetIsDefinition() && - (pContextName->GetArchitecture() != pAssemblyName->GetArchitecture())) - { - return FUSION_E_APP_DOMAIN_LOCKED; - } - - return S_OK; - } - - - // - // Tests whether a candidate assembly's name matches the requested. - // This does not do a version check. The binder applies version policy - // further up the stack once it gets a successful bind. - // - BOOL TestCandidateRefMatchesDef(AssemblyName *pRequestedAssemblyName, - AssemblyName *pBoundAssemblyName, - BOOL tpaListAssembly) - { - DWORD dwIncludeFlags = AssemblyName::INCLUDE_DEFAULT; - - if (!tpaListAssembly) - { - if (pRequestedAssemblyName->IsNeutralCulture()) - { - dwIncludeFlags |= AssemblyName::EXCLUDE_CULTURE; - } - } - - if (pRequestedAssemblyName->GetArchitecture() != peNone) - { - dwIncludeFlags |= AssemblyName::INCLUDE_ARCHITECTURE; - } - - return pBoundAssemblyName->Equals(pRequestedAssemblyName, dwIncludeFlags); - } - - namespace - { - HRESULT BindSatelliteResourceFromBundle( - AssemblyName* pRequestedAssemblyName, - SString &relativePath, - BindResult* pBindResult) - { - HRESULT hr = S_OK; - - BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(relativePath, /* pathIsBundleRelative */ true); - if (!bundleFileLocation.IsValid()) - { - return hr; - } - - ReleaseHolder pAssembly; - hr = AssemblyBinderCommon::GetAssembly(relativePath, - FALSE /* fIsInTPA */, - &pAssembly, - bundleFileLocation); - - BinderTracing::PathProbed(relativePath, BinderTracing::PathSource::Bundle, hr); - - // Missing files are okay and expected when probing - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - return S_OK; - } - - pBindResult->SetAttemptResult(hr, pAssembly); - if (FAILED(hr)) - return hr; - - AssemblyName* pBoundAssemblyName = pAssembly->GetAssemblyName(); - if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pBoundAssemblyName, false /*tpaListAssembly*/)) - { - pBindResult->SetResult(pAssembly); - hr = S_OK; - } - else - { - hr = FUSION_E_REF_DEF_MISMATCH; - } - - pBindResult->SetAttemptResult(hr, pAssembly); - return hr; - } - - HRESULT BindSatelliteResourceByProbingPaths( - const StringArrayList *pResourceRoots, - AssemblyName *pRequestedAssemblyName, - SString &relativePath, - BindResult *pBindResult, - BinderTracing::PathSource pathSource) - { - HRESULT hr = S_OK; - - for (UINT i = 0; i < pResourceRoots->GetCount(); i++) - { - ReleaseHolder pAssembly; - SString &wszBindingPath = (*pResourceRoots)[i]; - SString fileName(wszBindingPath); - CombinePath(fileName, relativePath, fileName); - - hr = AssemblyBinderCommon::GetAssembly(fileName, - FALSE /* fIsInTPA */, - &pAssembly); - BinderTracing::PathProbed(fileName, pathSource, hr); - - // Missing files are okay and expected when probing - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - continue; - } - - pBindResult->SetAttemptResult(hr, pAssembly); - if (FAILED(hr)) - return hr; - - AssemblyName *pBoundAssemblyName = pAssembly->GetAssemblyName(); - if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pBoundAssemblyName, false /*tpaListAssembly*/)) - { - pBindResult->SetResult(pAssembly); - hr = S_OK; - } - else - { - hr = FUSION_E_REF_DEF_MISMATCH; - } - - pBindResult->SetAttemptResult(hr, pAssembly); - return hr; - } - - // Up-stack expects S_OK when we don't find any candidate assemblies and no fatal error occurred (ie, no S_FALSE) - return S_OK; - } - - HRESULT BindSatelliteResource( - ApplicationContext* pApplicationContext, - AssemblyName* pRequestedAssemblyName, - BindResult* pBindResult) - { - // Satellite resource probing strategy is to look: - // * First within the single-file bundle - // * Then under each of the Platform Resource Roots - // * Then under each of the App Paths. - // - // During each search, if we find a platform resource file with matching file name, but whose ref-def didn't match, - // fall back to application resource lookup to handle case where a user creates resources with the same - // names as platform ones. - - HRESULT hr = S_OK; - const SString& simpleNameRef = pRequestedAssemblyName->GetSimpleName(); - SString& cultureRef = pRequestedAssemblyName->GetCulture(); - - _ASSERTE(!pRequestedAssemblyName->IsNeutralCulture()); - - ReleaseHolder pAssembly; - SString fileName; - CombinePath(fileName, cultureRef, fileName); - CombinePath(fileName, simpleNameRef, fileName); - fileName.Append(W(".dll")); - - hr = BindSatelliteResourceFromBundle(pRequestedAssemblyName, fileName, pBindResult); - - if (pBindResult->HaveResult() || FAILED(hr)) - { - return hr; - } - - hr = BindSatelliteResourceByProbingPaths(pApplicationContext->GetPlatformResourceRoots(), - pRequestedAssemblyName, - fileName, - pBindResult, - BinderTracing::PathSource::PlatformResourceRoots); - - if (pBindResult->HaveResult() || FAILED(hr)) - { - return hr; - } - - hr = BindSatelliteResourceByProbingPaths(pApplicationContext->GetAppPaths(), - pRequestedAssemblyName, - fileName, - pBindResult, - BinderTracing::PathSource::AppPaths); - - return hr; - } - - HRESULT BindAssemblyByProbingPaths( - const StringArrayList *pBindingPaths, - AssemblyName *pRequestedAssemblyName, - Assembly **ppAssembly) - { - const SString &simpleName = pRequestedAssemblyName->GetSimpleName(); - BinderTracing::PathSource pathSource = BinderTracing::PathSource::AppPaths; - // Loop through the binding paths looking for a matching assembly - for (DWORD i = 0; i < pBindingPaths->GetCount(); i++) - { - HRESULT hr; - ReleaseHolder pAssembly; - LPCWSTR wszBindingPath = (*pBindingPaths)[i]; - - PathString fileNameWithoutExtension(wszBindingPath); - CombinePath(fileNameWithoutExtension, simpleName, fileNameWithoutExtension); - - // Look for a matching dll first - PathString fileName(fileNameWithoutExtension); - fileName.Append(W(".dll")); - hr = AssemblyBinderCommon::GetAssembly(fileName, - FALSE, // fIsInTPA - &pAssembly); - BinderTracing::PathProbed(fileName, pathSource, hr); - - if (FAILED(hr)) - { - fileName.Set(fileNameWithoutExtension); - fileName.Append(W(".exe")); - hr = AssemblyBinderCommon::GetAssembly(fileName, - FALSE, // fIsInTPA - &pAssembly); - BinderTracing::PathProbed(fileName, pathSource, hr); - } - - // Since we're probing, file not founds are ok and we should just try another - // probing path - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - continue; - } - - // Set any found assembly. It is up to the caller to check the returned HRESULT for errors due to validation - *ppAssembly = pAssembly.Extract(); - if (FAILED(hr)) - return hr; - - // We found a candidate. - // - // Below this point, we either establish that the ref-def matches, or - // we fail the bind. - - // Compare requested AssemblyName with that from the candidate assembly - if (!TestCandidateRefMatchesDef(pRequestedAssemblyName, pAssembly->GetAssemblyName(), false /*tpaListAssembly*/)) - return FUSION_E_REF_DEF_MISMATCH; - - return S_OK; - } - - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - } - } - - /* - * BindByTpaList is the entry-point for the custom binding algorithm in CoreCLR. - * - * The search for assemblies will proceed in the following order: - * - * If this application is a single-file bundle, the meta-data contained in the bundle - * will be probed to find the requested assembly. If the assembly is not found, - * The list of platform assemblies (TPAs) are considered next. - * - * Platform assemblies are specified as a list of files. This list is the only set of - * assemblies that we will load as platform. They can be specified as IL or NIs. - * - * Resources for platform assemblies are located by probing starting at the Platform Resource Roots, - * a set of folders configured by the host. - * - * If a requested assembly identity cannot be found in the TPA list or the resource roots, - * it is considered an application assembly. We probe for application assemblies in the - * AppPaths, a list of paths containing IL files and satellite resource folders. - * - */ - /* static */ - HRESULT AssemblyBinderCommon::BindByTpaList(ApplicationContext *pApplicationContext, - AssemblyName *pRequestedAssemblyName, - bool excludeAppPaths, - BindResult *pBindResult) - { - HRESULT hr = S_OK; - - bool fPartialMatchOnTpa = false; - - if (!pRequestedAssemblyName->IsNeutralCulture()) - { - IF_FAIL_GO(BindSatelliteResource(pApplicationContext, pRequestedAssemblyName, pBindResult)); - } - else - { - ReleaseHolder pTPAAssembly; - const SString& simpleName = pRequestedAssemblyName->GetSimpleName(); - - // Is assembly in the bundle? - // Single-file bundle contents take precedence over TPA. - // The list of bundled assemblies is contained in the bundle manifest, and NOT in the TPA. - // Therefore the bundle is first probed using the assembly's simple name. - // If found, the assembly is loaded from the bundle. - if (Bundle::AppIsBundle()) - { - // Search Assembly.ni.dll, then Assembly.dll - // The Assembly.ni.dll paths are rare, and intended for supporting managed C++ R2R assemblies. - const WCHAR* const candidates[] = { W(".ni.dll"), W(".dll") }; - - // Loop through the binding paths looking for a matching assembly - for (int i = 0; i < 2; i++) - { - SString assemblyFileName(simpleName); - assemblyFileName.Append(candidates[i]); - - SString assemblyFilePath(Bundle::AppBundle->BasePath()); - assemblyFilePath.Append(assemblyFileName); - - BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(assemblyFileName, /* pathIsBundleRelative */ true); - if (bundleFileLocation.IsValid()) - { - hr = GetAssembly(assemblyFilePath, - TRUE, // fIsInTPA - &pTPAAssembly, - bundleFileLocation); - - BinderTracing::PathProbed(assemblyFilePath, BinderTracing::PathSource::Bundle, hr); - - if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - // Any other error is fatal - IF_FAIL_GO(hr); - - if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) - { - // We have found the requested assembly match in the bundle with validation of the full-qualified name. - // Bind to it. - pBindResult->SetResult(pTPAAssembly); - GO_WITH_HRESULT(S_OK); - } - } - } - } - } - - // Is assembly on TPA list? - SimpleNameToFileNameMap * tpaMap = pApplicationContext->GetTpaList(); - const SimpleNameToFileNameMapEntry *pTpaEntry = tpaMap->LookupPtr(simpleName.GetUnicode()); - if (pTpaEntry != nullptr) - { - if (pTpaEntry->m_wszNIFileName != nullptr) - { - SString fileName(pTpaEntry->m_wszNIFileName); - - hr = GetAssembly(fileName, - TRUE, // fIsInTPA - &pTPAAssembly); - BinderTracing::PathProbed(fileName, BinderTracing::PathSource::ApplicationAssemblies, hr); - } - else - { - _ASSERTE(pTpaEntry->m_wszILFileName != nullptr); - SString fileName(pTpaEntry->m_wszILFileName); - - hr = GetAssembly(fileName, - TRUE, // fIsInTPA - &pTPAAssembly); - BinderTracing::PathProbed(fileName, BinderTracing::PathSource::ApplicationAssemblies, hr); - } - - pBindResult->SetAttemptResult(hr, pTPAAssembly); - - // On file not found, simply fall back to app path probing - if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - // Any other error is fatal - IF_FAIL_GO(hr); - - if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) - { - // We have found the requested assembly match on TPA with validation of the full-qualified name. Bind to it. - pBindResult->SetResult(pTPAAssembly); - pBindResult->SetAttemptResult(S_OK, pTPAAssembly); - GO_WITH_HRESULT(S_OK); - } - else - { - // We found the assembly on TPA but it didn't match the RequestedAssembly assembly-name. In this case, lets proceed to see if we find the requested - // assembly in the App paths. - pBindResult->SetAttemptResult(FUSION_E_REF_DEF_MISMATCH, pTPAAssembly); - fPartialMatchOnTpa = true; - } - } - - // We either didn't find a candidate, or the ref-def failed. Either way; fall back to app path probing. - } - - if (!excludeAppPaths) - { - // Probe AppPaths - ReleaseHolder pAssembly; - hr = BindAssemblyByProbingPaths(pApplicationContext->GetAppPaths(), - pRequestedAssemblyName, - &pAssembly); - - pBindResult->SetAttemptResult(hr, pAssembly); - if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - IF_FAIL_GO(hr); - - // At this point, we have found an assembly with the expected name in the App paths. If this was also found on TPA, - // make sure that the app assembly has the same fullname (excluding version) as the TPA version. If it does, then - // we should bind to the TPA assembly. If it does not, then bind to the app assembly since it has a different fullname than the - // TPA assembly. - if (fPartialMatchOnTpa) - { - if (TestCandidateRefMatchesDef(pAssembly->GetAssemblyName(), pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) - { - // Fullname (SimpleName+Culture+PKT) matched for TPA and app assembly - so bind to TPA instance. - pBindResult->SetResult(pTPAAssembly); - pBindResult->SetAttemptResult(hr, pTPAAssembly); - GO_WITH_HRESULT(S_OK); - } - else - { - // Fullname (SimpleName+Culture+PKT) did not match for TPA and app assembly - so bind to app instance. - pBindResult->SetResult(pAssembly); - GO_WITH_HRESULT(S_OK); - } - } - else - { - // We didn't see this assembly on TPA - so simply bind to the app instance. - pBindResult->SetResult(pAssembly); - GO_WITH_HRESULT(S_OK); - } - } - } - } - - // Couldn't find a matching assembly in any of the probing paths - // Return S_FALSE here. BindByName will interpret a successful HRESULT - // and lack of BindResult as a failure to find a matching assembly. - hr = S_FALSE; - - Exit: - return hr; - } - - /* static */ - HRESULT AssemblyBinderCommon::GetAssembly(SString &assemblyPath, - BOOL fIsInTPA, - Assembly **ppAssembly, - BundleFileLocation bundleFileLocation) - { - HRESULT hr = S_OK; - - _ASSERTE(ppAssembly != NULL); - - ReleaseHolder pAssembly; - PEImage *pPEImage = NULL; - - // Allocate assembly object - SAFE_NEW(pAssembly, Assembly); - - // Obtain assembly meta data - { - LPCTSTR szAssemblyPath = const_cast(assemblyPath.GetUnicode()); - - hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, bundleFileLocation); - IF_FAIL_GO(hr); - } - - // Initialize assembly object - IF_FAIL_GO(pAssembly->Init(pPEImage, fIsInTPA)); - - // We're done - *ppAssembly = pAssembly.Extract(); - - Exit: - - SAFE_RELEASE(pPEImage); - - // Normalize file not found - if ((FAILED(hr)) && IsFileNotFound(hr)) - { - hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - } - - return hr; - } - - - /* static */ - HRESULT AssemblyBinderCommon::Register(ApplicationContext *pApplicationContext, - BindResult *pBindResult) - { - _ASSERTE(!pBindResult->GetIsContextBound()); - - pApplicationContext->IncrementVersion(); - - // Register the bindResult in the ExecutionContext only if we dont have it already. - // This method is invoked under a lock (by its caller), so we are thread safe. - Assembly *pAssembly = NULL; - HRESULT hr = FindInExecutionContext(pApplicationContext, pBindResult->GetAssemblyName(), &pAssembly); - if (FAILED(hr)) - return hr; - - if (pAssembly == NULL) - { - ExecutionContext *pExecutionContext = pApplicationContext->GetExecutionContext(); - pExecutionContext->Add(pBindResult->GetAssembly(/*fAddRef*/ TRUE)); - } - else - { - // Update the BindResult with the assembly we found - pBindResult->SetResult(pAssembly, /*isContextBound*/ true); - } - - return S_OK; - } - - /* static */ - HRESULT AssemblyBinderCommon::RegisterAndGetHostChosen(ApplicationContext *pApplicationContext, - LONG kContextVersion, - BindResult *pBindResult, - BindResult *pHostBindResult) - { - HRESULT hr = S_OK; - - _ASSERTE(pBindResult != NULL); - _ASSERTE(pBindResult->HaveResult()); - _ASSERTE(pHostBindResult != NULL); - - if (!pBindResult->GetIsContextBound()) - { - pHostBindResult->SetResult(pBindResult); - - { - // Lock the application context - CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); - - // Only perform costly validation if other binds succeeded before us - if (kContextVersion != pApplicationContext->GetVersion()) - { - IF_FAIL_GO(AssemblyBinderCommon::OtherBindInterfered(pApplicationContext, - pBindResult)); - - if (hr == S_FALSE) - { - // Another bind interfered - GO_WITH_HRESULT(hr); - } - } - - // No bind interfered, we can now register - IF_FAIL_GO(Register(pApplicationContext, - pHostBindResult)); - } - } - else - { - // No work required. Return the input - pHostBindResult->SetResult(pBindResult); - } - - Exit: - return hr; - } - - /* static */ - HRESULT AssemblyBinderCommon::OtherBindInterfered(ApplicationContext *pApplicationContext, - BindResult *pBindResult) - { - HRESULT hr = S_FALSE; - AssemblyName *pAssemblyName = pBindResult->GetAssemblyName(); - PathString assemblyDisplayName; - - _ASSERTE(pAssemblyName != NULL); - - // Look for already cached binding failure (ignore PA, every PA will lock the context) - pAssemblyName->GetDisplayName(assemblyDisplayName, AssemblyName::INCLUDE_VERSION); - hr = pApplicationContext->GetFailureCache()->Lookup(assemblyDisplayName); - - if (hr == S_OK) - { - Assembly *pAssembly = NULL; - hr = FindInExecutionContext(pApplicationContext, pAssemblyName, &pAssembly); - if (SUCCEEDED(hr) && (pAssembly == NULL)) - { - // We can accept this bind in the domain - GO_WITH_HRESULT(S_OK); - } - } - - // Some other bind interfered - GO_WITH_HRESULT(S_FALSE); + *ppPEImage = pSystemAssembly.Extract(); Exit: return hr; } - - + #if !defined(DACCESS_COMPILE) -HRESULT AssemblyBinderCommon::BindUsingHostAssemblyResolver(/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, - /* in */ AssemblyName *pAssemblyName, - /* in */ DefaultAssemblyBinder *pDefaultBinder, - /* in */ AssemblyBinder *pBinder, - /* out */ Assembly **ppAssembly) -{ - HRESULT hr = E_FAIL; - - _ASSERTE(pManagedAssemblyLoadContextToBindWithin != NULL); - - // RuntimeInvokeHostAssemblyResolver will perform steps 2-4 of CustomAssemblyBinder::BindAssemblyByName. - BINDER_SPACE::Assembly *pLoadedAssembly = NULL; - hr = RuntimeInvokeHostAssemblyResolver(pManagedAssemblyLoadContextToBindWithin, - pAssemblyName, pDefaultBinder, pBinder, &pLoadedAssembly); - if (SUCCEEDED(hr)) - { - _ASSERTE(pLoadedAssembly != NULL); - *ppAssembly = pLoadedAssembly; - } - - return hr; -} - -/* static */ -HRESULT AssemblyBinderCommon::BindUsingPEImage(/* in */ AssemblyBinder* pBinder, - /* in */ BINDER_SPACE::AssemblyName *pAssemblyName, - /* in */ PEImage *pPEImage, - /* in */ bool excludeAppPaths, - /* [retval] [out] */ Assembly **ppAssembly) -{ - HRESULT hr = E_FAIL; - - LONG kContextVersion = 0; - BindResult bindResult; - - // Prepare binding data - *ppAssembly = NULL; - ApplicationContext* pApplicationContext = pBinder->GetAppContext(); - - // Tracing happens outside the binder lock to avoid calling into managed code within the lock - BinderTracing::ResolutionAttemptedOperation tracer{pAssemblyName, pBinder, 0 /*managedALC*/, hr}; - - // Attempt the actual bind (eventually more than once) -Retry: - bool mvidMismatch = false; - { - // Lock the application context - CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); - - // Attempt uncached bind and register stream if possible - // We skip version compatibility check - so assemblies with same simple name will be reported - // as a successful bind. Below we compare MVIDs in that case instead (which is a more precise equality check). - hr = BindByName(pApplicationContext, - pAssemblyName, - true, // skipFailureCaching - true, // skipVersionCompatibilityCheck - excludeAppPaths, // excludeAppPaths - &bindResult); - - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - IF_FAIL_GO(CreateImageAssembly(pPEImage, &bindResult)); - } - else if (hr == S_OK) - { - if (bindResult.HaveResult()) - { - // Attempt was made to load an assembly that has the same name as a previously loaded one. Since same name - // does not imply the same assembly, we will need to check the MVID to confirm it is the same assembly as being - // requested. - - GUID incomingMVID; - GUID boundMVID; - - // GetMVID can throw exception - EX_TRY - { - pPEImage->GetMVID(&incomingMVID); - bindResult.GetAssembly()->GetPEImage()->GetMVID(&boundMVID); - } - EX_CATCH - { - hr = GET_EXCEPTION()->GetHR(); - goto Exit; - } - EX_END_CATCH(SwallowAllExceptions); - - - mvidMismatch = incomingMVID != boundMVID; - if (mvidMismatch) - { - // MVIDs do not match, so fail the load. - IF_FAIL_GO(COR_E_FILELOAD); - } - - // MVIDs match - request came in for the same assembly that was previously loaded. - // Let it through... - } - } - - // Remember the post-bind version of the context - kContextVersion = pApplicationContext->GetVersion(); - - } // lock(pApplicationContext) - - if (bindResult.HaveResult()) - { - BindResult hostBindResult; - - // This has to happen outside the binder lock as it can cause new binds - IF_FAIL_GO(RegisterAndGetHostChosen(pApplicationContext, - kContextVersion, - &bindResult, - &hostBindResult)); - - if (hr == S_FALSE) - { - tracer.TraceBindResult(bindResult); - - // Another bind interfered. We need to retry entire bind. - // This by design loops as long as needed because by construction we eventually - // will succeed or fail the bind. - bindResult.Reset(); - goto Retry; - } - else if (hr == S_OK) - { - *ppAssembly = hostBindResult.GetAssembly(TRUE /* fAddRef */); - } - } - -Exit: - tracer.TraceBindResult(bindResult, mvidMismatch); - return hr; -} - HRESULT AssemblyBinderCommon::CreateDefaultBinder(DefaultAssemblyBinder** ppDefaultBinder) { HRESULT hr = S_OK; @@ -1293,8 +186,6 @@ HRESULT AssemblyBinderCommon::CreateDefaultBinder(DefaultAssemblyBinder** ppDefa NewHolder pBinder; SAFE_NEW(pBinder, DefaultAssemblyBinder); - BINDER_SPACE::ApplicationContext* pApplicationContext = pBinder->GetAppContext(); - hr = pApplicationContext->Init(); if (SUCCEEDED(hr)) { pBinder->SetManagedAssemblyLoadContext((INT_PTR)NULL); diff --git a/src/coreclr/binder/bindertracing.cpp b/src/coreclr/binder/bindertracing.cpp index 82b31932bd030..cbe083157d3d2 100644 --- a/src/coreclr/binder/bindertracing.cpp +++ b/src/coreclr/binder/bindertracing.cpp @@ -13,7 +13,7 @@ #include "common.h" #include "bindertracing.h" -#include "bindresult.hpp" +#include "assemblybinder.h" #include "activitytracker.h" @@ -180,175 +180,6 @@ namespace BinderTracing namespace BinderTracing { - ResolutionAttemptedOperation::ResolutionAttemptedOperation(AssemblyName *assemblyName, AssemblyBinder* binder, INT_PTR managedALC, const HRESULT& hr) - : m_hr { hr } - , m_stage { Stage::NotYetStarted } - , m_tracingEnabled { BinderTracing::IsEnabled() } - , m_assemblyNameObject { assemblyName } - , m_pFoundAssembly { nullptr } - { - _ASSERTE(binder != nullptr || managedALC != 0); - - if (!m_tracingEnabled) - return; - - // When binding the main assembly (by code base instead of name), the assembly name will be null. In this special case, we just - // leave the assembly name empty. - if (m_assemblyNameObject != nullptr) - m_assemblyNameObject->GetDisplayName(m_assemblyName, AssemblyName::INCLUDE_VERSION | AssemblyName::INCLUDE_PUBLIC_KEY_TOKEN); - - if (managedALC != 0) - { - AssemblyBinder::GetNameForDiagnosticsFromManagedALC(managedALC, m_assemblyLoadContextName); - } - else - { - binder->GetNameForDiagnostics(m_assemblyLoadContextName); - } - } - - // This function simply traces out the two stages represented by the bind result. - // It does not change the stage/assembly of the ResolutionAttemptedOperation class instance. - void ResolutionAttemptedOperation::TraceBindResult(const BindResult &bindResult, bool mvidMismatch) - { - if (!m_tracingEnabled) - return; - - // Use the error message that would be reported in the file load exception - StackSString errorMsg; - if (mvidMismatch) - { - StackSString format; - format.LoadResource(CCompRC::Error, IDS_EE_FILELOAD_ERROR_GENERIC); - StackSString details; - details.LoadResource(CCompRC::Error, IDS_HOST_ASSEMBLY_RESOLVER_ASSEMBLY_ALREADY_LOADED_IN_CONTEXT); - errorMsg.FormatMessage(FORMAT_MESSAGE_FROM_STRING, format.GetUnicode(), 0, 0, m_assemblyName, details); - } - - const BindResult::AttemptResult *inContextAttempt = bindResult.GetAttempt(true /*foundInContext*/); - const BindResult::AttemptResult *appAssembliesAttempt = bindResult.GetAttempt(false /*foundInContext*/); - - if (inContextAttempt != nullptr) - { - // If there the attempt HR represents a success, but the tracked HR represents a failure (e.g. from further validation), report the failed HR - bool isLastAttempt = appAssembliesAttempt == nullptr; - TraceStage(Stage::FindInLoadContext, - isLastAttempt && FAILED(m_hr) && SUCCEEDED(inContextAttempt->HResult) ? m_hr : inContextAttempt->HResult, - inContextAttempt->Assembly, - mvidMismatch && isLastAttempt ? errorMsg.GetUnicode() : nullptr); - } - - if (appAssembliesAttempt != nullptr) - TraceStage(Stage::ApplicationAssemblies, FAILED(m_hr) && SUCCEEDED(appAssembliesAttempt->HResult) ? m_hr : appAssembliesAttempt->HResult, appAssembliesAttempt->Assembly, mvidMismatch ? errorMsg.GetUnicode() : nullptr); - } - - void ResolutionAttemptedOperation::TraceStage(Stage stage, HRESULT hr, BINDER_SPACE::Assembly *resultAssembly, const WCHAR *customError) - { - if (!m_tracingEnabled || stage == Stage::NotYetStarted) - return; - - PathString resultAssemblyName; - StackSString resultAssemblyPath; - if (resultAssembly != nullptr) - { - resultAssembly->GetAssemblyName()->GetDisplayName(resultAssemblyName, AssemblyName::INCLUDE_VERSION | AssemblyName::INCLUDE_PUBLIC_KEY_TOKEN); - resultAssemblyPath = resultAssembly->GetPEImage()->GetPath(); - } - - Result result; - StackSString errorMsg; - if (customError != nullptr) - { - errorMsg.Set(customError); - result = Result::Failure; - } - else if (!m_exceptionMessage.IsEmpty()) - { - errorMsg = m_exceptionMessage; - result = Result::Exception; - } - else - { - switch (hr) - { - case S_FALSE: - case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): - static_assert(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == COR_E_FILENOTFOUND, - "COR_E_FILENOTFOUND has sane value"); - - result = Result::AssemblyNotFound; - errorMsg.Set(s_assemblyNotFoundMessage); - break; - - case FUSION_E_APP_DOMAIN_LOCKED: - result = Result::IncompatibleVersion; - - { - SString errorMsgUtf8(SString::Utf8, "Requested version"); - if (m_assemblyNameObject != nullptr) - { - const auto &reqVersion = m_assemblyNameObject->GetVersion(); - errorMsgUtf8.AppendPrintf(" %d.%d.%d.%d", - reqVersion->GetMajor(), - reqVersion->GetMinor(), - reqVersion->GetBuild(), - reqVersion->GetRevision()); - } - - errorMsgUtf8.AppendUTF8(" is incompatible with found version"); - if (resultAssembly != nullptr) - { - const auto &foundVersion = resultAssembly->GetAssemblyName()->GetVersion(); - errorMsgUtf8.AppendPrintf(" %d.%d.%d.%d", - foundVersion->GetMajor(), - foundVersion->GetMinor(), - foundVersion->GetBuild(), - foundVersion->GetRevision()); - } - errorMsg.Set(errorMsgUtf8.GetUnicode()); - } - break; - - case FUSION_E_REF_DEF_MISMATCH: - result = Result::MismatchedAssemblyName; - errorMsg.Append(W("Requested assembly name '")); - errorMsg.Append(m_assemblyName.GetUnicode()); - errorMsg.Append(W("' does not match found assembly name")); - if (resultAssembly != nullptr) - { - errorMsg.Append(W(" '")); - errorMsg.Append(resultAssemblyName.GetUnicode()); - errorMsg.Append(W("'")); - } - - break; - - default: - if (SUCCEEDED(hr)) - { - result = Result::Success; - _ASSERTE(resultAssembly != nullptr); - // Leave errorMsg empty in this case. - } - else - { - result = Result::Failure; - errorMsg.Printf("Resolution failed with HRESULT (%08x)", m_hr); - } - } - } - - FireEtwResolutionAttempted( - GetClrInstanceId(), - m_assemblyName, - static_cast(stage), - m_assemblyLoadContextName, - static_cast(result), - resultAssemblyName, - resultAssemblyPath, - errorMsg); - } - // static void ResolutionAttemptedOperation::TraceAppDomainAssemblyResolve(AssemblySpec *spec, PEAssembly *resultAssembly, Exception *exception) { diff --git a/src/coreclr/binder/customassemblybinder.cpp b/src/coreclr/binder/customassemblybinder.cpp index 99297c2bd9ce3..cc5e53ee396b0 100644 --- a/src/coreclr/binder/customassemblybinder.cpp +++ b/src/coreclr/binder/customassemblybinder.cpp @@ -10,139 +10,6 @@ using namespace BINDER_SPACE; -// ============================================================================ -// CustomAssemblyBinder implementation -// ============================================================================ -HRESULT CustomAssemblyBinder::BindAssemblyByNameWorker(BINDER_SPACE::AssemblyName *pAssemblyName, - BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly) -{ - VALIDATE_ARG_RET(pAssemblyName != nullptr && ppCoreCLRFoundAssembly != nullptr); - HRESULT hr = S_OK; - -#ifdef _DEBUG - // CoreLib should be bound using BindToSystem - _ASSERTE(!pAssemblyName->IsCoreLib()); -#endif - - // Do we have the assembly already loaded in the context of the current binder? - hr = AssemblyBinderCommon::BindAssembly(this, - pAssemblyName, - false, //excludeAppPaths, - ppCoreCLRFoundAssembly); - if (!FAILED(hr)) - { - _ASSERTE(*ppCoreCLRFoundAssembly != NULL); - (*ppCoreCLRFoundAssembly)->SetBinder(this); - } - - return hr; -} - -HRESULT CustomAssemblyBinder::BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName, - BINDER_SPACE::Assembly** ppAssembly) -{ - // When LoadContext needs to resolve an assembly reference, it will go through the following lookup order: - // - // 1) Lookup the assembly within the LoadContext itself. If assembly is found, use it. - // 2) Invoke the LoadContext's Load method implementation. If assembly is found, use it. - // 3) Lookup the assembly within DefaultBinder (except for satellite requests). If assembly is found, use it. - // 4) Invoke the LoadContext's ResolveSatelliteAssembly method (for satellite requests). If assembly is found, use it. - // 5) Invoke the LoadContext's Resolving event. If assembly is found, use it. - // 6) Raise exception. - // - // This approach enables a LoadContext to override assemblies that have been loaded in TPA context by loading - // a different (or even the same!) version. - - HRESULT hr = S_OK; - ReleaseHolder pCoreCLRFoundAssembly; - - { - // Step 1 - Try to find the assembly within the LoadContext. - hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly); - if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || - (hr == FUSION_E_APP_DOMAIN_LOCKED) || (hr == FUSION_E_REF_DEF_MISMATCH)) - { - // If we are here, one of the following is possible: - // - // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR - // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def - // mismatch (either due to version difference or strong-name difference). - // - // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call - // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly - // that has been loaded. - // - hr = AssemblyBinderCommon::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, - m_pDefaultBinder, this, &pCoreCLRFoundAssembly); - if (SUCCEEDED(hr)) - { - // We maybe returned an assembly that was bound to a different AssemblyBinder instance. - // In such a case, we will not overwrite the binder (which would be wrong since the assembly would not - // be present in the cache of the current binding context). - if (pCoreCLRFoundAssembly->GetBinder() == NULL) - { - pCoreCLRFoundAssembly->SetBinder(this); - } - } - } - } - - IF_FAIL_GO(hr); - - // Extract the assembly reference. - // - // For TPA assemblies that were bound, DefaultBinder - // would have already set the binder reference for the assembly, so we just need to - // extract the reference now. - *ppAssembly = pCoreCLRFoundAssembly.Extract(); - -Exit:; - - return hr; -} - -HRESULT CustomAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage, - /* in */ bool excludeAppPaths, - /* [retval][out] */ BINDER_SPACE::Assembly **ppAssembly) -{ - HRESULT hr = S_OK; - - EX_TRY - { - ReleaseHolder pCoreCLRFoundAssembly; - ReleaseHolder pAssemblyName; - - // Using the information we just got, initialize the assemblyname - SAFE_NEW(pAssemblyName, BINDER_SPACE::AssemblyName); - IF_FAIL_GO(pAssemblyName->Init(pPEImage)); - - // Validate architecture - if (!AssemblyBinderCommon::IsValidArchitecture(pAssemblyName->GetArchitecture())) - { - IF_FAIL_GO(CLR_E_BIND_ARCHITECTURE_MISMATCH); - } - - // Disallow attempt to bind to the core library. Aside from that, - // the LoadContext can load any assembly (even if it was in a different LoadContext like TPA). - if (pAssemblyName->IsCoreLib()) - { - IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); - } - - hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, excludeAppPaths, &pCoreCLRFoundAssembly); - if (hr == S_OK) - { - _ASSERTE(pCoreCLRFoundAssembly != NULL); - pCoreCLRFoundAssembly->SetBinder(this); - *ppAssembly = pCoreCLRFoundAssembly.Extract(); - } -Exit:; - } - EX_CATCH_HRESULT(hr); - - return hr; -} - AssemblyLoaderAllocator* CustomAssemblyBinder::GetLoaderAllocator() { return m_pAssemblyLoaderAllocator; @@ -161,7 +28,7 @@ HRESULT CustomAssemblyBinder::SetupContext(DefaultAssemblyBinder *pDefaultBinder UINT_PTR ptrAssemblyLoadContext, CustomAssemblyBinder **ppBindContext) { - HRESULT hr = E_FAIL; + HRESULT hr = S_OK; EX_TRY { if(ppBindContext != NULL) @@ -169,7 +36,6 @@ HRESULT CustomAssemblyBinder::SetupContext(DefaultAssemblyBinder *pDefaultBinder NewHolder pBinder; SAFE_NEW(pBinder, CustomAssemblyBinder); - hr = pBinder->GetAppContext()->Init(); if(SUCCEEDED(hr)) { // Save reference to the DefaultBinder that is required to be present. diff --git a/src/coreclr/binder/defaultassemblybinder.cpp b/src/coreclr/binder/defaultassemblybinder.cpp deleted file mode 100644 index 54a70e51f05ec..0000000000000 --- a/src/coreclr/binder/defaultassemblybinder.cpp +++ /dev/null @@ -1,212 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "assemblybindercommon.hpp" -#include "defaultassemblybinder.h" - -using namespace BINDER_SPACE; - -//============================================================================= -// Helper functions -//----------------------------------------------------------------------------- - -HRESULT DefaultAssemblyBinder::BindAssemblyByNameWorker(BINDER_SPACE::AssemblyName *pAssemblyName, - BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly, - bool excludeAppPaths) -{ - VALIDATE_ARG_RET(pAssemblyName != nullptr && ppCoreCLRFoundAssembly != nullptr); - HRESULT hr = S_OK; - -#ifdef _DEBUG - // CoreLib should be bound using BindToSystem - _ASSERTE(!pAssemblyName->IsCoreLib()); -#endif - - hr = AssemblyBinderCommon::BindAssembly(this, - pAssemblyName, - excludeAppPaths, - ppCoreCLRFoundAssembly); - if (!FAILED(hr)) - { - (*ppCoreCLRFoundAssembly)->SetBinder(this); - } - - return hr; -} - -// ============================================================================ -// DefaultAssemblyBinder implementation -// ============================================================================ -HRESULT DefaultAssemblyBinder::BindUsingAssemblyName(BINDER_SPACE::AssemblyName *pAssemblyName, - BINDER_SPACE::Assembly **ppAssembly) -{ - HRESULT hr = S_OK; - VALIDATE_ARG_RET(pAssemblyName != nullptr && ppAssembly != nullptr); - - *ppAssembly = nullptr; - - ReleaseHolder pCoreCLRFoundAssembly; - - hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly, false /* excludeAppPaths */); - -#if !defined(DACCESS_COMPILE) - if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || - (hr == FUSION_E_APP_DOMAIN_LOCKED) || (hr == FUSION_E_REF_DEF_MISMATCH)) - { - // If we are here, one of the following is possible: - // - // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR - // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def - // mismatch (either due to version difference or strong-name difference). - // - // Attempt to resolve the assembly via managed ALC instance. This can either fail the bind or return reference to an existing - // assembly that has been loaded - INT_PTR pManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(); - if (pManagedAssemblyLoadContext == (INT_PTR)NULL) - { - // For satellite assemblies, the managed ALC has additional resolution logic (defined by the runtime) which - // should be run even if the managed default ALC has not yet been used. (For non-satellite assemblies, any - // additional logic comes through a user-defined event handler which would have initialized the managed ALC, - // so if the managed ALC is not set yet, there is no additional logic to run) - if (!pAssemblyName->IsNeutralCulture()) - { - // Make sure the managed default ALC is initialized. - GCX_COOP(); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__INITIALIZE_DEFAULT_CONTEXT); - DECLARE_ARGHOLDER_ARRAY(args, 0); - CALL_MANAGED_METHOD_NORET(args) - - pManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(); - _ASSERTE(pManagedAssemblyLoadContext != (INT_PTR)NULL); - } - } - - if (pManagedAssemblyLoadContext != (INT_PTR)NULL) - { - hr = AssemblyBinderCommon::BindUsingHostAssemblyResolver(pManagedAssemblyLoadContext, pAssemblyName, - NULL, this, &pCoreCLRFoundAssembly); - if (SUCCEEDED(hr)) - { - // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance. - // In such a case, we will not overwrite the binding context (which would be wrong since it would not - // be present in the cache of the current binding context). - if (pCoreCLRFoundAssembly->GetBinder() == NULL) - { - pCoreCLRFoundAssembly->SetBinder(this); - } - } - } - } -#endif // !defined(DACCESS_COMPILE) - - IF_FAIL_GO(hr); - - *ppAssembly = pCoreCLRFoundAssembly.Extract(); - -Exit:; - - return hr; -} - -#if !defined(DACCESS_COMPILE) -HRESULT DefaultAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage, - /* in */ bool excludeAppPaths, - /* [retval][out] */ BINDER_SPACE::Assembly **ppAssembly) -{ - HRESULT hr = S_OK; - - EX_TRY - { - ReleaseHolder pCoreCLRFoundAssembly; - ReleaseHolder pAssemblyName; - - // Using the information we just got, initialize the assemblyname - SAFE_NEW(pAssemblyName, AssemblyName); - IF_FAIL_GO(pAssemblyName->Init(pPEImage)); - - // Validate architecture - if (!AssemblyBinderCommon::IsValidArchitecture(pAssemblyName->GetArchitecture())) - { - IF_FAIL_GO(CLR_E_BIND_ARCHITECTURE_MISMATCH); - } - - // Easy out for CoreLib - if (pAssemblyName->IsCoreLib()) - { - IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); - } - - { - // Ensure we are not being asked to bind to a TPA assembly - // - const SString& simpleName = pAssemblyName->GetSimpleName(); - SimpleNameToFileNameMap* tpaMap = GetAppContext()->GetTpaList(); - if (tpaMap->LookupPtr(simpleName.GetUnicode()) != NULL) - { - // The simple name of the assembly being requested to be bound was found in the TPA list. - // Now, perform the actual bind to see if the assembly was really in the TPA assembly list or not. - hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly, true /* excludeAppPaths */); - if (SUCCEEDED(hr)) - { - if (pCoreCLRFoundAssembly->GetIsInTPA()) - { - *ppAssembly = pCoreCLRFoundAssembly.Extract(); - goto Exit; - } - } - } - } - - hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, excludeAppPaths, &pCoreCLRFoundAssembly); - if (hr == S_OK) - { - _ASSERTE(pCoreCLRFoundAssembly != NULL); - pCoreCLRFoundAssembly->SetBinder(this); - *ppAssembly = pCoreCLRFoundAssembly.Extract(); - } -Exit:; - } - EX_CATCH_HRESULT(hr); - - return hr; -} -#endif // !defined(DACCESS_COMPILE) - -HRESULT DefaultAssemblyBinder::SetupBindingPaths(SString &sTrustedPlatformAssemblies, - SString &sPlatformResourceRoots, - SString &sAppPaths) -{ - HRESULT hr = S_OK; - - EX_TRY - { - hr = GetAppContext()->SetupBindingPaths(sTrustedPlatformAssemblies, sPlatformResourceRoots, sAppPaths, TRUE /* fAcquireLock */); - } - EX_CATCH_HRESULT(hr); - return hr; -} - -HRESULT DefaultAssemblyBinder::BindToSystem(BINDER_SPACE::Assembly** ppSystemAssembly) -{ - HRESULT hr = S_OK; - _ASSERTE(ppSystemAssembly != NULL); - - EX_TRY - { - ReleaseHolder pAsm; - StackSString systemPath(SystemDomain::System()->SystemDirectory()); - hr = AssemblyBinderCommon::BindToSystem(systemPath, &pAsm); - if (SUCCEEDED(hr)) - { - _ASSERTE(pAsm != NULL); - *ppSystemAssembly = pAsm.Extract(); - (*ppSystemAssembly)->SetBinder(this); - } - - } - EX_CATCH_HRESULT(hr); - - return hr; -} - diff --git a/src/coreclr/binder/failurecache.cpp b/src/coreclr/binder/failurecache.cpp deleted file mode 100644 index c6e1f286fbb7e..0000000000000 --- a/src/coreclr/binder/failurecache.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// FailureCache.cpp -// - - -// -// Implements the FailureCache class -// -// ============================================================ - -#include "failurecache.hpp" - -namespace BINDER_SPACE -{ - FailureCache::FailureCache() : SHash::SHash() - { - // Nothing to do here - } - - FailureCache::~FailureCache() - { - // Delete entries and contents array - for (Hash::Iterator i = Hash::Begin(), end = Hash::End(); i != end; i++) - { - const FailureCacheEntry *pFailureCacheEntry = *i; - delete pFailureCacheEntry; - } - RemoveAll(); - } - - HRESULT FailureCache::Add(SString &assemblyNameorPath, - HRESULT hrBindingResult) - { - HRESULT hr = S_OK; - - NewHolder pFailureCacheEntry; - SAFE_NEW(pFailureCacheEntry, FailureCacheEntry); - - // No error occurred; report the original error - hr = hrBindingResult; - - pFailureCacheEntry->GetAssemblyNameOrPath().Set(assemblyNameorPath); - pFailureCacheEntry->SetBindingResult(hrBindingResult); - - Hash::Add(pFailureCacheEntry); - pFailureCacheEntry.SuppressRelease(); - - Exit: - return hr; - } - - HRESULT FailureCache::Lookup(SString &assemblyNameorPath) - { - HRESULT hr = S_OK; - FailureCacheEntry *pFailureCachEntry = Hash::Lookup(assemblyNameorPath); - - if (pFailureCachEntry != NULL) - { - hr = pFailureCachEntry->GetBindingResult(); - } - - return hr; - } - - void FailureCache::Remove(SString &assemblyName) - { - FailureCacheEntry *pFailureCachEntry = Hash::Lookup(assemblyName); - - // Hash::Remove does not clean up entries - Hash::Remove(assemblyName); - SAFE_DELETE(pFailureCachEntry); - } -}; diff --git a/src/coreclr/binder/inc/applicationcontext.hpp b/src/coreclr/binder/inc/applicationcontext.hpp deleted file mode 100644 index 721f571af1e5d..0000000000000 --- a/src/coreclr/binder/inc/applicationcontext.hpp +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// ApplicationContext.hpp -// - - -// -// Defines the ApplicationContext class -// -// ============================================================ - -#ifndef __BINDER__APPLICATION_CONTEXT_HPP__ -#define __BINDER__APPLICATION_CONTEXT_HPP__ - -#include "bindertypes.hpp" -#include "failurecache.hpp" -#include "stringarraylist.h" - -namespace BINDER_SPACE -{ - //============================================================================================= - // Data structures for Simple Name -> File Name hash - - // Entry in SHash table that maps namespace to list of files - struct SimpleNameToFileNameMapEntry - { - LPWSTR m_wszSimpleName; - LPWSTR m_wszILFileName; - LPWSTR m_wszNIFileName; - }; - - // SHash traits for Namespace -> FileNameList hash - class SimpleNameToFileNameMapTraits : public NoRemoveSHashTraits< DefaultSHashTraits< SimpleNameToFileNameMapEntry > > - { - public: - typedef PCWSTR key_t; - static const SimpleNameToFileNameMapEntry Null() { SimpleNameToFileNameMapEntry e; e.m_wszSimpleName = nullptr; return e; } - static bool IsNull(const SimpleNameToFileNameMapEntry & e) { return e.m_wszSimpleName == nullptr; } - static key_t GetKey(const SimpleNameToFileNameMapEntry & e) - { - key_t key; - key = e.m_wszSimpleName; - return key; - } - static count_t Hash(const key_t &str) - { - SString ssKey(SString::Literal, str); - return ssKey.HashCaseInsensitive(); - } - static BOOL Equals(const key_t &lhs, const key_t &rhs) { LIMITED_METHOD_CONTRACT; return (SString::_wcsicmp(lhs, rhs) == 0); } - - void OnDestructPerEntryCleanupAction(const SimpleNameToFileNameMapEntry & e) - { - if (e.m_wszILFileName == nullptr && e.m_wszNIFileName == nullptr) - { - // Don't delete simple name here since it's a filename only entry and will be cleaned up - // by the SimpleName -> FileName entry which reuses the same filename pointer. - return; - } - - if (e.m_wszSimpleName != nullptr) - { - delete [] e.m_wszSimpleName; - } - if (e.m_wszILFileName != nullptr) - { - delete [] e.m_wszILFileName; - } - if (e.m_wszNIFileName != nullptr) - { - delete [] e.m_wszNIFileName; - } - } - static const bool s_DestructPerEntryCleanupAction = true; - }; - - typedef SHash SimpleNameToFileNameMap; - - class AssemblyHashTraits; - typedef SHash ExecutionContext; - - class ApplicationContext - { - public: - // ApplicationContext methods - ApplicationContext(); - ~ApplicationContext(); - HRESULT Init(); - - inline SString &GetApplicationName(); - - HRESULT SetupBindingPaths(/* in */ SString &sTrustedPlatformAssemblies, - /* in */ SString &sPlatformResourceRoots, - /* in */ SString &sAppPaths, - /* in */ BOOL fAcquireLock); - - // Getters/Setter - inline ExecutionContext *GetExecutionContext(); - inline FailureCache *GetFailureCache(); - inline HRESULT AddToFailureCache(SString &assemblyNameOrPath, - HRESULT hrBindResult); - inline StringArrayList *GetAppPaths(); - inline SimpleNameToFileNameMap *GetTpaList(); - inline StringArrayList *GetPlatformResourceRoots(); - - // Using a host-configured Trusted Platform Assembly list - bool IsTpaListProvided(); - inline CRITSEC_COOKIE GetCriticalSectionCookie(); - inline LONG GetVersion(); - inline void IncrementVersion(); - - private: - Volatile m_cVersion; - SString m_applicationName; - ExecutionContext *m_pExecutionContext; - FailureCache *m_pFailureCache; - CRITSEC_COOKIE m_contextCS; - - StringArrayList m_platformResourceRoots; - StringArrayList m_appPaths; - - SimpleNameToFileNameMap * m_pTrustedPlatformAssemblyMap; - }; - -#include "applicationcontext.inl" - -}; - -#endif diff --git a/src/coreclr/binder/inc/applicationcontext.inl b/src/coreclr/binder/inc/applicationcontext.inl deleted file mode 100644 index 16ec0c6d87de2..0000000000000 --- a/src/coreclr/binder/inc/applicationcontext.inl +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// ApplicationContext.inl -// - - -// -// Implements inlined methods of ApplicationContext -// -// ============================================================ - -#ifndef __BINDER__APPLICATION_CONTEXT_INL__ -#define __BINDER__APPLICATION_CONTEXT_INL__ - -LONG ApplicationContext::GetVersion() -{ - return m_cVersion; -} - -void ApplicationContext::IncrementVersion() -{ - InterlockedIncrement(&m_cVersion); -} - -SString &ApplicationContext::GetApplicationName() -{ - return m_applicationName; -} - -ExecutionContext *ApplicationContext::GetExecutionContext() -{ - return m_pExecutionContext; -} - -FailureCache *ApplicationContext::GetFailureCache() -{ - _ASSERTE(m_pFailureCache != NULL); - return m_pFailureCache; -} - -HRESULT ApplicationContext::AddToFailureCache(SString &assemblyNameOrPath, - HRESULT hrBindResult) -{ - HRESULT hr = GetFailureCache()->Add(assemblyNameOrPath, hrBindResult); - IncrementVersion(); - return hr; -} - -StringArrayList *ApplicationContext::GetAppPaths() -{ - return &m_appPaths; -} - -SimpleNameToFileNameMap * ApplicationContext::GetTpaList() -{ - return m_pTrustedPlatformAssemblyMap; -} - -StringArrayList * ApplicationContext::GetPlatformResourceRoots() -{ - return &m_platformResourceRoots; -} - -CRITSEC_COOKIE ApplicationContext::GetCriticalSectionCookie() -{ - return m_contextCS; -} - -#endif diff --git a/src/coreclr/binder/inc/assembly.hpp b/src/coreclr/binder/inc/assembly.hpp deleted file mode 100644 index 908e938793265..0000000000000 --- a/src/coreclr/binder/inc/assembly.hpp +++ /dev/null @@ -1,94 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// Assembly.hpp -// - - -// -// Defines the Assembly class -// -// ============================================================ - -#ifndef __BINDER__ASSEMBLY_HPP__ -#define __BINDER__ASSEMBLY_HPP__ - -#include "bindertypes.hpp" -#include "assemblyname.hpp" - -#include "corpriv.h" - -#include "defaultassemblybinder.h" - -#if !defined(DACCESS_COMPILE) -#include "customassemblybinder.h" -#endif // !defined(DACCESS_COMPILE) - -#include "bundle.h" -#include - -class DomainAssembly; - -namespace BINDER_SPACE -{ - // BINDER_SPACE::Assembly represents a result of binding to an actual assembly (PEImage) - // It is basically a tuple of 1) physical assembly and 2) binder which created/owns this binding - // We also store whether it was bound using TPA list - class Assembly - { - public: - ULONG AddRef(); - ULONG Release(); - - Assembly(); - virtual ~Assembly(); - - HRESULT Init(PEImage *pPEImage, BOOL fIsInTPA); - - LPCWSTR GetSimpleName(); - AssemblyName *GetAssemblyName(BOOL fAddRef = FALSE); - PEImage* GetPEImage(); - BOOL GetIsInTPA(); - - PTR_AssemblyBinder GetBinder() - { - return m_pBinder; - } - - DomainAssembly* GetDomainAssembly() - { - return m_domainAssembly; - } - - void SetDomainAssembly(DomainAssembly* value) - { - _ASSERTE(value == NULL || m_domainAssembly == NULL); - m_domainAssembly = value; - } - - private: - LONG m_cRef; - PEImage *m_pPEImage; - AssemblyName *m_pAssemblyName; - PTR_AssemblyBinder m_pBinder; - bool m_isInTPA; - DomainAssembly *m_domainAssembly; - -#if !defined(DACCESS_COMPILE) - inline void SetBinder(AssemblyBinder *pBinder) - { - _ASSERTE(m_pBinder == NULL || m_pBinder == pBinder); - m_pBinder = pBinder; - } - - friend class ::CustomAssemblyBinder; -#endif // !defined(DACCESS_COMPILE) - - friend class ::DefaultAssemblyBinder; - }; - -#include "assembly.inl" -}; - -#endif diff --git a/src/coreclr/binder/inc/assembly.inl b/src/coreclr/binder/inc/assembly.inl deleted file mode 100644 index a256be8ff5196..0000000000000 --- a/src/coreclr/binder/inc/assembly.inl +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// Assembly.inl -// - - -// -// Implements the inline methods of Assembly -// -// ============================================================ - -#ifndef __BINDER__ASSEMBLY_INL__ -#define __BINDER__ASSEMBLY_INL__ - -inline ULONG Assembly::AddRef() -{ - return InterlockedIncrement(&m_cRef); -} - -inline ULONG Assembly::Release() -{ - ULONG ulRef = InterlockedDecrement(&m_cRef); - - if (ulRef == 0) - { - delete this; - } - - return ulRef; -} - -inline AssemblyName *Assembly::GetAssemblyName(BOOL fAddRef /* = FALSE */) -{ - AssemblyName *pAssemblyName = m_pAssemblyName; - - if (fAddRef && (pAssemblyName != NULL)) - { - pAssemblyName->AddRef(); - } - return pAssemblyName; -} - -inline BOOL Assembly::GetIsInTPA() -{ - return m_isInTPA; -} - -#endif diff --git a/src/coreclr/binder/inc/assemblybindercommon.hpp b/src/coreclr/binder/inc/assemblybindercommon.hpp index cdc091b980244..d67e859838735 100644 --- a/src/coreclr/binder/inc/assemblybindercommon.hpp +++ b/src/coreclr/binder/inc/assemblybindercommon.hpp @@ -15,7 +15,6 @@ #define __BINDER__ASSEMBLY_BINDER_COMMON_HPP__ #include "bindertypes.hpp" -#include "bindresult.hpp" #include "bundle.h" class AssemblyBinder; @@ -28,76 +27,14 @@ namespace BINDER_SPACE class AssemblyBinderCommon { public: - static HRESULT BindAssembly(/* in */ AssemblyBinder *pBinder, - /* in */ AssemblyName *pAssemblyName, - /* in */ bool excludeAppPaths, - /* out */ Assembly **ppAssembly); - static HRESULT BindToSystem(/* in */ SString &systemDirectory, - /* out */ Assembly **ppSystemAssembly); - - static HRESULT BindToSystemSatellite(/* in */ SString &systemDirectory, - /* in */ SString &simpleName, - /* in */ SString &cultureName, - /* out */ Assembly **ppSystemAssembly); - - static HRESULT GetAssembly(/* in */ SString &assemblyPath, - /* in */ BOOL fIsInTPA, - /* out */ Assembly **ppAssembly, - /* in */ BundleFileLocation bundleFileLocation = BundleFileLocation::Invalid()); - -#if !defined(DACCESS_COMPILE) - static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, - /* in */ AssemblyName *pAssemblyName, - /* in */ DefaultAssemblyBinder *pDefaultBinder, - /* in */ AssemblyBinder *pBinder, - /* out */ Assembly **ppAssembly); - - static HRESULT BindUsingPEImage(/* in */ AssemblyBinder *pBinder, - /* in */ BINDER_SPACE::AssemblyName *pAssemblyName, - /* in */ PEImage *pPEImage, - /* in */ bool excludeAppPaths, - /* [retval] [out] */ Assembly **ppAssembly); -#endif // !defined(DACCESS_COMPILE) + /* out */ PEImage **ppPEImage); static HRESULT TranslatePEToArchitectureType(DWORD *pdwPAFlags, PEKIND *PeKind); static HRESULT CreateDefaultBinder(DefaultAssemblyBinder** ppDefaultBinder); static BOOL IsValidArchitecture(PEKIND kArchitecture); - - private: - static HRESULT BindByName(/* in */ ApplicationContext *pApplicationContext, - /* in */ AssemblyName *pAssemblyName, - /* in */ bool skipFailureCaching, - /* in */ bool skipVersionCompatibilityCheck, - /* in */ bool excludeAppPaths, - /* out */ BindResult *pBindResult); - - static HRESULT BindLocked(/* in */ ApplicationContext *pApplicationContext, - /* in */ AssemblyName *pAssemblyName, - /* in */ bool skipVersionCompatibilityCheck, - /* in */ bool excludeAppPaths, - /* out */ BindResult *pBindResult); - - static HRESULT FindInExecutionContext(/* in */ ApplicationContext *pApplicationContext, - /* in */ AssemblyName *pAssemblyName, - /* out */ Assembly **ppAssembly); - - static HRESULT BindByTpaList(/* in */ ApplicationContext *pApplicationContext, - /* in */ AssemblyName *pRequestedAssemblyName, - /* in */ bool excludeAppPaths, - /* out */ BindResult *pBindResult); - - static HRESULT Register(/* in */ ApplicationContext *pApplicationContext, - /* in */ BindResult *pBindResult); - static HRESULT RegisterAndGetHostChosen(/* in */ ApplicationContext *pApplicationContext, - /* in */ LONG kContextVersion, - /* in */ BindResult *pBindResult, - /* out */ BindResult *pHostBindResult); - - static HRESULT OtherBindInterfered(/* in */ ApplicationContext *pApplicationContext, - /* in */ BindResult *pBindResult); }; }; diff --git a/src/coreclr/binder/inc/assemblyhashtraits.hpp b/src/coreclr/binder/inc/assemblyhashtraits.hpp deleted file mode 100644 index c9d0cd2d7c71d..0000000000000 --- a/src/coreclr/binder/inc/assemblyhashtraits.hpp +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// AssemblyHashTraits.hpp -// - - -// -// Defines the AssemblyHashTraits template class -// -// ============================================================ - -#ifndef __BINDER__ASSEMBLY_HASH_TRAITS_HPP__ -#define __BINDER__ASSEMBLY_HASH_TRAITS_HPP__ - -#include "bindertypes.hpp" -#include "assembly.hpp" -#include "shash.h" - -namespace BINDER_SPACE -{ - class AssemblyHashTraits : public NoRemoveSHashTraits> - { - public: - typedef AssemblyName* key_t; - - // GetKey, Equals and Hash can throw due to SString - static const bool s_NoThrow = false; - - static key_t GetKey(const element_t& entry) - { - return entry->GetAssemblyName(); - } - static BOOL Equals(key_t pAssemblyName1, key_t pAssemblyName2) - { - return pAssemblyName1->Equals(pAssemblyName2, AssemblyName::INCLUDE_DEFAULT); - } - static count_t Hash(key_t pAssemblyName) - { - return pAssemblyName->Hash(AssemblyName::INCLUDE_DEFAULT); - } - - static const bool s_DestructPerEntryCleanupAction = true; - static void OnDestructPerEntryCleanupAction(element_t elem) - { - elem->Release(); - } - }; -}; - -#endif diff --git a/src/coreclr/binder/inc/bindertracing.h b/src/coreclr/binder/inc/bindertracing.h index d4442086ffcfc..d6efc028b69af 100644 --- a/src/coreclr/binder/inc/bindertracing.h +++ b/src/coreclr/binder/inc/bindertracing.h @@ -13,7 +13,6 @@ class PEAssembly; namespace BINDER_SPACE { - class Assembly; class AssemblyName; } @@ -92,53 +91,6 @@ namespace BinderTracing public: // static static void TraceAppDomainAssemblyResolve(AssemblySpec *spec, PEAssembly *resultAssembly, Exception *exception = nullptr); - public: - // One of native bindContext or managedALC is expected to be non-zero. If the managed ALC is set, bindContext is ignored. - ResolutionAttemptedOperation(BINDER_SPACE::AssemblyName *assemblyName, AssemblyBinder* bindContext, INT_PTR managedALC, const HRESULT& hr); - - void TraceBindResult(const BINDER_SPACE::BindResult &bindResult, bool mvidMismatch = false); - - void SetFoundAssembly(BINDER_SPACE::Assembly *assembly) - { - m_pFoundAssembly = assembly; - } - - void GoToStage(Stage stage) - { - assert(m_stage != stage); - assert(stage != Stage::NotYetStarted); - - if (!m_tracingEnabled) - return; - - // Going to a different stage should only happen if the current - // stage failed (or if the binding process wasn't yet started). - // Firing the event at this point not only helps timing each binding - // stage, but avoids keeping track of which stages were reached to - // resolve the assembly. - TraceStage(m_stage, m_hr, m_pFoundAssembly); - m_stage = stage; - m_exceptionMessage.Clear(); - } - - void SetException(Exception *ex) - { - if (!m_tracingEnabled) - return; - - ex->GetMessage(m_exceptionMessage); - } - -#ifdef FEATURE_EVENT_TRACE - ~ResolutionAttemptedOperation() - { - if (!m_tracingEnabled) - return; - - TraceStage(m_stage, m_hr, m_pFoundAssembly); - } -#endif // FEATURE_EVENT_TRACE - private: // This must match the ResolutionAttemptedResult value map in ClrEtwAll.man @@ -151,25 +103,6 @@ namespace BinderTracing Failure = 4, Exception = 5, }; - - // A reference to an HRESULT stored in the same scope as this object lets - // us determine if the last requested stage was successful or not, regardless - // if it was set through a function call (e.g. BindAssemblyByNameWorker()), or - // if an exception was thrown and captured by the EX_CATCH_HRESULT() macro. - const HRESULT &m_hr; - - Stage m_stage; - - bool m_tracingEnabled; - - BINDER_SPACE::AssemblyName *m_assemblyNameObject; - PathString m_assemblyName; - SString m_assemblyLoadContextName; - - SString m_exceptionMessage; - BINDER_SPACE::Assembly *m_pFoundAssembly; - - void TraceStage(Stage stage, HRESULT hr, BINDER_SPACE::Assembly *resultAssembly, const WCHAR *errorMessage = nullptr); }; // This must match the BindingPathSource value map in ClrEtwAll.man diff --git a/src/coreclr/binder/inc/bindertypes.hpp b/src/coreclr/binder/inc/bindertypes.hpp index 8ec573f8040d5..8592f41b7dbb6 100644 --- a/src/coreclr/binder/inc/bindertypes.hpp +++ b/src/coreclr/binder/inc/bindertypes.hpp @@ -21,11 +21,6 @@ namespace BINDER_SPACE { class AssemblyVersion; class AssemblyName; - class Assembly; - - class ApplicationContext; - - class BindResult; }; typedef enum __AssemblyContentType diff --git a/src/coreclr/binder/inc/bindresult.hpp b/src/coreclr/binder/inc/bindresult.hpp deleted file mode 100644 index 88d1a2f4b221c..0000000000000 --- a/src/coreclr/binder/inc/bindresult.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// BindResult.hpp -// - - -// -// Defines the BindResult class -// -// ============================================================ - -#ifndef __BINDER__BIND_RESULT_HPP__ -#define __BINDER__BIND_RESULT_HPP__ - -#include "bindertypes.hpp" - -namespace BINDER_SPACE -{ - class BindResult - { - public: - inline BindResult(); - - inline AssemblyName *GetAssemblyName(BOOL fAddRef = FALSE); - inline Assembly *GetAssembly(BOOL fAddRef = FALSE); - - inline BOOL GetIsContextBound(); - - inline void SetResult(Assembly *pAssembly, bool isInContext = false); - inline void SetResult(BindResult *pBindResult); - - inline void SetNoResult(); - inline BOOL HaveResult(); - - inline void Reset(); - - struct AttemptResult - { - HRESULT HResult; - ReleaseHolder Assembly; - bool Attempted = false; - - void Set(const AttemptResult *result); - - void Reset() - { - Assembly = nullptr; - Attempted = false; - } - }; - - // Set attempt result for binding to existing context entry or platform assemblies - void SetAttemptResult(HRESULT hr, Assembly *pAssembly, bool isInContext = false); - - const AttemptResult* GetAttempt(bool foundInContext) const; - - protected: - bool m_isContextBound; - ReleaseHolder m_pAssembly; - - AttemptResult m_inContextAttempt; - AttemptResult m_applicationAssembliesAttempt; - }; -}; - -#endif diff --git a/src/coreclr/binder/inc/bindresult.inl b/src/coreclr/binder/inc/bindresult.inl deleted file mode 100644 index ab4a61c24fbd4..0000000000000 --- a/src/coreclr/binder/inc/bindresult.inl +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// BindResult.inl -// - - -// -// Implements the inline methods of BindResult -// -// ============================================================ - -#ifndef __BINDER__BIND_RESULT_INL__ -#define __BINDER__BIND_RESULT_INL__ - -#include "assembly.hpp" - -namespace BINDER_SPACE -{ -BindResult::BindResult() -{ - m_isContextBound = false; - m_pAssembly = NULL; -} - -AssemblyName *BindResult::GetAssemblyName(BOOL fAddRef /* = FALSE */) -{ - Assembly *pAssembly = m_pAssembly; - if (pAssembly == NULL) - return NULL; - - return pAssembly->GetAssemblyName(fAddRef); -} - -Assembly *BindResult::GetAssembly(BOOL fAddRef /* = FALSE */) -{ - Assembly* pAssembly = m_pAssembly; - - if (fAddRef && (pAssembly != NULL)) - { - pAssembly->AddRef(); - } - - return pAssembly; -} - -BOOL BindResult::GetIsContextBound() -{ - return m_isContextBound; -} - -void BindResult::SetResult(Assembly *pAssembly, bool isInContext) -{ - _ASSERTE(pAssembly != NULL); - - pAssembly->AddRef(); - m_pAssembly = pAssembly; - m_isContextBound = isInContext; -} - -void BindResult::SetResult(BindResult *pBindResult) -{ - _ASSERTE(pBindResult != NULL); - - m_isContextBound = pBindResult->m_isContextBound; - m_pAssembly = pBindResult->GetAssembly(TRUE /* fAddRef */); - - const AttemptResult *attempt = pBindResult->GetAttempt(true /*foundInContext*/); - if (attempt != nullptr) - m_inContextAttempt.Set(attempt); - - attempt = pBindResult->GetAttempt(false /*foundInContext*/); - if (attempt != nullptr) - m_applicationAssembliesAttempt.Set(attempt); -} - -void BindResult::SetNoResult() -{ - m_pAssembly = NULL; -} - -BOOL BindResult::HaveResult() -{ - return m_pAssembly != NULL; -} - -void BindResult::Reset() -{ - m_pAssembly = NULL; - m_isContextBound = false; - m_inContextAttempt.Reset(); - m_applicationAssembliesAttempt.Reset(); -} - -void BindResult::SetAttemptResult(HRESULT hr, Assembly *pAssembly, bool isInContext) -{ - if (pAssembly != nullptr) - pAssembly->AddRef(); - - BindResult::AttemptResult &result = isInContext ? m_inContextAttempt : m_applicationAssembliesAttempt; - result.Assembly = pAssembly; - result.HResult = hr; - result.Attempted = true; -} - -const BindResult::AttemptResult* BindResult::GetAttempt(bool foundInContext) const -{ - const BindResult::AttemptResult &result = foundInContext ? m_inContextAttempt : m_applicationAssembliesAttempt; - return result.Attempted ? &result : nullptr; -} - -void BindResult::AttemptResult::Set(const BindResult::AttemptResult *result) -{ - BINDER_SPACE::Assembly *assembly = result->Assembly; - if (assembly != nullptr) - assembly->AddRef(); - - Assembly = assembly; - HResult = result->HResult; - Attempted = result->Attempted; -} - -} -#endif diff --git a/src/coreclr/binder/inc/customassemblybinder.h b/src/coreclr/binder/inc/customassemblybinder.h index 7a89c5033b9e5..1e6c9bda1bfef 100644 --- a/src/coreclr/binder/inc/customassemblybinder.h +++ b/src/coreclr/binder/inc/customassemblybinder.h @@ -5,7 +5,6 @@ #ifndef __CUSTOM_ASSEMBLY_BINDER_H__ #define __CUSTOM_ASSEMBLY_BINDER_H__ -#include "applicationcontext.hpp" #include "defaultassemblybinder.h" #if !defined(DACCESS_COMPILE) @@ -17,13 +16,6 @@ class CustomAssemblyBinder final : public AssemblyBinder { public: - HRESULT BindUsingPEImage(PEImage* pPEImage, - bool excludeAppPaths, - BINDER_SPACE::Assembly** ppAssembly) override; - - HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName, - BINDER_SPACE::Assembly** ppAssembly) override; - AssemblyLoaderAllocator* GetLoaderAllocator() override; bool IsDefault() override @@ -48,8 +40,6 @@ class CustomAssemblyBinder final : public AssemblyBinder void ReleaseLoadContext(); private: - HRESULT BindAssemblyByNameWorker(BINDER_SPACE::AssemblyName *pAssemblyName, BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly); - DefaultAssemblyBinder *m_pDefaultBinder; // A strong GC handle to the managed AssemblyLoadContext. This handle is set when the unload of the AssemblyLoadContext is initiated diff --git a/src/coreclr/binder/inc/defaultassemblybinder.h b/src/coreclr/binder/inc/defaultassemblybinder.h index 3d35854e09f3f..420fc6b9e332e 100644 --- a/src/coreclr/binder/inc/defaultassemblybinder.h +++ b/src/coreclr/binder/inc/defaultassemblybinder.h @@ -5,7 +5,6 @@ #ifndef __DEFAULT_ASSEMBLY_BINDER_H__ #define __DEFAULT_ASSEMBLY_BINDER_H__ -#include "applicationcontext.hpp" #include "assemblybinder.h" class PEAssembly; @@ -15,13 +14,6 @@ class DefaultAssemblyBinder final : public AssemblyBinder { public: - HRESULT BindUsingPEImage(PEImage* pPEImage, - bool excludeAppPaths, - BINDER_SPACE::Assembly** ppAssembly) override; - - HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName, - BINDER_SPACE::Assembly** ppAssembly) override; - AssemblyLoaderAllocator* GetLoaderAllocator() override { // Not supported by this binder @@ -32,21 +24,6 @@ class DefaultAssemblyBinder final : public AssemblyBinder { return true; } - -public: - - HRESULT SetupBindingPaths(SString &sTrustedPlatformAssemblies, - SString &sPlatformResourceRoots, - SString &sAppPaths); - - HRESULT BindToSystem(BINDER_SPACE::Assembly **ppSystemAssembly); - -private: - - HRESULT BindAssemblyByNameWorker( - BINDER_SPACE::AssemblyName *pAssemblyName, - BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly, - bool excludeAppPaths); }; #endif // __DEFAULT_ASSEMBLY_BINDER_H__ diff --git a/src/coreclr/binder/inc/failurecache.hpp b/src/coreclr/binder/inc/failurecache.hpp deleted file mode 100644 index bce67b33c50c6..0000000000000 --- a/src/coreclr/binder/inc/failurecache.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// FailureCache.hpp -// - - -// -// Defines the FailureCache class -// -// ============================================================ - - -#ifndef __BINDER__FAILURE_CACHE_HPP__ -#define __BINDER__FAILURE_CACHE_HPP__ - -#include "failurecachehashtraits.hpp" - -namespace BINDER_SPACE -{ - class FailureCache : protected SHash - { - private: - typedef SHash Hash; - public: - FailureCache(); - ~FailureCache(); - - HRESULT Add(/* in */ SString &assemblyNameorPath, - /* in */ HRESULT hrBindResult); - HRESULT Lookup(/* in */ SString &assemblyNameorPath); - void Remove(/* in */ SString &assemblyName); - }; -}; - -#endif diff --git a/src/coreclr/binder/inc/failurecachehashtraits.hpp b/src/coreclr/binder/inc/failurecachehashtraits.hpp deleted file mode 100644 index 1ffa96f56b198..0000000000000 --- a/src/coreclr/binder/inc/failurecachehashtraits.hpp +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// FailureCache.hpp -// - - -// -// Defines the FailureCache class -// -// ============================================================ - -#ifndef __BINDER__FAILURE_CACHE_HASH_TRAITS_HPP__ -#define __BINDER__FAILURE_CACHE_HASH_TRAITS_HPP__ - -#include "bindertypes.hpp" -#include "sstring.h" -#include "shash.h" - -namespace BINDER_SPACE -{ - class FailureCacheEntry - { - public: - inline FailureCacheEntry() - { - m_hrBindingResult = S_OK; - } - inline ~FailureCacheEntry() - { - // Nothing to do here - } - - // Getters/Setters - inline SString &GetAssemblyNameOrPath() - { - return m_assemblyNameOrPath; - } - inline HRESULT GetBindingResult() - { - return m_hrBindingResult; - } - inline void SetBindingResult(HRESULT hrBindingResult) - { - m_hrBindingResult = hrBindingResult; - } - - protected: - SString m_assemblyNameOrPath; - HRESULT m_hrBindingResult; - }; - - class FailureCacheHashTraits : public DefaultSHashTraits - { - public: - typedef SString& key_t; - - // GetKey, Equals, and Hash can throw due to SString - static const bool s_NoThrow = false; - - static key_t GetKey(element_t pFailureCacheEntry) - { - return pFailureCacheEntry->GetAssemblyNameOrPath(); - } - static BOOL Equals(key_t pAssemblyNameOrPath1, key_t pAssemblyNameOrPath2) - { - return pAssemblyNameOrPath1.EqualsCaseInsensitive(pAssemblyNameOrPath2); - } - static count_t Hash(key_t pAssemblyNameOrPath) - { - return pAssemblyNameOrPath.HashCaseInsensitive(); - } - static element_t Null() - { - return NULL; - } - static bool IsNull(const element_t &propertyEntry) - { - return (propertyEntry == NULL); - } - - }; -}; - -#endif diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def index 7f94e9e0996a8..30a3e3ef0c37c 100644 --- a/src/coreclr/inc/CrstTypes.def +++ b/src/coreclr/inc/CrstTypes.def @@ -87,6 +87,7 @@ End Crst AssemblyLoader AcquiredBefore DeadlockDetection UniqueStack DebuggerMutex + ThreadStore GlobalStrLiteralMap LoaderAllocatorReferences End Crst AvailableClass diff --git a/src/coreclr/inc/assemblybinderutil.h b/src/coreclr/inc/assemblybinderutil.h index 4d6a6bcce71f7..841fa4cf34654 100644 --- a/src/coreclr/inc/assemblybinderutil.h +++ b/src/coreclr/inc/assemblybinderutil.h @@ -8,11 +8,6 @@ #ifndef __ASSEMBLY_BINDER_UTIL_H__ #define __ASSEMBLY_BINDER_UTIL_H__ -//===================================================================================================================== -// Forward declarations -typedef DPTR(BINDER_SPACE::Assembly) PTR_BINDER_SPACE_Assembly; -typedef DPTR(AssemblyBinder) PTR_AssemblyBinder; - //===================================================================================================================== #define VALIDATE_CONDITION(condition, fail_op) \ do { \ diff --git a/src/coreclr/inc/crsttypes_generated.h b/src/coreclr/inc/crsttypes_generated.h index 79864b97db018..ed8b92d17fd8a 100644 --- a/src/coreclr/inc/crsttypes_generated.h +++ b/src/coreclr/inc/crsttypes_generated.h @@ -147,7 +147,7 @@ int g_rgCrstLevelMap[] = 10, // CrstAppDomainCache 3, // CrstArgBasedStubCache 3, // CrstAssemblyList - 14, // CrstAssemblyLoader + 19, // CrstAssemblyLoader 4, // CrstAvailableClass 5, // CrstAvailableParamTypes 7, // CrstBaseDomain @@ -240,7 +240,7 @@ int g_rgCrstLevelMap[] = 5, // CrstSingleUseLock 0, // CrstSpecialStatics 0, // CrstStackSampler - 15, // CrstStaticBoxInit + 20, // CrstStaticBoxInit -1, // CrstStressLog 5, // CrstStubCache 0, // CrstStubDispatchCache diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 489f2ebc22806..963be04a7061a 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1177,6 +1177,43 @@ void SystemDomain::Init() #endif // _DEBUG } +void SystemDomain::PostStartInit() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + GCX_COOP(); + + // AdHoc setting logic for CoreLib, since managed code isn't available during CoreLib bootstrap + + // Initialize managed default binder and set binder assembly for CoreLib + MethodDescCallSite initializeDefault(METHOD__ASSEMBLYLOADCONTEXT__INITIALIZE_DEFAULT); + ARG_SLOT arg = PtrToArgSlot(m_pSystemAssembly); + OBJECTREF coreLibHostAssembly = initializeDefault.Call_RetOBJECTREF(&arg); + + // Default binder should be initialized + _ASSERTE(GetAppDomain()->GetDefaultBinder()->GetManagedAssemblyLoadContext() != NULL); + + m_pSystemPEAssembly->SetHostAssemblyAdHoc(CreateHandle(coreLibHostAssembly)); + + m_pSystemAssembly->GetDomainAssembly()->RegisterWithHostAssembly(); + + // Managed and unmanaged representations of CoreLib should be correctly linked + _ASSERTE(((BINDERASSEMBLYREF)coreLibHostAssembly)->m_pDomainAssembly == m_pSystemAssembly->GetDomainAssembly()); + _ASSERTE(ObjectFromHandle(m_pSystemPEAssembly->GetHostAssembly()) == coreLibHostAssembly); + + // Add CoreLib to AssemblyBindingCache + AssemblySpec spec; + spec.InitializeSpec(m_pSystemPEAssembly); + GetAppDomain()->AddAssemblyToCache(&spec, m_pSystemAssembly->GetDomainAssembly()); +} + void SystemDomain::LazyInitGlobalStringLiteralMap() { CONTRACTL @@ -2738,10 +2775,15 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, { LoaderAllocator *pLoaderAllocator = NULL; - AssemblyBinder *pAssemblyBinder = pPEAssembly->GetAssemblyBinder(); - // Assemblies loaded with CustomAssemblyBinder need to use a different LoaderAllocator if - // marked as collectible - pLoaderAllocator = pAssemblyBinder->GetLoaderAllocator(); + // AssemblyBinder isn't set for CoreLib during bootstrap + if (!pPEAssembly->IsSystem()) + { + AssemblyBinder *pAssemblyBinder = pPEAssembly->GetAssemblyBinder(); + // Assemblies loaded with CustomAssemblyBinder need to use a different LoaderAllocator if + // marked as collectible + pLoaderAllocator = pAssemblyBinder->GetLoaderAllocator(); + } + if (pLoaderAllocator == NULL) { pLoaderAllocator = this->GetLoaderAllocator(); @@ -2795,24 +2837,45 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, result->EnsureLoadLevel(targetLevel); } - if (registerNewAssembly) + if (registerNewAssembly && !pDomainAssembly->GetAssembly()->IsSystem()) // CoreLib bootstrap { - pPEAssembly->GetAssemblyBinder()->AddLoadedAssembly(pDomainAssembly->GetAssembly()); + // Ensure to call AddLoadedAssembly under load lock + BaseDomain::LoadLockHolder lock(AppDomain::GetCurrentDomain()); + + GCX_COOP(); + + ASSEMBLYLOADCONTEXTREF managedALC = (ASSEMBLYLOADCONTEXTREF)ObjectFromHandle((OBJECTHANDLE)(pPEAssembly->GetAssemblyBinder()->GetManagedAssemblyLoadContext())); + + GCPROTECT_BEGIN(managedALC); + + MethodDescCallSite methAddLoadedAssem(METHOD__ASSEMBLYLOADCONTEXT__ADD_LOADED_EDASSEMBLY); + ARG_SLOT args[2] = + { + ObjToArgSlot(managedALC), + PtrToArgSlot(pDomainAssembly->GetAssembly()) + }; + methAddLoadedAssem.Call(args); + + GCPROTECT_END(); } } else result->EnsureLoadLevel(targetLevel); // Cache result in all cases, since found pPEAssembly could be from a different AssemblyRef than pIdentity - if (pIdentity == NULL) + if (!result->IsSystem()) { - AssemblySpec spec; - spec.InitializeSpec(result->GetPEAssembly()); - GetAppDomain()->AddAssemblyToCache(&spec, result); - } - else - { - GetAppDomain()->AddAssemblyToCache(pIdentity, result); + // CoreLib bootstrap: this method is called before managed binder available + if (pIdentity == NULL) + { + AssemblySpec spec; + spec.InitializeSpec(result->GetPEAssembly()); + GetAppDomain()->AddAssemblyToCache(&spec, result); + } + else + { + GetAppDomain()->AddAssemblyToCache(pIdentity, result); + } } RETURN result; @@ -3086,7 +3149,9 @@ DomainAssembly * AppDomain::FindAssembly(PEAssembly * pPEAssembly, FindAssemblyO if (pPEAssembly->HasHostAssembly()) { - DomainAssembly * pDA = pPEAssembly->GetHostAssembly()->GetDomainAssembly(); + GCX_COOP(); + + DomainAssembly * pDA = ((BINDERASSEMBLYREF)ObjectFromHandle(pPEAssembly->GetHostAssembly()))->m_pDomainAssembly; if (pDA != nullptr && (pDA->IsLoaded() || (includeFailedToLoad && pDA->IsError()))) { return pDA; @@ -3519,7 +3584,7 @@ PEAssembly * AppDomain::BindAssemblySpec( PRECONDITION(pSpec->GetAppDomain() == this); PRECONDITION(this==::GetAppDomain()); - GCX_PREEMP(); + GCX_COOP(); BOOL fForceReThrow = FALSE; @@ -3536,12 +3601,14 @@ PEAssembly * AppDomain::BindAssemblySpec( { { - ReleaseHolder boundAssembly; + BINDERASSEMBLYREF boundAssembly = NULL; + GCPROTECT_BEGIN(boundAssembly); + hrBindResult = pSpec->Bind(this, &boundAssembly); - if (boundAssembly) + if (boundAssembly != NULL) { - if (SystemDomain::SystemPEAssembly() && boundAssembly->GetAssemblyName()->IsCoreLib()) + if (SystemDomain::SystemPEAssembly() && boundAssembly->m_isCoreLib) { // Avoid rebinding to another copy of CoreLib result = SystemDomain::SystemPEAssembly(); @@ -3585,6 +3652,8 @@ PEAssembly * AppDomain::BindAssemblySpec( } } } + + GCPROTECT_END(); } } } @@ -4862,234 +4931,6 @@ AppDomain::AssemblyIterator::Next_Unlocked( return FALSE; } // AppDomain::AssemblyIterator::Next_Unlocked -#if !defined(DACCESS_COMPILE) - -// Returns S_OK if the assembly was successfully loaded -HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, BINDER_SPACE::AssemblyName *pAssemblyName, DefaultAssemblyBinder *pDefaultBinder, AssemblyBinder *pBinder, BINDER_SPACE::Assembly **ppLoadedAssembly) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(pAssemblyName != NULL); - PRECONDITION(ppLoadedAssembly != NULL); - } - CONTRACTL_END; - - HRESULT hr = E_FAIL; - - // Switch to COOP mode since we are going to work with managed references - GCX_COOP(); - - struct - { - ASSEMBLYNAMEREF oRefAssemblyName; - ASSEMBLYREF oRefLoadedAssembly; - } _gcRefs; - - ZeroMemory(&_gcRefs, sizeof(_gcRefs)); - - GCPROTECT_BEGIN(_gcRefs); - - BINDER_SPACE::Assembly *pResolvedAssembly = NULL; - - bool fResolvedAssembly = false; - BinderTracing::ResolutionAttemptedOperation tracer{pAssemblyName, 0 /*binderID*/, pManagedAssemblyLoadContextToBindWithin, hr}; - - // Allocate an AssemblyName managed object - _gcRefs.oRefAssemblyName = (ASSEMBLYNAMEREF) AllocateObject(CoreLibBinder::GetClass(CLASS__ASSEMBLY_NAME)); - - // Initialize the AssemblyName object - AssemblySpec::InitializeAssemblyNameRef(pAssemblyName, &_gcRefs.oRefAssemblyName); - - bool isSatelliteAssemblyRequest = !pAssemblyName->IsNeutralCulture(); - - EX_TRY - { - if (pDefaultBinder != NULL) - { - // Step 2 (of CustomAssemblyBinder::BindAssemblyByName) - Invoke Load method - // This is not invoked for TPA Binder since it always returns NULL. - tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::AssemblyLoadContextLoad); - - // Finally, setup arguments for invocation - MethodDescCallSite methLoadAssembly(METHOD__ASSEMBLYLOADCONTEXT__RESOLVE); - - // Setup the arguments for the call - ARG_SLOT args[2] = - { - PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance - ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance - }; - - // Make the call - _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methLoadAssembly.Call_RetOBJECTREF(args); - if (_gcRefs.oRefLoadedAssembly != NULL) - { - fResolvedAssembly = true; - } - - hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; - - // Step 3 (of CustomAssemblyBinder::BindAssemblyByName) - if (!fResolvedAssembly && !isSatelliteAssemblyRequest) - { - tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::DefaultAssemblyLoadContextFallback); - - // If we could not resolve the assembly using Load method, then attempt fallback with TPA Binder. - // Since TPA binder cannot fallback to itself, this fallback does not happen for binds within TPA binder. - // - // Switch to pre-emp mode before calling into the binder - GCX_PREEMP(); - BINDER_SPACE::Assembly *pCoreCLRFoundAssembly = NULL; - hr = pDefaultBinder->BindUsingAssemblyName(pAssemblyName, &pCoreCLRFoundAssembly); - if (SUCCEEDED(hr)) - { - _ASSERTE(pCoreCLRFoundAssembly != NULL); - pResolvedAssembly = pCoreCLRFoundAssembly; - fResolvedAssembly = true; - } - } - } - - if (!fResolvedAssembly && isSatelliteAssemblyRequest) - { - // Step 4 (of CustomAssemblyBinder::BindAssemblyByName) - // - // Attempt to resolve it using the ResolveSatelliteAssembly method. - // Finally, setup arguments for invocation - tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::ResolveSatelliteAssembly); - - MethodDescCallSite methResolveSateliteAssembly(METHOD__ASSEMBLYLOADCONTEXT__RESOLVESATELLITEASSEMBLY); - - // Setup the arguments for the call - ARG_SLOT args[2] = - { - PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance - ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance - }; - - // Make the call - _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methResolveSateliteAssembly.Call_RetOBJECTREF(args); - if (_gcRefs.oRefLoadedAssembly != NULL) - { - // Set the flag indicating we found the assembly - fResolvedAssembly = true; - } - - hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; - } - - if (!fResolvedAssembly) - { - // Step 5 (of CustomAssemblyBinder::BindAssemblyByName) - // - // If we couldn't resolve the assembly using TPA LoadContext as well, then - // attempt to resolve it using the Resolving event. - // Finally, setup arguments for invocation - tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::AssemblyLoadContextResolvingEvent); - - MethodDescCallSite methResolveUsingEvent(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUSINGEVENT); - - // Setup the arguments for the call - ARG_SLOT args[2] = - { - PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance - ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance - }; - - // Make the call - _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methResolveUsingEvent.Call_RetOBJECTREF(args); - if (_gcRefs.oRefLoadedAssembly != NULL) - { - // Set the flag indicating we found the assembly - fResolvedAssembly = true; - } - - hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; - } - - if (fResolvedAssembly && pResolvedAssembly == NULL) - { - // If we are here, assembly was successfully resolved via Load or Resolving events. - _ASSERTE(_gcRefs.oRefLoadedAssembly != NULL); - - // We were able to get the assembly loaded. Now, get its name since the host could have - // performed the resolution using an assembly with different name. - DomainAssembly *pDomainAssembly = _gcRefs.oRefLoadedAssembly->GetDomainAssembly(); - PEAssembly *pLoadedPEAssembly = NULL; - bool fFailLoad = false; - if (!pDomainAssembly) - { - // Reflection emitted assemblies will not have a domain assembly. - fFailLoad = true; - } - else - { - pLoadedPEAssembly = pDomainAssembly->GetPEAssembly(); - if (!pLoadedPEAssembly->HasHostAssembly()) - { - // Reflection emitted assemblies will not have a domain assembly. - fFailLoad = true; - } - } - - // The loaded assembly's BINDER_SPACE::Assembly* is saved as HostAssembly in PEAssembly - if (fFailLoad) - { - PathString name; - pAssemblyName->GetDisplayName(name, BINDER_SPACE::AssemblyName::INCLUDE_ALL); - COMPlusThrowHR(COR_E_INVALIDOPERATION, IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED, name); - } - - // For collectible assemblies, ensure that the parent loader allocator keeps the assembly's loader allocator - // alive for all its lifetime. - if (pDomainAssembly->IsCollectible()) - { - LoaderAllocator *pResultAssemblyLoaderAllocator = pDomainAssembly->GetLoaderAllocator(); - LoaderAllocator *pParentLoaderAllocator = pBinder->GetLoaderAllocator(); - if (pParentLoaderAllocator == NULL) - { - // The AssemblyLoadContext for which we are resolving the Assembly is not collectible. - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible")); - } - - _ASSERTE(pResultAssemblyLoaderAllocator); - pParentLoaderAllocator->EnsureReference(pResultAssemblyLoaderAllocator); - } - - pResolvedAssembly = pLoadedPEAssembly->GetHostAssembly(); - } - - if (fResolvedAssembly) - { - _ASSERTE(pResolvedAssembly != NULL); - - // Get the BINDER_SPACE::Assembly reference to return back to. - *ppLoadedAssembly = clr::SafeAddRef(pResolvedAssembly); - hr = S_OK; - - tracer.SetFoundAssembly(static_cast(pResolvedAssembly)); - } - else - { - hr = COR_E_FILENOTFOUND; - } - } - EX_HOOK - { - Exception* ex = GET_EXCEPTION(); - tracer.SetException(ex); - } - EX_END_HOOK - - GCPROTECT_END(); - - return hr; -} -#endif // !defined(DACCESS_COMPILE) - //approximate size of loader data //maintained for each assembly #define APPROX_LOADER_DATA_PER_ASSEMBLY 8196 diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index e72080f914e9a..ceea8d35b82d3 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -2363,6 +2363,7 @@ class SystemDomain : public BaseDomain void operator delete(void *pMem); #endif void Init(); + void PostStartInit(); void Stop(); static void LazyInitGlobalStringLiteralMap(); static void LazyInitFrozenObjectsHeap(); diff --git a/src/coreclr/vm/assemblybinder.cpp b/src/coreclr/vm/assemblybinder.cpp index 36c07df10b2f4..2ffc7b97d6e78 100644 --- a/src/coreclr/vm/assemblybinder.cpp +++ b/src/coreclr/vm/assemblybinder.cpp @@ -8,25 +8,6 @@ #ifndef DACCESS_COMPILE -HRESULT AssemblyBinder::BindAssemblyByName(AssemblyNameData* pAssemblyNameData, - BINDER_SPACE::Assembly** ppAssembly) -{ - _ASSERTE(pAssemblyNameData != nullptr && ppAssembly != nullptr); - - HRESULT hr = S_OK; - *ppAssembly = nullptr; - - ReleaseHolder pAssemblyName; - SAFE_NEW(pAssemblyName, BINDER_SPACE::AssemblyName); - IF_FAIL_GO(pAssemblyName->Init(*pAssemblyNameData)); - - hr = BindUsingAssemblyName(pAssemblyName, ppAssembly); - -Exit: - return hr; -} - - NativeImage* AssemblyBinder::LoadNativeImage(Module* componentModule, LPCUTF8 nativeImageName) { STANDARD_VM_CONTRACT; @@ -41,131 +22,6 @@ NativeImage* AssemblyBinder::LoadNativeImage(Module* componentModule, LPCUTF8 na return nativeImage; } -#ifdef FEATURE_READYTORUN -static void MvidMismatchFatalError(GUID mvidActual, GUID mvidExpected, LPCUTF8 simpleName, bool compositeComponent, LPCUTF8 assemblyRequirementName) -{ - CHAR assemblyMvidText[GUID_STR_BUFFER_LEN]; - GuidToLPSTR(mvidActual, assemblyMvidText); - - CHAR componentMvidText[GUID_STR_BUFFER_LEN]; - GuidToLPSTR(mvidExpected, componentMvidText); - - SString message; - if (compositeComponent) - { - message.Printf("MVID mismatch between loaded assembly '%s' (MVID = %s) and an assembly with the same simple name embedded in the native image '%s' (MVID = %s)", - simpleName, - assemblyMvidText, - assemblyRequirementName, - componentMvidText); - } - else - { - message.Printf("MVID mismatch between loaded assembly '%s' (MVID = %s) and version of assembly '%s' expected by assembly '%s' (MVID = %s)", - simpleName, - assemblyMvidText, - simpleName, - assemblyRequirementName, - componentMvidText); - } - - EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, message.GetUnicode()); -} - -void AssemblyBinder::DeclareDependencyOnMvid(LPCUTF8 simpleName, GUID mvid, bool compositeComponent, LPCUTF8 imageName) -{ - _ASSERTE(imageName != NULL); - - // If the table is empty, then we didn't fill it with all the loaded assemblies as they were loaded. Record this detail, and fix after adding the dependency - bool addAllLoadedModules = false; - if (m_assemblySimpleNameMvidCheckHash.GetCount() == 0) - { - addAllLoadedModules = true; - } - - SimpleNameToExpectedMVIDAndRequiringAssembly* foundElem = (SimpleNameToExpectedMVIDAndRequiringAssembly*)m_assemblySimpleNameMvidCheckHash.LookupPtr(simpleName); - if (foundElem == NULL) - { - SimpleNameToExpectedMVIDAndRequiringAssembly newElem(simpleName, mvid, compositeComponent, imageName); - m_assemblySimpleNameMvidCheckHash.Add(newElem); - } - else - { - // Elem already exists. Determine if the existing elem is another one with the same mvid, in which case just record that a dependency is in play. - // If the existing elem has a different mvid, fail. - if (IsEqualGUID(mvid, foundElem->Mvid)) - { - // Mvid matches exactly. - if (foundElem->AssemblyRequirementName == NULL) - { - foundElem->AssemblyRequirementName = imageName; - foundElem->CompositeComponent = compositeComponent; - } - } - else - { - MvidMismatchFatalError(foundElem->Mvid, mvid, simpleName, compositeComponent, imageName); - } - } - - if (addAllLoadedModules) - { - for (COUNT_T assemblyIndex = 0; assemblyIndex < m_loadedAssemblies.GetCount(); assemblyIndex++) - { - DeclareLoadedAssembly(m_loadedAssemblies[assemblyIndex]); - } - } -} - -void AssemblyBinder::DeclareLoadedAssembly(Assembly* loadedAssembly) -{ - // If table is empty, then no mvid dependencies have been declared, so we don't need to record this information - if (m_assemblySimpleNameMvidCheckHash.GetCount() == 0) - return; - - GUID mvid; - loadedAssembly->GetMDImport()->GetScopeProps(NULL, &mvid); - - LPCUTF8 simpleName = loadedAssembly->GetSimpleName(); - - SimpleNameToExpectedMVIDAndRequiringAssembly* foundElem = (SimpleNameToExpectedMVIDAndRequiringAssembly*)m_assemblySimpleNameMvidCheckHash.LookupPtr(simpleName); - if (foundElem == NULL) - { - SimpleNameToExpectedMVIDAndRequiringAssembly newElem(simpleName, mvid, false, NULL); - m_assemblySimpleNameMvidCheckHash.Add(newElem); - } - else - { - // Elem already exists. Determine if the existing elem is another one with the same mvid, in which case do nothing. Everything is fine here. - // If the existing elem has a different mvid, but isn't a dependency on exact mvid elem, then set the mvid to all 0. - // If the existing elem has a different mvid, and is a dependency on exact mvid elem, then we've hit a fatal error. - if (IsEqualGUID(mvid, foundElem->Mvid)) - { - // Mvid matches exactly. - } - else if (foundElem->AssemblyRequirementName == NULL) - { - // Another loaded assembly, set the stored Mvid to all zeroes to indicate that it isn't a unique mvid - memset(&foundElem->Mvid, 0, sizeof(GUID)); - } - else - { - MvidMismatchFatalError(mvid, foundElem->Mvid, simpleName, foundElem->CompositeComponent, foundElem->AssemblyRequirementName); - } - } -} -#endif // FEATURE_READYTORUN - -void AssemblyBinder::AddLoadedAssembly(Assembly* loadedAssembly) -{ - BaseDomain::LoadLockHolder lock(AppDomain::GetCurrentDomain()); - m_loadedAssemblies.Append(loadedAssembly); - -#ifdef FEATURE_READYTORUN - DeclareLoadedAssembly(loadedAssembly); -#endif // FEATURE_READYTORUN -} - void AssemblyBinder::GetNameForDiagnosticsFromManagedALC(INT_PTR managedALC, /* out */ SString& alcName) { if (managedALC == GetAppDomain()->GetDefaultBinder()->GetManagedAssemblyLoadContext()) diff --git a/src/coreclr/vm/assemblybinder.h b/src/coreclr/vm/assemblybinder.h index 8fa57302cfa40..3d157edbb4ce2 100644 --- a/src/coreclr/vm/assemblybinder.h +++ b/src/coreclr/vm/assemblybinder.h @@ -5,7 +5,6 @@ #define _ASSEMBLYBINDER_H #include -#include "../binder/inc/applicationcontext.hpp" class PEImage; class NativeImage; @@ -18,10 +17,6 @@ class AssemblyBinder { public: - HRESULT BindAssemblyByName(AssemblyNameData* pAssemblyNameData, BINDER_SPACE::Assembly** ppAssembly); - virtual HRESULT BindUsingPEImage(PEImage* pPEImage, bool excludeAppPaths, BINDER_SPACE::Assembly** ppAssembly) = 0; - virtual HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName, BINDER_SPACE::Assembly** ppAssembly) = 0; - /// /// Get LoaderAllocator for binders that contain it. For other binders, return NULL. /// @@ -32,11 +27,6 @@ class AssemblyBinder /// virtual bool IsDefault() = 0; - inline BINDER_SPACE::ApplicationContext* GetAppContext() - { - return &m_appContext; - } - INT_PTR GetManagedAssemblyLoadContext() { return m_ptrManagedAssemblyLoadContext; @@ -48,86 +38,17 @@ class AssemblyBinder } NativeImage* LoadNativeImage(Module* componentModule, LPCUTF8 nativeImageName); - void AddLoadedAssembly(Assembly* loadedAssembly); void GetNameForDiagnostics(/*out*/ SString& alcName); static void GetNameForDiagnosticsFromManagedALC(INT_PTR managedALC, /* out */ SString& alcName); static void GetNameForDiagnosticsFromSpec(AssemblySpec* spec, /*out*/ SString& alcName); -#ifdef FEATURE_READYTORUN - // Must be called under the LoadLock - void DeclareDependencyOnMvid(LPCUTF8 simpleName, GUID mvid, bool compositeComponent, LPCUTF8 imageName); -#endif // FEATURE_READYTORUN - private: -#ifdef FEATURE_READYTORUN - // Must be called under the LoadLock - void DeclareLoadedAssembly(Assembly* loadedAssembly); - - struct SimpleNameToExpectedMVIDAndRequiringAssembly - { - LPCUTF8 SimpleName; - - // When an assembly is loaded, this Mvid value will be set to the mvid of the assembly. If there are multiple assemblies - // with different mvid's loaded with the same simple name, then the Mvid value will be set to all zeroes. - GUID Mvid; - - // If an assembly of this simple name is not yet loaded, but a depedency on an exact mvid is registered, then this field will - // be filled in with the simple assembly name of the first assembly loaded with an mvid dependency. - LPCUTF8 AssemblyRequirementName; - - // To disambiguate between component images of a composite image and requirements from a non-composite --inputbubble assembly, use this bool - bool CompositeComponent; - - SimpleNameToExpectedMVIDAndRequiringAssembly() : - SimpleName(NULL), - Mvid({0}), - AssemblyRequirementName(NULL), - CompositeComponent(false) - { - } - - SimpleNameToExpectedMVIDAndRequiringAssembly(LPCUTF8 simpleName, GUID mvid, bool compositeComponent, LPCUTF8 AssemblyRequirementName) : - SimpleName(simpleName), - Mvid(mvid), - AssemblyRequirementName(AssemblyRequirementName), - CompositeComponent(compositeComponent) - {} - - static SimpleNameToExpectedMVIDAndRequiringAssembly GetNull() { return SimpleNameToExpectedMVIDAndRequiringAssembly(); } - bool IsNull() const { return SimpleName == NULL; } - }; - - class SimpleNameWithMvidHashTraits : public NoRemoveSHashTraits< DefaultSHashTraits > - { - public: - typedef LPCUTF8 key_t; - - static SimpleNameToExpectedMVIDAndRequiringAssembly Null() { return SimpleNameToExpectedMVIDAndRequiringAssembly::GetNull(); } - static bool IsNull(const SimpleNameToExpectedMVIDAndRequiringAssembly& e) { return e.IsNull(); } - - static LPCUTF8 GetKey(const SimpleNameToExpectedMVIDAndRequiringAssembly& e) { return e.SimpleName; } - - static BOOL Equals(LPCUTF8 a, LPCUTF8 b) { return strcmp(a, b) == 0; } // Use a case senstive comparison here even though - // assembly name matching should be case insensitive. Case insensitive - // comparisons are slow and have throwing scenarios, and this hash table - // provides a best-effort match to prevent problems, not perfection - - static count_t Hash(LPCUTF8 a) { return HashStringA(a); } // As above, this is a case sensitive hash - }; - - SHash m_assemblySimpleNameMvidCheckHash; -#endif // FEATURE_READYTORUN - - BINDER_SPACE::ApplicationContext m_appContext; - // A GC handle to the managed AssemblyLoadContext. // It is a long weak handle for collectible AssemblyLoadContexts and strong handle for non-collectible ones. INT_PTR m_ptrManagedAssemblyLoadContext; - - SArray m_loadedAssemblies; }; #endif diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index b7fdcd6a21bff..5357b9d7ca038 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -135,7 +135,6 @@ Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pIma CONTRACT_END; Assembly *pLoadedAssembly = NULL; - ReleaseHolder pAssembly; // Set the caller's assembly to be CoreLib DomainAssembly *pCallersAssembly = SystemDomain::System()->SystemAssembly()->GetDomainAssembly(); @@ -149,7 +148,30 @@ Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pIma HRESULT hr = S_OK; PTR_AppDomain pCurDomain = GetAppDomain(); - hr = pBinder->BindUsingPEImage(pImage, excludeAppPaths, &pAssembly); + + GCX_COOP(); + + struct { + OBJECTREF pBinder; + BINDERASSEMBLYREF pAssembly; + } gc; + + gc.pBinder = ObjectFromHandle((OBJECTHANDLE)(pBinder->GetManagedAssemblyLoadContext())); + gc.pAssembly = NULL; + + GCPROTECT_BEGIN(gc); + + MethodDescCallSite methBind(METHOD__ASSEMBLYLOADCONTEXT__BIND_USING_PEIMAGE, &gc.pBinder); + ARG_SLOT args[4] = + { + ObjToArgSlot(gc.pBinder), + PtrToArgSlot(pImage), + BoolToArgSlot(excludeAppPaths), + PtrToArgSlot(&gc.pAssembly) + }; + hr = methBind.Call_RetHR(args); + + GCPROTECT_END(); if (hr != S_OK) { @@ -171,7 +193,7 @@ Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pIma } } - PEAssemblyHolder pPEAssembly(PEAssembly::Open(pAssembly->GetPEImage(), pAssembly)); + PEAssemblyHolder pPEAssembly(PEAssembly::Open(gc.pAssembly->m_peImage, gc.pAssembly)); bindOperation.SetResult(pPEAssembly.GetValue()); DomainAssembly *pDomainAssembly = pCurDomain->LoadDomainAssembly(&spec, pPEAssembly, FILE_LOADED); @@ -1379,13 +1401,33 @@ extern "C" void QCALLTYPE AssemblyNative_TraceAssemblyLoadFromResolveHandlerInvo } // static -extern "C" void QCALLTYPE AssemblyNative_TraceSatelliteSubdirectoryPathProbed(LPCWSTR filePath, HRESULT hr) +extern "C" void QCALLTYPE AssemblyNative_TracePathProbed(LPCWSTR filePath, uint16_t source, HRESULT hr) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + BinderTracing::PathProbed(filePath, static_cast(source), hr); + + END_QCALL; +} + +// static +extern "C" void QCALLTYPE AssemblyNative_TraceResolutionAttempted(LPCWSTR assemblyName, uint16_t stage, LPCWSTR assemblyLoadContextName, uint16_t result, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath, LPCWSTR errorMsg) { QCALL_CONTRACT; BEGIN_QCALL; - BinderTracing::PathProbed(filePath, BinderTracing::PathSource::SatelliteSubdirectory, hr); + FireEtwResolutionAttempted( + GetClrInstanceId(), + assemblyName, + stage, + assemblyLoadContextName, + result, + resultAssemblyName, + resultAssemblyPath, + errorMsg); END_QCALL; } @@ -1453,3 +1495,257 @@ extern "C" BOOL QCALLTYPE AssemblyNative_IsApplyUpdateSupported() return result; } + +extern "C" IMDInternalImport * QCALLTYPE AssemblyNative_GetMDImport(Assembly * pAssembly) +{ + QCALL_CONTRACT; + + IMDInternalImport* result = NULL; + + BEGIN_QCALL; + + result = pAssembly->GetMDImport(); + + END_QCALL; + + return result; +} + +extern "C" LPCUTF8 QCALLTYPE AssemblyNative_GetSimpleNameNative(Assembly * pAssembly) +{ + QCALL_CONTRACT; + + LPCUTF8 result = NULL; + + BEGIN_QCALL; + + result = pAssembly->GetSimpleName(); + + END_QCALL; + + return result; +} + +extern "C" void QCALLTYPE AssemblyNative_GetExposedObject(Assembly * pAssembly, QCall::ObjectHandleOnStack rtAssembly) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + GCX_COOP(); + rtAssembly.Set(pAssembly->GetExposedObject()); + + END_QCALL; +} + +extern "C" PEImage * QCALLTYPE AssemblyNative_GetPEImage(Assembly * pAssembly) +{ + QCALL_CONTRACT; + + PEImage* result = NULL; + + BEGIN_QCALL; + + result = pAssembly->GetPEAssembly()->GetPEImage(); + + END_QCALL; + + return result; +} + +extern "C" void QCALLTYPE AssemblyNative_SetSymbolBytes(Assembly * pAssembly, BYTE* ptrSymbolArray, int32_t cbSymbolArrayLength) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + pAssembly->GetModule()->SetSymbolBytes(ptrSymbolArray, (DWORD)cbSymbolArrayLength); + + END_QCALL; +} + +extern "C" IMDInternalImport * QCALLTYPE PEImage_BinderAcquireImport(PEImage * pPEImage, DWORD * pdwPAFlags) +{ + QCALL_CONTRACT; + + IMDInternalImport* ret = NULL; + + BEGIN_QCALL; + + // The same logic of BinderAcquireImport + + PEImageLayout* pLayout = pPEImage->GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); + + // CheckCorHeader includes check of NT headers too + if (!pLayout->CheckCorHeader()) + ThrowHR(COR_E_ASSEMBLYEXPECTED); + + if (!pLayout->CheckFormat()) + ThrowHR(COR_E_BADIMAGEFORMAT); + + pPEImage->GetPEKindAndMachine(&pdwPAFlags[0], &pdwPAFlags[1]); + + ret = pPEImage->GetMDImport(); + if (!ret) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + // No AddRef + + END_QCALL; + + return ret; +} + +extern "C" HRESULT QCALLTYPE PEImage_BinderAcquirePEImage(LPCWSTR wszAssemblyPath, PEImage * *ppPEImage, BundleFileLocation bundleFileLocation) +{ + QCALL_CONTRACT; + + HRESULT hr = S_OK; + + BEGIN_QCALL; + + *ppPEImage = NULL; + + EX_TRY + { + PEImageHolder pImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_Default, bundleFileLocation); + + // Make sure that the IL image can be opened. + hr = pImage->TryOpenFile(); + if (SUCCEEDED(hr)) + { + *ppPEImage = pImage.Extract(); + } + } + EX_CATCH_HRESULT(hr); + + END_QCALL; + + return hr; +} + +extern "C" void QCALLTYPE PEImage_Release(PEImage * pPEImage) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + pPEImage->Release(); + + END_QCALL; +} + +extern "C" void QCALLTYPE PEImage_GetMVID(PEImage * pPEImage, GUID* pMVID) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + pPEImage->GetMVID(pMVID); + + END_QCALL; +} + +extern "C" LPCWSTR QCALLTYPE PEImage_GetPath(PEImage * pPEImage) +{ + QCALL_CONTRACT; + + LPCWSTR result = NULL; + + BEGIN_QCALL; + + result = pPEImage->GetPath().GetUnicode(); + + END_QCALL; + + return result; +} + +extern "C" PEAssembly * QCALLTYPE DomainAssembly_GetPEAssembly(DomainAssembly * pDomainAssembly) +{ + QCALL_CONTRACT; + + PEAssembly* result = NULL; + + BEGIN_QCALL; + + result = pDomainAssembly->GetPEAssembly(); + + END_QCALL; + + return result; +} + +extern "C" void QCALLTYPE DomainAssembly_EnsureReferenceBinder(DomainAssembly * pDomainAssembly, AssemblyBinder * pBinder) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + LoaderAllocator *pResultAssemblyLoaderAllocator = pDomainAssembly->GetLoaderAllocator(); + LoaderAllocator *pParentLoaderAllocator = pBinder->GetLoaderAllocator(); + if (pParentLoaderAllocator == NULL) + { + // The AssemblyLoadContext for which we are resolving the Assembly is not collectible. + COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible")); + } + + _ASSERTE(pResultAssemblyLoaderAllocator); + pParentLoaderAllocator->EnsureReference(pResultAssemblyLoaderAllocator); + + END_QCALL; +} + +extern "C" INT_PTR QCALLTYPE PEAssembly_GetHostAssembly(PEAssembly * pPEAssembly) +{ + QCALL_CONTRACT; + + INT_PTR result = NULL; + + BEGIN_QCALL; + + result = (INT_PTR)pPEAssembly->GetHostAssembly(); + + END_QCALL; + + return result; +} + +extern "C" BOOL QCALLTYPE Bundle_AppIsBundle() +{ + QCALL_CONTRACT; + + BOOL result = FALSE; + + BEGIN_QCALL; + + result = Bundle::AppIsBundle(); + + END_QCALL; + + return result; +} + +extern "C" void QCALLTYPE Bundle_ProbeAppBundle(LPCWSTR path, BOOL pathIsBundleRelative, BundleFileLocation* result) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + *result = Bundle::ProbeAppBundle(SString(path), pathIsBundleRelative); + + END_QCALL; +} + +extern "C" void QCALLTYPE Bundle_GetAppBundleBasePath(QCall::StringHandleOnStack path) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + path.Set(Bundle::AppBundle->BasePath().GetUnicode()); + + END_QCALL; +} diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index 80ae3da8c2bd5..150b0943150d3 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -139,8 +139,9 @@ extern "C" void QCALLTYPE AssemblyNative_TraceAssemblyResolveHandlerInvoked(LPCW extern "C" void QCALLTYPE AssemblyNative_TraceAssemblyLoadFromResolveHandlerInvoked(LPCWSTR assemblyName, bool isTrackedAssembly, LPCWSTR requestingAssemblyPath, LPCWSTR requestedAssemblyPath); -extern "C" void QCALLTYPE AssemblyNative_TraceSatelliteSubdirectoryPathProbed(LPCWSTR filePath, HRESULT hr); +extern "C" void QCALLTYPE AssemblyNative_TracePathProbed(LPCWSTR filePath, uint16_t source, HRESULT hr); +extern "C" void QCALLTYPE AssemblyNative_TraceResolutionAttempted(LPCWSTR assemblyName, uint16_t stage, LPCWSTR assemblyLoadContextName, uint16_t result, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath, LPCWSTR errorMsg); extern "C" void QCALLTYPE AssemblyNative_ApplyUpdate(QCall::AssemblyHandle assembly, UINT8* metadataDelta, INT32 metadataDeltaLength, UINT8* ilDelta, INT32 ilDeltaLength, UINT8* pdbDelta, INT32 pdbDeltaLength); @@ -148,4 +149,35 @@ extern "C" BOOL QCALLTYPE AssemblyNative_IsApplyUpdateSupported(); extern "C" void QCALLTYPE AssemblyName_InitializeAssemblySpec(NativeAssemblyNameParts* pAssemblyNameParts, BaseAssemblySpec* pAssemblySpec); +extern "C" IMDInternalImport * QCALLTYPE AssemblyNative_GetMDImport(Assembly * pAssembly); + +extern "C" LPCUTF8 QCALLTYPE AssemblyNative_GetSimpleNameNative(Assembly * pAssembly); + +extern "C" void QCALLTYPE AssemblyNative_GetExposedObject(Assembly * pAssembly, QCall::ObjectHandleOnStack rtAssembly); + +extern "C" PEImage * QCALLTYPE AssemblyNative_GetPEImage(Assembly * pAssembly); + +extern "C" void QCALLTYPE AssemblyNative_SetSymbolBytes(Assembly * pAssembly, BYTE* ptrSymbolArray, int32_t cbSymbolArrayLength); + +extern "C" IMDInternalImport * QCALLTYPE PEImage_BinderAcquireImport(PEImage * pPEImage, DWORD * pdwPAFlags); + +extern "C" HRESULT QCALLTYPE PEImage_BinderAcquirePEImage(LPCWSTR wszAssemblyPath, PEImage * *ppPEImage, BundleFileLocation bundleFileLocation); + +extern "C" void QCALLTYPE PEImage_Release(PEImage * pPEImage); + +extern "C" void QCALLTYPE PEImage_GetMVID(PEImage * pPEImage, GUID* pMVID); + +extern "C" LPCWSTR QCALLTYPE PEImage_GetPath(PEImage * pPEImage); + +extern "C" PEAssembly * QCALLTYPE DomainAssembly_GetPEAssembly(DomainAssembly * pDomainAssembly); + +extern "C" void QCALLTYPE DomainAssembly_EnsureReferenceBinder(DomainAssembly * pDomainAssembly, AssemblyBinder * pBinder); + +extern "C" INT_PTR QCALLTYPE PEAssembly_GetHostAssembly(PEAssembly * pPEAssembly); + +extern "C" BOOL QCALLTYPE Bundle_AppIsBundle(); + +extern "C" void QCALLTYPE Bundle_ProbeAppBundle(LPCWSTR path, BOOL pathIsBundleRelative, BundleFileLocation* result); + +extern "C" void QCALLTYPE Bundle_GetAppBundleBasePath(QCall::StringHandleOnStack path); #endif diff --git a/src/coreclr/vm/assemblyspec.cpp b/src/coreclr/vm/assemblyspec.cpp index a4872b732097b..867997c67690c 100644 --- a/src/coreclr/vm/assemblyspec.cpp +++ b/src/coreclr/vm/assemblyspec.cpp @@ -209,8 +209,12 @@ void AssemblySpec::InitializeSpec(PEAssembly * pFile) { AssemblyBinder* pExpectedBinder = pFile->GetAssemblyBinder(); // We should always have the binding context in the PEAssembly. - _ASSERTE(pExpectedBinder != NULL); - SetBinder(pExpectedBinder); + if (!IsCoreLib()) + { + // Binder of CoreLib will be set afterwards. + _ASSERTE(pExpectedBinder != NULL); + SetBinder(pExpectedBinder); + } } } diff --git a/src/coreclr/vm/assemblyspec.hpp b/src/coreclr/vm/assemblyspec.hpp index 23a7c96e64756..25b8155ea3433 100644 --- a/src/coreclr/vm/assemblyspec.hpp +++ b/src/coreclr/vm/assemblyspec.hpp @@ -171,7 +171,7 @@ class AssemblySpec : public BaseAssemblySpec HRESULT Bind( AppDomain* pAppDomain, - BINDER_SPACE::Assembly** ppAssembly); + BINDERASSEMBLYREF* ppAssembly); Assembly *LoadAssembly(FileLoadLevel targetLevel, BOOL fThrowOnFileNotFound = TRUE); diff --git a/src/coreclr/vm/assemblyspecbase.h b/src/coreclr/vm/assemblyspecbase.h index c9db2e04b9f83..113c3117c1c1c 100644 --- a/src/coreclr/vm/assemblyspecbase.h +++ b/src/coreclr/vm/assemblyspecbase.h @@ -15,7 +15,14 @@ #ifndef __ASSEMBLY_SPEC_BASE_H__ #define __ASSEMBLY_SPEC_BASE_H__ -#include "../binder/inc/assembly.hpp" +#include "../binder/inc/assemblyname.hpp" +#include "assemblybinder.h" + +#include "../binder/inc/defaultassemblybinder.h" + +#if !defined(DACCESS_COMPILE) +#include "../binder/inc/customassemblybinder.h" +#endif // !defined(DACCESS_COMPILE) #include "baseassemblyspec.h" #include "baseassemblyspec.inl" diff --git a/src/coreclr/vm/baseassemblyspec.cpp b/src/coreclr/vm/baseassemblyspec.cpp index 469535aa7f4db..4bbb2a1b0c33f 100644 --- a/src/coreclr/vm/baseassemblyspec.cpp +++ b/src/coreclr/vm/baseassemblyspec.cpp @@ -20,7 +20,7 @@ BOOL BaseAssemblySpec::IsCoreLib() { THROWS; INSTANCE_CHECK; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 5cb624d3d7b36..ee3e21bbf59a8 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -2990,7 +2990,7 @@ DomainAssembly * Module::LoadAssemblyImpl(mdAssemblyRef kAssemblyRef) _ASSERTE( pDomainAssembly->IsSystem() || // GetAssemblyIfLoaded will not find CoreLib (see AppDomain::FindCachedFile) !pDomainAssembly->IsLoaded() || // GetAssemblyIfLoaded will not find not-yet-loaded assemblies - GetAssemblyIfLoaded(kAssemblyRef, NULL, FALSE, pDomainAssembly->GetPEAssembly()->GetHostAssembly()->GetBinder()) != NULL); // GetAssemblyIfLoaded should find all remaining cases + GetAssemblyIfLoaded(kAssemblyRef, NULL, FALSE, pDomainAssembly->GetPEAssembly()->GetAssemblyBinder()) != NULL); // GetAssemblyIfLoaded should find all remaining cases if (pDomainAssembly->GetAssembly() != NULL) { diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index f60d447f0b2ae..1b6176de2756d 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -968,6 +968,8 @@ void EEStartupHelper() } #endif // _DEBUG + // Initialize managed binder. Ensure g_fEEStarted and COM initialized before executing managed code. + SystemDomain::System()->PostStartInit(); ErrExit: ; } diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index 8b8ff9e842b3a..386aeecdb3e49 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -105,6 +105,7 @@ typedef VPTR(class AppDomain) PTR_AppDomain; typedef DPTR(class ArrayBase) PTR_ArrayBase; typedef DPTR(class Assembly) PTR_Assembly; typedef DPTR(class AssemblyBaseObject) PTR_AssemblyBaseObject; +typedef DPTR(class BinderAssemblyObject) PTR_BinderAssemblyObject; typedef DPTR(class AssemblyLoadContextBaseObject) PTR_AssemblyLoadContextBaseObject; typedef DPTR(class AssemblyBinder) PTR_AssemblyBinder; typedef DPTR(class AssemblyNameBaseObject) PTR_AssemblyNameBaseObject; diff --git a/src/coreclr/vm/coreassemblyspec.cpp b/src/coreclr/vm/coreassemblyspec.cpp index 886df7e4afbe5..069dfff4e9d91 100644 --- a/src/coreclr/vm/coreassemblyspec.cpp +++ b/src/coreclr/vm/coreassemblyspec.cpp @@ -21,18 +21,18 @@ #include "strongnameinternal.h" #include "../binder/inc/assemblyidentity.hpp" -#include "../binder/inc/assembly.hpp" #include "../binder/inc/assemblyname.hpp" #include "../binder/inc/assemblybindercommon.hpp" -#include "../binder/inc/applicationcontext.hpp" -HRESULT AssemblySpec::Bind(AppDomain *pAppDomain, BINDER_SPACE::Assembly** ppAssembly) +HRESULT AssemblySpec::Bind(AppDomain *pAppDomain, BINDERASSEMBLYREF* ppAssembly) { CONTRACTL { INSTANCE_CHECK; - STANDARD_VM_CHECK; + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; PRECONDITION(CheckPointer(ppAssembly)); PRECONDITION(CheckPointer(pAppDomain)); PRECONDITION(IsCoreLib() == FALSE); // This should never be called for CoreLib (explicit loading) @@ -41,11 +41,21 @@ HRESULT AssemblySpec::Bind(AppDomain *pAppDomain, BINDER_SPACE::Assembly** ppAs HRESULT hr=S_OK; + GCX_COOP(); + + struct + { + OBJECTREF pBinder; + BINDERASSEMBLYREF pPrivAsm; + } gc; + // Have a default binding context setup - AssemblyBinder *pBinder = GetBinderFromParentAssembly(pAppDomain); + gc.pBinder = ObjectFromHandle((OBJECTHANDLE)GetBinderFromParentAssembly(pAppDomain)->GetManagedAssemblyLoadContext()); + gc.pPrivAsm = NULL; + + _ASSERTE(gc.pBinder != NULL); - ReleaseHolder pPrivAsm; - _ASSERTE(pBinder != NULL); + GCPROTECT_BEGIN(gc); if (IsCoreLibSatellite()) { @@ -57,21 +67,41 @@ HRESULT AssemblySpec::Bind(AppDomain *pAppDomain, BINDER_SPACE::Assembly** ppAs if (m_context.szLocale != NULL) SString(SString::Utf8Literal, m_context.szLocale).ConvertToUnicode(sCultureName); - hr = BINDER_SPACE::AssemblyBinderCommon::BindToSystemSatellite(sSystemDirectory, sSimpleName, sCultureName, &pPrivAsm); + MethodDescCallSite methSatellite(METHOD__ASSEMBLYBINDERCOMMON__BIND_TO_SYSTEM_SATELLITE); + ARG_SLOT args[4] = + { + PtrToArgSlot(sSystemDirectory.GetUnicode()), + PtrToArgSlot(sSimpleName.GetUnicode()), + PtrToArgSlot(sCultureName.GetUnicode()), + PtrToArgSlot(&gc.pPrivAsm) + }; + + hr = methSatellite.Call_RetHR(args); } else { AssemblyNameData assemblyNameData = { 0 }; PopulateAssemblyNameData(assemblyNameData); - hr = pBinder->BindAssemblyByName(&assemblyNameData, &pPrivAsm); + + MethodDescCallSite methBindAssemblyByName(METHOD__ASSEMBLYLOADCONTEXT__BIND_ASSEMBLY_BY_NAME, &gc.pBinder); + ARG_SLOT args[3] = + { + ObjToArgSlot(gc.pBinder), + PtrToArgSlot(&assemblyNameData), + PtrToArgSlot(&gc.pPrivAsm) + }; + + hr = methBindAssemblyByName.Call_RetHR(args); } if (SUCCEEDED(hr)) { - _ASSERTE(pPrivAsm != nullptr); - *ppAssembly = pPrivAsm.Extract(); + _ASSERTE(gc.pPrivAsm != NULL); + *ppAssembly = gc.pPrivAsm; } + GCPROTECT_END(); + return hr; } diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 17f74503bdc28..8ab11a27d6fc2 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -865,11 +865,8 @@ DEFINE_FIELD_U(_id, AssemblyLoadContextBaseObject, _id) DEFINE_FIELD_U(_state, AssemblyLoadContextBaseObject, _state) DEFINE_FIELD_U(_isCollectible, AssemblyLoadContextBaseObject, _isCollectible) DEFINE_CLASS(ASSEMBLYLOADCONTEXT, Loader, AssemblyLoadContext) -DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVE, Resolve, SM_IntPtr_AssemblyName_RetAssemblyBase) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUNMANAGEDDLL, ResolveUnmanagedDll, SM_Str_IntPtr_RetIntPtr) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUNMANAGEDDLLUSINGEVENT, ResolveUnmanagedDllUsingEvent, SM_Str_AssemblyBase_IntPtr_RetIntPtr) -DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUSINGEVENT, ResolveUsingResolvingEvent, SM_IntPtr_AssemblyName_RetAssemblyBase) -DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVESATELLITEASSEMBLY, ResolveSatelliteAssembly, SM_IntPtr_AssemblyName_RetAssemblyBase) DEFINE_FIELD(ASSEMBLYLOADCONTEXT, ASSEMBLY_LOAD, AssemblyLoad) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ON_ASSEMBLY_LOAD, OnAssemblyLoad, SM_Assembly_RetVoid) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ON_RESOURCE_RESOLVE, OnResourceResolve, SM_Assembly_Str_RetAssembly) @@ -877,7 +874,26 @@ DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ON_TYPE_RESOLVE, OnTypeResolve, DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ON_ASSEMBLY_RESOLVE, OnAssemblyResolve, SM_Assembly_Str_RetAssembly) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, START_ASSEMBLY_LOAD, StartAssemblyLoad, SM_RefGuid_RefGuid_RetVoid) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, STOP_ASSEMBLY_LOAD, StopAssemblyLoad, SM_RefGuid_RetVoid) -DEFINE_METHOD(ASSEMBLYLOADCONTEXT, INITIALIZE_DEFAULT_CONTEXT, InitializeDefaultContext, SM_RetVoid) +DEFINE_METHOD(ASSEMBLYLOADCONTEXT, INITIALIZE_DEFAULT, InitializeDefault, SM_IntPtr_RetBinderAssembly) +DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ADD_LOADED_EDASSEMBLY, AddLoadedAssembly, IM_IntPtr_RetVoid) +DEFINE_METHOD(ASSEMBLYLOADCONTEXT, BIND_USING_PEIMAGE, BindUsingPEImage, IM_IntPtr_Bool_RefBinderAssembly_RetInt) +DEFINE_METHOD(ASSEMBLYLOADCONTEXT, BIND_ASSEMBLY_BY_NAME, BindAssemblyByName, IM_PtrVoid_RefBinderAssembly_RetInt) +DEFINE_METHOD(ASSEMBLYLOADCONTEXT, DECLARE_DEPENDENCY_ON_MVID, DeclareDependencyOnMvid, IM_PtrByte_RefGuid_Bool_PtrByte_RetVoid) +DEFINE_METHOD(ASSEMBLYLOADCONTEXT, SETUP_BINDING_PATHS, SetupBindingPaths, SM_PtrChar_PtrChar_PtrChar_RetVoid) + +DEFINE_CLASS(ASSEMBLYBINDERCOMMON, Loader, AssemblyBinderCommon) +DEFINE_METHOD(ASSEMBLYBINDERCOMMON, BIND_TO_SYSTEM_SATELLITE, BindToSystemSatellite, SM_PtrChar_PtrChar_PtrChar_RefBinderAssembly_RetInt) + +// Managed binder types + +DEFINE_CLASS(BINDER_ASSEMBLY, Loader, BinderAssembly) +DEFINE_CLASS_U(Loader, BinderAssembly, BinderAssemblyObject) +DEFINE_FIELD_U(m_binder, BinderAssemblyObject, m_binder) +DEFINE_FIELD_U(m_assemblyName, BinderAssemblyObject, m_assemblyName) +DEFINE_FIELD_U(m_peImage, BinderAssemblyObject, m_peImage) +DEFINE_FIELD_U(m_pDomainAssembly, BinderAssemblyObject, m_pDomainAssembly) +DEFINE_FIELD_U(m_isInTPA, BinderAssemblyObject, m_isInTPA) +DEFINE_FIELD_U(m_isCoreLib, BinderAssemblyObject, m_isCoreLib) DEFINE_CLASS(VALUE_TYPE, System, ValueType) DEFINE_METHOD(VALUE_TYPE, GET_HASH_CODE, GetHashCode, IM_RetInt) diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp index c0e4091af38a8..961d169db5e2e 100644 --- a/src/coreclr/vm/corhost.cpp +++ b/src/coreclr/vm/corhost.cpp @@ -627,16 +627,17 @@ HRESULT CorHost2::CreateAppDomainWithManager( pDomain->SetNativeDllSearchDirectories(pwzNativeDllSearchDirectories); { - SString sTrustedPlatformAssemblies(pwzTrustedPlatformAssemblies); - SString sPlatformResourceRoots(pwzPlatformResourceRoots); - SString sAppPaths(pwzAppPaths); - - DefaultAssemblyBinder *pBinder = pDomain->GetDefaultBinder(); - _ASSERTE(pBinder != NULL); - IfFailThrow(pBinder->SetupBindingPaths( - sTrustedPlatformAssemblies, - sPlatformResourceRoots, - sAppPaths)); + GCX_COOP(); + + MethodDescCallSite methSetupBindingPaths(METHOD__ASSEMBLYLOADCONTEXT__SETUP_BINDING_PATHS); + ARG_SLOT args[3] = + { + PtrToArgSlot(pwzTrustedPlatformAssemblies), + PtrToArgSlot(pwzPlatformResourceRoots), + PtrToArgSlot(pwzAppPaths) + }; + + methSetupBindingPaths.Call(args); } #if defined(TARGET_UNIX) diff --git a/src/coreclr/vm/domainassembly.cpp b/src/coreclr/vm/domainassembly.cpp index 75995f477a32f..623bc24a0e054 100644 --- a/src/coreclr/vm/domainassembly.cpp +++ b/src/coreclr/vm/domainassembly.cpp @@ -721,7 +721,8 @@ void DomainAssembly::RegisterWithHostAssembly() if (GetPEAssembly()->HasHostAssembly()) { - GetPEAssembly()->GetHostAssembly()->SetDomainAssembly(this); + GCX_COOP(); + ((BINDERASSEMBLYREF)ObjectFromHandle(GetPEAssembly()->GetHostAssembly()))->m_pDomainAssembly = this; } } @@ -737,7 +738,8 @@ void DomainAssembly::UnregisterFromHostAssembly() if (GetPEAssembly()->HasHostAssembly()) { - GetPEAssembly()->GetHostAssembly()->SetDomainAssembly(nullptr); + GCX_COOP(); + ((BINDERASSEMBLYREF)ObjectFromHandle(GetPEAssembly()->GetHostAssembly()))->m_pDomainAssembly = nullptr; } } diff --git a/src/coreclr/vm/domainassembly.h b/src/coreclr/vm/domainassembly.h index 40262412f4753..494c0b5c05c0e 100644 --- a/src/coreclr/vm/domainassembly.h +++ b/src/coreclr/vm/domainassembly.h @@ -308,6 +308,7 @@ class DomainAssembly final friend class Assembly; friend class Module; friend class FileLoadLock; + friend class SystemDomain; DomainAssembly(PEAssembly* pPEAssembly, LoaderAllocator* pLoaderAllocator); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 98a38b8603419..ac988be2c101a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -180,6 +180,9 @@ FCFuncStart(gMetaDataImport) FCFuncElement("GetPInvokeMap", MetaDataImport::GetPInvokeMap) FCFuncElement("IsValidToken", MetaDataImport::IsValidToken) FCFuncElement("GetMarshalAs", MetaDataImport::GetMarshalAs) + + FCFuncElement("GetAssemblyFromScope", MetaDataImport::GetAssemblyFromScope) + FCFuncElement("GetAssemblyProps", MetaDataImport::GetAssemblyProps) FCFuncEnd() FCFuncStart(gSignatureNative) diff --git a/src/coreclr/vm/managedmdimport.cpp b/src/coreclr/vm/managedmdimport.cpp index 8ab00a85f5320..4e8d922726773 100644 --- a/src/coreclr/vm/managedmdimport.cpp +++ b/src/coreclr/vm/managedmdimport.cpp @@ -440,6 +440,32 @@ FCIMPL3(HRESULT, MetaDataImport::GetMemberRefProps, } FCIMPLEND +FCIMPL2(HRESULT, MetaDataImport::GetAssemblyFromScope, + IMDInternalImport* pScope, + mdAssembly* ptkAssembly) +{ + FCALL_CONTRACT; + + return pScope->GetAssemblyFromScope(ptkAssembly); +} +FCIMPLEND + +FCIMPL8(HRESULT, MetaDataImport::GetAssemblyProps, + IMDInternalImport* pScope, + mdAssembly mda, + const void** ppbPublicKey, + ULONG* pcbPublicKey, + ULONG* pulHashAlgId, + LPCSTR* pszName, + AssemblyMetaDataInternal* pMetaData, + DWORD* pdwAsselblyFlags) +{ + FCALL_CONTRACT; + + return pScope->GetAssemblyProps(mda, ppbPublicKey, pcbPublicKey, pulHashAlgId, pszName, pMetaData, pdwAsselblyFlags); +} +FCIMPLEND + #if defined(_MSC_VER) && defined(TARGET_X86) #pragma optimize("", on) // restore command line optimization defaults #endif diff --git a/src/coreclr/vm/managedmdimport.hpp b/src/coreclr/vm/managedmdimport.hpp index 4db05291d6fd0..1968bdd1c6775 100644 --- a/src/coreclr/vm/managedmdimport.hpp +++ b/src/coreclr/vm/managedmdimport.hpp @@ -67,6 +67,17 @@ class MetaDataImport LPUTF8* marshalType, LPUTF8* marshalCookie, INT32* iidParamIndex); + + static FCDECL2(HRESULT, GetAssemblyFromScope, IMDInternalImport* pScope, mdAssembly* ptkAssembly); + static FCDECL8(HRESULT, GetAssemblyProps, + IMDInternalImport* pScope, + mdAssembly mda, + const void** ppbPublicKey, + ULONG* pcbPublicKey, + ULONG* pulHashAlgId, + LPCSTR* pszName, + AssemblyMetaDataInternal* pMetaData, + DWORD* pdwAsselblyFlags); }; extern "C" void QCALLTYPE MetadataImport_Enum( diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 35e6472b4b8da..e97335255ea58 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -632,6 +632,14 @@ DEFINE_METASIG(SM(PtrByte_RetStr, P(b), s)) DEFINE_METASIG(SM(Str_RetPtrByte, s, P(b))) DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v)) +DEFINE_METASIG(SM(PtrChar_PtrChar_PtrChar_RetVoid, P(u) P(u) P(u), v)) +DEFINE_METASIG_T(IM(IntPtr_Bool_RefBinderAssembly_RetInt, I F r(C(BINDER_ASSEMBLY)), i)) +DEFINE_METASIG_T(IM(PtrVoid_RefBinderAssembly_RetInt, P(v) r(C(BINDER_ASSEMBLY)), i)) +DEFINE_METASIG_T(SM(PtrChar_PtrChar_PtrChar_RefBinderAssembly_RetInt, P(u) P(u) P(u) r(C(BINDER_ASSEMBLY)), i)) +DEFINE_METASIG_T(IM(PtrByte_RefGuid_Bool_PtrByte_RetVoid, P(b) r(g(GUID)) F P(b), v)) +DEFINE_METASIG_T(IM(RetLoaderAllocator, ,C(LOADERALLOCATOR))) +DEFINE_METASIG_T(SM(IntPtr_RetBinderAssembly, I, C(BINDER_ASSEMBLY))) + // Undefine macros in case we include the file again in the compilation unit #undef DEFINE_METASIG diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index a282dd867e94a..af78621d05810 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1433,6 +1433,9 @@ class AssemblyLoadContextBaseObject : public Object // Modifying the order or fields of this object may require other changes to the // classlib class definition of this object. #ifdef TARGET_64BIT + OBJECTREF _appContext; + OBJECTREF _assemblySimpleNameMvidCheckHash; + OBJECTREF _loadedAssemblies; OBJECTREF _unloadLock; OBJECTREF _resolvingUnmanagedDll; OBJECTREF _resolving; @@ -1444,6 +1447,9 @@ class AssemblyLoadContextBaseObject : public Object CLR_BOOL _isCollectible; #else // TARGET_64BIT int64_t _id; // On 32-bit platforms this 64-bit value type is larger than a pointer so JIT places it first + OBJECTREF _appContext; + OBJECTREF _assemblySimpleNameMvidCheckHash; + OBJECTREF _loadedAssemblies; OBJECTREF _unloadLock; OBJECTREF _resolvingUnmanagedDll; OBJECTREF _resolving; @@ -1503,6 +1509,8 @@ typedef REF THREADBASEREF; typedef REF ASSEMBLYREF; +typedef REF BINDERASSEMBLYREF; + typedef REF ASSEMBLYLOADCONTEXTREF; typedef REF ASSEMBLYNAMEREF; @@ -1547,6 +1555,7 @@ typedef PTR_ReflectMethodObject REFLECTMETHODREF; typedef PTR_ReflectFieldObject REFLECTFIELDREF; typedef PTR_ThreadBaseObject THREADBASEREF; typedef PTR_AssemblyBaseObject ASSEMBLYREF; +typedef PTR_BinderAssemblyObject BINDERASSEMBLYREF; typedef PTR_AssemblyLoadContextBaseObject ASSEMBLYLOADCONTEXTREF; typedef PTR_AssemblyNameBaseObject ASSEMBLYNAMEREF; @@ -1558,6 +1567,27 @@ typedef PTR_AssemblyNameBaseObject ASSEMBLYNAMEREF; #endif //USE_CHECKED_OBJECTREFS +class PEImage; + +#include +// managed System.Runtime.Loader.BinderAssembly +class BinderAssemblyObject : public Object +{ +public: + OBJECTREF m_assemblyName; + AssemblyBinder* m_binder; + PEImage* m_peImage; + DomainAssembly* m_pDomainAssembly; + CLR_BOOL m_isInTPA; + CLR_BOOL m_isCoreLib; + + PTR_AssemblyBinder GetBinder() + { + return PTR_AssemblyBinder(m_binder); + } +}; +#include + #define PtrToArgSlot(ptr) ((ARG_SLOT)(SIZE_T)(ptr)) #define ArgSlotToPtr(s) ((LPVOID)(SIZE_T)(s)) diff --git a/src/coreclr/vm/peassembly.cpp b/src/coreclr/vm/peassembly.cpp index c7c618b485283..6272b993a12a6 100644 --- a/src/coreclr/vm/peassembly.cpp +++ b/src/coreclr/vm/peassembly.cpp @@ -16,8 +16,8 @@ #include "peimagelayout.inl" #include "invokeutil.h" #include "strongnameinternal.h" +#include "object.h" -#include "../binder/inc/applicationcontext.hpp" #include "../binder/inc/assemblybindercommon.hpp" #include "sha1.h" @@ -125,10 +125,12 @@ BOOL PEAssembly::Equals(PEAssembly *pPEAssembly) // because another thread beats it; the losing thread will pick up the PEAssembly in the cache. if (pPEAssembly->HasHostAssembly() && this->HasHostAssembly()) { - AssemblyBinder* otherBinder = pPEAssembly->GetHostAssembly()->GetBinder(); - AssemblyBinder* thisBinder = this->GetHostAssembly()->GetBinder(); + GCX_COOP(); - if (otherBinder != thisBinder || otherBinder == NULL) + BINDERASSEMBLYREF otherAssembly = (BINDERASSEMBLYREF)ObjectFromHandle(pPEAssembly->GetHostAssembly()); + BINDERASSEMBLYREF thisAssembly = (BINDERASSEMBLYREF)ObjectFromHandle(this->GetHostAssembly()); + + if (otherAssembly->m_binder != thisAssembly->m_binder || otherAssembly->m_binder == NULL) return FALSE; } @@ -653,18 +655,20 @@ ULONG PEAssembly::GetPEImageTimeDateStamp() #ifndef DACCESS_COMPILE PEAssembly::PEAssembly( - BINDER_SPACE::Assembly* pBindResultInfo, + BINDERASSEMBLYREF pBindResultInfo, IMetaDataEmit* pEmit, BOOL isSystem, PEImage * pPEImage /*= NULL*/, - BINDER_SPACE::Assembly * pHostAssembly /*= NULL*/) + BINDERASSEMBLYREF pHostAssembly /*= NULL*/) { CONTRACTL { CONSTRUCTOR_CHECK; PRECONDITION(CheckPointer(pEmit, NULL_OK)); - PRECONDITION(pBindResultInfo == NULL || pPEImage == NULL); - STANDARD_VM_CHECK; + // PRECONDITION(pBindResultInfo == NULL || pPEImage == NULL); // disabled for corelib + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; } CONTRACTL_END; @@ -681,7 +685,7 @@ PEAssembly::PEAssembly( m_pHostAssembly = nullptr; m_pFallbackBinder = nullptr; - pPEImage = pBindResultInfo ? pBindResultInfo->GetPEImage() : pPEImage; + pPEImage = (pBindResultInfo != NULL) ? pBindResultInfo->m_peImage : pPEImage; if (pPEImage) { _ASSERTE(pPEImage->CheckUniqueInstance()); @@ -718,17 +722,16 @@ PEAssembly::PEAssembly( // Set the host assembly and binding context as the AssemblySpec initialization // for CoreCLR will expect to have it set. - if (pHostAssembly != nullptr) + if (pHostAssembly != NULL) { - m_pHostAssembly = clr::SafeAddRef(pHostAssembly); + m_pHostAssembly = GetAppDomain()->CreateHandle(pHostAssembly); } - if(pBindResultInfo != nullptr) + if (pBindResultInfo != NULL) { // Cannot have both pHostAssembly and a coreclr based bind - _ASSERTE(pHostAssembly == nullptr); - pBindResultInfo = clr::SafeAddRef(pBindResultInfo); - m_pHostAssembly = pBindResultInfo; + _ASSERTE(m_pHostAssembly == NULL); + m_pHostAssembly = GetAppDomain()->CreateHandle(pBindResultInfo); } #ifdef LOGGING @@ -741,16 +744,22 @@ PEAssembly::PEAssembly( PEAssembly *PEAssembly::Open( PEImage * pPEImageIL, - BINDER_SPACE::Assembly * pHostAssembly) + BINDERASSEMBLYREF pManagedHostAssembly) { - STANDARD_VM_CONTRACT; + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; PEAssembly * pPEAssembly = new PEAssembly( - nullptr, // BindResult + NULL, // BindResult nullptr, // IMetaDataEmit FALSE, // isSystem pPEImageIL, - pHostAssembly); + pManagedHostAssembly); return pPEAssembly; } @@ -791,7 +800,10 @@ PEAssembly::~PEAssembly() m_PEImage->Release(); if (m_pHostAssembly != NULL) - m_pHostAssembly->Release(); + { + DestroyHandle(m_pHostAssembly); + m_pHostAssembly = NULL; + } } /* static */ @@ -830,15 +842,22 @@ PEAssembly *PEAssembly::DoOpenSystem() CONTRACT_END; ETWOnStartup (FusionBinding_V1, FusionBindingEnd_V1); - ReleaseHolder pBoundAssembly; - IfFailThrow(GetAppDomain()->GetDefaultBinder()->BindToSystem(&pBoundAssembly)); + ReleaseHolder pBoundAssembly; + + StackSString systemPath(SystemDomain::System()->SystemDirectory()); + IfFailThrow(BINDER_SPACE::AssemblyBinderCommon::BindToSystem(systemPath, &pBoundAssembly)); + + { + GCX_COOP(); - RETURN new PEAssembly(pBoundAssembly, NULL, TRUE); + // HostAssembly is set afterwards for CoreLib + RETURN new PEAssembly(NULL, NULL, TRUE, pBoundAssembly); + } } -PEAssembly* PEAssembly::Open(BINDER_SPACE::Assembly* pBindResult) +PEAssembly* PEAssembly::Open(BINDERASSEMBLYREF pManagedBindResult) { - return new PEAssembly(pBindResult,NULL,/*isSystem*/ false); + return new PEAssembly(pManagedBindResult,NULL,/*isSystem*/ false); }; /* static */ @@ -856,7 +875,11 @@ PEAssembly *PEAssembly::Create(IMetaDataAssemblyEmit *pAssemblyEmit) // we have.) SafeComHolder pEmit; pAssemblyEmit->QueryInterface(IID_IMetaDataEmit, (void **)&pEmit); - RETURN new PEAssembly(NULL, pEmit, FALSE); + + { + GCX_COOP(); + RETURN new PEAssembly(NULL, pEmit, FALSE); + } } #endif // #ifndef DACCESS_COMPILE @@ -1096,10 +1119,11 @@ PTR_AssemblyBinder PEAssembly::GetAssemblyBinder() PTR_AssemblyBinder pBinder = NULL; - PTR_BINDER_SPACE_Assembly pHostAssembly = GetHostAssembly(); + OBJECTHANDLE pHostAssembly = GetHostAssembly(); if (pHostAssembly) { - pBinder = pHostAssembly->GetBinder(); + GCX_COOP(); + pBinder = ((BINDERASSEMBLYREF)ObjectFromHandle(pHostAssembly))->GetBinder(); } else { diff --git a/src/coreclr/vm/peassembly.h b/src/coreclr/vm/peassembly.h index 49a9a3ffc5b21..3a40ecf2f52ee 100644 --- a/src/coreclr/vm/peassembly.h +++ b/src/coreclr/vm/peassembly.h @@ -301,12 +301,19 @@ class PEAssembly final } // Returns a non-AddRef'ed BINDER_SPACE::Assembly* - PTR_BINDER_SPACE_Assembly GetHostAssembly() + OBJECTHANDLE GetHostAssembly() { STATIC_CONTRACT_LIMITED_METHOD; return m_pHostAssembly; } + // Set host assembly after PEAssembly was created. + // Adhoc for CoreLib + void SetHostAssemblyAdHoc(OBJECTHANDLE pHostAssembly) + { + m_pHostAssembly = pHostAssembly; + } + // Returns the AssemblyBinder* instance associated with the PEAssembly // which owns the context into which the current PEAssembly was loaded. // For Dynamic assemblies this is the fallback binder. @@ -334,12 +341,12 @@ class PEAssembly final static PEAssembly* Open( PEImage* pPEImageIL, - BINDER_SPACE::Assembly* pHostAssembly); + BINDERASSEMBLYREF pHostAssembly); // This opens the canonical System.Private.CoreLib.dll static PEAssembly* OpenSystem(); - static PEAssembly* Open(BINDER_SPACE::Assembly* pBindResult); + static PEAssembly* Open(BINDERASSEMBLYREF pBindResult); static PEAssembly* Create(IMetaDataAssemblyEmit* pEmit); @@ -368,12 +375,13 @@ class PEAssembly final ~PEAssembly() {}; PEAssembly() = default; #else + PEAssembly( - BINDER_SPACE::Assembly* pBindResultInfo, + BINDERASSEMBLYREF pBindResultInfo, IMetaDataEmit* pEmit, BOOL isSystem, PEImage* pPEImageIL = NULL, - BINDER_SPACE::Assembly* pHostAssembly = NULL + BINDERASSEMBLYREF pHostAssembly = NULL ); ~PEAssembly(); @@ -424,7 +432,7 @@ class PEAssembly final Volatile m_refCount; bool m_isSystem; - PTR_BINDER_SPACE_Assembly m_pHostAssembly; + OBJECTHANDLE m_pHostAssembly; // For certain assemblies, we do not have m_pHostAssembly since they are not bound using an actual binder. // An example is Ref-Emitted assemblies. Thus, when such assemblies trigger load of their dependencies, diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 99e76824a5df9..32fbe60ba72f4 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -202,6 +202,11 @@ static const Entry s_QCall[] = DllImportEntry(AssemblyNative_IsApplyUpdateSupported) DllImportEntry(AssemblyNative_InitializeAssemblyLoadContext) DllImportEntry(AssemblyNative_PrepareForAssemblyLoadContextRelease) + DllImportEntry(AssemblyNative_GetMDImport) + DllImportEntry(AssemblyNative_GetSimpleNameNative) + DllImportEntry(AssemblyNative_GetExposedObject) + DllImportEntry(AssemblyNative_GetPEImage) + DllImportEntry(AssemblyNative_SetSymbolBytes) DllImportEntry(AssemblyNative_LoadFromPath) DllImportEntry(AssemblyNative_LoadFromStream) #ifdef TARGET_WINDOWS @@ -211,11 +216,23 @@ static const Entry s_QCall[] = DllImportEntry(AssemblyNative_TraceResolvingHandlerInvoked) DllImportEntry(AssemblyNative_TraceAssemblyResolveHandlerInvoked) DllImportEntry(AssemblyNative_TraceAssemblyLoadFromResolveHandlerInvoked) - DllImportEntry(AssemblyNative_TraceSatelliteSubdirectoryPathProbed) DllImportEntry(AssemblyNative_GetLoadedAssemblies) + DllImportEntry(AssemblyNative_TracePathProbed) + DllImportEntry(AssemblyNative_TraceResolutionAttempted) DllImportEntry(AssemblyNative_GetAssemblyCount) DllImportEntry(AssemblyNative_GetEntryAssembly) DllImportEntry(AssemblyNative_GetExecutingAssembly) + DllImportEntry(PEImage_BinderAcquireImport) + DllImportEntry(PEImage_BinderAcquirePEImage) + DllImportEntry(PEImage_Release) + DllImportEntry(PEImage_GetMVID) + DllImportEntry(PEImage_GetPath) + DllImportEntry(DomainAssembly_GetPEAssembly) + DllImportEntry(DomainAssembly_EnsureReferenceBinder) + DllImportEntry(PEAssembly_GetHostAssembly) + DllImportEntry(Bundle_AppIsBundle) + DllImportEntry(Bundle_ProbeAppBundle) + DllImportEntry(Bundle_GetAppBundleBasePath) #if defined(FEATURE_MULTICOREJIT) DllImportEntry(MultiCoreJIT_InternalSetProfileRoot) DllImportEntry(MultiCoreJIT_InternalStartProfile) diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 4facca3a37bfb..feafad1906bf6 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -758,7 +758,28 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat LPCSTR assemblyName; IfFailThrow(pNativeMDImport->GetAssemblyRefProps(assemblyRef, NULL, NULL, &assemblyName, NULL, NULL, NULL, NULL)); - binder->DeclareDependencyOnMvid(assemblyName, *componentMvid, pNativeImage != NULL, pModule != NULL ? pModule->GetSimpleName() : pNativeImage->GetFileName()); + { + GCX_COOP(); + + OBJECTREF alc = ObjectFromHandle((OBJECTHANDLE)binder->GetManagedAssemblyLoadContext()); + + GCPROTECT_BEGIN(alc); + + MethodDescCallSite methDeclareDependencyOnMvid(METHOD__ASSEMBLYLOADCONTEXT__DECLARE_DEPENDENCY_ON_MVID); + ARG_SLOT args[5] = + { + ObjToArgSlot(alc), + PtrToArgSlot(componentMvid), + PtrToArgSlot(assemblyName), + BoolToArgSlot(pNativeImage != NULL), + PtrToArgSlot(pModule != NULL ? pModule->GetSimpleName() : pNativeImage->GetFileName()) + }; + + methDeclareDependencyOnMvid.Call(args); + + GCPROTECT_END(); + } + manifestAssemblyCount++; } } diff --git a/src/coreclr/vm/util.cpp b/src/coreclr/vm/util.cpp index d03ecb7326073..a765183746e80 100644 --- a/src/coreclr/vm/util.cpp +++ b/src/coreclr/vm/util.cpp @@ -1795,16 +1795,12 @@ int GetRandomInt(int maxVal) // These wrap the SString:L:CompareCaseInsensitive function in a way that makes it // easy to fix code that uses _stricmp. _stricmp should be avoided as it uses the current // C-runtime locale rather than the invariance culture. -// -// Note that unlike the real _stricmp, these functions unavoidably have a throws/gc_triggers/inject_fault -// contract. So if need a case-insensitive comparison in a place where you can't tolerate this contract, -// you've got a problem. int __cdecl stricmpUTF8(const char* szStr1, const char* szStr2) { CONTRACTL { THROWS; - GC_TRIGGERS; + GC_NOTRIGGER; INJECT_FAULT(COMPlusThrowOM()); } CONTRACTL_END diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index 3c5d82f73848c..94b0dffd0b82e 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -105,6 +105,7 @@ class OBJECTREF { class ReflectClassBaseObject* m_asReflectClass; class ExecutionContextObject* m_asExecutionContext; class AssemblyLoadContextBaseObject* m_asAssemblyLoadContextBase; + class BinderAssemblyObject* m_asBinderAssembly; }; public: diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index c4504f140527a..44d1859fbd04d 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4319,4 +4319,13 @@ Emitting debug info is not supported for this member. + + Could not load file or assembly '{0}'. {1} + + + Assembly with same name is already loaded + + + Dynamically emitted assemblies are unsupported during host-based resolution. + diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs index 7fb7a66815956..562acc8a68611 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs @@ -10,11 +10,16 @@ internal static partial class AssemblyNameHelpers { public static byte[]? ComputePublicKeyToken(byte[]? publicKey) { - if (publicKey == null) + if (publicKey is null) return null; + return ComputePublicKeyToken(publicKey.AsSpan()); + } + + public static byte[] ComputePublicKeyToken(ReadOnlySpan publicKey) + { if (publicKey.Length == 0) - return Array.Empty(); + return []; if (!IsValidPublicKey(publicKey)) throw new SecurityException(SR.Security_InvalidAssemblyPublicKey); @@ -35,16 +40,14 @@ internal static partial class AssemblyNameHelpers // // This validation logic is a port of StrongNameIsValidPublicKey() from src\coreclr\md\runtime\strongnameinternal.cpp // - private static bool IsValidPublicKey(byte[] publicKey) + private static bool IsValidPublicKey(ReadOnlySpan publicKeyBlob) { - uint publicKeyLength = (uint)(publicKey.Length); + uint publicKeyLength = (uint)(publicKeyBlob.Length); // The buffer must be at least as large as the public key structure (for compat with desktop, we actually compare with the size of the header + 4). if (publicKeyLength < SizeOfPublicKeyBlob + 4) return false; - // Poor man's reinterpret_cast into the PublicKeyBlob structure. - ReadOnlySpan publicKeyBlob = new ReadOnlySpan(publicKey); uint sigAlgID = BinaryPrimitives.ReadUInt32LittleEndian(publicKeyBlob); uint hashAlgID = BinaryPrimitives.ReadUInt32LittleEndian(publicKeyBlob.Slice(4)); uint cbPublicKey = BinaryPrimitives.ReadUInt32LittleEndian(publicKeyBlob.Slice(8)); @@ -71,7 +74,7 @@ private static bool IsValidPublicKey(byte[] publicKey) return false; // The key blob must indicate that it is a PUBLICKEYBLOB - if (publicKey[SizeOfPublicKeyBlob] != PUBLICKEYBLOB) + if (publicKeyBlob[SizeOfPublicKeyBlob] != PUBLICKEYBLOB) return false; return true; @@ -94,7 +97,7 @@ private static uint GetAlgSid(uint x) private const uint ALG_CLASS_SIGNATURE = (1 << 13); private const uint PUBLICKEYBLOB = 0x6; - private const uint SizeOfPublicKeyBlob = 12; + private const int SizeOfPublicKeyBlob = 12; private const int PublicKeyTokenLength = 8; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index 498aa1e4d4a9c..fe070d14f3efd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -822,7 +822,7 @@ internal IntPtr GetResolvedUnmanagedDll(Assembly assembly, string unmanagedDllNa } } - internal sealed class DefaultAssemblyLoadContext : AssemblyLoadContext + internal sealed partial class DefaultAssemblyLoadContext : AssemblyLoadContext { internal static readonly AssemblyLoadContext s_loadContext = new DefaultAssemblyLoadContext();