views:

985

answers:

4

I am in the process of porting an application from x86 to x64. I am using Visual Studio 2009; most of the code is C++ and some portions are plain C. The __asm keyword is not supported when compiling towards x64 and our application contains a few portions of inline assembler. I did not write this code so I don't know exactly what et is supposed to do:

int CallStackSize() {
    DWORD Frame;
    PDWORD pFrame;
    __asm
        {
            mov EAX, EBP
            mov Frame, EAX
        }
    pFrame = (PDWORD)Frame;
    /*... do stuff with pFrame here*/
}

EBP is the base pointer to the stack of the current function. Is there some way to obtain the stack pointer without using inline asm? I have been looking at the intrinsics that Microsoft offers as a substitute for inline asm but I could not find anything that gave me something usefull. Any ideas?

Andreas asked what stuff is done with pFrame. Here is the complete function:

int CallStackSize(DWORD frameEBP = 0)
{
    DWORD pc;
    int tmpint = 0;
    DWORD Frame;
    PDWORD pFrame, pPrevFrame;

    if(!frameEBP) // No frame supplied. Use current.
    {
        __asm
        {
            mov EAX, EBP
            mov Frame, EAX
        }
    }
    else Frame = frameEBP;

    pFrame = (PDWORD)Frame;
    do
    {
        pc = pFrame[1];
        pPrevFrame = pFrame;
        pFrame = (PDWORD)pFrame[0]; // precede to next higher frame on stack

        if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a DWORD boundary. Bail if not so.
        break;

        if (pFrame <= pPrevFrame)
        break;

        // Can two DWORDs be read from the supposed frame address?
        if(IsBadWritePtr(pFrame, sizeof(PVOID)*2))
        break;

        tmpint++;
    } while (true);
    return tmpint;
}

The variable pc is not used. It looks like this function walks down the stack until it fails. It assumes that it can't read outside the applications stack so when it fails it has measured the depth of the call stack. This code does not need to compile on _EVERY_SINGLE compiler out there. Just VS2009. The application does not need to run on EVERY_SINGLE computer out there. We have complete control of deployment since we install/configure it ourselves and deliver the whole thing to our customers.

+2  A: 

The really right thing to do would be to rewrite whatever this function does so that it does not require access to the actual frame pointer. That is definitely bad behavior.

But, to do what you are looking for you should be able to do:

int CallStackSize() {
    __int64 Frame = 0; /* MUST be the very first thing in the function */
    PDWORD pFrame;

    Frame++; /* make sure that Frame doesn't get optimized out */

    pFrame = (PDWORD)(&Frame);
    /*... do stuff with pFrame here*/
}

The reason this works is that in C usually the first thing a function does is save off the location of the base pointer (ebp) before allocating local variables. By creating a local variable (Frame) and then getting the address of if, we're really getting the address of the start of this function's stack frame.

Note: Some optimizations could cause the "Frame" variable to be removed. Probably not, but be careful.

Second Note: Your original code and also this code manipulates the data pointed to by "pFrame" when "pFrame" itself is on the stack. It is possible to overwrite pFrame here by accident and then you would have a bad pointer, and could get some weird behavior. Be especially mindful of this when moving from x86 to x64, because pFrame is now 8 bytes instead of 4, so if your old "do stuff with pFrame" code was accounting for the size of Frame and pFrame before messing with memory, you'll need to account for the new, larger size.

SoapBox
I don't think the variable can be removed if you take its address. However, the compiler is free to rearrange variables however it likes. Technically there's no language-level guarantee Frame is even located on the stack (but in practice I would expect this to work fine).
Jason Orendorff
My thought exactly, I was on my way to submit a similar answer but I stopped myself because it feels so fragile.
Andreas Brinck
What if you make it `volatile`?
jleedev
This should be extremely rare in application code, but in system code it's pretty common. Conservative garbage collectors can make use of it. Mozilla's JavaScript engine uses it for a different reason: to avoid overflowing the C stack. http://mxr.mozilla.org/mozilla-central/ident?i=JS_CHECK_STACK_SIZE
Jason Orendorff
Isn't there some kind of win32 system call you can use to get the stack size?
Earlz
A: 

If you need the precise "base pointer" then inline assembly is the only way to go.

It is, surprisingly, possible to write code that munges the stack with relatively little platform-specific code, but it's hard to avoid assembly altogether (depending on what you're doing).

If all you're trying to do is avoid overflowing the stack, you can just take the address of any local variable.

Jason Orendorff
It appears you'll have to use a dedicated assembler (ml64) to create a routine that inspects the stack pointer, discovers the address of the previous stack frame (to account for the movement in the stack pointer when calling your assembler routine), and returns it (I don't know how you'd do this). Then link it in to your C program.
PP
A: 

You can use the _AddressOfReturnAddress() intrinsic to determine a location in the current frame pointer, assuming it hasn't been completely optimized away. I'm assuming that the compiler will prevent that function from optimizing away the frame pointer if you explicitly refer to it. Or, if you only use a single thread, you can use the IMAGE_NT_HEADER.OptionalHeader.SizeOfStackReserve and IMAGE_NT_HEADER.OptionalHeader.SizeOfStackCommit to determine the main thread's stack size. See this for how to access the IMAGE_NT_HEADER for the current image.

I would also recommend against using IsBadWritePtr to determine the end of the stack. At the very least you will probably cause the stack to grow until you hit the reserve, as you'll trip a guard page. If you really want to find the current size of the stack, use VirtualQuery with the address you are checking.

And if the original use is to walk the stack, you can use StackWalk64 for that.

MSN
It's not seeing how much space is remaining, it's walking back up the chain of previous stack frames. Basically doing a backtrace.
caf
If that's the case there's an API for that: StackWalk64.
MSN
I suggest you add that as a new answer then, it sounds like it might well be what the OP needs.
caf
A: 
.code

PUBLIC getStackFrameADDR _getStackFrameADDR
getStackFrameADDR:
    mov RAX, RBP
    ret 0

END

Something like that could work for you.

Compile it with ml64 or jwasm and call it using this in your code extern "C" void getstackFrameADDR(void);

toto