为什么"OpCode.Value"具有"wrong"字节序?



事实:

  1. CIL指令rethrow操作码的正确编码是双字节序列FE 1A

  2. OpCodes.Rethrow.Value(类型为 short)在我的小端机器上具有价值0xFE1A

  3. BitConverter在转换为字节序列/从字节序列转换时尊重机器的字节序。

  4. 在我的小端机器上,BitConverter.GetBytes(OpCodes.Rethrow.Value) 导致字节序列1A FE .

这意味着,使用 BitConverter 在小端计算机上序列化OpCode.Value不会为操作码生成正确的编码;字节顺序是相反的。

问题:

  • OpCode.Value的字节顺序是记录在案的(如果是,在哪里?),还是"实现细节"?

  • 大端计算机上的上述步骤 4 是否也会导致错误的字节排序?也就是说,OpCodes.Rethrow.Value会在大端机器上0x1AFE吗?

Value 属性在引用源中如下所示:

public short Value
{
    get
    {
        if (m_size == 2)
            return (short) (m_s1 << 8 | m_s2);
        return (short) m_s2;
    }
}

当然,这看起来完全理智,m_s2始终是最低有效字节。 看看ILGenerator:

    internal void InternalEmit(OpCode opcode)
    {
        if (opcode.m_size == 1)
        {
            m_ILStream[m_length++] = opcode.m_s2;
        }
        else
        {
            m_ILStream[m_length++] = opcode.m_s1;
            m_ILStream[m_length++] = opcode.m_s2;
        }
        UpdateStackSize(opcode, opcode.StackChange());
    }

这是您想要的,首先发出0xfe字节。

因此,框架代码小心翼翼地避免依赖于字节序。 CIL 没有字节序依赖性,也没有可变长度数据。 对于文本文件、utf-8 编码、x86 核心机器代码指令为 true。 一个 CIL。 因此,如果将可变长度数据转换为单个值,就像 Value 属性 getter 所做的那样,那么该代码不可避免地从非字节序数据转换为字节序数据。 这不可避免地会让世界上一半的人感到不安,因为他们认为这是错误的方式。 以及 100% 遇到它的所有程序员。

可能最好的方法是像框架一样执行此操作,并使用您自己的操作码类型版本尽快恢复m_s1和m_s2。 易于使用:

foo.m_s1 = opc.Value >> 8;
foo.m_s2 = opc.Value & 0xff;
foo.m_size = opc.Size;

它没有字节序依赖性。

我得出的结论是,基于 OpCode.Value 属性序列化操作码表示,即:

OpCode someOpCode = …;
byte[] someOpCodeEncoding = BitConverter.GetBytes(someOpCode.Value);

是一个坏主意,但不是因为使用了BitConverter.GetBytes(short),其行为是有据可查的。罪魁祸首是OpCode.Value财产,其文件在两个方面含糊不清:

  1. 它声明此属性包含"直接操作数的值",该值可能引用也可能不引用操作码的编码;该术语不会出现在 CLI 规范中的任何位置。

  2. 即使我们假设它实际上包含操作码的编码,文档也没有提到字节顺序。(字节顺序在 byte[]short 之间转换时发挥作用。

为什么我的论点基于 MSDN 文档,而不是 CLI 标准?因为System.Reflection.Emit不是 CLI 标准定义的反射库的一部分。出于这个原因,我认为可以相当肯定地说,此命名空间的 MSDN 参考文档与官方规范一样接近。(但与@Hans Passant的回答不同,我不会更进一步,声称参考源以任何方式是一个规范。

结论:

有两种

方法可以输出给定OpCode对象的操作码编码:

  • 保持System.Reflection.Emit功能并使用ILGenerator.Emit(someOpCode)。在某些情况下,这可能过于严格。

  • 在操作码编码之间创建自己的映射(即 byte[]序列)和各种OpCode对象。

尝试:

var yourStream = MemoryStream();
var writer = new System.IO.BinaryWriter(yourStream);
writer.Write(OpCodes.Rethrow.Value);

您无需担心字节顺序,因为 BinaryWriter(或读取器)将为您处理实现细节。我怀疑你得到"错误"字节顺序的原因是你在OpCode值上应用BitConverter,而它已经被解码为小端序,并且再次应用BitConverter.GetShort()调用将反转字节顺序,给你"错误"的结果。

相关内容

  • 没有找到相关文章

最新更新