Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to load assembly in an application that publishes as a single file? #45309

Closed
alexdi220 opened this issue Nov 29, 2020 · 10 comments
Closed
Labels
area-System.Reflection.Metadata untriaged New issue has not been triaged by the area owner

Comments

@alexdi220
Copy link

Description

Hi, I have an application that uses Mono.Cecil and AssemblyDefinition. I take the loaded assembly (System.Reflection.Assembly) and create a definition by Assembly.Location. Unfortunately, the PublishSingleFile mode loads references from resources instead of a disk. In this case the Assembly.Location is null and I can't found any other way to create the definition. The Mono's AssemblyDefinition can be created from the stream, but the NetCore\Net5 has no BinnaryFormatter. I've used the internal method Assembly.GetRawBytes and it also removed from the new version of the framework. Could you give me a way how can I get bytes[]/stream/location for loaded assembly?

Configuration

  • .NET 5 / .NetCore 3
  • Windows 10
  • x64, x86

Regression?

Other information

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-AssemblyLoader-coreclr untriaged New issue has not been triaged by the area owner labels Nov 29, 2020
@ghost
Copy link

ghost commented Nov 29, 2020

Tagging subscribers to this area: @vitek-karas, @agocke, @CoffeeFlux
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

Hi, I have an application that uses Mono.Cecil and AssemblyDefinition. I take the loaded assembly (System.Reflection.Assembly) and create a definition by Assembly.Location. Unfortunately, the PublishSingleFile mode loads references from resources instead of a disk. In this case the Assembly.Location is null and I can't found any other way to create the definition. The Mono's AssemblyDefinition can be created from the stream, but the NetCore\Net5 has no BinnaryFormatter. I've used the internal method Assembly.GetRawBytes and it also removed from the new version of the framework. Could you give me a way how can I get bytes[]/stream/location for loaded assembly?

Configuration

  • .NET 5 / .NetCore 3
  • Windows 10
  • x64, x86

Regression?

Other information

Author: alexdi220
Assignees: -
Labels:

area-AssemblyLoader-coreclr, untriaged

Milestone: -

@jkotas
Copy link
Member

jkotas commented Nov 29, 2020

You can try to use AssemblyExtensions.TryGetRawMetadata. This method returns just the metadata blob, not the whole assembly. It works well for System.Reflection.Metadata reader and Roslyn. I am not sure whether it works for Cecil.

@alexdi220
Copy link
Author

alexdi220 commented Nov 29, 2020

Hi, @jkotas. Thanks for a super quick answer. It makes my day. But I faced with the BadImageFormatException when I getting MetadaReader. Please take a look code. What I do wrong?

private static unsafe void ReadAssembly()
        {
            var asm = typeof(JsonConvert).Assembly; //from Newtonsoft.Json package
            asm.TryGetRawMetadata(out byte* blob, out int length);
            byte[] buffer = new byte[length];
            Marshal.Copy((IntPtr)blob, buffer, 0, length);
            using var stream = new MemoryStream(buffer);
            var reader = new PEReader(stream);
            var meta = reader.GetMetadataReader(); // throws System.BadImageFormatException: 'Image is too small.'
        }

The StackTrace:
System.Reflection.Metadata.dll!System.Reflection.Throw.ImageTooSmall() Unknown
System.Reflection.Metadata.dll!System.Reflection.PortableExecutable.SectionHeader.SectionHeader(ref System.Reflection.PortableExecutable.PEBinaryReader reader) Unknown
System.Reflection.Metadata.dll!System.Reflection.PortableExecutable.PEHeaders.ReadSectionHeaders(ref System.Reflection.PortableExecutable.PEBinaryReader reader) Unknown
System.Reflection.Metadata.dll!System.Reflection.PortableExecutable.PEHeaders.PEHeaders(System.IO.Stream peStream, int size, bool isLoadedImage) Unknown
System.Reflection.Metadata.dll!System.Reflection.PortableExecutable.PEReader.InitializePEHeaders() Unknown
System.Reflection.Metadata.dll!System.Reflection.PortableExecutable.PEReader.GetMetadataBlock() Unknown
System.Reflection.Metadata.dll!System.Reflection.Metadata.PEReaderExtensions.GetMetadataReader(System.Reflection.PortableExecutable.PEReader peReader, System.Reflection.Metadata.MetadataReaderOptions options, System.Reflection.Metadata.MetadataStringDecoder utf8Decoder) Unknown
System.Reflection.Metadata.dll!System.Reflection.Metadata.PEReaderExtensions.GetMetadataReader(System.Reflection.PortableExecutable.PEReader peReader) Unknown

@jkotas
Copy link
Member

jkotas commented Nov 30, 2020

Try this:

asm.TryGetRawMetadata(out byte* blob, out int length);
var meta = new MetadataReader(blob, length);

@alexdi220
Copy link
Author

It works great. Thank you a lot!

@alexdi220 alexdi220 changed the title How to load assembly in application that publish as single file? How to load assembly in an application that publishes as a single file? Dec 1, 2020
@alexdi220 alexdi220 reopened this Dec 2, 2020
@alexdi220
Copy link
Author

Hi, @jkotas the Mono.Cecil's AssemblyDefinition needs the whole assembly instead of metadata only. With DOSHeader, PEFileHeader, and other info. Is there any way to get the whole assembly's bytes? And how the references' assemblies are distributed in the "SingleFile" mode? I mean, I can load assembly myself if it shipped in resources of the application or any other known place.

@jkotas
Copy link
Member

jkotas commented Dec 2, 2020

Yes, you can add the reference assemblies as resources yourself.

@alexdi220
Copy link
Author

Thank you. But I can't do it, could you advise another solution\workaround? We develop a third party that is used by other developers. In the projects of our clients, there are NuGet packages to our components. Some assemblies of these packages will be patched in runtime by our code (I told about it in the first message). The new behavior of .NET 5 when the location of assembly is null - critical breaking change for our feature.

@jkotas
Copy link
Member

jkotas commented Dec 2, 2020

You can set <IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract> that will extract the assemblies to disk like in .NET Core 3.1.

Also, we are likely to introduce more optimized form factors going forward that may be missing the IL or metadata completely. https://github.com/dotnet/designs/blob/main/accepted/2020/form-factors.md talks about it in more details. If your feature depends on IL and metadata to be present and patched at runtime, it will not be compatible with these form factors.

@alexdi220
Copy link
Author

Thank you, Jan. You really help me. Also, we'll research a new approach of form factors to improve our code in the future.

@ghost ghost locked as resolved and limited conversation to collaborators Jan 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Reflection.Metadata untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

4 participants