peverify校验普通托管代码报无法解析token错误的原因
问题背景
使用 peverify /quiet 工具校验QuickFIXn相关程序集时,遇到如下IL校验错误:
[IL]: Error :[QuickFIXn.xyz.dll : QuickFIXn.MessageFactories+DefaultMessageFactory::GetTransportDataDictionary][offset 0x00000039] Unable to resolve token.
触发错误的托管代码(已省略无关方法、字段定义)
namespace QuickFIXn { public static class MessageFactories { ... internal class DefaultMessageFactory : IMessageFactory { private readonly System.Collections.Generic.Dictionary<string, IMessageFactory> _factories = new System.Collections.Generic.Dictionary<string, IMessageFactory>(); ... public DataDictionary.DataDictionary GetTransportDataDictionary(string beginString) { IMessageFactory f; // FIXME: This is a hack. FIXT11 could mean 50 or 50sp1 or 50sp2. // We need some way to choose which 50 version it is. if (beginString.Equals(FixValues.BeginString.FIXT11)) { f = _factories["FIX50"]; } else if (!_factories.TryGetValue(beginString, out f)) throw new UnsupportedVersion(beginString); return f.GetTransportDataDictionary(beginString); } } } }
反编译得到的对应方法IL代码
.method public hidebysig newslot virtual final instance class [QuickFIXn]QuickFIXn.DataDictionary.DataDictionary GetTransportDataDictionary(string beginString) cil managed { // code size 63 (0x3f) .maxstack 3 .locals init ([0] class [QuickFIXn]QuickFIXn.IMessageFactory f) IL_0000: ldarg.1 IL_0001: ldstr "FIXT.1.1" IL_0006: callvirt instance bool [mscorlib]System.String::Equals(string) IL_000b: brfalse.s IL_0020 IL_000d: ldarg.0 IL_000e: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,class [QuickFIXn]QuickFIXn.IMessageFactory> QuickFIXn.MessageFactories/DefaultMessageFactory::_factories IL_0013: ldstr "FIX50" IL_0018: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<string,class [QuickFIXn]QuickFIXn.IMessageFactory>::get_Item(!0) IL_001d: stloc.0 IL_001e: br.s IL_0037 IL_0020: ldarg.0 IL_0021: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,class [QuickFIXn]QuickFIXn.IMessageFactory> QuickFIXn.MessageFactories/DefaultMessageFactory::_factories IL_0026: ldarg.1 IL_0027: ldloca.s f IL_0029: callvirt instance bool class [mscorlib]System.Collections.Generic.Dictionary`2<string,class [QuickFIXn]QuickFIXn.IMessageFactory>::TryGetValue(!0, !1&) IL_002e: brtrue.s IL_0037 IL_0030: ldarg.1 IL_0031: newobj instance void [QuickFIXn]QuickFIXn.UnsupportedVersion::.ctor(string) IL_0036: throw IL_0037: ldloc.0 IL_0038: ldarg.1 IL_0039: callvirt instance class [QuickFIXn]QuickFIXn.DataDictionary.DataDictionary [QuickFIXn]QuickFIXn.IMessageFactory::GetTransportDataDictionary(string) IL_003e: ret } // end of method DefaultMessageFactory::GetTransportDataDictionary
补充信息
代码中使用的IMessageFactory接口不存在默认接口实现,接口定义如下:
namespace QuickFIXn { public interface IMessageFactory { DataDictionary.DataDictionary GetTransportDataDictionary(string beginString); ... } }
结论
这个报错既不是代码本身有缺陷,也不算peverify的误报,本质是工具和目标程序集的适配问题:
- 首先可以排除代码问题:逻辑上本地变量
f走到0x39位置调用方法前肯定已经被赋值:命中FIXT11分支时会直接从字典取FIX50对应的工厂,key不存在会直接抛KeyNotFoundException,根本走不到后面的调用;走其他版本分支时如果TryGetValue匹配失败会直接抛UnsupportedVersion,只有匹配成功才会给f赋值后走到调用位置,不存在未赋值变量调用的问题。另外已经确认接口没有默认实现、方法确实存在,编译出来的程序集运行是完全正常的。 - 报错的核心原因是使用的
peverify是.NET Framework SDK带的老版本工具,它只支持解析.NET Framework格式的程序集元数据,碰到.NET Core/.NET 5+ 新格式的程序集,就没法正确识别跨程序集的接口方法token,再加上如果运行peverify的时候没把所有依赖的dll放到它的搜索路径里,就会报这个"无法解析token"的错。 - 要校验新版.NET程序集的IL正确性,不要用老的Framework版peverify,换成.NET SDK自带的
dotnet peverify命令即可,合法的代码用新工具校验不会出这个错。
内容的提问来源于stack exchange,提问作者Evgeniy Berezovsky
相关产品推荐
相关产品推荐

