diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index 296e35c0e754..1ebf7deb2e2d 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -632,6 +632,9 @@ <_ExtraTrimmerArgs>$(_ExtraTrimmerArgs) --enable-serialization-discovery + + <_ExtraTrimmerArgs Condition="'$(_UseNativeAot)' == 'true'">$(_ExtraTrimmerArgs) --keep-dep-attributes + false diff --git a/tools/common/ErrorHelper.tools.cs b/tools/common/ErrorHelper.tools.cs index 8f564d565f35..2b0f0e46cd47 100644 --- a/tools/common/ErrorHelper.tools.cs +++ b/tools/common/ErrorHelper.tools.cs @@ -332,7 +332,8 @@ static void ShowInner (Exception e) if (Verbosity > 3) { Console.Error.WriteLine ("--- inner exception"); - Console.Error.WriteLine (ie); + Console.Error.WriteLine (ie.Message); + Console.Error.WriteLine (ie.StackTrace); Console.Error.WriteLine ("---"); } else if (Verbosity > 0 || ie is ProductException) { Console.Error.WriteLine ("\t{0}", ie.Message); diff --git a/tools/create-dotnet-linker-launch-json/create-dotnet-linker-launch-json.csproj b/tools/create-dotnet-linker-launch-json/create-dotnet-linker-launch-json.csproj index 7a25f66db1fa..8ddd2b65b3ab 100644 --- a/tools/create-dotnet-linker-launch-json/create-dotnet-linker-launch-json.csproj +++ b/tools/create-dotnet-linker-launch-json/create-dotnet-linker-launch-json.csproj @@ -2,14 +2,14 @@ Exe - net7.0 + net8.0 create_dotnet_linker_launch_json enable enable - + diff --git a/tools/dotnet-linker/AppBundleRewriter.cs b/tools/dotnet-linker/AppBundleRewriter.cs index ae5ad1e0ad1a..322a826b0f39 100644 --- a/tools/dotnet-linker/AppBundleRewriter.cs +++ b/tools/dotnet-linker/AppBundleRewriter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Mono.Cecil; @@ -126,6 +127,10 @@ public MethodReference GetMethodReference (AssemblyDefinition assembly, TypeRefe md.IsPublic = true; SaveAssembly (md.Module.Assembly); } + + // Also mark it + // configuration.Context.Annotations.Mark (md); + // configuration.Context.Annotations.Mark (tuple.Item2); } method = tuple.Item1; @@ -309,6 +314,12 @@ public TypeReference System_Diagnostics_CodeAnalysis_DynamicDependencyAttribute } } + public TypeReference System_Diagnostics_CodeAnalysis_DynamicallyAccessedMemberTypes { + get { + return GetTypeReference (CorlibAssembly, "System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes", out var _); + } + } + public TypeReference System_Reflection_MethodBase { get { return GetTypeReference (CorlibAssembly, "System.Reflection.MethodBase", out var _); @@ -461,17 +472,41 @@ public MethodReference Dictionary2_Add { } } + public MethodReference DynamicDependencyAttribute_ctor__String { + get { + return GetMethodReference (CorlibAssembly, + System_Diagnostics_CodeAnalysis_DynamicDependencyAttribute, + ".ctor", + ".ctor(String)", + isStatic: false, + System_String); + } + } + public MethodReference DynamicDependencyAttribute_ctor__String_Type { get { return GetMethodReference (CorlibAssembly, System_Diagnostics_CodeAnalysis_DynamicDependencyAttribute, ".ctor", + ".ctor(String,Type)", isStatic: false, System_String, System_Type); } } + public MethodReference DynamicDependencyAttribute_ctor__DynamicallyAccessedMemberTypes_Type { + get { + return GetMethodReference (CorlibAssembly, + System_Diagnostics_CodeAnalysis_DynamicDependencyAttribute, + ".ctor", + ".ctor(DynamicallyAccessedMemberTypes,Type)", + isStatic: false, + System_Diagnostics_CodeAnalysis_DynamicallyAccessedMemberTypes, + System_Type); + } + } + public MethodReference RuntimeTypeHandle_Equals { get { return GetMethodReference (CorlibAssembly, System_RuntimeTypeHandle, "Equals", isStatic: false, System_RuntimeTypeHandle); @@ -1147,5 +1182,29 @@ public void ClearCurrentAssembly () method_map.Clear (); field_map.Clear (); } + + public CustomAttribute CreateDynamicDependencyAttribute (string memberSignature) + { + var attribute = new CustomAttribute (DynamicDependencyAttribute_ctor__String); + attribute.ConstructorArguments.Add (new CustomAttributeArgument (System_String, memberSignature)); + return attribute; + } + + public CustomAttribute CreateDynamicDependencyAttribute (string memberSignature, TypeDefinition type) + { + var attribute = new CustomAttribute (DynamicDependencyAttribute_ctor__String_Type); + attribute.ConstructorArguments.Add (new CustomAttributeArgument (System_String, memberSignature)); + attribute.ConstructorArguments.Add (new CustomAttributeArgument (System_Type, type)); + return attribute; + } + + public CustomAttribute CreateDynamicDependencyAttribute (DynamicallyAccessedMemberTypes memberTypes, TypeDefinition type) + { + var attribute = new CustomAttribute (DynamicDependencyAttribute_ctor__DynamicallyAccessedMemberTypes_Type); + // typed as 'int' because that's how the linker expects it: https://github.com/dotnet/runtime/blob/3c5ad6c677b4a3d12bc6a776d654558cca2c36a9/src/tools/illink/src/linker/Linker/DynamicDependency.cs#L97 + attribute.ConstructorArguments.Add (new CustomAttributeArgument (System_Diagnostics_CodeAnalysis_DynamicallyAccessedMemberTypes, (int) memberTypes)); + attribute.ConstructorArguments.Add (new CustomAttributeArgument (System_Type, type)); + return attribute; + } } } diff --git a/tools/dotnet-linker/ApplyPreserveAttributeBase.cs b/tools/dotnet-linker/ApplyPreserveAttributeBase.cs index f030e446ca4e..2f2417f6c67d 100644 --- a/tools/dotnet-linker/ApplyPreserveAttributeBase.cs +++ b/tools/dotnet-linker/ApplyPreserveAttributeBase.cs @@ -3,18 +3,30 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Text; using Mono.Linker; using Mono.Linker.Steps; using Mono.Cecil; +using Mono.Cecil.Cil; + +using Xamarin.Bundler; +using Xamarin.Linker; #nullable enable namespace Mono.Tuner { - public abstract class ApplyPreserveAttributeBase : BaseSubStep { + public abstract class ApplyPreserveAttributeBase : ConfigurationAwareSubStep { + + AppBundleRewriter? abr; + + protected override string Name { get => "ApplyPreserveAttribute"; } + + protected override int ErrorCode { get => 2450; } // set 'removeAttribute' to true if you want the preserved attribute to be removed from the final assembly protected abstract bool IsPreservedAttribute (ICustomAttributeProvider provider, CustomAttribute attribute, out bool removeAttribute); @@ -30,28 +42,36 @@ public override SubStepTargets Targets { } } + public override void Initialize (LinkContext context) + { + base.Initialize (context); + + if (Configuration.Application.XamarinRuntime == XamarinRuntime.NativeAOT) + abr = Configuration.AppBundleRewriter; + } + public override bool IsActiveFor (AssemblyDefinition assembly) { return Annotations.GetAction (assembly) == AssemblyAction.Link; } - public override void ProcessType (TypeDefinition type) + protected override void Process (TypeDefinition type) { TryApplyPreserveAttribute (type); } - public override void ProcessField (FieldDefinition field) + protected override void Process (FieldDefinition field) { foreach (var attribute in GetPreserveAttributes (field)) Mark (field, attribute); } - public override void ProcessMethod (MethodDefinition method) + protected override void Process (MethodDefinition method) { MarkMethodIfPreserved (method); } - public override void ProcessProperty (PropertyDefinition property) + protected override void Process (PropertyDefinition property) { foreach (var attribute in GetPreserveAttributes (property)) { MarkMethod (property.GetMethod, attribute); @@ -59,7 +79,7 @@ public override void ProcessProperty (PropertyDefinition property) } } - public override void ProcessEvent (EventDefinition @event) + protected override void Process (EventDefinition @event) { foreach (var attribute in GetPreserveAttributes (@event)) { MarkMethod (@event.AddMethod, attribute); @@ -103,6 +123,7 @@ void PreserveConditional (IMetadataTokenProvider provider) } Annotations.AddPreservedMethod (method.DeclaringType, method); + AddDynamicDependencyAttribute (method.DeclaringType, method); } static bool IsConditionalAttribute (CustomAttribute? attribute) @@ -120,6 +141,7 @@ static bool IsConditionalAttribute (CustomAttribute? attribute) void PreserveUnconditional (IMetadataTokenProvider provider) { Annotations.Mark (provider); + AddDynamicDependencyAttribute (provider); var member = provider as IMemberDefinition; if (member is null || member.DeclaringType is null) @@ -131,14 +153,7 @@ void PreserveUnconditional (IMetadataTokenProvider provider) void TryApplyPreserveAttribute (TypeDefinition type) { foreach (var attribute in GetPreserveAttributes (type)) { - Annotations.Mark (type); - - if (!attribute.HasFields) - continue; - - foreach (var named_argument in attribute.Fields) - if (named_argument.Name == "AllMembers" && (bool) named_argument.Argument.Value) - Annotations.SetPreserve (type, TypePreserve.All); + PreserveType (type, attribute); } } @@ -165,5 +180,184 @@ List GetPreserveAttributes (ICustomAttributeProvider provider) return attrs; } + + protected void PreserveType (TypeDefinition type, CustomAttribute preserveAttribute) + { + var allMembers = false; + if (preserveAttribute.HasFields) { + foreach (var named_argument in preserveAttribute.Fields) + if (named_argument.Name == "AllMembers" && (bool) named_argument.Argument.Value) + allMembers = true; + } + + PreserveType (type, allMembers); + } + + protected void PreserveType (TypeDefinition type, bool allMembers) + { + Annotations.Mark (type); + if (allMembers) + Annotations.SetPreserve (type, TypePreserve.All); + AddDynamicDependencyAttribute (type, allMembers); + } + + ModuleDefinition GetModule (IMetadataTokenProvider provider) + { + if (provider is TypeDefinition td) + return td.Module; + + if (provider is IMemberDefinition md) + return md.DeclaringType.Module; + + throw new NotImplementedException (provider.GetType ().FullName); + } + + MethodDefinition GetModuleConstructor (IMetadataTokenProvider provider) + { + return GetModuleConstructor (GetModule (provider)); + } + + MethodDefinition GetModuleConstructor (ModuleDefinition @module) + { + var moduleType = @module.Types.SingleOrDefault (v => v.Name == ""); + if (moduleType is null) + throw ErrorHelper.CreateError (99, $"No type found in {@module.Name}"); + var moduleConstructor = moduleType.GetTypeConstructor (); + if (moduleConstructor is null) { + moduleConstructor = moduleType.AddMethod (".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.Static, abr!.System_Void); + moduleConstructor.CreateBody (out var il); + il.Emit (OpCodes.Ret); + } + return moduleConstructor; + } + + void AddDynamicDependencyAttribute (TypeDefinition type, bool allMembers) + { + if (abr is null) + return; + + abr.ClearCurrentAssembly (); + abr.SetCurrentAssembly (type.Module.Assembly); + + var moduleConstructor = GetModuleConstructor (type); + var attrib = abr.CreateDynamicDependencyAttribute (allMembers ? DynamicallyAccessedMemberTypes.All : DynamicallyAccessedMemberTypes.None, type); + moduleConstructor.CustomAttributes.Add (attrib); + Console.WriteLine ($"Added dynamic dependency attribute to module constructor (allMembers: {allMembers}) for: {type} in {type.Module.Assembly.Name}"); + + abr.ClearCurrentAssembly (); + } + + void AddDynamicDependencyAttribute (TypeDefinition onType, MethodDefinition forMethod) + { + if (abr is null) + return; + + abr.ClearCurrentAssembly (); + abr.SetCurrentAssembly (onType.Module.Assembly); + + Console.WriteLine ($"Unable to add dependency from the type {onType.FullName} to its member {forMethod.FullName}"); + + abr.ClearCurrentAssembly (); + } + + void AddDynamicDependencyAttribute (IMetadataTokenProvider provider) + { + if (abr is null) + return; + + var member = provider as IMemberDefinition; + if (member is null) { + Console.WriteLine ("Huh?"); + return; + } + + abr.ClearCurrentAssembly (); + abr.SetCurrentAssembly (GetModule (member).Assembly); + + var moduleConstructor = GetModuleConstructor (member); + var signature = GetSignature (member, false); + var attrib = abr.CreateDynamicDependencyAttribute (signature, member.DeclaringType); + moduleConstructor.CustomAttributes.Add (attrib); + Console.WriteLine ($"Added dynamic dependency attribute to module constructor for: {member}"); + + abr.ClearCurrentAssembly (); + } + + // signature format: https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/documentation-comments.md#d42-id-string-format + string GetSignature (IMetadataTokenProvider member, bool withType) + { + if (member is FieldDefinition fd) { + var signature = GetSignature (fd); + if (withType) + return GetSignature (fd.DeclaringType) + "." + signature; + return signature; + } + + if (member is MethodDefinition md) { + var signature = GetSignature (md); + if (withType) + return GetSignature (md.DeclaringType) + "." + signature; + return signature; + } + + if (member is TypeDefinition td) + return GetSignature (td); + + throw new NotImplementedException (member.GetType ().FullName); + } + + string GetSignature (TypeDefinition type) + { + if (type.IsNested) + return type.Name; + return type.FullName; + } + + string GetSignature (FieldDefinition field) + { + return field.Name.Replace ('.', '#'); + } + + string GetSignature (MethodDefinition method) + { + var sb = new StringBuilder (); + sb.Append (method.Name.Replace ('.', '#')); + sb.Append ('('); + for (var i = 0; i < method.Parameters.Count; i++) { + if (i > 0) + sb.Append (','); + + var parameterType = method.Parameters [i].ParameterType; + WriteTypeSignature (sb, parameterType); + } + sb.Append (')'); + + Console.WriteLine ($"Created signature '{sb}' for {method.FullName}"); + + return sb.ToString (); + } + + void WriteTypeSignature (StringBuilder sb, TypeReference type) + { + if (type is ByReferenceType brt) { + WriteTypeSignature (sb, brt.GetElementType ()); + sb.Append ('@'); + return; + } + + if (type is ArrayType at) { + WriteTypeSignature (sb, at.GetElementType ()); + sb.Append ("[]"); + return; + } + + if (type is PointerType pt) { + WriteTypeSignature (sb, pt.GetElementType ()); + sb.Append ('*'); + return; + } + + sb.Append (type.FullName.Replace ('/', '.')); + } } } diff --git a/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs b/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs index 03d0d81500f1..d262e3b5b373 100644 --- a/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs +++ b/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs @@ -292,7 +292,7 @@ void CreateUnmanagedCallersMethod (MethodDefinition method, AssemblyTrampolineIn infos.Add (new TrampolineInfo (callback, method, name)); // If the target method is marked, then we must mark the trampoline as well. - method.CustomAttributes.Add (CreateDynamicDependencyAttribute (callbackType, callback.Name)); + method.CustomAttributes.Add (abr.CreateDynamicDependencyAttribute (callback.Name, callbackType)); callback.AddParameter ("pobj", abr.System_IntPtr); @@ -1122,14 +1122,6 @@ CustomAttribute CreateUnmanagedCallersAttribute (string entryPoint) return unmanagedCallersAttribute; } - CustomAttribute CreateDynamicDependencyAttribute (TypeDefinition type, string member) - { - var attribute = new CustomAttribute (abr.DynamicDependencyAttribute_ctor__String_Type); - attribute.ConstructorArguments.Add (new CustomAttributeArgument (abr.System_String, member)); - attribute.ConstructorArguments.Add (new CustomAttributeArgument (abr.System_Type, type)); - return attribute; - } - void GenerateConversionToManaged (MethodDefinition method, ILProcessor il, TypeReference inputType, TypeReference outputType, string descriptiveMethodName, int parameter, out TypeReference nativeCallerType) { // This is a mirror of the native method xamarin_generate_conversion_to_managed (for the dynamic registrar). diff --git a/tools/linker/ApplyPreserveAttribute.cs b/tools/linker/ApplyPreserveAttribute.cs index a04c36ab5e97..d2ea827ff674 100644 --- a/tools/linker/ApplyPreserveAttribute.cs +++ b/tools/linker/ApplyPreserveAttribute.cs @@ -27,8 +27,9 @@ public override bool IsActiveFor (AssemblyDefinition assembly) } #if NET - public override void ProcessAssembly (AssemblyDefinition assembly) + protected override void Process (AssemblyDefinition assembly) { + base.Process (assembly); ProcessAssemblyAttributes (assembly); } #else @@ -62,6 +63,10 @@ void ProcessAssemblyAttributes (AssemblyDefinition assembly) // (a) we're potentially processing a different assembly and `is_active` represent the current one // (b) it will try to fetch the [Preserve] attribute on the type (and it's not there) as `base` would var type = tr.Resolve (); + +#if NET + PreserveType (type, attribute); +#else Annotations.Mark (type); if (attribute.HasFields) { foreach (var named_argument in attribute.Fields) { @@ -69,6 +74,7 @@ void ProcessAssemblyAttributes (AssemblyDefinition assembly) Annotations.SetPreserve (type, TypePreserve.All); } } +#endif // In .NET6, ApplyPreserveAttribute no longer runs on all assemblies. // [assembly: Preserve (typeof (SomeAttribute))] no longer gives SomeAttribute "Preserve" semantics.