New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting the method name of a Delegate instance #35

Open
JeffCyr opened this Issue Dec 3, 2015 · 4 comments

Comments

Projects
None yet
4 participants
@JeffCyr

JeffCyr commented Dec 3, 2015

It's difficult to find the corresponding method of a Delegate instance while debugging with clrmd, sos have the same limitation.

This blog explained how to do it with windbg + sos:
http://geekswithblogs.net/akraus1/archive/2012/05/20/149699.aspx

I translated that to ClrMD and it work with instance delegate (I think it won't work with static methods).

ulong magicPtr = ...// Delegate._methodPtr + 5;
ulong magicValue1, magicValue2;
session.Heap.ReadPointer(magicPtr+1, out magicValue1);
session.Heap.ReadPointer(magicPtr+2, out magicValue2);

ulong mysticPtr = magicPtr + 8 * (magicValue2 & 0xFF) + 3;
ulong mysticOffset = 8 * (magicValue1 & 0xFF);

ulong mysticValue;
session.Runtime.ReadPointer(mysticPtr, out mysticValue);
ulong methodDescriptorPtr = mysticValue + mysticOffset;

return (string)session.Runtime.GetType().GetMethod("GetNameForMD", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(session.Runtime, new object[] { methodDescriptorPtr });

This is very hacky and does not work with all Delegate instance, do you think a proper implementation should be implemented in ClrMD?

@leculver

This comment has been minimized.

Show comment
Hide comment
@leculver

leculver Dec 3, 2015

Contributor

I agree there should be a method in ClrMD to do the dirty work for you. I'll add that to the todo list.

Also, your last line of code you should be able to replace with this:

ClrMethod method = clrRuntime.GetMethodByHandle(methodDescriptorPtr);
return method.Name;  // or return the method itself

As for it not working on all instances...sometimes _methodPtrAux is set and it means something slightly different (I think it's the MethodDesc itself but that's based on my fuzzy memory). If you handle that case then you should be able to handle all delegate types.

In any case I agree this should be part of ClrMD itself so I'll try to get to it next time I have a chance to work on ClrMD (probably a big effort the last week of December).

Contributor

leculver commented Dec 3, 2015

I agree there should be a method in ClrMD to do the dirty work for you. I'll add that to the todo list.

Also, your last line of code you should be able to replace with this:

ClrMethod method = clrRuntime.GetMethodByHandle(methodDescriptorPtr);
return method.Name;  // or return the method itself

As for it not working on all instances...sometimes _methodPtrAux is set and it means something slightly different (I think it's the MethodDesc itself but that's based on my fuzzy memory). If you handle that case then you should be able to handle all delegate types.

In any case I agree this should be part of ClrMD itself so I'll try to get to it next time I have a chance to work on ClrMD (probably a big effort the last week of December).

@mattwarren

This comment has been minimized.

Show comment
Hide comment
@mattwarren

mattwarren May 5, 2017

As for it not working on all instances...sometimes _methodPtrAux is set and it means something slightly different (I think it's the MethodDesc itself but that's based on my fuzzy memory). If you handle that case then you should be able to handle all delegate types.

FYI this table from the CoreCLR source explains the difference usages of _methodPtr and _methodPtrAux:

    // DELEGATE KINDS TABLE
    //
    //                                  _target         _methodPtr              _methodPtrAux       _invocationList     _invocationCount
    //
    // 1- Instance closed               'this' ptr      target method           null                null                0
    // 2- Instance open non-virt        delegate        shuffle thunk           target method       null                0
    // 3- Instance open virtual         delegate        Virtual-stub dispatch   method id           null                0
    // 4- Static closed                 first arg       target method           null                null                0
    // 5- Static closed (special sig)   delegate        specialSig thunk        target method       first arg           0
    // 6- Static opened                 delegate        shuffle thunk           target method       null                0
    // 7- Secure                        delegate        call thunk              MethodDesc (frame)  target delegate     creator assembly 
    //
    // Delegate invoke arg count == target method arg count - 2, 3, 6
    // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5
    //
    // 1, 4     - MulticastDelegate.ctor1 (simply assign _target and _methodPtr)
    // 5        - MulticastDelegate.ctor2 (see table, takes 3 args)
    // 2, 6     - MulticastDelegate.ctor3 (take shuffle thunk)
    // 3        - MulticastDelegate.ctor4 (take shuffle thunk, retrieve MethodDesc) ???
    //
    // 7 - Needs special handling

As for it not working on all instances...sometimes _methodPtrAux is set and it means something slightly different (I think it's the MethodDesc itself but that's based on my fuzzy memory). If you handle that case then you should be able to handle all delegate types.

FYI this table from the CoreCLR source explains the difference usages of _methodPtr and _methodPtrAux:

    // DELEGATE KINDS TABLE
    //
    //                                  _target         _methodPtr              _methodPtrAux       _invocationList     _invocationCount
    //
    // 1- Instance closed               'this' ptr      target method           null                null                0
    // 2- Instance open non-virt        delegate        shuffle thunk           target method       null                0
    // 3- Instance open virtual         delegate        Virtual-stub dispatch   method id           null                0
    // 4- Static closed                 first arg       target method           null                null                0
    // 5- Static closed (special sig)   delegate        specialSig thunk        target method       first arg           0
    // 6- Static opened                 delegate        shuffle thunk           target method       null                0
    // 7- Secure                        delegate        call thunk              MethodDesc (frame)  target delegate     creator assembly 
    //
    // Delegate invoke arg count == target method arg count - 2, 3, 6
    // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5
    //
    // 1, 4     - MulticastDelegate.ctor1 (simply assign _target and _methodPtr)
    // 5        - MulticastDelegate.ctor2 (see table, takes 3 args)
    // 2, 6     - MulticastDelegate.ctor3 (take shuffle thunk)
    // 3        - MulticastDelegate.ctor4 (take shuffle thunk, retrieve MethodDesc) ???
    //
    // 7 - Needs special handling
@dudikeleti

This comment has been minimized.

Show comment
Hide comment
@dudikeleti

dudikeleti Jan 2, 2018

Is this still supposed to work? Because at the moment I can not make it work.
GetMethodByHandle return always null.

In the dump (x86) I have this: Func<string, bool> func = M; (M is an instance method) I take the methodPtr of func and do that:

   var methodPtrWithOffset = methodPtr + 5;
   heap.ReadPointer(methodPtrWithOffset + 2, out ulong someVal1);
   heap.ReadPointer(methodPtrWithOffset + 1, out ulong someVal2);
   heap.ReadPointer(methodPtrWithOffset + (someVal1 & 0xFF) * 8 + 3, out ulong baseMethodDesc);
   var offset = (someVal2 & 0xFF) * 8;
   var handle = baseMethodDesc + offset;
   var method = runtime.GetMethodByHandle(handle);

dudikeleti commented Jan 2, 2018

Is this still supposed to work? Because at the moment I can not make it work.
GetMethodByHandle return always null.

In the dump (x86) I have this: Func<string, bool> func = M; (M is an instance method) I take the methodPtr of func and do that:

   var methodPtrWithOffset = methodPtr + 5;
   heap.ReadPointer(methodPtrWithOffset + 2, out ulong someVal1);
   heap.ReadPointer(methodPtrWithOffset + 1, out ulong someVal2);
   heap.ReadPointer(methodPtrWithOffset + (someVal1 & 0xFF) * 8 + 3, out ulong baseMethodDesc);
   var offset = (someVal2 & 0xFF) * 8;
   var handle = baseMethodDesc + offset;
   var method = runtime.GetMethodByHandle(handle);
@dudikeleti

This comment has been minimized.

Show comment
Hide comment
@dudikeleti

dudikeleti Jan 2, 2018

It seems to work for me if I do it that way,

For x86: var offset = (someVal2 & 0xFF) * 4;
For x64: var offset = (someVal2 & 0xFF) * 8;
(or just use runtime.PointerSize)

It's works for instance (methodPtr) and for static method (methodPtrAux). Still need to check for all types @mattwarren posted above

Update
I tried it in several cases including with invocation list and it works, If somebody think of special case that I need to test, please let me know.

dudikeleti commented Jan 2, 2018

It seems to work for me if I do it that way,

For x86: var offset = (someVal2 & 0xFF) * 4;
For x64: var offset = (someVal2 & 0xFF) * 8;
(or just use runtime.PointerSize)

It's works for instance (methodPtr) and for static method (methodPtrAux). Still need to check for all types @mattwarren posted above

Update
I tried it in several cases including with invocation list and it works, If somebody think of special case that I need to test, please let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment