From 32a82700ba2018eb5d7dba9312ef4e358bfb548f Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 24 Feb 2025 14:12:45 -0800 Subject: [PATCH] Fix getting resource when ResourceResolve returns assembly with resource that is an assembly ref (#112810) When getting a resource where `ResourceResolve` handler returns an assembly with a manifest resource that is an assembly ref, we incorrectly resolved the reference on the original assembly instead of the assembly returned by the handler and then also looked for the resource on the original assembly again instead of using the referenced assembly. This change includes a test for this case using IL. The manifest resource file (as opposed to assembly ref) case is already covered in libraries tests. --- src/coreclr/vm/peassembly.cpp | 24 +++++----- .../ManifestResourceAssemblyRef.il | 19 ++++++++ .../ManifestResourceAssemblyRef.ilproj | 8 ++++ .../ResourceResolve/ResourceAssembly.csproj | 8 ++++ .../Loader/ResourceResolve/ResourceResolve.cs | 47 +++++++++++++++++++ .../ResourceResolve/ResourceResolve.csproj | 10 ++++ 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 src/tests/Loader/ResourceResolve/ManifestResourceAssemblyRef.il create mode 100644 src/tests/Loader/ResourceResolve/ManifestResourceAssemblyRef.ilproj create mode 100644 src/tests/Loader/ResourceResolve/ResourceAssembly.csproj create mode 100644 src/tests/Loader/ResourceResolve/ResourceResolve.cs create mode 100644 src/tests/Loader/ResourceResolve/ResourceResolve.csproj diff --git a/src/coreclr/vm/peassembly.cpp b/src/coreclr/vm/peassembly.cpp index 3f54dbff556af2..8594e62c9a0e6b 100644 --- a/src/coreclr/vm/peassembly.cpp +++ b/src/coreclr/vm/peassembly.cpp @@ -518,7 +518,6 @@ BOOL PEAssembly::GetResource(LPCSTR szName, DWORD *cbResource, } CONTRACTL_END; - mdToken mdLinkRef; DWORD dwResourceFlags; DWORD dwOffset; @@ -567,30 +566,31 @@ BOOL PEAssembly::GetResource(LPCSTR szName, DWORD *cbResource, } - switch(TypeFromToken(mdLinkRef)) { + switch(TypeFromToken(mdLinkRef)) + { case mdtAssemblyRef: { if (pAssembly == NULL) return FALSE; AssemblySpec spec; - spec.InitializeSpec(mdLinkRef, GetMDImport(), pAssembly); - DomainAssembly* pDomainAssembly = spec.LoadDomainAssembly(FILE_LOADED); + spec.InitializeSpec(mdLinkRef, pAssembly->GetMDImport(), pAssembly); + Assembly* pLoadedAssembly = spec.LoadAssembly(FILE_LOADED); if (dwLocation) { if (pAssemblyRef) - *pAssemblyRef = pDomainAssembly->GetAssembly(); + *pAssemblyRef = pLoadedAssembly; *dwLocation = *dwLocation | 2; // ResourceLocation.containedInAnotherAssembly } - return GetResource(szName, - cbResource, - pbInMemoryResource, - pAssemblyRef, - szFileName, - dwLocation, - pDomainAssembly->GetAssembly()); + return pLoadedAssembly->GetResource( + szName, + cbResource, + pbInMemoryResource, + pAssemblyRef, + szFileName, + dwLocation); } case mdtFile: diff --git a/src/tests/Loader/ResourceResolve/ManifestResourceAssemblyRef.il b/src/tests/Loader/ResourceResolve/ManifestResourceAssemblyRef.il new file mode 100644 index 00000000000000..c4891a07551f14 --- /dev/null +++ b/src/tests/Loader/ResourceResolve/ManifestResourceAssemblyRef.il @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) +} + +.assembly extern ResourceAssembly +{ + .publickeytoken = (00 00 00 00 00 00 00 00) +} + +.assembly ManifestResourceAssemblyRef { } + +.mresource public 'MyResource' +{ + .assembly extern 'ResourceAssembly' +} diff --git a/src/tests/Loader/ResourceResolve/ManifestResourceAssemblyRef.ilproj b/src/tests/Loader/ResourceResolve/ManifestResourceAssemblyRef.ilproj new file mode 100644 index 00000000000000..bfc5d0bb00df9b --- /dev/null +++ b/src/tests/Loader/ResourceResolve/ManifestResourceAssemblyRef.ilproj @@ -0,0 +1,8 @@ + + + library + + + + + diff --git a/src/tests/Loader/ResourceResolve/ResourceAssembly.csproj b/src/tests/Loader/ResourceResolve/ResourceAssembly.csproj new file mode 100644 index 00000000000000..e014b87610bd87 --- /dev/null +++ b/src/tests/Loader/ResourceResolve/ResourceAssembly.csproj @@ -0,0 +1,8 @@ + + + Library + + + + + diff --git a/src/tests/Loader/ResourceResolve/ResourceResolve.cs b/src/tests/Loader/ResourceResolve/ResourceResolve.cs new file mode 100644 index 00000000000000..2cf30a36216ffe --- /dev/null +++ b/src/tests/Loader/ResourceResolve/ResourceResolve.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Reflection; +using Xunit; + +[ConditionalClass(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotNativeAot))] +public unsafe class ResourceResolve +{ + [Fact] + [SkipOnMono("AssemblyRef manifest resource is not supported")] + public static void AssemblyRef() + { + string resourceName = "MyResource"; + Assembly assembly = typeof(ResourceResolve).Assembly; + + // Manifest resource is not in the current assembly + Stream stream = assembly.GetManifestResourceStream(resourceName); + Assert.Null(stream); + + // Handler returns assembly with a manifest resource assembly ref that + // points to another assembly with the resource + ResolveEventHandler handler = (sender, args) => + { + if (args.Name == resourceName && args.RequestingAssembly == assembly) + return Assembly.Load("ManifestResourceAssemblyRef"); + + return null; + }; + AppDomain.CurrentDomain.ResourceResolve += handler; + stream = assembly.GetManifestResourceStream(resourceName); + AppDomain.CurrentDomain.ResourceResolve -= handler; + Assert.NotNull(stream); + + // Verify that the stream matches the expected one in the resource assembly + Assembly resourceAssembly = Assembly.Load("ResourceAssembly"); + Stream expected = resourceAssembly.GetManifestResourceStream(resourceName); + Assert.Equal(expected.Length, stream.Length); + Span expectedBytes = new byte[expected.Length]; + expected.Read(expectedBytes); + Span streamBytes = new byte[stream.Length]; + stream.Read(streamBytes); + Assert.Equal(expectedBytes, streamBytes); + } +} diff --git a/src/tests/Loader/ResourceResolve/ResourceResolve.csproj b/src/tests/Loader/ResourceResolve/ResourceResolve.csproj new file mode 100644 index 00000000000000..5fc4c77bada41d --- /dev/null +++ b/src/tests/Loader/ResourceResolve/ResourceResolve.csproj @@ -0,0 +1,10 @@ + + + + + + + + + +