Why does "OpCode.Value" have a "wrong" entity?

Facts:

  • The correct encoding for the CIL rethrow op-code instruction is the FE 1A double-byte sequence.

  • OpCodes.Rethrow.Value (which is of type short ) is set to 0xFE1A on my little-endian machine.

  • BitConverter evaluates the finiteness of a machine when converting to a sequence of / from bytes.

  • On my machine with a thumbnail BitConverter.GetBytes(OpCodes.Rethrow.Value) , a 1A FE byte sequence is obtained.

This means that serializing OpCode.Value on a low-rise computer using BitConverter does not provide the correct encoding for the op code; the byte order is canceled.

Questions:

  • Is the byte order of OpCode.Value (and if so, where?), Or is it an "implementation detail"?

  • Does step 4 above on a machine with a large number of sides have the wrong byte order? That is, OpCodes.Rethrow.Value be 0x1AFE on a big-end machine?

+7
source share
3 answers

I came to the conclusion that serialization of the op code view is based on the OpCode.Value property, i.e.:

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

- A bad idea, but not due to the use of BitConverter.GetBytes(short) , are documented. The main culprit is the OpCode.Value property, whose documentation is vague in two ways:

  • Indicates that this property contains an "immediate operand value", which may or may not relate to the encoding of the op code; this term does not appear anywhere in the CLI specification.

  • Even assuming it actually contains an encoding with an op code, the documentation says nothing about the byte order. (Byte order comes into play when converting between byte[] and short .)

Why am I base my argument on the MSDN documentation and not on the CLI standard? Because System.Reflection.Emit not part of the reflection library, as defined by the CLI standard. For this reason, I find it fairly safe to say that the MSDN reference documentation for this namespace is as close as possible to the official specification. (But, unlike @Hans Passant's answer, I would not take it one step further and claim that the original source is in some way a specification.)

Output:

There are two ways to output the opcode encoding for a given OpCode object:

  • Stay with the functionality of System.Reflection.Emit and use ILGenerator.Emit(someOpCode) . This may be too restrictive in some situations.

  • Create your own mapping between code encodings (for example, byte[] ) and various OpCode objects.

+1
source

The Value property looks like this in the original source:

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

This looks pretty normal, m_s2 is always the least significant byte. Looking at 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()); } 

What do you expect, byte 0xfe is first issued.

Thus, the program code carefully avoids end-user dependency. CIL is not dependent on endian-ness; variable-length data is never executed. True for text files, utf-8 encoding, instructions for x86 machine code. CIL. Therefore, if you convert variable-length data to a single value, for example, the getter property of a property, then this code inevitably does the conversion from non-infinite data to endian-ness data. This inevitably leads to the fact that half the world is upset because it thinks it was wrong. And 100% of all programmers who encounter this.

Probably the best way is to do as the framework does, and restore m_s1 and m_s2 as fast as you can, using your own version of the Opcode type. Simple approach:

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

which has no dependence on the end.

+3
source

Try:

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

You do not need to worry about the byte order since the BinaryWriter (or reader) will handle the implementation details for you. I suspect that the reason you get the "wrong" byte order is because you use the bit converter by the OpCode value when it is already decoded as a small endian, and using the BitConverter.GetShort () call will again reverse the byte order giving you the β€œwrong” result.

0
source

All Articles