Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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).

like image 436
evolvedmicrobe Avatar asked Aug 20 '14 16:08

evolvedmicrobe


3 Answers

My alternatives... Also @ This similar question

#region AddressOf

    /// <summary>
    /// Provides the current address of the given object.
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static System.IntPtr AddressOf(object obj)
    {
        if (obj == null) return System.IntPtr.Zero;

        System.TypedReference reference = __makeref(obj);

        System.TypedReference* pRef = &reference;

        return (System.IntPtr)pRef; //(&pRef)
    }

    /// <summary>
    /// Provides the current address of the given element
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="t"></param>
    /// <returns></returns>
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static System.IntPtr AddressOf<T>(T t)
        //refember ReferenceTypes are references to the CLRHeader
        //where TOriginal : struct
    {
        System.TypedReference reference = __makeref(t);

        return *(System.IntPtr*)(&reference);
    }

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    static System.IntPtr AddressOfRef<T>(ref T t)
    //refember ReferenceTypes are references to the CLRHeader
    //where TOriginal : struct
    {
        System.TypedReference reference = __makeref(t);

        System.TypedReference* pRef = &reference;

        return (System.IntPtr)pRef; //(&pRef)
    }

    /// <summary>
    /// Returns the unmanaged address of the given array.
    /// </summary>
    /// <param name="array"></param>
    /// <returns><see cref="IntPtr.Zero"/> if null, otherwise the address of the array</returns>
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static System.IntPtr AddressOfByteArray(byte[] array)
    {
        if (array == null) return System.IntPtr.Zero;

        fixed (byte* ptr = array)
            return (System.IntPtr)(ptr - 2 * sizeof(void*)); //Todo staticaly determine size of void?
    }

    #endregion
like image 132
Jay Avatar answered Oct 19 '22 17:10

Jay


You should be able to use the GCHandle construct to accomplish this.

GCHandle objHandle = GCHandle.Alloc(obj,GCHandleType.WeakTrackResurrection);
int address = GCHandle.ToIntPtr(objHandle).ToInt32(); 

Where 'obj' is the object whose address you're trying to get.

like image 14
s.burns Avatar answered Oct 19 '22 17:10

s.burns


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"));
}
like image 6
evolvedmicrobe Avatar answered Oct 19 '22 18:10

evolvedmicrobe