why to use ebp in function prologue/epilogue?

后端 未结 3 972
长情又很酷
长情又很酷 2020-12-19 04:26

Some time ago I was experimenting with writing assembly routines and linking it with C programs and I found that I just can skip standard C-call prologue epilogue



        
相关标签:
3条回答
  • 2020-12-19 04:43

    The use of EBP is of great help when debugging code, as it allows debuggers to traverse the stack frames in a call chain.

    It [creates] a singly linked list that linked the frame pointer for each of the callers to a function. From the EBP for a routine, you could recover the entire call stack for a function.

    See http://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames
    And in particular the page it links to which covers your question: http://blogs.msdn.com/b/larryosterman/archive/2007/03/12/fpo.aspx

    0 讨论(0)
  • 2020-12-19 04:45

    It works, However, once you'll get an interrupt, the processor will push all it's registers and flags into the stack, overwriting your value. The stack is there for a reason, use it...

    0 讨论(0)
  • 2020-12-19 04:59

    There's no requirement to use a stack frame, but there are certainly some advantages:

    Firstly, if every function has uses this same process, we can use this knowledge to easily determine a sequence of calls (the call stack) by reversing the process. We know that after a call instruction, ESP points to the return address, and that the first thing the called function will do is push the current EBP and then copy ESP into EBP. So, at any point we can look at the data pointed to by EBP which will be the previous EBP and that EBP+4 will be the return address of the last function call. We can therefore print the call stack (assuming 32bit) using something like (excuse the rusty C++):

    void LogStack(DWORD ebp)
    {
        DWORD prevEBP = *((DWORD*)ebp);
        DWORD retAddr = *((DWORD*)(ebp+4));
    
        if (retAddr == 0) return;
    
        HMODULE module;
        GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*)retAddr, &module);
        char* fileName = new char[256];
        fileName[255] = 0;
        GetModuleFileNameA(module, fileName, 255);
        printf("0x%08x: %s\n", retAddr, fileName);
        delete [] fileName;
        if (prevEBP != 0) LogStack(prevEBP);
    }
    

    This will then print out the entire sequence of calls (well, their return addresses) up until that point.

    Furthermore, since EBP doesn't change unless you explicitly update it (unlike ESP, which changes when you push/pop), it's usually easier to reference data on the stack relative to EBP, rather than relative to ESP, since with the latter, you have to be aware of any push/pop instructions that might have been called between the start of the function and the reference.

    As others have mentioned, you should avoid using stack addresses below ESP as any calls you make to other functions are likely to overwrite the data at these addresses. You should instead reserve space on the stack for use by your function by the usual:

    sub esp, [number of bytes to reserve]
    

    After this, the region of the stack between the initial ESP and ESP - [number of bytes reserved] is safe to use. Before exiting your function you must release the reserved stack space using a matching:

    add esp, [number of bytes reserved]
    
    0 讨论(0)
提交回复
热议问题