我的理解是允许运行“不可验证的”和“可验证的”.NET字节码。但是,在这两种情况下,字节码在ECMA-CIL方面都必须是“正确的CIL”。使用C#的不安全特性可以生成正确但不可验证的字节码。可验证的字节码可以来自日常的C#。
无论哪种方式,.NET CLR必须以某种方式保证字节码是正确的。为此,必须在每次指令之前静态推断有关堆栈状态的基本信息。例如,元素的数量和非常粗糙的类型推断。如果推断的信息有一个以上的前身,则必须在基本块的开头合并它。
我的问题是,是否允许合并不同类型的托管指针?我指的是正确的CIL,但不一定是可验证的CIL。
.method public static void Bar (int32& a, uint32& b, bool d) cil managed
{
.maxstack 8
IL_0003: ldarg.2
IL_0004: brfalse.s IL_000b
IL_0006: ldarg.0
IL_0009: br.s IL_000d
IL_000b: ldarg.1
IL_000d: pop
IL_000e: ret
}ILVerify报告:
IL]: Error [PathStackUnexpected]: [Test.dll : .Test::Bar(int32&, uint32&, bool)][offset 0x00000006][found address of Int32][expected address of UInt32] Non-compatible types on stack depending on path.我的问题是,我不知道这是关于字节码的可验证性还是正确性。我指的是“可验证性”和“正确性”,就像ECMA中定义的那样。我也不知道我是否误解了标准。
发布于 2022-07-31 13:10:58
ECMA-335,第83页说:
程序中任意给定点的堆栈类型状态(堆栈深度和堆栈上每个元素的类型)对于所有可能的控制流路径都应该是相同的。例如,在每次迭代时循环未知次数并在堆栈上推送一个新元素的程序将被禁止。
第85页加强了这一点:
无论允许执行到那里的控制流是什么,堆栈上的每个插槽在方法主体内的任何给定点上都应该具有相同的数据类型。
因此,一般来说,堆栈上的元素类型必须是相同的。但是,第I.12.3.2.1节继续指定计算堆栈仅由以下类型组成:int64、int32、native int、F、&、O、*( native int或&)或任意用户定义的值类型。
这似乎意味着,虽然CIL将int32&和uint32&视为不同类型,但评估堆栈将它们都视为&,因此对于两个控件流而言,“类型状态”是相同的。因此,CIL是正确的。
可验证性是一个更强的标准,确保程序最终是内存安全的,并且在程序内存的所有情况下都有可预测的结果。验证不使用CLI用于计算堆栈的有限数量的类型,而是要求类型状态是兼容的,通常是通过具有相同的验证类型。
在您的例子中,重要的是引用变量的类型:int32和uint32。如第36页所述:
类型T的验证类型如下:
..。
2.如果T是托管指针类型S&而减少的S类型是:
..。
int32,那么它的验证类型是is 32&。
同样,在同一页上:
T类型的简化类型如下:
1.如果T的基本类型是:
..。
int32,或无符号int32,则其简化类型为int32。
因此,简化的类型都是int32,因此这两个参数的验证类型都是int32&,而不管它们的签名性如何,因此CLI是可验证的。
https://stackoverflow.com/questions/72722687
复制相似问题