Skip to content

Commit

Permalink
Fix bad assembly when a nested exported type is marked via link.xml
Browse files Browse the repository at this point in the history
When a nested exported type is marked via link.xml and the declaring type is not marked, cecil will write out an invalid exported type table which will lead to exceptions such as
```
System.ArgumentOutOfRangeException : Specified argument was out of the range of valid values.
   at Mono.Collections.Generic.Collection`1.get_Item(Int32 index)
   at Mono.Cecil.MetadataReader.ReadExportedTypes()
   at Mono.Cecil.ModuleDefinition.<>c.<get_ExportedTypes>b__116_0(ModuleDefinition _, MetadataReader reader)
   at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TRet& variable, TItem item, Func`3 read)
   at Mono.Cecil.ModuleDefinition.get_ExportedTypes()
   at Mono.Cecil.MetadataResolver.GetType(ModuleDefinition module, TypeReference reference)
   at Mono.Cecil.MetadataResolver.Resolve(TypeReference type)
   at Mono.Linker.Tests.TestCasesRunner.TestResolver.TryResolve(TypeReference typeReference) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\test\Trimming.Tests.Shared\TestResolver.cs:line 12
   at Mono.Linker.ModuleDefinitionExtensions.ResolveType(ModuleDefinition module, String typeFullName, ITryResolveMetadata resolver) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\src\linker\Linker\ModuleDefinitionExtensions.cs:line 50
   at Mono.Linker.TypeNameResolver.<ResolveTypeName>g__GetSimpleTypeFromModule|6_0(TypeName typeName, ModuleDefinition module) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\src\linker\Linker\TypeNameResolver.cs:line 135
   at Mono.Linker.TypeNameResolver.ResolveTypeName(AssemblyDefinition originalAssembly, TypeName typeName, List`1 typeResolutionRecords) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\src\linker\Linker\TypeNameResolver.cs:line 101
   at Mono.Linker.TypeNameResolver.TryResolveTypeName(AssemblyDefinition assembly, String typeNameString, TypeReference& typeReference, List`1& typeResolutionRecords) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\src\linker\Linker\TypeNameResolver.cs:line 44
   at Mono.Linker.Tests.TestCasesRunner.ResultChecker.VerifyLinkingOfOtherAssemblies(AssemblyDefinition original) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\test\Mono.Linker.Tests\TestCasesRunner\ResultChecker.cs:line 320
   at Mono.Linker.Tests.TestCasesRunner.ResultChecker.Check(TrimmedTestCaseResult linkResult) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\test\Mono.Linker.Tests\TestCasesRunner\ResultChecker.cs:line 114
   at Mono.Linker.Tests.TestCases.All.Run(TestCase testCase) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\test\Mono.Linker.Tests\TestCases\TestSuites.cs:line 305
   at Mono.Linker.Tests.TestCases.All.TypeForwardingTests(TestCase testCase) in E:\UnitySrc\dev\unity-runtime-1\src\tools\illink\test\Mono.Linker.Tests\TestCases\TestSuites.cs:line 262
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
```

Or if you were to try and open the linked `Forwarder.dll` without the fix in something like ILSpy, it would crash ILSpy.

An easy way to fix this seems to be marking the declaring type.
  • Loading branch information
mrvoorhe committed Sep 17, 2024
1 parent 8ae3796 commit 0bb3349
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ void MarkAndPreserveAll (TypeDefinition type, XPathNavigator nav)
protected override TypeDefinition? ProcessExportedType (ExportedType exported, AssemblyDefinition assembly, XPathNavigator nav)
{
_context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation), GetMessageOriginForPosition (nav));

// If a nested exported type is marked, then the declaring type must also be marked otherwise cecil will write out an invalid exported type table
// and anything that tries to read the assembly with cecil will crash
if (exported.DeclaringType != null) {
var currentType = exported.DeclaringType;
while (currentType != null) {
var parent = currentType.DeclaringType;
_context.MarkingHelpers.MarkExportedType (currentType, assembly.MainModule, new DependencyInfo(DependencyKind.DeclaringType, currentType), GetMessageOriginForPosition (nav));
currentType = parent;
}
}

return base.ProcessExportedType (exported, assembly, nav);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.


#if INCLUDE_FORWARDERS
[assembly: System.Runtime.CompilerServices.TypeForwardedTo (typeof (Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary))]
// [assembly: System.Runtime.CompilerServices.TypeForwardedTo (typeof (Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary.Nested))]
#endif

#if INCLUDE_REFERENCE_IMPL
namespace Mono.Linker.Tests.Cases.TypeForwarding.Dependencies;

public class ForwardedNestedTypeLibrary
{
public class NestedOne
{
public class NestedTwo
{
public class NestedThree
{
}
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;

namespace Mono.Linker.Tests.Cases.TypeForwarding;

[SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ForwardedNestedTypeLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })]
[SetupCompileBefore ("Implementation.dll", new[] { "Dependencies/ForwardedNestedTypeLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })]
[SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwardedNestedTypeLibrary.cs" }, references: new[] { "Implementation.dll" }, defines: new[] { "INCLUDE_FORWARDERS" })]
[SetupLinkerDescriptorFile("NestedTypeForwarder.xml")]
[KeptAssembly("Forwarder.dll")]
[KeptTypeInAssembly("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary")]
[KeptTypeInAssembly("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary/NestedOne")]
[KeptTypeInAssembly("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary/NestedOne/NestedTwo")]
[KeptTypeInAssembly("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary/NestedOne/NestedTwo/NestedThree")]
public class NestedTypeForwarder
{
public static void Main ()
{

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<linker>
<assembly fullname="Forwarder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary/NestedOne/NestedTwo/NestedThree" preserve="all">
</type>
</assembly>
</linker>

0 comments on commit 0bb3349

Please # to comment.