【发布时间】:2021-01-17 00:42:47
【问题描述】:
GNU return address documentation 声明 __builtin_return_address(1) 产生当前函数调用者的返回地址。
有人可以详细说明这个描述的含义吗?在做了一些测试后,我意识到它似乎并没有按照我的预期做(所以我可能没有正确理解它)。
例如,我制作了以下非常简单的测试代码,以进一步了解此功能(以及其他功能)的工作原理:
#include <stdio.h>
#include <stdint.h>
void foo(uint64_t x, uint64_t y){
void *ptr = __builtin_extract_return_addr(__builtin_return_address(0));
void *ptr2 = __builtin_extract_return_addr(__builtin_return_address(1));
printf("ret_addr(0)=%p\nret_addr(1)=%p\n", ptr, ptr2);
return;
}
int main(int argc, char **argv)
{
foo(1,1);
foo(1,1);
}
反汇编代码(供参考)
0000000000400536 <main>:
400536: 55 push %rbp
------- skipped -------
40054f: e8 93 ff ff ff callq 4004e7 <foo>
400554: be 01 00 00 00 mov $0x1,%esi
------- skipped -------
40055e: e8 84 ff ff ff callq 4004e7 <foo>
400563: b8 00 00 00 00 mov $0x0,%eax
------- skipped -------
执行此代码后,会输出以下内容:
ret_addr(0)=0x400554
ret_addr(1)=0x7f609c67cb97
ret_addr(0)=0x400563
ret_addr(1)=0x7f609c67cb97
因此,我有点困惑,想请教一下。
我可以看到 __builtin_return_address(0) [ret_addr(0)] 工作正常,因为它返回正确的返回地址值 0x400554 和 0x400563。
但是,对于__bulitin_return_address(1) [ret_addr(1)],返回值不应该是0x40054f和0x40055e吗?因为这两个地址是反汇编代码中显示的callq 4004e7 <foo>指令(这是我从描述中理解的)。
相反,我得到了一些 0x7f609c67cb97 的垃圾值,这两个 foo 函数的值相同,即使对于垃圾值,我也希望两者不同。
所以总结一下,__builtin_return_address(1) 函数的目的是什么?是否应该返回当前函数调用者的确切地址? (而不是简单地找到返回地址)。如果没有,是否有可能找到这样的地址? (我觉得这可能有点太难了)
我相信我的问题与此类似:Getting the caller's Return Address。
【问题讨论】:
-
此函数仅在所有涉及的堆栈帧都使用帧指针设置时才能正常工作(即,如果您的代码使用
-fno-omit-frame-pointer编译)。如果不能保证,则需要使用 libunwind 之类的工具来评估程序元数据以查找堆栈帧。除此之外,您还可以使用 glibc 中的backtrace函数。 -
参数是嵌套级别。
(1)为您提供调用者的调用者,即在调用您的main的 C 库函数中,因此两次调用都是相同的。手册说:“值为 0 产生当前函数的返回地址,值为 1 产生当前函数调用者的返回地址,依此类推。”. -
@fuz 感谢您对 libunwind 和 backtrace 的建议,我会看看它们。
-
@Jester 啊,我明白我在哪里误解了。非常感谢您的澄清,我在我的 foo 函数中添加了另一个 bar 的调用函数(现在在 foo 中调用 bar),我能够理解它的含义。