最近编译keil项目时,遇到一个比较奇怪的link warning,如下所示。
根据这个warning,容易知道这两个函数存在递归(相互)调用关系。其实在源代码中不存在直接递归调用关系,而是间接地调用,从map文件摘出这个calltree如下
+--> _CBUSMSCREADECBUSSPEEDSDONE/SI_DRVCBUSRX
| | _CBUSMSCREADTMDSSPEEDSDONE/SI_DRVCBUSRX
| | _CBUSMSCREADECBUSDEVROLESDONE/SI_DRVCBUSRX
| | _CBUSMSCREADLOGDEVMAPDONE/SI_DRVCBUSRX
| | _CBUSMSCREADBANDWIDTHDONE/SI_DRVCBUSRX
| | _CBUSMSCREADFEATUREFLAGDONE/SI_DRVCBUSRX
| | _CBUSMSCREADDEVICEIDHDONE/SI_DRVCBUSRX
| | _CBUSMSCREADDEVICEIDLDONE/SI_DRVCBUSRX
| | _CBUSMSCREADSCRATCHPADSIZEDONE/SI_DRVCBUSRX
<--+ _CBUSMSCREADINTSTATSIZEDONE/SI_DRVCBUSRX
简单梳理一下上面的调用树,就是CBUSMSCREADECBUSSPEEDSDONE调用了CBUSMSCREADTMDSSPEEDSDONE, CBUSMSCREADECBUSDEVROLESDONE调用了CBUSMSCREADECBUSDEVROLESDONE。。。 。。。最后CBUSMSCREADINTSTATSIZEDONE又调用了CBUSMSCREADECBUSSPEEDSDONE。
众所周知,Keil51由于内存资源比较紧张,函数的参数及局部变量一般地放在data segment, 为了避免一个函数的变量被另外一个函数的变量覆盖,keil会分析函数之间的相互调用关系,对于没有相互调用的关系的函数变量就可以重叠在一起,即overlay。 对于存在调用关系的函数,则变量存放在data区间的不同地址。但是,递归调用是一种例外,由于
是同一个函数,所以变量都会重叠在一起,这样会造成变量还没用完就被覆盖了。 基于此,原则上,keil51是不允许递归调用的。
关键点:如何解决这类问题?
1)如果是直接递归调用, 如下所示
void B(void)
{
... ...
B()
... ....
}
就将B函数申明为void B(void) reentrant, 这样就会生成模拟栈来储存自动变量和参数,以避免变量被覆盖。
2)如果是间接递归调用,如我的项目遇到的case,怎么办?
在option->LX51 Misc->Misc controls中,有一个Overlay的空白框,这个是干什么用的呢? 查阅LX51 user guide,知道Overlay是link的一个指示符,我们可以利用它来手动
修改或标明call tree关系。比方说,A通过函数指针调用B,为安全起见,可以增加一条OVERLAY(A!(B)) 又比如A和B其实不存在调用关系,如我的项目中遇到的case,这种情况下,只需要增加OVERLAY(A~(B))
其实LX51指示符除了Overlay外还有很多,如removeunused, disablewarning等。如不把它们弄清楚 就很容易造成项目的各种不稳定性。