dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.17k stars 4.72k forks source link

Microsoft.XmlSerializer.Generator fails with BadImageFormatException if used for a .NET Framework project using MEF #40010

Open AStefanits opened 4 years ago

AStefanits commented 4 years ago

Description

We are currently in the process to migrate our projects to use the dotnet compiler instead of MSBuild. For that we have exchanged the old csproj format with the Microsoft.NET.Sdk. But still targeting the .NET Framework 4.6.1.

In order to do that we have also exchanged the old sgen with the Microsoft.XmlSerializer.Generator nuget package. While running the dotnet build we are facing the issue that the Microsoft.XmlSerializer.Generator fails with " SGEN : warning SGEN1: Could not load file or assembly 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL'. Reference assemblies should not be loaded for execution. They can only be loaded in the Reflection-only loader context. (0x80131058)"

After digging into the issue and getting the source from the master branch I could find the issue. When the Sgen.cs class checks if the ObsoleteAttribute is used, by using the type.GetCustomAttributes, it tries to load the types. The FileNotFoundException, if a custom attribute is used but defined in another assembly, is handled in the way that the exception is logged and the type is ignored. But the use case the attribute is defined in the .NET Framework itself a ArgumentException containing the BadImageFormatException is thrown.

I tried to create a small change for testing purpose which handles the ArgumentException the same way as the FileNotFoundException. After the change the XmlSerializer assembly could be generated.

A small project showing the issue is added to my GitHub repo: https://github.com/AStefanits/XmlSerializerGeneratorIssue Build execution by command line (dotnet build \source\repos\ClassLibrary1\ClassLibrary1\ClassLibrary1.csproj)

Configuration

Project format: Microsoft.NET.Sdk .NET Framework 4.6.1 (also tested with 4.7.2 and 4.8) Windows 10 Enterprise 1909 x64 Visual Studio 2019 Microsoft (R) Build Engine version 16.7.0-preview-20360-03+188921e2f for .NET (also tested with different Versions)

Regression?

Tested with Microsoft.XmlSerializer.Generator

Other information

Exception message and stack trace: A BadImageFormatException has been thrown while parsing the signature. This is likely due to lack of a generic context. Ensure genericTypeArguments and genericMethodArguments are provided and contain enough context.

 at System.Reflection.RuntimeModule.ResolveType(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
 at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(MetadataToken caCtorToken, MetadataImport& scope, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1& derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctor, Boolean& ctorHasParameters, Boolean& isVarArg)
 at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes)
 at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType)
 at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)
 at System.RuntimeType.GetCustomAttributes(Type attributeType, Boolean inherit)
 at Microsoft.XmlSerializer.Generator.Sgen.GenerateFile(List`1 typeNames, String assemblyName, Boolean proxyOnly, Boolean silent, Boolean warnings, Boolean force, String outputDirectory, Boolean parsableerrors) in \source\repos\runtime\src\libraries\Microsoft.XmlSerializer.Generator\src\Sgen.cs:line 262

Added to Method GenerateFile in Sgen.cs line 289 for testing purpose:

            catch (ArgumentException e)
            {
                if (warnings)
                {
                    Console.Out.WriteLine(FormatMessage(parsableerrors, true,
                        SR.Format(SR.InfoIgnoreType, type.FullName)));
                    WriteWarning(e, parsableerrors);
                }

                continue;
            }
ericstj commented 4 years ago

I believe this is effectively a duplicate of https://github.com/dotnet/runtime/issues/630 or at least should be solved by that. @mconnew

mconnew commented 4 years ago

Different problem but possibly solved by the same fix. I'm not sure what the behavior is with the metadata reflection implementation when asked to load a reference assembly. If it still throws, then that won't solve this issue.

ericstj commented 4 years ago

It does not throw. It would fix this issue.

HongGit commented 4 years ago

You’re hitting this bug because XmlSerializerGenerator is doing runtime-reflection on reference assemblies. You might be able to workaround it by using this hack which is shared for others hitting this problem on other build tools which load build assemblies for execution: https://github.com/dotnet/sdk/issues/1522#issuecomment-324141767.

The fix for this is quite complex, we would not be able to get to in near term.