Search code examples
c#mono.cecilildasm

Getting "Type load failed" from PEVerify after removing methods


Short story

I'm using the following Mono.Cecil code to remove all but a couple of methods from an interface and the class that implements the interface.

static void Main(string[] args)
{
    var moduleDef = ModuleDefinition.ReadModule("SomeService.dll");

    var bigService = moduleDef.Types.Single(t => t.Name == "BigService"); // interface
    var bigServiceClient = moduleDef.Types.Single(t => t.Name == "BigServiceClient"); // class implementing above interface
    var toSave = new[] {"method1", "method2"};

    SaveMethods(bigService, toSave);
    SaveMethods(bigServiceClient, toSave);

    moduleDef.Write(@"C:\AutobooksCode\MAIN\API\JHA\JHA.SymXchange.V2\bin\Release\JHA.SymXchange.V2_updated.dll");
}

private static void SaveMethods(TypeDefinition typeDef, string[] names)
{
    var methodsToSave = typeDef.Methods.Where(m => names.Contains(m.Name, StringComparer.OrdinalIgnoreCase)).ToList();
    var ctors = typeDef.Methods.Where(m => m.Name == ".ctor");

    methodsToSave.AddRange(ctors);

    // remove all methods except the ones I want to save
    var pos = 0;
    while (true)
    {
        if (typeDef.Methods.Count == methodsToSave.Count)
            break;

        if (methodsToSave.Contains(typeDef.Methods[pos]))
            pos++;
        else
        {
            typeDef.Methods.RemoveAt(pos);
        }
    }
}

The code works, everything looks good with both ILDASM and JustDecompile and, best of all, the DLL works fine with clients. However, running it thorough PEVerify generates a Type load failed. error for [token 0x02001C35].

I used ILDASM /TOK to see which token is 0x02001C35 and it is the class I am modifying (BigServiceClient). I just can't see what the issue is.

Any pointers/ideas/tips on how to further investigate the PEVerify error?

Long Story

I'm working with a legacy SOAP interface. I use svcutil to generate the proxies, but due to the legacy nature of this interface, these proxies are huge -- like 1500+ methods in a single class. This has caused some performance issues in object creation as well as some proxying with Castle DynamicProxy.

As a quick test, I removed all but two of the methods I need (yes, only 2 out of 1500+) from the generated .cs proxy files, recompiled and got much better results. Timings went from 6-7 seconds to under 20 milliseconds.

Editing these files to manually remove this is tedious so I thought I'd automate it with Mono.Cecil. As noted above, everything is functioning properly, but I'm concerned with the peverify error.

I know you can do some pretty complex and crazy stuff with IL weaving, but I just need to remove a bunch of unused methods. Yes, I could manually parse the generated source code and remove it there, but if you've ever seen generated SOAP proxies from svcutil, it's a path I'd like to avoid.


Solution

  • Found the issue. Ironically, I figured it out while writing a code to manually process the source files and trim them down. :)

    While writing the source file processor, I noticed svcutil generated both an implicit interface implementation as well as an explicit interface implementation. My previous SaveMethods code was being too aggressive and removing the explicit implementations. I suspect that was what caused the Type load failed error from PEVerify.

    Here's my updated SaveMethods implementation that produces an assembly that successfully completes a PEVerify:

    private static void SaveMethods(TypeDefinition typeDef, IEnumerable<string> names)
    {
        var pos = 0;
        while (true)
        {
            if (pos == typeDef.Methods.Count)
                break;
    
            var md = typeDef.Methods[pos];
    
            // save ctors, method implementations and explicit interface implementations
            if (md.Name == ".ctor" ||
                names.Contains(md.Name) ||
                (md.Overrides.Count > 0 && names.Contains(md.Overrides[0].Name)))
            {
                pos++;
            }
            else
            {
                typeDef.Methods.RemoveAt(pos);
            }
        }
    }