How is the Visual Studio compiler compiling Security Attributes to CIL?

176 views Asked by At

I have the following attribute SecurityPermission(SecurityAction.Assert) on a method in my class. I compile it (debug build) and view the output in ildasm.exe by viewing the raw heaps and looking at the blob heap containing the PermissionSet blob. What I expect (per ECMA-335) is:

2e 01 80 84 53 79 73 74  65 6d 2e 53 65 63 75 72 >.   System.Secur<
69 74 79 2e 50 65 72 6d  69 73 73 69 6f 6e 73 2e >ity.Permissions.<
53 65 63 75 72 69 74 79  50 65 72 6d 69 73 73 69 >SecurityPermissi<
6f 6e 41 74 74 72 69 62  75 74 65 2c 20 6d 73 63 >onAttribute, msc<
6f 72 6c 69 62 2c 20 56  65 72 73 69 6f 6e 3d 32 >orlib, Version=2<
2e 30 2e 30 2e 30 2c 20  43 75 6c 74 75 72 65 3d >.0.0.0, Culture=<
6e 65 75 74 72 61 6c 2c  20 50 75 62 6c 69 63 4b >neutral, PublicK<
65 79 54 6f 6b 65 6e 3d  62 37 37 61 35 63 35 36 >eyToken=b77a5c56<
31 39 33 34 65 30 38 39  00 00

But what I saw was this:

2e 01 80 84 53 79 73 74  65 6d 2e 53 65 63 75 72 >.   System.Secur<
69 74 79 2e 50 65 72 6d  69 73 73 69 6f 6e 73 2e >ity.Permissions.<
53 65 63 75 72 69 74 79  50 65 72 6d 69 73 73 69 >SecurityPermissi<
6f 6e 41 74 74 72 69 62  75 74 65 2c 20 6d 73 63 >onAttribute, msc<
6f 72 6c 69 62 2c 20 56  65 72 73 69 6f 6e 3d 32 >orlib, Version=2<
2e 30 2e 30 2e 30 2c 20  43 75 6c 74 75 72 65 3d >.0.0.0, Culture=<
6e 65 75 74 72 61 6c 2c  20 50 75 62 6c 69 63 4b >neutral, PublicK<
65 79 54 6f 6b 65 6e 3d  62 37 37 61 35 63 35 36 >eyToken=b77a5c56<
31 39 33 34 65 30 38 39  01 00

In particular, note the 01 00 at the end where I expected a 00 00. The spec says that after the counted string should be the number of named arguments. Since I am not passing in any named arguments I expected that number to be a 16-bit 0.

This is compiled against .NET 2.0 using Visual Studio 2013.

To complicate matters even more, if I add in a named argument I get this:

2e 01 80 84 53 79 73 74  65 6d 2e 53 65 63 75 72 >.   System.Secur<
69 74 79 2e 50 65 72 6d  69 73 73 69 6f 6e 73 2e >ity.Permissions.<
53 65 63 75 72 69 74 79  50 65 72 6d 69 73 73 69 >SecurityPermissi<
6f 6e 41 74 74 72 69 62  75 74 65 2c 20 6d 73 63 >onAttribute, msc<
6f 72 6c 69 62 2c 20 56  65 72 73 69 6f 6e 3d 32 >orlib, Version=2<
2e 30 2e 30 2e 30 2c 20  43 75 6c 74 75 72 65 3d >.0.0.0, Culture=<
6e 65 75 74 72 61 6c 2c  20 50 75 62 6c 69 63 4b >neutral, PublicK<
65 79 54 6f 6b 65 6e 3d  62 37 37 61 35 63 35 36 >eyToken=b77a5c56<
31 39 33 34 65 30 38 39  12 01 54 02 0d 55 6e 6d >1934e089  T  Unm<
61 6e 61 67 65 64 43 6f  64 65 01                >anagedCode      <

Once again, look at the end of the counted string for the attribute and you can see the 12 01 followed by the named argument list (a list of one item). I expected this to be 01 00, a 16-bit little endian 1 for the number of named arguments.

Based on this, I am assuming that the second byte after the counted string is the named parameter count but I still don't understand what that first byte is (0x01 in the first example, 0x12 in the next).

If I add a second named attribute the first byte changes to a 26, if I add a third named attribute it changes to a 33. I don't see an obvious pattern to the numbers other than the fact that they are increasing.

I am asking this question because I am attempting to build a PermissionSet blob by hand (I am writing a CLR profiler) and I need to know what to put in that byte.

1

There are 1 answers

7
mockinterface On BEST ANSWER

I think you are correct in your dismay, I remember this from my previous experience with the named parameters - the NumNamed was implemented as a compressed int instead of int16 stated in the spec, and unlike the example given §VI.B.3. I don't know if this has changed in subsequent .NET 3.0+ implementations.

For the PermissionSet you are looking for,

... * A set of properties, encoded as the named arguments to a custom attribute would be (as in §II.23.3, beginning with NumNamed).

... §II.23.3 ...

Next is a description of the optional “named” fields and properties. This starts with NumNamed – an unsigned int16 giving the number of “named” properties or fields that follow. ... In the case where NumNamed is non-zero, it is followed by NumNamed repeats of NamedArgs.

The 12 01 is the little endian integer "giving the number of properties that follow". One named property, and a total length of 18 (in decimal, inclusive).

This total length is the pattern that you were looking for, but beware since I think this length is optional and sometimes the compiler manages to pack the number of properties in the preceding int16, discarding the length. You'll have to experiment with a different number of attributes to make sure you are correctly parsing all cases.

NamesArgs:

... SerString – a PackedLen count of bytes, followed by the UTF8 characters

PROPERTY is the single byte 0x54. The FieldOrPropName is the name of the field or property, stored as a SerString (defined above)