获取来电者的回信地址
Getting the caller's Return Address
我正在尝试弄清楚如何在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
输出:return address of caller_function: 0x15AFA70
在 Windows 中,您可以使用RtlCaptureStackBackTrace
或RtlWalkFrameChain
安全地执行此操作,而无需依赖调试模式代码生成。 在评论中查看RBMN的答案
在 GNU C/C++(文档)中,等效项是void * __builtin_return_address (unsigned int level)
. 所以__builtin_return_address(0)
得到你自己的,__builtin_return_address(1)
得到你父母的。 该手册警告说,在 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 编译器资源管理器),因为 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内联结构。尽管编译器允许一整套内部函数来处理这个问题。
从上面给出的例子来看,这里使用的调用召集__cdecl
没有正确的顺序。这是当前MVSC++编译器代码规范中应该有的样子。
// getting our own return address is easy, and should always work
// using inline asm at all forces MSVC to set up EBP as a frame pointer even with optimization enabled
// But this function might still inline into its caller
void __cdecl *get_own_retaddr()
{
// 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
}
这同样适用于上面提供的其他示例。
- 将数组的地址分配给变量并删除
- 空基优化子对象的地址
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 如何在c++程序中找到函数的地址
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 被解释为低级别const的const对象的地址
- 将地址分配给本地指针后,公共对象的变量将消失
- 为什么我在leetcode上收到AddressSanitizer:地址0x602000000058上的堆缓冲区溢出错误
- 内联程序集printf将整数解释为地址
- 为什么指针不写入类的地址?
- 如何在C++中获取该对象的类声明中对象的地址?
- 通过按地址访问变量
- 当一个新对象被分配到它的地址时,对象是否必须被销毁
- 函数名是c中该函数的第一条指令的地址吗
- Visual Studio(或任何其他工具)能否将地址解释为调用堆栈(boost上下文)的开头
- ReadProcessMemory() 不适用于像 0x2840C6C68D8 这样的长地址
- CUDA:统一内存和指针地址的更改
- 当我们从/tp地址中添加/减去一个整数时会发生什么
- 获取来电者的回信地址