Update This answer is now obsolete for recent version of .NET: see here How to get current value of EIP in managed code?
The real short answer is: the CLR VM is a stack machine, so no EIP there.
The slightly longer answer is: if you rely on undocumented implementation-specific details, you could extrapolate a useable ID from the CPU EIP in unmanaged code.
Proof Of Concept
I just managed the following proof of concept, using mono 2.11 on Linux 32-bit. I hope the information might help. This implements unmanaged functions:
extern static string CurrentMethodDisplay();
extern static uint CurrentMethodAddress();
Native source: tracehelper.c [1]:
#include <string.h>
void* CurrentMethodAddress()
{
void* ip;
asm ("movl 4(%%ebp),%0" : "=r"(ip) );
return ip;
}
const char* const MethodDisplayFromAddress(void* ip);
const char* const CurrentMethodDisplay()
{
return MethodDisplayFromAddress(CurrentMethodAddress());
}
#ifndef USE_UNDOCUMENTED_APIS
extern char * mono_pmip (void *ip);
const char* const MethodDisplayFromAddress(void* ip)
{
const char* text = mono_pmip(ip);
return strdup(text? text:"(unknown)");
}
#else
/*
* undocumented structures, not part of public API
*
* mono_pmip only returns a rather ugly string representation of the stack frame
* this version of the code tries establish only the actual name of the method
*
* mono_pmip understands call trampolines as well, this function skips those
*/
struct _MonoDomain; // forward
struct _MonoMethod; // forward
typedef struct _MonoDomain MonoDomain;
typedef struct _MonoMethod MonoMethod;
struct _MonoJitInfo { MonoMethod* method; /* rest ommitted */ };
typedef struct _MonoJitInfo MonoJitInfo;
MonoDomain *mono_domain_get(void);
char* mono_method_full_name(MonoMethod *method, int signature);
MonoJitInfo *mono_jit_info_table_find(MonoDomain *domain, char *addr);
const char* const MethodDisplayFromAddress(void* ip)
{
MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get(), ip);
const char* text = ji? mono_method_full_name (ji->method, 1) : 0;
return text? text:strdup("(unknown, trampoline?)");
}
#endif
C# Source (client.cs) to call this native library function:
using System;
using System.Runtime.InteropServices;
namespace PoC
{
class MainClass
{
[DllImportAttribute("libtracehelper.so")] extern static string CurrentMethodDisplay();
[DllImportAttribute("libtracehelper.so")] extern static uint CurrentMethodAddress();
static MainClass()
{
Console.WriteLine ("TRACE 0 {0:X8} {1}", CurrentMethodAddress(), CurrentMethodDisplay());
}
public static void Main (string[] args)
{
Console.WriteLine ("TRACE 1 {0:X8} {1}", CurrentMethodAddress(), CurrentMethodDisplay());
{
var instance = new MainClass();
instance.OtherMethod();
}
Console.WriteLine ("TRACE 2 {0:X8} {1}", CurrentMethodAddress(), CurrentMethodDisplay());
{
var instance = new MainClass();
instance.OtherMethod();
}
Console.WriteLine ("TRACE 3 {0:X8} {1}", CurrentMethodAddress(), CurrentMethodDisplay());
Console.Read();
}
private void OtherMethod()
{
ThirdMethod();
Console.WriteLine ("TRACE 4 {0:X8} {1}", CurrentMethodAddress(), CurrentMethodDisplay());
}
private void ThirdMethod()
{
Console.WriteLine ("TRACE 5 {0:X8} {1}", CurrentMethodAddress(), CurrentMethodDisplay());
}
}
}
Compile and link using Makefile:
CFLAGS+=-DUSE_UNDOCUMENTED_APIS
CFLAGS+=-fomit-frame-pointer
CFLAGS+=-save-temps
CFLAGS+=-g -O3
all: client.exe libtracehelper.so
client.exe: client.cs | libtracehelper.so
gmcs -debug+ -optimize- client.cs
tracehelper.s libtracehelper.so: tracehelper.c
gcc -shared $(CFLAGS) -lmono -o $@ tracehelper.c
# gcc -g -O0 -shared -fomit-frame-pointer -save-temps -lmono -o $@ tracehelper.c
test: client.exe
LD_LIBRARY_PATH=".:..:/opt/mono/lib/" valgrind --tool=memcheck --leak-check=full --smc-check=all --suppressions=mono.supp mono --gc=sgen --debug ./client.exe
clean:
rm -fv *.so *.exe a.out *.[iso] *.mdb
Running this with LD_LIBRARY_PATH=. ./client.exe
results in:
TRACE 0 B57EF34B PoC.MainClass:.cctor ()
TRACE 1 B57EF1B3 PoC.MainClass:Main (string[])
TRACE 5 B57F973B PoC.MainClass:ThirdMethod ()
TRACE 4 B57F96E9 PoC.MainClass:OtherMethod ()
TRACE 2 B57EF225 PoC.MainClass:Main (string[])
TRACE 5 B57F973B PoC.MainClass:ThirdMethod ()
TRACE 4 B57F96E9 PoC.MainClass:OtherMethod ()
TRACE 3 B57EF292 PoC.MainClass:Main (string[])
Note that this is on Mono 2.11. It works on 2.6.7 as well, with and without optimization.
[1] I learned GNU extended asm for this purpose; thanks SO!
Conclusions ?
Delivered a proof of concept; this implementation is specific to Mono. A similar 'trick' could be delivered on MS .Net (using a ::LoadLibrary of SOS.dll, perhaps?) but is left as an exercise for the reader :)
I would personally still go with my other answer, but I suppose I succumbed to the challenge and, like I've said before: YMMV, Here be dragons, TIMTOWTDI, KISS etc.
Good night