.NET Internals Cookbook Part 12 — Memory structure, attributes, handles

This is the twelfth part of the .NET Internals Cookbook series. For your convenience you can find other parts in the table of contents in Part 0 – Table of contents

82. What is a bit-mapped attribute? Custom attribute? Pseudo-custom attribute?

There are three types of attributes:

Bit-mapped — for instance public Custom — the “normal” attributes like [MyCustom] Pseudo-custom — look like custom attributes but behave like bit-mapped ones, for instance Serializable

Let’s take this code:

using System; [Custom] [Serializable] public class Program { public static void Main() { } } class CustomAttribute : Attribute { public CustomAttribute() { } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 using System ; [ Custom ] [ Serializable ] public class Program { public static void Main ( ) { } } class CustomAttribute : Attribute { public CustomAttribute ( ) { } }

Which you can decompile to:

.class public auto ansi serializable beforefieldinit Program extends [mscorlib]System.Object { .custom instance void CustomAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public hidebysig static void Main () cil managed { // Method begins at RVA 0x2050 // Code size 2 (0x2) .maxstack 8 .entrypoint IL_0000: nop IL_0001: ret } // end of method Program::Main .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2053 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Program::.ctor } // end of class Program 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 . class public auto ansi serializable beforefieldinit Program extends [ mscorlib ] System . Object { . custom instance void CustomAttribute :: . ctor ( ) = ( 01 00 00 00 ) // Methods . method public hidebysig static void Main ( ) cil managed { // Method begins at RVA 0x2050 // Code size 2 (0x2) . maxstack 8 . entrypoint IL_0000 : nop IL_0001 : ret } // end of method Program::Main . method public hidebysig specialname rtspecialname instance void . ctor ( ) cil managed { // Method begins at RVA 0x2053 // Code size 8 (0x8) . maxstack 8 IL_0000 : ldarg . 0 IL_0001 : call instance void [ mscorlib ] System . Object :: . ctor ( ) IL_0006 : nop IL_0007 : ret } // end of method Program::.ctor } // end of class Program

So you can see that the Serializable attribute is applied in the class signature:

.class public auto ansi serializable beforefieldinit Program extends [mscorlib]System.Object 1 2 . class public auto ansi serializable beforefieldinit Program extends [ mscorlib ] System . Object

Custom attribute is initialized this way:

.custom instance void CustomAttribute::.ctor() = ( 01 00 00 00 ) 1 2 3 . custom instance void CustomAttribute :: . ctor ( ) = ( 01 00 00 00 )

See also this post.

83. What is a handle-recycling attack?

SafeHandle identifies a native handle assigned by operating system. Handles are just numbers in a handle table which can be reused. Imagine a situation that you push a handle on the stack and then the thread is paused. Other thread closes the handle, opens something new and gets the same integer value. Now our thread is resumed and continues with the handle it already pushed on the stack but now the handle identifies different resource.

84. What handle types do we have in .NET?

Strong handle – like a normal reference

Short weak handle – doesn’t stop the object from cleaning up, does not track the object if it is resurrected

Long weak handle – like short weak handle but tracks the object after it gets resurrected

Pinned handle – strong handle which doesn’t allow the object to be moved

Async pinned handle – like pinned handle but GC knows that it can be unpinned after async (typically I/O) operation is completed

Ref count handle – handle counting references, used for COM interop

Dependent handle – used by ConditionalWeakTable , connects two objects by strong handle, but from the outside is like a weak handle

You can see those by running !gchandles in WinDBG:

0:000> !gchandles GC Handle Statistics: Strong Handles: 259 Pinned Handles: 137 Async Pinned Handles: 1 Ref Count Handles: 79 Weak Long Handles: 197 Weak Short Handles: 650 Other Handles: 0 1 2 3 4 5 6 7 8 9 0 : 000 > ! gchandles GC Handle Statistics : Strong Handles : 259 Pinned Handles : 137 Async Pinned Handles : 1 Ref Count Handles : 79 Weak Long Handles : 197 Weak Short Handles : 650 Other Handles : 0

85. What is a ref local?

.NET has a concept of managed pointers — pointers not necessarily pointing to the beginning of the object. They can point to local variables, parameters and parts of an object (including array elements).

ref local is a variable storing a managed pointer. You can use it like this:

using System; namespace Program { public class Program { public static void Main(string[] args) { int a = 5; ref int b = ref a; Console.WriteLine(a); Console.WriteLine(b); a = 6; Console.WriteLine(a); Console.WriteLine(b); b = 7; Console.WriteLine(a); Console.WriteLine(b); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using System ; namespace Program { public class Program { public static void Main ( string [ ] args ) { int a = 5 ; ref int b = ref a ; Console . WriteLine ( a ) ; Console . WriteLine ( b ) ; a = 6 ; Console . WriteLine ( a ) ; Console . WriteLine ( b ) ; b = 7 ; Console . WriteLine ( a ) ; Console . WriteLine ( b ) ; } } }

The output is:

5 5 6 6 7 7 1 2 3 4 5 6 5 5 6 6 7 7

So you can see that both of the variables point to the same memory location. When you decompile the code, you get:

.maxstack 2 .locals init ([0] int32 a, [1] int32& b) IL_0000: nop IL_0001: ldc.i4.5 IL_0002: stloc.0 IL_0003: ldloca.s a IL_0005: stloc.1 IL_0006: ldloc.0 IL_0007: call void [mscorlib]System.Console::WriteLine(int32) IL_000c: nop IL_000d: ldloc.1 IL_000e: ldind.i4 IL_000f: call void [mscorlib]System.Console::WriteLine(int32) IL_0014: nop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 . maxstack 2 . locals init ( [ 0 ] int32 a , [ 1 ] int32 & b ) IL_0000 : nop IL_0001 : ldc . i4 . 5 IL_0002 : stloc . 0 IL_0003 : ldloca . s a IL_0005 : stloc . 1 IL_0006 : ldloc . 0 IL_0007 : call void [ mscorlib ] System . Console :: WriteLine ( int32 ) IL_000c : nop IL_000d : ldloc . 1 IL_000e : ldind . i4 IL_000f : call void [ mscorlib ] System . Console :: WriteLine ( int32 ) IL_0014 : nop

So you can see that the type of b is int32& .

You can also use ref return to return something from a function:

using System; namespace Program { public class Program { public static void Main(string[] args) { var array = new int[] { 5 }; ref int bar = ref Foo(array); Console.WriteLine(array[0]); Console.WriteLine(bar); array[0] = 6; Console.WriteLine(array[0]); Console.WriteLine(bar); bar = 7; Console.WriteLine(array[0]); Console.WriteLine(bar); } static ref int Foo(int[] array) { return ref array[0]; } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System ; namespace Program { public class Program { public static void Main ( string [ ] args ) { var array = new int [ ] { 5 } ; ref int bar = ref Foo ( array ) ; Console . WriteLine ( array [ 0 ] ) ; Console . WriteLine ( bar ) ; array [ 0 ] = 6 ; Console . WriteLine ( array [ 0 ] ) ; Console . WriteLine ( bar ) ; bar = 7 ; Console . WriteLine ( array [ 0 ] ) ; Console . WriteLine ( bar ) ; } static ref int Foo ( int [ ] array ) { return ref array [ 0 ] ; } } }

This prints:

5 5 6 6 7 7 1 2 3 4 5 6 5 5 6 6 7 7

As a sidenote, dotnetfiddle gives this:

Run-time exception (line -1): Operation could destabilize the runtime. Stack Trace: [System.Security.VerificationException: Operation could destabilize the runtime.] at Program.Program.Foo(Int32[] array) at Program.Program.Main(String[] args) 1 2 3 4 5 6 7 Run - time exception ( line - 1 ) : Operation could destabilize the runtime . Stack Trace : [ System . Security . VerificationException : Operation could destabilize the runtime . ] at Program . Program . Foo ( Int32 [ ] array ) at Program . Program . Main ( String [ ] args )

86. What is an interior pointer?

Interior pointer is a managed pointer which points to the inside of an object. See this:

using System; namespace Program { public class Program { public static void Main(string[] args) { var foo = new Foo(); foo.Bar = 5; ref int bar = ref foo.Bar; Console.WriteLine(foo.Bar); Console.WriteLine(bar); foo.Bar = 6; Console.WriteLine(foo.Bar); Console.WriteLine(bar); bar = 7; Console.WriteLine(foo.Bar); Console.WriteLine(bar); } } } class Foo{ public int Bar; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System ; namespace Program { public class Program { public static void Main ( string [ ] args ) { var foo = new Foo ( ) ; foo . Bar = 5 ; ref int bar = ref foo . Bar ; Console . WriteLine ( foo . Bar ) ; Console . WriteLine ( bar ) ; foo . Bar = 6 ; Console . WriteLine ( foo . Bar ) ; Console . WriteLine ( bar ) ; bar = 7 ; Console . WriteLine ( foo . Bar ) ; Console . WriteLine ( bar ) ; } } } class Foo { public int Bar ; }

This outputs:

5 5 6 6 7 7 1 2 3 4 5 6 5 5 6 6 7 7

You may ask: how does GC know that interior pointer needs to keep an object alive? GC uses brick table which partitions the heap into multiple blocks. It contains an offset to the first object inside such a block so then it can traverse the chunk of memory and find the object which should be held by the interior pointer. Since this algorithm is very expensive there are limitations what you can do. For instance you cannot have a “ref field” in an object.

87. What is a ref struct?

It is a structure which cannot be moved to the heap. It is always stored on the stack and because of that cannot be accessed by multiple threads. You declare it like this:

public ref struct Foo { public int Bar; } 1 2 3 4 public ref struct Foo { public int Bar ; }

If you try to use Foo as a field or property of a class, box it or create an array of it – you will get a compilation error.

88. What is an unsafe struct?

If you put an array inside a structure, only the reference to the array is stored. If you want to store whole array inside a structure you need to create so called “fixed size buffer”. It is a fixed array of primitives which you can use in unsafe structures only:

unsafe struct FixedBufferExample { public fixed int buffer[1024]; // This is a fixed buffer. } 1 2 3 4 unsafe struct FixedBufferExample { public fixed int buffer [ 1024 ] ; // This is a fixed buffer. }

89. What is a readonly struct?

It is a structure which cannot be modified after creation. It differs from normal structure with readonly fields that you cannot modify this :

public readonly struct Foo { public readonly int Bar; } 1 2 3 4 public readonly struct Foo { public readonly int Bar ; }

90. What is an unsafe type?

This is easy, it is just a class in which you can use unsafe code. Just like an unsafe method.

91. What is a blittable type?

It is a type which has the same binary representation in managed an unmanaged code. Integer, pointers and floating point values are blittable. Oddly enough, bool is not blittable as it can be stored as 1, 2 or 4-byte value, char and DateTime are non-blittable as well. See more here.

92. How is unmanaged class compiled in C++/CLI?

You can create both managed and unmanaged classes and methods, see this:

#include "stdafx.h" #include < cstdio> using namespace System; ref class Foo { }; class Bar { }; void Baz() { System::Console::WriteLine("In managed function."); } #pragma managed(push, off) void Taz() { printf("In unmanaged function.

"); } #pragma managed(pop) int main(array<System::String ^> ^args) { Console::WriteLine(L"Hello World"); return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include "stdafx.h" #include < cstdio> using namespace System ; ref class Foo { } ; class Bar { } ; void Baz ( ) { System :: Console :: WriteLine ( "In managed function." ) ; } #pragma managed(push, off) void Taz ( ) { printf ( "In unmanaged function.

" ) ; } #pragma managed(pop) int main ( array < System :: String ^ > ^ args ) { Console :: WriteLine ( L "Hello World" ) ; return 0 ; }

When you decompile it, you can find class Foo :

.class private auto ansi beforefieldinit Foo extends [mscorlib]System.Object { // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x10c0 // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Foo::.ctor } // end of class Foo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 . class private auto ansi beforefieldinit Foo extends [ mscorlib ] System . Object { // Methods . method public hidebysig specialname rtspecialname instance void . ctor ( ) cil managed { // Method begins at RVA 0x10c0 // Code size 7 (0x7) . maxstack 1 IL_0000 : ldarg . 0 IL_0001 : call instance void [ mscorlib ] System . Object :: . ctor ( ) IL_0006 : ret } // end of method Foo::.ctor } // end of class Foo

You can also find method main :

.method assembly static int32 main ( string[] args ) cil managed { // Method begins at RVA 0x10ec // Code size 16 (0x10) .maxstack 1 .locals ( [0] int32 ) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldstr "Hello World" IL_0007: call void [mscorlib]System.Console::WriteLine(string) IL_000c: ldc.i4.0 IL_000d: stloc.0 IL_000e: ldloc.0 IL_000f: ret } // end of method '<Module>'::main 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 . method assembly static int32 main ( string [ ] args ) cil managed { // Method begins at RVA 0x10ec // Code size 16 (0x10) . maxstack 1 . locals ( [ 0 ] int32 ) IL_0000 : ldc . i4 . 0 IL_0001 : stloc . 0 IL_0002 : ldstr "Hello World" IL_0007 : call void [ mscorlib ] System . Console :: WriteLine ( string ) IL_000c : ldc . i4 . 0 IL_000d : stloc . 0 IL_000e : ldloc . 0 IL_000f : ret } // end of method '<Module>'::main

And method Baz :

.method assembly static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) Baz () cil managed { // Method begins at RVA 0x10d4 // Code size 11 (0xb) .maxstack 1 IL_0000: ldstr "In managed function." IL_0005: call void [mscorlib]System.Console::WriteLine(string) IL_000a: ret } // end of method '<Module>'::Baz 1 2 3 4 5 6 7 8 9 10 11 . method assembly static void modopt ( [ mscorlib ] System . Runtime . CompilerServices . CallConvCdecl ) Baz ( ) cil managed { // Method begins at RVA 0x10d4 // Code size 11 (0xb) . maxstack 1 IL_0000 : ldstr "In managed function." IL_0005 : call void [ mscorlib ] System . Console :: WriteLine ( string ) IL_000a : ret } // end of method '<Module>'::Baz