如何递归使用 __builtin_return_address()?
Posted
技术标签:
【中文标题】如何递归使用 __builtin_return_address()?【英文标题】:How to use __builtin_return_address() recursively? 【发布时间】:2015-06-08 21:05:19 【问题描述】:我尝试使用 GCC 中的函数 __builtin_return_address
来学习一些关于运行时数据结构的基本概念。
我的测试代码是这样的
#include <stdio.h>
void a(int i)
if (i>0)
printf("The return address is %p\n", __builtin_return_address(0) );
a(--i);
else
return;
int main ()
a(10);
return 0;
输出是
The return address is 0x4005ee
The return address is 0x4005db
The return address is 0x4005db
The return address is 0x4005db
The return address is 0x4005db
...
所以我的问题是,为什么那些递归调用的函数的返回地址都一样,就像它们都返回到***调用者一样?它们不应该也是“递归直接调用者”吗?
【问题讨论】:
因为虽然每个函数调用的帧在堆栈上越来越低,但函数调用的每个实例都在执行相同的代码,并且返回地址是函数a()
的代码 中的指令紧跟call
指令到a()
本身。要查看稳定减少的地址,请在函数框架中打印局部变量的地址,例如 &i
。
这看起来像是尾递归优化。尝试在关闭所有优化的情况下编译代码 - 例如使用-O0
,这可能会阻止优化发生,从而产生您期望的结果。
@Peng 你注意到<- ESP
cmets 了吗? ESP 是 IA32 架构中的堆栈指针寄存器,它是 it 正在增长的。幻灯片中的“位置”不是返回地址;它是堆栈上返回地址的位置。在幻灯片 6 中发现 at Location 000FFFF8
和 Location 000FFFF4
的返回地址指针将是相同的,因为它们都是“经过f
调用的点f
" 在函数 f
的机器代码中。
@IwillnotexistIdonotexist,好吧,我想我明白了。夸张地说,我在 main 中手动调用了 a(10) 两次。正如您所说,“返回地址”实际上是紧跟在 objdump 的 a() 部分中的“调用”指令之后的下一条指令。无论如何以及何时调用 a()。正如您在上一篇文章中清晰的解释,我现在知道我混淆了 the 返回地址指令和它实际指向的地址,就像将指针与其指向的值混淆了一样。太感谢了。 :)
@IwillnotexistIdonotexist 再次感谢您的解释。 :) 我想我实际上是在寻找“取消引用”返回地址的方法,或者换句话说,在堆栈上找到“返回地址”。再次感谢。
【参考方案1】:
f5 f4 f3 f2 f1 a(5) --> f5
f5 f4 f3 f2 f1 a(5) a(4) --> f3
f5 f4 f3 f2 f1 a(5) a(4) a(3) --> f1
f5 f4 f3 f2 f1 a(5) a(4) a(3) a(2) --> a(4)
f5 f4 f3 f2 f1 a(5) a(4) a(3) a(2) a(1) --> a(2)
递归期间不要更改索引!
f5 f4 f3 f2 f1 a(5) --> f5
f5 f4 f3 f2 f1 a(5) a(5) --> f4
停在ret_add(i) == ret_add(0)
【讨论】:
避免不安全的递归,这里更喜欢 for while/loop !while(i>0) printf("The return address is %p\n", __builtin_return_address(i--));
以上是关于如何递归使用 __builtin_return_address()?的主要内容,如果未能解决你的问题,请参考以下文章