7

I have one field defined in CIL like this:

.field public int32 modopt(void*) fld

I compile this to an assembly. Now I change it to:

.field public int32 modopt(int16) fld

Both how is it possible now, that ILDASM reports (when shown as hex) both those fields as this?

Field #1 (04000001)
-------------------------------------------------------
    Field Name: fld (04000001)
    Flags     : [Public]  (00000006)
    CallCnvntn: [FIELD]
    Field type:  CMOD_OPT 1b000001 I4
    Signature : 06 20 06 08 

This code looks for both fields exactly the same (actually I've created the second field to match the reported signature). The signature obviously matches the second field, but the signature of the first field should look like this: 06 20 0f 01 08! What am I missing here?

Edit:

C# cannot emit this type of field, throwing an exception about pointer and array types not supported for custom type modifiers, so this apparently solves the signature mismatch. But the question why ILDASM allows creating an invalid signature which it cannot decompile remains.

Edit #2:

It seems ILASM is actually creating correct IL, there is a difference in the hex dump I missed last time:

//the first assembly
TypeSpec #1 (1b000001)
-------------------------------------------------------
    TypeSpec : Ptr Void
    Signature: 0f 01 

//the second assembly
TypeSpec #1 (1b000001)
-------------------------------------------------------
    TypeSpec : I2
    Signature: 06 

So there is just a bug in ILDASM hex dump reporting wrong member signature (though I wonder where the 06 in the wrong signature came from).

IS4
  • 11,945
  • 2
  • 47
  • 86
  • To ask the obvious question: what makes you think it should be `06 20 0f 01 08`? Is there a breakdown / reference to that? – Marc Gravell Jan 06 '15 at 20:54
  • @Marc I've looked in the ECMA of CLI. The element type of modopt is `20`, element type of pointer is `0f`, of void it is `01`. So, getting up all these together, the signature of `modopt(void*)` should be `20 0f 01` (`06` is field callconv and `08` is int32). – IS4 Jan 06 '15 at 20:58
  • is this C# ? I see void* ! – niceman Jan 06 '15 at 21:11
  • @niceman no, it is CIL – Brian Rasmussen Jan 06 '15 at 21:18
  • @BrianRasmussen I know, I mean is it a reflection of a C# application or C++ ? – niceman Jan 06 '15 at 21:19
  • 1
    @niceman `void*` is just as valid in C# as it is in C++. It could be any number of other languages too (including manually written CIL). –  Jan 06 '15 at 21:21
  • @hvd no it's not valid in C# , there are no pointers in C# ! – niceman Jan 06 '15 at 21:23
  • @niceman [Microsoft disagrees with you.](http://msdn.microsoft.com/en-us/library/y31yhkeb.aspx) –  Jan 06 '15 at 21:23
  • @hvd unless you use unsafe context ! well then yes – niceman Jan 06 '15 at 21:24
  • @niceman Stop being blinded by promises of type-safety. There are pointers in C#. :-D Anyway, you just wanted to point out that this question contained the `C#` tag while not being about C#. – IS4 Jan 06 '15 at 21:24
  • The CIL grammar seems to agree with what you found: the syntax is listed as "*Type* `modopt` '`(`' *TypeReference* '`)`'", and *TypeReference* is a reference to a user-defined type. You might be able to have `[mscorlib] System.Int16` there, but not `int16`, and there's no name that could refer to a pointer type either. No idea why it's accepted by ilasm, though. –  Jan 06 '15 at 21:40
  • @niceman pointers exist in C# too! `public unsafe void* A() { int b = 0; return &b; }` – George Simms Jan 06 '15 at 21:43
  • @IllidanS4 why would modopt be set differently here in real life? The CIL spec suggests modopt is mostly for the benefit of compilers and is largely ignored by the CLR (apart from to making the method signature different) – George Simms Jan 06 '15 at 21:51
  • @hvd You are right, modopt expects only typeref, not other elements. Thanks, this discovery solves lots of my heretic experiments! (see my library [SharpUtils](https://github.com/IllidanS4/SharpUtils) for these.) – IS4 Jan 06 '15 at 21:54
  • @GeorgeSimms I realize it is just an example, but that is a very dangerous example to use (handing back to a pointer on the stack that will be past the head of the stack the moment the method returns) – Marc Gravell Jan 07 '15 at 08:20
  • @Marc-gravell because b is on the stack and will thus get deleted when A returns, and so the return value of A will point to something unknown. – George Simms Jan 07 '15 at 08:39
  • @GeorgeSimms wiping happens at method entry (if `.locals init` is specified), not method exit, so it won't get *deleted*, but it isn't well-defined (and may corrupt arbitrary later stack values) – Marc Gravell Jan 07 '15 at 09:03

1 Answers1

1

Let's try to build the field signature manually based on the specification. To start, field signature is defined in §II.23.2.4. For our case with one custom modifier, it will be:

FIELD CustomMod Type

Since FIELD is defined as 0x06, we have:

06 CustomMod Type

Our custom modifier is modopt, so we get (based on §II.23.2.7):

06 CMOD_OPT TypeDefOrRefOrSpecEncoded Type

CMOD_OPT is 0x20 (§II.23.1.16):

06 20 TypeDefOrRefOrSpecEncoded Type

We want to reference TypeSpec 0x1b000001, which is encoded as 0b110 (10 for TypeSpec, 1 for 0x000001, §II.23.2.8). This is then "compressed" into the single byte 0x06 (§II.23.2):

06 20 06 Type

Finally, the type is int32, which is ELEMENT_TYPE_I4 = 0x08 (§II.23.2.12 and §II.23.1.16):

06 20 06 08

So we get exactly the same signature as the one shown in ILDasm.

svick
  • 236,525
  • 50
  • 385
  • 514
  • Thanks, this makes much more sense. I thought it was supposed to be type signature, not type token, so I misinterpreted the ILDasm's results. – IS4 Jan 08 '15 at 15:46