我想弄清楚如何在 MSVC 中获取调用者的返回地址。我可以使用 _ReturnAddress() 来获取函数的返回地址,但我似乎无法找到获取调用方地址的方法。
我试过使用 CaptureStackBackTrace,但出于某种原因,它在多次调用后崩溃了。我也更喜欢通过内联汇编的解决方案。
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
输出:
caller_function 的返回地址:0x15AFA70
最佳答案
在 Windows 中,您可以使用 RtlCaptureStackBackTrace
或 RtlWalkFrameChain
来安全执行此操作,而无需依赖 Debug模式代码生成。参见 RbMn's answer in comments
在 GNU C/C++ ( docs ) 中,等价于
void * __builtin_return_address(unsigned int 级别)
。所以 __builtin_return_address(0)
得到你自己的,__builtin_return_address(1)
得到你 parent 的。该手册警告说,只有 arg 为 0
时它才 100% 安全,并且可能会因更高的值而崩溃,但许多平台确实具有它可以使用的堆栈展开元数据。
仅限 MSVC 32 位调试/未优化构建
如果有一个保留的调用堆栈(即在调试构建或优化不存在时)并考虑将 MSVC x86 作为目标 PE,您可以执行以下操作:
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
}
在调试版本中,当优化在编译器上被禁用时(MSVC 编译器参数:/Od
)并且当帧指针未被省略时(MSVC 编译器参数:/Oy-
>) 对cdecl
函数的函数调用将始终将返回地址保存在被调用堆栈帧的偏移量+4
处。寄存器 EBP
存储运行函数的堆栈帧的头部。所以在上面的代码中,foo
将返回其调用者的返回地址。
启用优化后,即使这样也会中断:它可以内联到调用者中,并且 MSVC 甚至没有将 EBP 设置为该函数 (Godbolt compiler explorer) 的帧指针,因为 asm 没有' 引用任何 C 局部变量。使用 mov eax, [esp]
的 naked
函数; ret
将可靠地工作。
通过再次阅读您的问题,我认为您可能需要来电者的来电者的返回地址。您可以通过访问直接调用者的堆栈帧然后获取其返回地址来执行此操作。像这样:
// 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
}
}
重要的是要注意,这需要调用者将 EBP 设置为具有传统堆栈帧布局的帧指针。这不是现代操作系统中调用约定或 ABI 的一部分;异常的堆栈展开使用不同的元数据。但如果为调用者禁用了优化,就会出现这种情况。
正如 Michael Petch 所指出的,MSVC 不允许在 x86-64 C/C++ 代码上使用 asm inline 构造。尽管如此,编译器允许一整套 intrinsic functions 来处理这个问题。
关于c++ - 获取调用者的返回地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57621889/