//=====================================================================
//TITLE:
// TinyCLR的this赋值语句的缘起和解决
//AUTHOR:
// norains
//DATE:
// Wednesday 12-January-2011
//Environment:
// .Net Micro Framework Porting 4.1
// MDK V4.0.0
// VS2010
//=====================================================================
如果大家辛辛苦苦调试好NativeSample之后,将代码放到TinyCLR进行编译下载,运行时却突然发现莫名崩溃。然后一行一行代码去检查,发现问题居然出在CLR_RECORD_ASSEMBLY header = *this;这个语句。按理说,这个语句不应该出问题啊,究竟是怎么一回事呢?稍安勿躁,我们一起来看看。
追本溯源,我们从LoadKnowAssemblies函数看起,如图:
这里有两个变量需要我们注意,分别是TinyClr_Dat_Start和TinyClr_Dat_End。这两个变量是在tinyclr_dat.s中定义的,地址却是和scatterfile_tinyclr_xxx.xml文件有关,如图:
我们继续执行LoadKnowAssemblies函数,一直跑到GoodHeader函数,如图:
如果再继续往下执行,那么就会引发Hard Fault的错误。是不是很奇怪?我们在仔细看看上面的截图,这时候的this数值为0x0804A001。如果大家是一步步断点进去调试的话,应该知道this指向的地址其实就是TinyClr_Dat_Start地址。但为什么是0x0804A001呢?是不是调试器显示的信息有误?
我们可以打开TinyCLR.map文件,明确地发现,TinyClr_Dat_Start确实是被编译为0x0804A001,如图:
根据scatterfile_tinyclr_xxx.xml文件的配置,TinyClr_Dat_Start不应该是0x0804A001,而应该是0x0804A000才对啊!为什么偏偏多了1个Byte呢?看到这里,你的第一感觉是不是拼命折腾scatterfile_tinyclr_xxx.xml文件?但我很遗憾地说,无论你怎么折腾,TinyClr_Dat_Start的地址永远是%Data_BaseAddress% + 1!
那是不是我们就只能束手无措了呢?也不尽然。其实要修正这地址其实也很简单,只要打开tinyclr_dat.s文件,增加一个SPACE 0语句即可,如图:
编译之后会是什么结果呢?我们看看tinyclr.map文件,如图:
从图中可以看出,TinyClr_Dat_Start已经和我们设置的一样,变为正确的0x0804A000数值了。
问题是解决了,但让我们回到文章开头,为什么TinyClr_Dat_Start为0x0804A001时,也就是this为0x0804A00时,CLR_RECORD_ASSEMBLY header = *this语句会出现Hard Fault呢?最多也就是拷贝的数据不正确,再怎么着也不应该引发异常吧?
我们进入到汇编代码的层次来查看一下,发现这个赋值语句最后是调用了__rt_memcpy_w函数,如图:
出错的地方在__rt_memcpy_w函数中的LDM语句,如图:
那为什么LDM语句会出现异常呢?我们直接将地址转换为指针,写一些测试代码看看,如图:
这里需要注明的是,测试代码采用memcpy函数,其实最后还是调用了__rt_memcpy_w。从图中看出,__rt_memcpy_w的调用其实是有限制的,也就是说,地址一定要能被4整除,否则一定会产生Hard Fault异常!
回答本文开头的问题其实就简单了。虽然%Data_BaseAddress%我们设置的是0x0804A000,它能够被4整除,但偏偏编译器发了疯,将TinyClr_Dat_Start偏移了1个Byte,变成了0x0804A001,很明显这是一个不能被4整除的数值。而CLR_RECORD_ASSEMBLY header = *this赋值语句最后拿这个不能被4整除的0x0804A001地址来调用__rt_memcpy_w函数,最后肯定就产生了Hard Fault异常。
最后我们来思考一个很有意思的问题,当TinyClr_Dat_Start被编译为0x0804A001地址时,是不是tinyclr.dat的数据也会往后移1个Byte呢?
我们首先来看看TinyClr_Dat_Start被正确编译为0x0804A000时该区域的数据,如图:
而TinyClr_Dat_Start被错误编译为0x0804A001地址时,数据如下所示:
可以看出,无论TinyClr_Dat_Start是被编译为0x0804A000,还是0x0804A001,tinyclr.dat的数据都是以0x0804A000为起始的!换句话来说,当TinyClr_Dat_Start被编译为0x0804A001时,读取出来的数据肯定是错误的。这算不算是MDK的一个bug呢?