C++ C++;-从结束地址获取函数的开始地址/获取函数的大小

C++ C++;-从结束地址获取函数的开始地址/获取函数的大小,c++,visual-studio,profiling,compiler-options,C++,Visual Studio,Profiling,Compiler Options,我正在使用VisualStudio的/Gh和/Gh编译器选项来分析一组代码。使用的两种方法是_penter和_pexit,当在所分析的代码中输入或退出函数时调用它们。因为我需要分析/调试特定的函数,所以我使用一个已经定义的数组FuncTable,其中包含我需要以字符串形式插入的函数的地址。因此,当输入一个函数时,pStack[0]基本上包含寄存器内容,它包含正在执行的代码的当前行的地址。类似地,当函数退出时,pStack[0]包含代码最后一行的地址 void _stdcall EnterFunc

我正在使用VisualStudio的/Gh和/Gh编译器选项来分析一组代码。使用的两种方法是_penter和_pexit,当在所分析的代码中输入或退出函数时调用它们。因为我需要分析/调试特定的函数,所以我使用一个已经定义的数组FuncTable,其中包含我需要以字符串形式插入的函数的地址。因此,当输入一个函数时,pStack[0]基本上包含寄存器内容,它包含正在执行的代码的当前行的地址。类似地,当函数退出时,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
    }
}
问题:当输入一个函数时(_penter被调用),我在pStack[0]中得到函数第一行的地址,因此我可以通过减去一个常量(-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
    }
}

由于编译器确保在每个函数的开始和结束时调用
\u penter
\u pexit
,因此可以确定在调用
\u pexit
时,
\u penter
创建的堆栈顶部的函数指针始终指向当前函数。没有必要搜索它


(除非您手动调用其中一个函数(您不应该这样做),或者使用多线程程序,否则这应该是正确的。在后一种情况下,您应该为每个线程创建一个私有堆栈。当然,您还必须为
\u pexit
添加一个
堆栈\u Pop
调用,但我假设您已经计划好了这样做。)

事实证明,我对问题的看法是错误的。通过使用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");

您已经知道_pexit()函数中的地址,它是在_penter()函数中传递给您的。您所要做的就是支持嵌套函数调用。一个std::stack就可以做到这一点。使用push()将地址保存在_penter中,使用_pexit函数中的top()检索地址并调用pop()


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

配置属性>C/C++>命令行

将编译器选项添加到
附加选项

像这样

为_penterhook添加标志/Gh
为_pexithook添加标志/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;
    }
}

C不支持方法,只支持_函数。但是<>代码>外部“C”<代码>不是有效的C,而是不同的语言C++。使用正确的标记。这种方法的问题是,并不是每个函数都被跟踪,因为我只跟踪_penter中的特定函数,所以在_pexit中,我需要知道它是否确实是我在_penter中跟踪的函数。不幸的是,我不能为每个线程创建一个私有堆栈,库应该与可以是单线程或多线程的应用程序链接。而且我认为我无法访问进程库链接到的线程。@TahaRehmanSiddiqui不禁止使用(第二个)堆栈跟踪堆栈(双关语)。另外,我不明白你为什么这么做。请注意文档。我只是测试了一段代码(模拟器)。它使用多线程代码(可能是因为我在链接到多线程进程的库中使用它)。然而,它正在将执行速度放缓到一个无法忍受的程度。所以我仍然在寻找一种方法@Phillip,出于某种原因,如果我使用一个堆栈,当它应该有一些条目时,堆栈将变空。我只是想确认一下,对于stack,你的意思是我把penter推进去然后把pexit放进去?没错。只要您无条件地执行此操作,切勿手动调用这两个函数中的任何一个,并确保没有线程问题,就不会发生这种情况。您可以缓存
SymFromAddr
的结果,并且仅在获得新地址时才查找符号。这样可以避免大部分性能损失。您还应该在调用
SymFromAddr
时使用锁,因为这不是线程安全的(仅仅因为它似乎与多个线程一起工作并不能证明这样做是安全的——您可能需要在函数中使用两个活跃的线程才能看到问题)。