【问题标题】:C++ - Get Starting Address of a function from Ending Address / Get size of a functionC++ - 从结束地址获取函数的起始地址/获取函数的大小
【发布时间】:2016-07-07 17:58:52
【问题描述】:

我正在使用 Visual Studio 的 /Gh 和 /GH 编译器选项来分析一堆代码。使用的两种方法是 _penter 和 _pexit,它们在被分析的代码中进入或退出函数时调用。由于我需要对特定函数进行分析/调试,因此我使用了一个已定义的数组 FuncTable,其中包含我需要检测的函数的地址,它们的名称为字符串。因此,当输入一个函数时,pStack[0] 基本上包含寄存器内容,其中包含正在执行的代码的当前行的地址。类似地,当函数退出时,pStack[0] 包含代码最后一行的地址。

问题:当输入一个函数(调用_penter)时,我得到了pStack[0]中函数第一行的地址,因此我可以通过减去a来得到函数的地址常量(-5)并将其保存到我的列表中,以便稍后在 _pexit 函数中检索。但是由于在 _pexit 中我得到了函数最后一行的地址,所以我需要找到函数的大小,以便从 pStack[0] 中的地址中减去该大小以到达函数的起始地址,然后进行比较该地址保存在我的列表中。下面贴的是代码。

void _stdcall EnterFunc0(unsigned * pStack)
{
    void      * pCaller;
    pCaller = (void *)(pStack[0] - 5); // pStack[0] is first line, -5 for function address
    Signature * funct = FuncTable;

    while (funct->function)
    {
        const BYTE * func = (const BYTE *)funct->function;
        if ((func == (const BYTE *)pCaller) || ((*func == 0xE9) && ((func + *(DWORD *)(func + 1) + 5) == (const BYTE *)pCaller)))
        {
            Stack_Push(funct->name, funct->returnType, true, pCaller);          
        }
        funct++;
    }

}

extern "C" __declspec(naked) void __cdecl _penter()
{
    _asm
    {
        pushad              // save all general purpose registers
        mov    eax, esp     // current stack pointer
        add    eax, 32      // stack pointer before pushad
        push   eax          // push pointer to return address as parameter to EnterFunc0

        call   EnterFunc0

        popad               // restore general purpose registers
        ret                 // start executing original function
    }
}
void _stdcall ExitFunc0(unsigned * pStack)
{
    if (startRecording) 
    {
        StackEntry * start = top;
        while (start != NULL)
        {
            //**HERE I NEED TO COMPARE THE ADDRESS OF THE FUNCTION WITH THE ONE ALREADY IN MY STACK**
                            if ((void *)(pStack[0] - sizeOfTheFunction) == start->Address)
            {
                OutputDebugString("Function Found\n");
            }
            start = start->next;
        }
    }

}
extern "C" __declspec(naked) void __cdecl _pexit()
{
    _asm
    {
        pushad              // save all general purpose registers
        mov    eax, esp     // current stack pointer
        add    eax, 32      // stack pointer before pushad
        push   eax          // push pointer to return address as parameter to EnterFunc0

        call   ExitFunc0

        popad               // restore general purpose registers
        ret                 // start executing original function
    }
}

【问题讨论】:

  • C 不支持 methods,只支持 _functions。但是extern "C" 不是有效的 C,而是不同的语言 C++。使用正确的标签。

标签: c++ visual-studio profiling compiler-options


【解决方案1】:

您已经知道 _pexit() 函数中的地址,它是在 _penter() 函数中传递给您的。您所要做的就是支持嵌套函数调用。 std::stack 对此有好处。使用 push() 将地址保存在 _penter 中,在 _pexit 函数中使用 top() 检索它并调用 pop()。

再也不需要知道函数体的大小了。

【讨论】:

    【解决方案2】:

    由于编译器确保在每个函数的开头和结尾调用_penter_pexit,您可以确定在调用_pexit 时,由@ 创建的堆栈顶部的函数指针987654325@ 始终指向当前函数。不用找了。

    (这应该是正确的,除非您手动调用其中一个函数,这是您不应该这样做的,或者有一个多线程程序。在后一种情况下,您应该为每个线程创建一个私有堆栈。当然,您还必须将Stack_Pop 调用添加到_pexit,但我认为您无论如何都打算这样做。)

    【讨论】:

    • 这种方法的问题是不是每个函数都被跟踪,而且由于我只跟踪_penter中的特定函数,所以在_pexit中我需要知道它是否确实是我在_penter中跟踪的函数。不幸的是,我不能为每个线程创建一个私有堆栈,该库应该与可以是单线程或多线程的应用程序链接。而且我认为我无法访问所链接的进程库的线程。
    • @TahaRehmanSiddiqui 这并不禁止使用(第二个)堆栈来跟踪堆栈(双关语)。另外,我不明白你为什么couldn't use TLS
    【解决方案3】:

    原来我看错了问题。我通过使用 dbghelp.lib 中的 SymFromAddress 方法获取符号信息解决了这个问题。一旦我得到方法的名称,我就可以将它与我存储在 _penter 中的信息进行比较。

    SYMBOL_INFO  * mysymbol;
    HANDLE         process;
    char           temp[MAX_TEMP_LENGTH] = "                                                                       ";
    
    process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);
    
    mysymbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
    mysymbol->MaxNameLen = 255;
    mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    SymFromAddr(process, (DWORD64)((void *)pStack[0]), 0, mysymbol);
    OutputDebugString(mysymbol->Name);
    OutputDebugString("\n");
    

    【讨论】:

    • 好吧,我只是测试了一段代码(模拟器)。它正在使用多线程代码(可能是因为我在链接到多线程进程的库中使用它)。但是,它正在将执行速度减慢到无法忍受的程度。所以我还在寻找方法。 @Phillip,由于某种原因,如果我使用一个堆栈,当它应该有一些条目时,它会变空。只是为了确认一下,你的意思是我推入 _penter 并弹出 _pexit?
    • 没错。只要你无条件地这样做,永远不要手动调用这两个函数中的任何一个,并确保你没有线程问题,这不应该发生。
    • 您可以缓存SymFromAddr 的结果,只有在获得新地址时才查找符号。这应该可以避免您的大部分性能受到影响。您还应该在对SymFromAddr 的调用周围使用锁定,因为这不是线程安全的(仅仅因为它似乎与多个线程一起工作并不能证明这样做是安全的 - 您需要两个线程在功能可能会发现问题)。
    【解决方案4】:

    Configuration Properties > C/C++ > Command Line

    将编译器选项添加到Additional Options

    像这样

    为 _penter 钩子添加标志 /Gh
    为 _pexit 钩子添加标志 /GH

    我用于跟踪/记录的代码

    #include <intrin.h>
    
    extern "C"  void __declspec(naked) __cdecl _penter(void) {
        __asm {
            push ebp;               // standard prolog
            mov ebp, esp;
            sub esp, __LOCAL_SIZE
            pushad;                 // save registers
        }
        // _ReturnAddress always returns the address directly after the call, but that is not the start of the function!
        PBYTE addr;
        addr = (PBYTE)_ReturnAddress() - 5;
    
        SYMBOL_INFO* mysymbol;
        HANDLE       process;
        process = GetCurrentProcess();
        SymInitialize(process, NULL, TRUE);
        mysymbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
        mysymbol->MaxNameLen = 255;
        mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
        SymFromAddr(process, (DWORD64)((void*)addr), 0, mysymbol);
        myprintf("Entered Function: %s [0x%X]\n", mysymbol->Name, addr);
    
        _asm {
            popad;              // restore regs
            mov esp, ebp;       // standard epilog
            pop ebp;
            ret;
        }
    }
    

    extern "C"  void __declspec(naked) __cdecl _pexit(void) {
        __asm {
            push ebp;               // standard prolog
            mov ebp, esp;
            sub esp, __LOCAL_SIZE
            pushad;                 // save registers
        }
        // _ReturnAddress always returns the address directly after the call, but that is not the start of the function!
        PBYTE addr;
        addr = (PBYTE)_ReturnAddress() - 5;
    
        SYMBOL_INFO* mysymbol;
        HANDLE       process;
        process = GetCurrentProcess();
        SymInitialize(process, NULL, TRUE);
        mysymbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
        mysymbol->MaxNameLen = 255;
        mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
        SymFromAddr(process, (DWORD64)((void*)addr), 0, mysymbol);
        myprintf("Exit Function: %s [0x%X]\n", mysymbol->Name, addr);
    
        _asm {
            popad;              // restore regs
            mov esp, ebp;       // standard epilog
            pop ebp;
            ret;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-25
      • 1970-01-01
      • 2011-05-03
      • 1970-01-01
      • 2016-01-09
      相关资源
      最近更新 更多