How to diagnose "Type load failed" from PEVerify

691 views Asked by At

I'm working on a compiler that's producing bad output in certain cases of expanding generators. PEVerify simply says "Type load failed" without giving any explanation as to why. When I've seen this in the past, it's generally been because the generated type has the wrong number of generic parameters, but everything seems to match up here.

Is there any good way to get more detailed information about what went wrong with the generated type? Barring that, are there any good tips and techniques for how to track down the error?

Output from PEVerify:

C:\Build\Test>peverify testcase.exe /VERBOSE /UNIQUE

Microsoft (R) .NET Framework PE Verifier. Version 4.0.30319.0 Copyright (c) Microsoft Corporation. All rights reserved.

[IL]: Error: [C:\Build\Test\testcase.exe : Testing.Linq_operatorModule::IndexWhereImpl[T]][mdToken=0x6000002][offset 0x00000002] Unable to resolve token.

[IL]: Error: [C:\Build\Test\testcase.exe : Testing.Linq_operatorModule+$IndexWhereImpl$3`1[T]::.ctor][mdToken=0x6000006] [HRESULT 0 x8007000B] - An attempt was made to load a program with an incorrect format.

[token 0x02000004] Type load failed.

3 Error(s) Verifying testcase.exe

Comprehensive dump from ILDasm is here, as it's too large to put in a SO post.

1

There are 1 answers

2
Jeroen Mostert On BEST ANSWER

Something is wrong with the binding of your type parameters in whatever generates this code. The resulting IL can be assembled, but is so desperately invalid that PEVerify thoroughly chokes on it (which is arguably a bug in PEVerify, but things like Mono.Cecil don't like this code one bit either).

For instance:

  .method /*06000002*/ private hidebysig static 
          class [mscorlib/*23000004*/]System.Collections.Generic.IEnumerable`1/*01000003*/<int32> 
          IndexWhereImpl<T>(class [mscorlib/*23000004*/]System.Collections.Generic.IEnumerable`1/*01000003*/<!!T> coll,
                            class [mscorlib/*23000004*/]System.Func`2/*01000004*/<!!T,bool> 'filter') cil managed
  {
    // Code size       8 (0x8)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.1
    IL_0002:  newobj     instance void Testing.Linq_operatorModule/*02000002*//$IndexWhereImpl$3`1/*02000003*/::.ctor(class [mscorlib/*23000004*/]System.Collections.Generic.IEnumerable`1/*01000003*/<!!T>,
                                                                                                                      class [mscorlib/*23000004*/]System.Func`2/*01000004*/<!!T,bool>) /* 06000006 */
    IL_0007:  ret
  } // end of method Linq_operatorModule::IndexWhereImpl

The disassembled constructor call is invalid; the correct call would be

newobj instance void class Testing.Linq_operatorModule/$IndexWhereImpl$3`1<!!T>::.ctor(
    class [mscorlib]System.Collections.Generic.IEnumerable`1<!0>,
    class [mscorlib]System.Func`2<!0,bool>
)

The original call would be appropriate for a generic method, but we're not calling a generic method but an instance method of a generic class.

The remaining code is like this, including fields with invalid parameter references:

.field assembly !!0 $value$5

!!0 references the first type parameter of a generic method, and you can't declare fields in methods, so this is just always wrong. It assembles to 0x1e 0x00 (ELEMENT_TYPE_MVAR 0), you wanted 0x13 0x00 (ELEMENT_TYPE_VAR 0), corresponding to

.field assembly !0 $value$5

It's a little surprising that ilasm and ildasm even allow this, I'd expect them to be a little more discerning. Apparently, so did the author(s) of PEVerify, though that's no excuse.

I'm not sure how you generate code like this; it might be an error in one place that causes the rest to be invalid as well.