Stack overflow resolving TypeReference
See original GitHub issueIn certain scenarios where a type implements an interface in another assembly, attempting to resolve the TypeReference to the interface results in an infinite loop and a stack overflow.
This is an issue I encountered with a real application with fairly complex dependencies - I’ve been struggling to find a simple repro that copies the right dlls in, but it’s possible to manually copy them to the bin folder to reproduce.
The issue occurs when the build copies the following dlls to the bin dir:
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.0\System.dll
C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Runtime.dll
C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\netstandard.dll
The latter two get copied in when you have a net framework assembly which references a net standard assembly (as per some of the details in https://github.com/dotnet/corefx/issues/26456).
You can see the issue by creating a net461 library with a single type implementing ISet<string> (e.g. https://github.com/r-bennett/MonoCecilRepro/blob/master/ClassLibrary1/Class1.cs), building the library, manually copying the above dlls to the bin folder, then attempting to load the type and resolve its interface, something like:
void Main()
{
ResolveInterfaces(@"E:\workspace\ClassLibrary1\ClassLibrary1\bin\Debug\ClassLibrary1.dll");
}
static void ResolveInterfaces(string assemblyPath)
{
var module = ModuleDefinition.ReadModule(assemblyPath, new ReaderParameters { AssemblyResolver = new PublishedAssemblyResolver(assemblyPath) });
foreach (var type in module.Types)
{
foreach (var @interface in type.Interfaces)
{
@interface.Resolve();
}
}
}
public class PublishedAssemblyResolver : DefaultAssemblyResolver
{
public PublishedAssemblyResolver (string assemblyPath)
{
AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
}
}
Each of the 3 dlls mentioned above has only a single type (<Module>) listed on it when read with Mono Cecil, with all the expected types appearing only in exportedTypes. The issue seems to be related to net standard’s assembly unification (https://github.com/dotnet/standard/blob/315834b5eaa426ed37f6a767f15cf1d5eb7b4a85/docs/planning/netstandard-2.0/README.md#assembly-unification), although I’m not quite sure what’s going on here.
The infinite loop occurs with MetadataResolver.Resolve(TypeReference) - it tries to resolve the ISet interface first with netstandard.dll, then with System.dll, then with System.Runtime.dll, then looping back to netstandard.dll when it can’t find it in the types of any of those modules.
I’m happy to put some time into resolving this, but I’m not sure what the correct resolution behaviour should be here.
Issue Analytics
- State:
- Created 5 years ago
- Comments:9 (7 by maintainers)

Top Related StackOverflow Question
Thanks to everybody who’s looking at this.
My understanding of the issue lies in the very first
Resolvecall here. In a .NET Core program, you’re asking theDefaultAssemblyResolverto resolve the assemblySystemfor a program compiled for the .NET Framework and not .NET Core.The
DefaultAssemblyResolverimplementation as it is today, when running on .NET Core, is going to resolve the .NET Core assembly forSystem, and here begins the mixup. The resolver will pick first thenetstandardandSystem.Runtimefor the .NET Framework that you provide yourself and this will never resolve properly.I’m not sure there’s a good solution here. If you’re going to write a .NET Core program that can work on .NET Framework assemblies, the
DefaultAssemblyResolveris not a good default, and you’ll need to provide your own which knows how to resolve .NET Framework references when working on a .NET Framework assembly.@MarcoRossignoli cheers for the info - have been having a poke around with your reproducer and been finding the same things.
I tried a workaround similar to the first you suggested, although checking when the only
Typeon the loaded assembly was<Module>(as it affects more than justnetstandard.dll) and loading the assembly fromPath.GetDirectoryName(typeof(object).Assembly.Locationinstead. This fixed this reproduction case. There are assemblies in the the “shared” framework which have the same issue though (e.g. in your reproducer which I converted to 461/2.0, the problem dll isC:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.7\System.dll), so it isn’t a universal fix.The most robust solution I’ve found is to recursively add the subfolders from the GAC (under
%systemroot%/assembly/...) as search directories, thenPath.GetDirectoryName(typeof(object).Assembly.Location, then my assembly’s publish directory last of all - I haven’t found a project that doesn’t work for yet.