获取调用者的返回地址 [英] Getting the caller's Return Address

查看:49
本文介绍了获取调用者的返回地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想弄清楚如何在 MSVC 中获取调用者的返回地址.我可以使用 _ReturnAddress() 来获取我的函数的返回地址,但我似乎无法找到获取调用者的方法.

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.

我尝试过使用 CaptureStackBackTrace,但由于某种原因,它在多次调用后崩溃.我也更喜欢通过内联汇编的解决方案.

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

输出:caller_function 的返回地址:0x15AFA70

推荐答案

在 Windows 中,您可以使用 RtlCaptureStackBackTraceRtlWalkFrameChain安全地> 不依赖调试模式代码生成.请参阅RbMn 在评论中的回答

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

在 GNU C/C++(docs)中,等效的是
void * __builtin_return_address (unsigned int level).所以 __builtin_return_address(0) 得到你自己的,__builtin_return_address(1) 得到你父母的.该手册警告说,参数 0 的 arg 只有 100% 安全,并且可能会因更高的值而崩溃,但许多平台确实有可以使用的堆栈展开元数据.

In GNU C / C++ (docs), the equivalent is
void * __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.

如果有保留的调用堆栈(即在调试版本中或不存在优化时)并考虑将 MSVC x86 作为目标 PE,您可以执行以下操作:

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
}

在调试版本中,当在编译器上禁用优化(MSVC 编译器参数:/Od)并且未省略帧指针时(MSVC 编译器参数:/Oy->) 对cdecl 函数的函数调用将始终将返回地址保存在被调用者堆栈帧的偏移量+4 处.寄存器 EBP 存储正在运行的函数的堆栈帧的头部.所以在上面的代码中 foo 将返回其调用者的返回地址.

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.

启用优化后,即使这样也会中断:它可以内联到调用者中,并且 MSVC 甚至不会将 EBP 设置为该函数的帧指针(Godbolt 编译器资源管理器),因为 asm 不引用任何 C 局部变量.一个使用 mov eax, [esp]naked 函数;ret 会可靠地工作.

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

需要注意的是,这要求调用者将 EBP 设置为具有传统堆栈帧布局的帧指针.这不是现代操作系统中调用约定或 ABI 的一部分;异常的堆栈展开使用不同的元数据.但如果调用方禁用优化,则会出现这种情况.

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.

正如 Michael Petch 所指出的,MSVC 不允许在 x86-64 C/C++ 代码上使用 asm inline 构造.尽管编译器允许一整套 内在函数来解决这个问题.

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.

这篇关于获取调用者的返回地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆