I am trying to figure out how to grab the return address of a caller in MSVC. I can use _ReturnAddress() to get the return address of my function, but I can't seem to find a way to get the caller's.
I've tried using CaptureStackBackTrace, but for some reason, it crashes after many, many calls. I would also prefer a solution via inline assembly.
void my_function(){
cout << "return address of caller_function: " << [GET CALLER'S RETURN VALUE];
} // imaginary return address: 0x15AF7C0
void caller_function(){
my_function();
}// imaginary return address: 0x15AFA70
Output:
return address of caller_function: 0x15AFA70
In Windows, you can use RtlCaptureStackBackTrace
or RtlWalkFrameChain
to do this safely without relying on debug-mode code-gen. See RbMn's answer in comments
In GNU C / C++ (docs), the equivalent isvoid * __builtin_return_address (unsigned int level)
. So __builtin_return_address(0)
to get your own, __builtin_return_address(1)
to get your parent's. The manual warns that it's only 100% safe with an arg of 0
and might crash with higher values, but many platforms do have stack-unwind metadata that it can use.
If there is a preserved call stack (i.e. on debug builds or when optimizations are not present) and considering MSVC x86 as target PE you could do something like:
void *__cdecl get_own_retaddr_debugmode()
{
// consider you can put this asm inline snippet inside the function you want to get its return address
__asm
{
MOV EAX, DWORD PTR SS:[EBP + 4]
}
// fall off the end of a non-void function after asm writes EAX:
// supported by MSVC but not clang's -fasm-blocks option
}
On debug builds, when optimization are disabled on the compiler (MSVC compiler argument: /Od
) and when frame pointer is not omitted (MSVC compiler argument: /Oy-
) function calls to cdecl
functions will always save the return address at the offset +4
of the callee stack frame. The register EBP
stores the head of the running function's stack frame. So in the code above foo
will return the return address of its caller.
With optimization enabled, even this breaks: it can inline into the caller, and MSVC doesn't even set up EBP as a frame pointer for this function (Godbolt compiler explorer) because the asm doesn't reference any C local variables. A naked
function that used mov eax, [esp]
; ret
would work reliably.
By reading again your question I think you might want the return address of the caller of the caller. You can do this by accessing the immediate caller's stack frame and then getting its return address. Something like this:
// only works if *the caller* was compiled in debug mode
// as well as this function
void *__cdecl get_caller_retaddr_unsafe_debug_mode_only()
{
__asm
{
MOV ECX, DWORD PTR SS:[EBP + 0] // [EBP+0] points to caller stack frame pointer
MOV EAX, DWORD PTR SS:[ECX + 4] // get return address of the caller of the caller
}
}
It is important to note that this requires the caller to have set up EBP as a frame pointer with the traditional stack-frame layout. This isn't part of the calling convention or ABI in modern OSes; stack unwinding for exceptions uses different metadata. But it will be the case if optimization is disabled for the caller.
As noted by Michael Petch, MSVC doesn't allow the asm inline construct on x86-64 C/C++ code. Despite that the compiler allows a whole set of intrinsic functions to deal with that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With