如何获得EIP的管理code当前值?如何获得、EIP、code

2023-09-04 00:40:46 作者:玩挵ブ掵運

这个问题似乎是一个肮脏的黑客,你不应该做的,但让我先解释一下。最终的目标是有方法的局部静态像C ++。

 无效Func键()
{
   静态methodLocalObject =新ExpensiveThing();
   //使用methodlocal对象
}
 

什么有这个做的指令指针?我想这取决于我的来电来缓存数据。为了使这一快,我走回堆栈中拿到手机号码的地址,并以此作为唯一键的字典来存储数据。 这将允许以创建反射基于示踪剂不使用反射每次来得到当前的方法和类型,但只有一次的名称,并存储该反射的相关信息在哈希表中。

答案迄今只单为主。我想尝试一个通用的解决方案,适用于.NET 3.5 / 4.0 32/64位。我不知道,调用约定 64位是完全不同的,因此可以得到具有挑战性得到的东西可靠。但在另一方面,我有我的方法是如何栈看起来像里面的完全控制。堆栈看起来与.NET 3.5和4.0非常不同的,它不同于当然也与发布版本。我还是要检查是否NGEN并创造code有不同的堆栈布局为好。一种可能性是使用C ++的辅助方法,这需要5魔整型参数(在64只5日将在堆栈上),并检查在哪里可以找到他们在堆栈上。 另一种可能是简单地使用整个堆栈,直到我发现我的魔法标记堆栈上的一个键,并用这部分的堆栈足够独特的密钥。但我不知道,如果这种方法可以在所有的工作,如果有更好的选择。我知道我可以走栈中通过分析或调试API的安全方式,但他们都不是很快。

对于跟踪库通常的做法是使用反射来获取当前的方法名称和类型走栈。

 类跟踪器
{
    [MethodImpl(MethodImplOptions.NoInlining)
    公共示踪剂()
    {
        的StackFrame帧=新的堆栈跟踪()的getFrame(1)。 //获取主叫
        Console.WriteLine(输入方法{0} {1},frame.GetMethod()DeclaringType.FullName,frame.GetMethod()名称。);
    }

}
 

但是,这是很慢的。另一种解决方案是通过它更快字符串直接传递数据,但是它需要更多的输入。替代的解决方案是使用调用函数的指令指针(如果这可以在很快速的方式来确定),以绕过昂贵反射呼叫。 那么这将是可能的:

 类跟踪器
{
    静态字典< Int64的,串GT; _CachedMethods =新字典<的Int64,字符串和GT;();

    [MethodImpl(MethodImplOptions.NoInlining)
    公共示踪剂()
    {
        Int64的EIP = GetEIpOfParentFrame();
        字符串名称;
        锁定(_CachedMethods)
        {
            如果(!_CachedMethods.TryGetValue(EIP,出名字))
            {
                VAR callingMethod =新的堆栈跟踪()的getFrame(1).GetMethod()。
                名称= callingMethod.DeclaringType +。 + callingMethod.Name;
                _CachedMethods [EIP] =名称;
            }
        }
        Console.WriteLine(输入法{0},名称);

    }

    Int64的GetEIpOfParentFrame()
    {
        返回0; // TODO这个问题如何得到它
    }

}
 
在vsCode中如何使用git工具来管理代码

我知道,解决方案需要非托管。在C ++中有一个编译器的内部称为 _ReturnAddress 但根据该文档不与管理code工作。另一种方式来问同一个问题:是否有人知道的.NET 3.5 / 4 X32 / X64管理方法的调用约定和堆栈布局?

你的,   阿洛伊斯·克劳斯

解决方案

在C#5.0有一个新的,良好的隐藏功能,使这一点。

  

来电信息属性

注意显然,也有微软BCL便携包1.1 0.3 的NuGet包,以便您可以使用来电信息属性在.NET 4.0中。

这里做的事情,就是让你的可选参数的神奇的有无的的来电者相关的默认值的。它有

CallerFilePathAttribute 的全部包含该呼叫者的源文件的路径。这是在编译时的文件路径。 在该方法被调用在源文件中CallerLineNumberAttribute 的行号。 CallerMemberNameAttribute 方法或属性名称。请参见本主题后面 成员名称。

它有一些pretty的漂亮的功能:

来电信息值的的发出的文字到中间语言(IL)在编译时的。 不同于堆栈跟踪属性异常的结果, 的结果不受混淆的

文档示例如下:

  //使用System.Runtime.CompilerServices
//使用System.Diagnostics程序;

公共无效DoProcessing()
{
    TraceMessage(出事了。);
}

公共无效TraceMessage(字符串消息,
        [CallerMemberName]字符串成员名=,
        [CallerFilePath]字符串sourceFilePath =,
        [CallerLineNumber] INT sourceLineNumber = 0)
{
    Trace.WriteLine(信息:+消息);
    Trace.WriteLine(会员名称:+成员名);
    Trace.WriteLine(源文件路径:+ sourceFilePath);
    Trace.WriteLine(源行号:+ sourceLineNumber);
}

//示例输出:
//消息:出事了。
//会员名:DoProcessing
//源文件路径:C:\用户\用户名\文档\的Visual Studio 2012 \项目\ CallerInfoCS \ CallerInfoCS \ Form1.cs中
//源代码行数:31
 

The question seems like a dirty hack you should not do but let me explain first. The ultimate goal is to have method local statics like in C++.

void Func()
{
   static methodLocalObject = new ExpensiveThing();
   // use methodlocal Object
}

What has this to do with the instruction pointer? I want to cache data depending on my caller. To make this fast I walk back in the stack to get the address of my caller and use this as unique key for a dictionary to store the data. That would allow to create a reflection based tracer which does not use Reflection every time to get the name of the current method and type but only once and store the reflection infos in a hash table.

The answers so far were only mono based. I want to try a generic solution which works on .NET 3.5/4.0 32/64 bit. I do know that the calling convention for 64 bit is quite different so it could get challenging to get something reliable. But on the other hand I have full control inside my method how the stack does look like. The stack does look very different between .NET 3.5 and 4.0 and it differs of course also between release builds. I still have to check if NGen does create code with a different stack layout as well. One possibility would be to use a C++ helper method which takes 5 magic integer arguments (on x64 only the 5th will be on the stack) and check where I can find them on the stack. Another possiblity would be to simply use the whole stack until I find my magic marker on the stack as a key and use this part of the stack as unique enough key. But I am not sure if this approach can work at all or if there are better alternatives. I do know I can walk the stack in a safe way via the profiling or debugging apis but neither of them are fast.

For a tracing library the usual approach is to walk the stack using reflection to get the current method name and type.

class Tracer
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public Tracer()
    {
        StackFrame frame = new StackTrace().GetFrame(1); // get caller
        Console.WriteLine("Entered method {0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name);
    }

}

But this is very slow. The other solution is to pass the data directly via strings which is much faster but it needs more typing. The alternate solution would be to use the instruction pointer of the calling function (if this can be determined in a very fast way) to get around the expensive reflection calls. Then this would be possible:

class Tracer
{
    static Dictionary<Int64, string> _CachedMethods = new Dictionary<Int64, string>();

    [MethodImpl(MethodImplOptions.NoInlining)]
    public Tracer()
    {
        Int64 eip = GetEIpOfParentFrame();
        string name;
        lock (_CachedMethods)
        {
            if (!_CachedMethods.TryGetValue(eip, out name))
            {
                var callingMethod = new StackTrace().GetFrame(1).GetMethod();
                name =  callingMethod.DeclaringType + "." + callingMethod.Name;
                _CachedMethods[eip] = name;
            }
        }
        Console.WriteLine("Entered method {0}", name);

    }

    Int64 GetEIpOfParentFrame()
    {
        return 0; // todo this is the question how to get it
    }

}

I know that the solution needs to be unmanaged. In C++ there is a compiler intrinsic called _ReturnAddress but according to the docs it does not work with managed code. Another way to ask the same question: Does anybody know the calling convention and stack layout for managed methods for .NET 3.5/4 x32/x64?

Yours, Alois Kraus

解决方案

With C# 5.0 there is a new, well-hidden feature that enables this.

Caller Info attributes

Note Apparently, there is also the Microsoft BCL Portability Pack 1.1.3 Nuget package so you can use the Caller Info Attributes in .NET 4.0.

What this does, is make your optional parameters magically have caller-dependent default values. It has

CallerFilePathAttribute Full path of the source file that contains the caller. This is the file path at compile time. CallerLineNumberAttribute Line number in the source file at which the method is called. CallerMemberNameAttribute Method or property name of the caller. See Member Names later in this topic.

It has some pretty nifty features:

Caller Info values are emitted as literals into the Intermediate Language (IL) at compile time. Unlike the results of the StackTrace property for exceptions, the results aren't affected by obfuscation.

The documentation sample looks like this:

// using System.Runtime.CompilerServices 
// using System.Diagnostics; 

public void DoProcessing()
{
    TraceMessage("Something happened.");
}

public void TraceMessage(string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0)
{
    Trace.WriteLine("message: " + message);
    Trace.WriteLine("member name: " + memberName);
    Trace.WriteLine("source file path: " + sourceFilePath);
    Trace.WriteLine("source line number: " + sourceLineNumber);
}

// Sample Output: 
//  message: Something happened. 
//  member name: DoProcessing 
//  source file path: c:\Users\username\Documents\Visual Studio 2012\Projects\CallerInfoCS\CallerInfoCS\Form1.cs 
//  source line number: 31