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

Enable verbose linker output & add baseline warning suppressions for library builds #39133

Closed
wants to merge 2 commits into from

Conversation

layomia
Copy link
Contributor

@layomia layomia commented Jul 11, 2020

Fixes #38033.

as we need both linker annotations and xml files
suppress "error IL2047: All overridden members must have the same DynamicallyAccessedMembersAttribute usage",
as we don't add suppression files for ref projects -->
<ILLinkArgs>$(ILLinkArgs) --nowarn IL2025;IL2047</ILLinkArgs>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IL2047

This is a workaround for dotnet/linker#1348.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the 2047 should be fixed within the runtime. I know @MichalStrehovsky planned to look into it. I think we should have an issue for fixing these in the runtime - or add them to the baselines as well.

dotnet/linker#1348 might help make the baselines "smaller" if we link against implementation assemlies.

@mateoatr
Copy link
Contributor

/cc @sbomer @vitek-karas

@marek-safar
Copy link
Contributor

Should there be tracking issue to actually fix the warnings?


using System.Diagnostics.CodeAnalysis;

[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2006", Scope = "member", Target = "F:System.LazyDebugView`1._lazy")]
Copy link
Contributor

@marek-safar marek-safar Jul 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make sense to have the warnings for shared SPC in the shared location (there might not be many in runtime specific code)

@@ -0,0 +1,2 @@
# Exposed publicly in the implementation to suppress linker warnings.
TypesMustExist : Type 'System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute' does not exist in the reference but it does exist in the implementation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the type be public in CoreLib only, and be internal everywhere else to make this unnecessary?

It is what we do in other similar cases. For example:

[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2047", Scope = "member", Target = "M:System.Reflection.Emit.SymbolType.GetProperties(System.Reflection.BindingFlags)")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2047", Scope = "member", Target = "M:System.Reflection.Emit.SymbolType.GetPropertyImpl(System.String,System.Reflection.BindingFlags,System.Reflection.Binder,System.Type,System.Type[],System.Reflection.ParameterModifier[])")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2047", Scope = "member", Target = "M:System.Reflection.Emit.SymbolType.InvokeMember(System.String,System.Reflection.BindingFlags,System.Reflection.Binder,System.Object,System.Object[],System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[])")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2047", Scope = "member", Target = "M:System.Reflection.Emit.TypeBuilder.GetConstructorImpl(System.Reflection.BindingFlags,System.Reflection.Binder,System.Reflection.CallingConventions,System.Type[],System.Reflection.ParameterModifier[])")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above you seem to disable this warning all up - we should either disable it, or add the baselines for it... not both.

@@ -192,6 +192,13 @@
<ILLinkArgs>$(ILLinkArgs) --disable-opt unusedinterfaces</ILLinkArgs>
<!-- keep DynamicDependencyAttribute unless a project explicitly disables it -->
<ILLinkArgs Condition="'$(ILLinkKeepDepAttributes)' != 'false'">$(ILLinkArgs) --keep-dep-attributes true</ILLinkArgs>
<!-- enable verbose output -->
<ILLinkArgs>$(ILLinkArgs) --verbose</ILLinkArgs>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case these ever need to be conditioned out for a specific reason, can we add a property that individiual projects can override for this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is going to be removed as soon as the linker enables warnings by default. So there is probably no reason to add a property here - it is just going to be deleted.

Comment on lines 196 to 201
<ILLinkArgs>$(ILLinkArgs) --verbose</ILLinkArgs>
<!-- suppress "error IL2025: Duplicate preserve of" warnings from verbose output,
as we need both linker annotations and xml files
suppress "error IL2047: All overridden members must have the same DynamicallyAccessedMembersAttribute usage",
as we don't add suppression files for ref projects -->
<ILLinkArgs>$(ILLinkArgs) --nowarn IL2025;IL2047</ILLinkArgs>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<ILLinkArgs>$(ILLinkArgs) --verbose</ILLinkArgs>
<!-- suppress "error IL2025: Duplicate preserve of" warnings from verbose output,
as we need both linker annotations and xml files
suppress "error IL2047: All overridden members must have the same DynamicallyAccessedMembersAttribute usage",
as we don't add suppression files for ref projects -->
<ILLinkArgs>$(ILLinkArgs) --nowarn IL2025;IL2047</ILLinkArgs>
<ILLinkArgs Condition="'$(DisableILLinkWarnings)' != 'true'">$(ILLinkArgs) --verbose</ILLinkArgs>
<!-- suppress "error IL2025: Duplicate preserve of" warnings from verbose output,
as we need both linker annotations and xml files
suppress "error IL2047: All overridden members must have the same DynamicallyAccessedMembersAttribute usage",
as we don't add suppression files for ref projects -->
<ILLinkArgs Condition="'$(DisableILLinkWarnings)' != 'true'">$(ILLinkArgs) --nowarn IL2025;IL2047</ILLinkArgs>

so basically something like this is what I mean.

@@ -270,6 +270,7 @@
<Compile Include="$(CommonPath)Interop\Windows\OleAut32\Interop.SysAllocStringByteLen.cs">
<Link>Common\Interop\Windows\OleAut32\Interop.SysAllocStringByteLen.cs</Link>
</Compile>
<Compile Include="ILLinker\ILLink.Suppressions.cs" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Consider adding this on the eng\illink.targets file so that we automatically add this to the compile in case it exists. This is what we do in other baseline cases for example: https://github.com/dotnet/arcade/blob/master/src/Microsoft.DotNet.ApiCompat/build/Microsoft.DotNet.ApiCompat.targets#L31-L32

That way adding a new supression doesn't mean you have to go author the .csproj which might be harder for folks to do if they are not familiar with msbuild.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more than a NIT. 😄 We should pick it up automatically.

@@ -0,0 +1,276 @@
// Licensed to the .NET Foundation under one or more agreements.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing folder name in mono is ILLInk not ILLinker. Can we name the folder consistently throughout the repo?

@@ -57,38 +57,38 @@ public DefaultValueAttribute(Type type, string? value)
{
_value = Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was this change for? Is there a bug somewhere?

@@ -11,6 +11,9 @@
<PropertyGroup>
<GeneratePlatformNotSupportedAssemblyMessage Condition="'$(TargetFramework)' == 'netstandard2.0'">SR.PlatformNotSupported_ReflectionDispatchProxy</GeneratePlatformNotSupportedAssemblyMessage>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aren't doing this elsewhere, are we? If this is reporting linker warnings, we will probably need the attributes in this project so we might as well add the UnconditionalSuppressMessageAttribute now.

@@ -587,6 +587,9 @@
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
Link="Common\Interop\Unix\Interop.Libraries.cs" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('$(NetCoreAppCurrent)'))">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment here - we should add the internal attribute we need so we don't need to #if in the code when we apply them.

@@ -665,9 +643,7 @@

<!-- domain.c: mono_defaults.thread_class -->
<!-- FIXME: -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marek-safar @akoeplinger @vargaz - do you know what this FIXME is here for? Are we fixing it with this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vargaz any recollection why this was added?

[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2046", Scope = "member", Target = "M:System.Reflection.RuntimeModule.ResolveSignature(System.Int32)")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2046", Scope = "member", Target = "M:System.Reflection.RuntimeModule.ResolveString(System.Int32)")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2046", Scope = "member", Target = "M:System.Reflection.RuntimeModule.ResolveType(System.Int32,System.Type[],System.Type[])")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2047", Scope = "member", Target = "M:System.Reflection.Emit.EnumBuilder.GetConstructorImpl(System.Reflection.BindingFlags,System.Reflection.Binder,System.Reflection.CallingConventions,System.Type[],System.Reflection.ParameterModifier[])")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't we disabling IL2047 all together? Do we need these here as well?

[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2006", Scope = "member", Target = "M:Internal.Runtime.InteropServices.ComActivator.BasicClassFactory.CreateInstance(System.Object,System.Guid@,System.Object@)")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2006", Scope = "member", Target = "M:Internal.Runtime.InteropServices.ComActivator.ClassRegistrationScenarioForType(Internal.Runtime.InteropServices.ComActivationContext,System.Boolean)")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2006", Scope = "member", Target = "M:Internal.Runtime.InteropServices.ComponentActivator.InternalGetFunctionPointer(System.Runtime.Loader.AssemblyLoadContext,System.String,System.String,System.IntPtr)")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2006", Scope = "member", Target = "M:Internal.Runtime.InteropServices.LicenseInteropProxy.#ctor")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are we running linker on this (what's the command line)? Linker should be able to analyze this. I've seen this warning show up when linker isn't passed a reference to System.ComponentMode.TypeConverter assembly and can't analyze the reflection as a result.

(There's multiple instances of the same problem within this suppression file.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are the arguments that are passed when linking CoreLib:

-reference "D:\source\repos\runtime\artifacts\obj\coreclr\System.Private.CoreLib\x64\Debug\PreTrim/System.Private.CoreLib.dll"
-out "D:\source\repos\runtime\artifacts\obj\coreclr\System.Private.CoreLib\x64\Debug"
-r System.Private.CoreLib -c skip -u skip -p link System.Private.CoreLib -t -b true -v true --strip-descriptors false --strip-substitutions false --skip-unresolved true --disable-opt unusedinterfaces --keep-dep-attributes true

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any chance we can have linker see the whole framework when trimming the individual libraries? (I think this comes back to an old topic...)

Without that, we're going to see warnings for all Type.GetType("Foo, X").GetMethod("Bar") patterns because even though linker could know what Foo refers to, it won't be able to resolve that and has to treat it as "don't know". We'll never know if that reflection is actually safe (linker will be able to reason about it for actual app build) or not (linker can no longer reason about it when seeing the whole framework either because e.g. someone refactored things).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any chance we can have linker see the whole framework when trimming the individual libraries?

This approach was rejected in #31712

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to revisit that? What are the options to avoid the bogus warnings like this one?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to revisit that?

In my mind, this is no different than the advantages already outlined in that issue - #31712 (comment). Specifically:

  1. The linker can better understand the code when it sees the entire "distributable".

This is just another case of the linker not being able to reason about everything at once.


What are the options to avoid the bogus warnings like this one?

  • One option is to keep doing what we are doing in this PR. It suppresses the warning.
  • One option is to pass in suppress messages separately to the linker that are only for the "Library Build", just like we do with the ILLinkTrim_LibraryBuild.xml files.
  • Lastly, we could revisit Run illink task with at the end of build #31712 and change the way we run the linker during the build to do the whole shared fx/runtimepack at once.
    • A variation on this approach is to only run the validation against the whole shared fx/runtimepack at the of the build, and throw away the outputted binaries. We still run the linker during the individual library build, and suppress all warnings like we currently are. The warnings only come at the end, during the full build.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A variation on this approach is to only run the validation against the whole shared fx/runtimepack at the of the build, and throw away the outputted binaries

That sounds most reasonable option to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, if we want to do the precise analysis we have to run against the whole framework and every runtime pack. Doing it the way as it's today will rely on manual information copying which is very error-prone in our scale.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first two options (suppress warnings unconditionally or suppress warning during library build) run the risk that if something happens and linker stops recognizing the pattern we either don't see the warning at all, or only customers see the warnings. That's quite undesirable for things as fragile as "app-specific customization of the framework" (which is what trimming+feature switches really is - no two frameworks will be the same and it's hard to prove correctness without automation help).

I like option 3 the best too even though it's probably more work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I don't like running the same tool twice, it sounds as the best option.


using System.Diagnostics.CodeAnalysis;

[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2001", Scope = "type", Target = "T:Mono.ValueTuple")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems pretty excessive to ship inside the shipping assembly if the goal is just to result in no warnings. Does the linker provide a way that we could list granular suppressions for the purpose of our build, but then a single assembly-wide suppression in the shipping assembly?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have probably assumed that the linker would actually take these into consideration for verbose warnings, but then linked them out, is that not the case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For assembly-wide suppression we can do this:

[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2001")]
[assembly: UnconditionalSuppressMessage ("ILLinker", "IL2002")]
...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if we do assembly-wide suppressions, we won't get new warnings when writing new code in the assembly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have probably assumed that the linker would actually take these into consideration for verbose warnings, but then linked them out, is that not the case?

My understanding is that they remain in the final binaries in the runtime-pack and shared framework, since today those are the same and need to be considered as input to customer app when linking.

if we do assembly-wide suppressions, we won't get new warnings when writing new code in the assembly.

Can we still have granular suppressions though some mechanism when we're building? Re-reading your original issue I see the goal is not to "always suppress" these in framework code, but only to "temporarily suppress" them until we can properly annotate everything. I guess in that case this bloat is a point-in-time problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed this with @eerhardt - there was a disconnect on our end... anyway:

  • Hardcoding the suppressions into the library source code feels like a too much of a final solution (it means there's no way to "reenable" these warnings without rebuilding the library)
  • We want the baselines to exist for the runtime repo build, but we don't necessarily want/need them for the application build
  • Linker/SDK will have a solution to suppress all of these warnings by default anyway (Introduce option to enable trim analysis warnings sdk#12388) - so we don't NEED to have a solution for this in the runtime repo
  • We would like to have the freedom to tweak the user experience (as in how much warnings we show to the user) later on - this means the libraries themselves should not hide anything - if we need to we will hide it in higher layers (linker or SDK)

So the technical solution we thought would be best is to:

@layomia
Copy link
Contributor Author

layomia commented Jul 22, 2020

Thanks all for the review feedback & discussion. Closing this PR based on the comments in #39133 (comment). Will revisit it when the needed support in the linker (generation of suppression XML files) is added.

cc @eerhardt, @vitek-karas, @mateoatr, @sbomer

@layomia layomia closed this Jul 22, 2020
@layomia layomia deleted the baselines branch August 10, 2020 23:18
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enable linker analysis and add baselines for warnings
9 participants