7

事实:

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

  2. OpCodes.Rethrow.Value(具有 type short0xFE1A在我的 little-endian 机器上具有价值。

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

  4. 在我的 little-endian 机器上,BitConverter.GetBytes(OpCodes.Rethrow.Value)结果为 byte sequence 1A FE

这意味着,OpCode.Value在 little-endian 机器上使用序列化 aBitConverter不会为操作码生成正确的编码;字节顺序颠倒了。

问题:

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

  • 上面的步骤 4 在大端机器上是否也会导致错误的字节顺序?也就是说,会OpCodes.Rethrow.Value0x1AFE大端机器上吗?

4

3 回答 3

3

参考源中的 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 核心机器码指令。一个 CIL。因此,如果您将可变长度数据转换为单个值,就像 Value 属性 getter 所做的那样,那么该代码不可避免地从非字节序数据转换为字节序数据。这不可避免地让世界上的一半人感到不安,因为他们认为这是错误的方式。以及 100% 遇到它的所有程序员。

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

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

没有字节序依赖性。

于 2012-08-18T08:03:43.610 回答
1

我得出的结论是,基于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对象之间创建您自己的映射。

于 2012-08-18T10:06:19.910 回答
0

尝试:

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

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

于 2012-08-18T12:26:07.440 回答