Taha Rehman Siddiqui
Taha Rehman Siddiqui

Reputation: 2533

C++ - Get Starting Address of a function from Ending Address / Get size of a function

I am using /Gh and /GH compiler option of visual studio to profile a bunch of code. Two methods used are _penter and _pexit which are called when a function is entered or exited in the code being profiled. Since I need specific functions being profiled/debugged, I use an already defined array FuncTable which contains the addresses of the functions I need to instrument with their names as string. So when a function is entered, the pStack[0], which basically contains the register contents, contains the address to current line of the code being executed. Similarly when a function is exited, pStack[0] contains the address of last line of the code.

THE ISSUE: When a function is entered (_penter is called), I get the address of first line of the function in pStack[0] and hence I can get the function's address by subtracting a constant(-5) and save that to my list to be retrieved later in the _pexit function. But since in _pexit I am getting address to the last line of the function, I need to find the size of the function in order to subtract that size from the address in pStack[0] to reach the starting address of the function and then compare that address to the ones saved in my list. Pasted below is the code.

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
    }
}

Upvotes: 3

Views: 1649

Answers (4)

SSpoke
SSpoke

Reputation: 5836

Configuration Properties > C/C++ > Command Line

Add compiler option to Additional Options box

Like so example settings

Add flag /Gh for _penter hook
Add flag /GH for _pexit hook

Code I use for tracing / logging

#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;
    }
}

Upvotes: 0

Hans Passant
Hans Passant

Reputation: 941455

You already know the address in your _pexit() function, it was handed to you in the _penter() function. All you have to do is support nested function calls. An std::stack<> is good for that. Use push() to save the address in _penter, retrieve it with top() in your _pexit function and call pop().

No need to know the function body size at all anymore.

Upvotes: 2

Taha Rehman Siddiqui
Taha Rehman Siddiqui

Reputation: 2533

It turns out I was looking at the problem the wrong way. I solved the problem by getting symbol information using SymFromAddress method, from dbghelp.lib. Once I got the name of the method, I was able to compare it with the information that I stored in _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");

Upvotes: 0

Phillip
Phillip

Reputation: 13668

Since the compiler ensures that _penter and _pexit are called at the start and end of every function, you can be certain that upon a call to _pexit, the function pointer on top of the stack created by _penter always points to the current function. There is no need to search for it.

(This should be true unless you call one of the functions manually, which you shouldn't do, or have a multi-threaded program. In the latter case you should create a private stack per thread. Of course, you'll also have to add a Stack_Pop invocation to _pexit, but I assume that you planned on doing that anyway.)

Upvotes: 0

Related Questions