如何从 PEVerify 诊断“类型加载失败”

Posted

技术标签:

【中文标题】如何从 PEVerify 诊断“类型加载失败”【英文标题】:How to diagnose "Type load failed" from PEVerify 【发布时间】:2017-05-15 21:11:58 【问题描述】:

我正在开发一个编译器,它在某些扩展生成器的情况下会产生错误的输出。 PEVerify 只是说“类型加载失败”而没有解释原因。当我过去看到这种情况时,通常是因为生成的类型具有错误数量的泛型参数,但这里似乎一切都匹配。

有没有什么好的方法可以获取有关生成的类型出了什么问题的更详细信息?除此之外,是否有任何好的提示和技术来追踪错误?

PEVerify 的输出:

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

Microsoft (R) .NET Framework PE 验证程序。版本 4.0.30319.0 版权所有 (c) 微软公司。保留所有权利。

[IL]:错误:[C:\Build\Test\testcase.exe : Testing.Linq_operatorModule::IndexWhereImpl[T]][mdToken=0x6000002][offset 0x00000002] 无法解析令牌。

[IL]:错误:[C:\Build\Test\testcase.exe:Testing.Linq_operatorModule+$IndexWhereImpl$3`1[T]::.ctor][mdToken=0x6000006] [HRESULT 0 x8007000B] - 试图加载格式不正确的程序。

[token 0x02000004] 类型加载失败。

3 验证 testcase.exe 的错误

来自 ILDasm 的综合转储是 here,因为它太大而无法放入 SO 帖子。

【问题讨论】:

这不是你的原因,但我得到了“类型加载失败”,因为我忘记将“newslot”修饰符添加到方法中。 【参考方案1】:

在生成此代码的任何内容中绑定类型参数都有问题。生成的 IL 可以组装,但非常无效,以至于 PEVerify 彻底窒息了它(这可以说是 PEVerify 中的一个错误,但像 Mono.Cecil 这样的代码一点也不喜欢这个代码)。

例如:

  .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

反汇编的构造函数调用无效;正确的调用是

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>
)

原始调用适用于泛型方法,但我们调用的不是泛型方法,而是泛型类的实例方法。

剩下的代码是这样的,包括参数引用无效的字段:

.field assembly !!0 $value$5

!!0 引用了泛型方法 的第一个类型参数,并且您不能在方法中声明字段,所以这总是错误的。它组装到0x1e 0x00ELEMENT_TYPE_MVAR 0),你想要0x13 0x00ELEMENT_TYPE_VAR 0),对应于

.field assembly !0 $value$5

ilasm 和 ildasm 甚至允许这样做有点令人惊讶,我希望他们更有洞察力。显然,PEVerify 的作者也是如此,但这不是借口。

我不确定您如何生成这样的代码;可能是某个地方的错误导致其余部分也无效。

【讨论】:

我用 CCI 生成这样的代码,它会很高兴地生成完全、可笑地无效的 IL...但与 Reflection.Emit 不同的是,它会实际生成它,而不是窒息关于类型问题并给我一个黑盒错误,无法运行 ILDasm 和 PEVerify 来找出问题所在。 所以,谢谢。我将研究泛型类型绑定。这实际上是有道理的。编译器将泛型方法转换为生成器,并将方法局部变量提升到生成器类的字段中,这意味着它们仍被标识为使用来自原始方法的泛型类型参数。是时候解决这个问题了。

以上是关于如何从 PEVerify 诊断“类型加载失败”的主要内容,如果未能解决你的问题,请参考以下文章

从 Pepper QiSDK 获取诊断

如何禁用诊断工具?

如何诊断自己的网站 SSL 证书配置正确与否?

控制车子的软件的远程诊断需要多长时间出结果

诊断卡显示D1是啥意思?该怎么解决?

我应该如何诊断和防止 JVM 崩溃?