Search code examples
c#monogarbage-collection

Get Memory Address of .NET Object (C#)


I am trying to track down a bug in the mono runtime where a variable appears to be allocated to one valid object, and then is reassigned later to a bogus object, specifically

//early in code I allocate, fine
var o = new object(); // valid allocation
// later in code this is called, not fine
lock(o) // <- is triggering bug due to "o" now referencing a nonsense memory location.

I would like to know when the reference to "o" becomes nonsense, and to do this am looking for a way to determine the address of "o" at various timepoints within the C# code. I know is similar to other questions with answers "don't do that there is a GC", but the GC doesn't work so I need a workaround.

Does anyone know how I can determine the address of a mono object in C#? Am fine to link in unmanaged code or whatever. (Any other clues to ways to diagnose the main issue appreciated to).


Solution

  • Turns out this is not possible in .NET directly, but can be accomplished by altering the mono runtime code. To create a C# method that can read the memory address, make the following changes to the mono source code:

    Alter gc-internal.h to add

    gpointer    ves_icall_System_GCHandle_GetAddrOfObject (MonoObject *obj) MONO_INTERNAL;
    

    Alter gc.c to add:

    gpointer    ves_icall_System_GCHandle_GetAddrOfObject (MonoObject *obj) {
        return (char*)obj;
    }
    

    Alter GCHandle.cs to add:

    MethodImplAttribute(MethodImplOptions.InternalCall)]
    private extern static IntPtr GetAddrOfObject(object obj);
    
    public static IntPtr AddrOfObject(object o)
    {
        IntPtr res = GetAddrOfObject(o);
        return res;
    }
    

    Alter icall-def.h to add

    ICALL(GCH_6, "GetAddrOfObject", ves_icall_System_GCHandle_GetAddrOfObject)
    

    Note that these must be in order, so add it above the GetAddrOfPinnedObject line Rebuild

    Finally, call it from C#

    for (int i = 0; i < 100; i++) {
        object o = new object ();
        var ptr = GCHandle.AddrOfObject (o);
        Console.WriteLine ("Address: " + ptr.ToInt64().ToString ("x"));
    }