diff --git a/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs b/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs index 9f737f9cc4ba..bd9aeeb5417e 100644 --- a/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs +++ b/tools/dotnet-linker/Steps/ManagedRegistrarStep.cs @@ -390,7 +390,7 @@ void CreateUnmanagedCallersMethod (MethodDefinition method, AssemblyTrampolineIn } var callback = callbackType.AddMethod (name, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, placeholderType); - callback.CustomAttributes.Add (CreateUnmanagedCallersAttribute (name)); + callback.CustomAttributes.Add (CreateUnmanagedCallersAttribute (name, method.DeclaringType)); infos.Add (new TrampolineInfo (callback, method, name)); // If the target method is marked, then we must mark the trampoline as well. @@ -1242,10 +1242,15 @@ StaticRegistrar StaticRegistrar { get { return DerivedLinkContext.StaticRegistrar; } } - CustomAttribute CreateUnmanagedCallersAttribute (string entryPoint) + CustomAttribute CreateUnmanagedCallersAttribute (string entryPoint, TypeReference? associatedSourceType = null) { var unmanagedCallersAttribute = new CustomAttribute (abr.UnmanagedCallersOnlyAttribute_Constructor); unmanagedCallersAttribute.Fields.Add (new CustomAttributeNamedArgument ("EntryPoint", new CustomAttributeArgument (abr.System_String, entryPoint))); + // The AssociatedSourceType field is only recognized by the NativeAOT compiler (ILC). The CoreCLR + // runtime's attribute parser rejects any UnmanagedCallersOnly named argument it doesn't know about, + // so only emit it when we're compiling for NativeAOT. + if (associatedSourceType is not null && App.XamarinRuntime == XamarinRuntime.NativeAOT) + unmanagedCallersAttribute.Fields.Add (new CustomAttributeNamedArgument ("AssociatedSourceType", new CustomAttributeArgument (abr.System_Type, associatedSourceType))); return unmanagedCallersAttribute; }