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, specificall
You can't get the address of a manged object in managed code, in general. If the object has a field like an int, you could take it's address with the fixed C# statement and then you'd have a pointer inside the object. For debugging purposes, you could make some assumptions and get the offset to the base pointer of the object (on 32 bit platforms the object header size on mono is 8 bytes, 16 bytes on 64 bit architectures, at this time).
Your bug report claims you're using the Boehm collector, though, and that collector doesn't move objects in memory, the bug could be caused by some unrelated memory corruption, by the object being incorrectly freed or some other logic bug in the GC (I'm not sure the zero size you pointed out is relevant, since a managed object has at least the 8-16 byte header).
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
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"));
}
There is a quick way to view the memory address allocated to a variable is:
Code
string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");
Output
Memory address:45687608
Explanation
The method GCHandle.AddrOfPinnedObject retrieves the address of an object in a Pinned handle.
Disassembly
You can view EVERY memory address allocated to each method and variable you should analize the JIT-compiled code with the Disassembly window in Visual Studio.
Enable the Disassembly by selecting Enable address-level debugging, under Tools > Options > Debugging > General.
Set the a brake point at the beginning of the application and start the debug. Once the application hit the brake-point open the Disassembly window by selecting Debug > Windows > Disassembly.
--- C:\Users\Ivan Porta\source\repos\ConsoleApp1\Program.cs --------------------
{
0066084A in al,dx
0066084B push edi
0066084C push esi
0066084D push ebx
0066084E sub esp,4Ch
00660851 lea edi,[ebp-58h]
00660854 mov ecx,13h
00660859 xor eax,eax
0066085B rep stos dword ptr es:[edi]
0066085D cmp dword ptr ds:[5842F0h],0
00660864 je 0066086B
00660866 call 744CFAD0
0066086B xor edx,edx
0066086D mov dword ptr [ebp-3Ch],edx
00660870 xor edx,edx
00660872 mov dword ptr [ebp-48h],edx
00660875 xor edx,edx
00660877 mov dword ptr [ebp-44h],edx
0066087A xor edx,edx
0066087C mov dword ptr [ebp-40h],edx
0066087F nop
Sealed sealedClass = new Sealed();
00660880 mov ecx,584E1Ch
00660885 call 005730F4
0066088A mov dword ptr [ebp-4Ch],eax
0066088D mov ecx,dword ptr [ebp-4Ch]
00660890 call 00660468
00660895 mov eax,dword ptr [ebp-4Ch]
00660898 mov dword ptr [ebp-3Ch],eax
sealedClass.DoStuff();
0066089B mov ecx,dword ptr [ebp-3Ch]
0066089E cmp dword ptr [ecx],ecx
006608A0 call 00660460
006608A5 nop
Derived derivedClass = new Derived();
006608A6 mov ecx,584F3Ch
006608AB call 005730F4
006608B0 mov dword ptr [ebp-50h],eax
006608B3 mov ecx,dword ptr [ebp-50h]
006608B6 call 006604A8
006608BB mov eax,dword ptr [ebp-50h]
006608BE mov dword ptr [ebp-40h],eax
derivedClass.DoStuff();
006608C1 mov ecx,dword ptr [ebp-40h]
006608C4 mov eax,dword ptr [ecx]
006608C6 mov eax,dword ptr [eax+28h]
006608C9 call dword ptr [eax+10h]
006608CC nop
Base BaseClass = new Base();
006608CD mov ecx,584EC0h
006608D2 call 005730F4
006608D7 mov dword ptr [ebp-54h],eax
006608DA mov ecx,dword ptr [ebp-54h]
006608DD call 00660490
006608E2 mov eax,dword ptr [ebp-54h]
006608E5 mov dword ptr [ebp-44h],eax
BaseClass.DoStuff();
006608E8 mov ecx,dword ptr [ebp-44h]
006608EB mov eax,dword ptr [ecx]
006608ED mov eax,dword ptr [eax+28h]
006608F0 call dword ptr [eax+10h]
006608F3 nop
}
0066091A nop
0066091B lea esp,[ebp-0Ch]
0066091E pop ebx
0066091F pop esi
00660920 pop edi
00660921 pop ebp
00660922 ret
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.