为什么CLR的jmp指令不可验证?

14

这是因为它无法验证当前部分代码块执行的影响,然后将其拼接到目标代码块中吗?我的小梦境似乎与堆栈/堆追踪有关...虽然我不确定...但这立即被这里的信息抵消了... http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.jmp.aspx ...其中明确指出没有跟踪/传输堆栈信息。也许微软在“前端”下隐藏了一些东西。 - War
我不知道为什么它是无法验证的,但我有一条有趣的信息。我们的EQATEC Profiler收集所有代码的匿名统计信息(顺便说一下,使用EQATEC Analytics)。由于jmp很棘手且未被最佳处理,我们决定跟踪它实际发生的频率。我们现在拥有2年的数据,从超过100万个已分析程序集中收集了200亿个CIL指令。相当多。而其中有多少是jmp呢?没有!是的,jmp从未在任何用户应用程序(NETCF、SL、WP7、F#等)中发生过。相当惊讶。 - Richard Flamsholt
1
其实我并不太惊讶。除了托管C++之外,我所知道的编译器都不会发出jmp指令,因为它是不可验证的。任何jmp的使用可能都被隐藏在受信任的类库中。 - naasking
我知道这已经超过一年了,但是还是要给提问者点个赞。我在搜索CIL指令的统计数据时遇到了这个问题。虽然它没有回答我的问题,但它确实增加了我的知识。非常好的问题。 - luis.espinal
1个回答

4
因为与callcallvirtcalli不同,调用者的堆栈框架将保留在堆栈上,以供由被调用方触发的未来代码访问安全性堆栈步行器看到,而jmp指令在转换为被调用方之前会拆除调用者的堆栈框架,因此对于被调用方可能触发的任何CAS堆栈步行器都是不可见的。
编辑:我认为naasking关于上面的答案是错误的。我现在认为(可验证的)tail.call序列和(不可验证的)jmp序列之间的区别可能在于尾调用需要将调用的参数推送到评估堆栈中,在那里它们可以按照正常方式进行验证,而jmp需要评估堆栈为空,并导致跳转者继承跳转者的参数。可能没有理由复杂化验证器以检查jmp指令,但是在类似于强加于tail.call序列的条件下可能是可能的(其中之一是调用者和被调用者必须在同一个程序集中,这排除了我的CAS猜测,至少直到显式.Deny()调用)。如果是这样,这将是规范的相关部分:(第III部分,第3.37节)当前参数被传输到目标方法。执行此指令时,评估堆栈必须为空。目标地址处的调用约定、参数数量和类型必须与当前方法相匹配。

乍一看似乎合理,但经过更仔细的审查后就不成立了。.tailcall前缀具有大致相同的行为,尽管更通用,但.tailcall是可验证的。文档只是说明在从不受信任的代码调用到受信任的代码时,.tailcall会被忽略。如果这就是jmp不可验证的原因,我不明白他们为什么不直接使用相同的行为来代替jmp。 - naasking
我认为在链接的博客页面上发布的"Rodrigo Kumpera"是正确的:如果任何参数都是按引用传递,则可以存储对局部变量的引用,然后执行jmp。需要进行控制流分析以确保不会发生这种情况,尽管这是相对简单的。我敢打赌他们只是不想麻烦,因为jmp主要是为了支持C++而添加的。对于.tailcall也存在同样的危险,所以我不确定它是如何处理的,即不可验证还是忽略.tailcall。我将不得不在某个时间点进行测试。 - naasking

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接