<strong><font color="#004a85">引言</font> </strong>
PCROP全称为Proprietary code read out protection(专用代码保护),它提供了一种新的代码保护机制,在PCROP区域的内容只能为可执行,不能读取或写入。这种机制可以为OEM厂商提供保护,方便保护自己IP的代码。本文主要记录在使用PCROP上遇到的Hardfault问题。
<strong>如何使用PCROP呢?</strong>
参考AN4968中PCROP的描述,PCROP的使用大致有以下几个步骤:
1、将需要保护的文件,在IDE中标识为仅可执行,IAR和KEIL,CubeIDE都有此类标识。
2、需要修改Link文件,将受保护文件中的rw data, ro data, ro code进行分区存放。
3、通过修改选项字,将受保护文件中的ro code中的内容进行PCROP保护。
4、编译工程产生保护文件的.o文件,并把符号导出给实际应用工程使用。
<strong><font color="#004a85">问题描述</font> </strong>
在我使用IAR进行PCROP的测试时,发生了如下错误。
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226961-1.png&…; alt=“Figure1 IAR中的Hard Fault提示窗口"></center><center><i>Figure1 IAR中的Hard Fault提示窗口</i></center>
<strong><font color="#004a85">问题分析与定位</font> </strong>
PCROP调试时,会遇到一个问题,那就是由于代码已经被保护起来,无法单步调试。目前知道的只是当PCROP打开的时候会导致Hard Fault,而不使用PCROP没有问题。那么这个问题和PCROP强相关了。
接下来,只能通过一些简单的测试来定位发生问题的大概位置了,比如在函数入口添加一些简单的汇编代码,比如IAR中可以添加代码往寄存器R5中写入特殊测试值:__ASM(" MOVS R5, #11"),通过这个简单的测试,发现函数并没有执行到这里来。所以大概在跳转之后,执行这一句代码之前就已经发生了问题。
Hard Fault的错误描述为:INVSTATE,无效状态代表的是试图切换到ARM状态,查看文档后有以下信息:PCROP调试时,会遇到一个问题,那就是由于代码已经被保护起来,无法单步调试。目前知道的只是当PCROP打开的时候会导致Hard Fault,而不使用PCROP没有问题。那么这个问题和PCROP强相关了。
接下来,只能通过一些简单的测试来定位发生问题的大概位置了,比如在函数入口添加一些简单的汇编代码,比如IAR中可以添加代码往寄存器R5中写入特殊测试值:__ASM(" MOVS R5, #11"),通过这个简单的测试,发现函数并没有执行到这里来。所以大概在跳转之后,执行这一句代码之前就已经发生了问题。
Hard Fault的错误描述为:INVSTATE,无效状态代表的是试图切换到ARM状态,查看文档后有以下信息:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226963-2.png&…; alt=“Figure 2 UFSR寄存器描述"></center><center><i>Figure 2 UFSR寄存器描述</i></center>
把PCROP取消掉以后,查看汇编代码如下图3:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226965-3.png&…; alt=“Figure 3 汇编窗口"></center><center><i>Figure 3 汇编窗口</i></center>
0x9003开头的是外部flash地址,首先会跳转到0x9003’6fd8去执行,如下图4。
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226967-4.png&…; alt=“Figure 4 汇编窗口"></center><center><i>Figure 4 汇编窗口</i></center>
红色框出来的部分就是0x9003’6fd8的内容了,这里会取下一个PC指针指向区域的内容,赋给PC指针,也就是PC会跳转到0x0801’f0b0去执行,LSB=1代表的是下一条为thumb指令,如下图5。
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226969-5.png&…; alt=“Figure 5 汇编窗口"></center><center><i>Figure 5 汇编窗口</i></center>
然后就会进行压栈操作,到此下面自己写的汇编测试指令还没有走到就已经出现Hard Fault了,目前看来,要么是跳转的问题,要么是压栈操作导致了Hard Fault。
为了方便继续测试,又写了一个测试函数,如下图6:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226971-6.png&…; alt=“Figure 6 测试代码"></center><center><i>Figure 6 测试代码</i></center>
该函数加入到PCROP区域后,发现没有问题,和之前的汇编代码对比,该函数由于局部变量很少所以并没有压栈的操作,那么我可以尝试把局部变量加大,让编译器产生PUSH等压栈操作。修改后的代码如下图7:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226973-7.png&…; alt=“Figure 7 测试代码"></center><center><i>Figure 7 测试代码</i></center>
修改完成后,再次测试,确认已经产生了PUSH操作,果然Hard Fault又出现了。
但是PUSH操作不应该导致hard fault啊,继续查看汇编代码,查找可疑的指令:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226975-8.png&…; alt=“Figure 8 汇编窗口"></center><center><i>Figure 8 汇编窗口</i></center>
在0x801’f01e这个位置,有一段跳转指令,代码会跳转到0x801’f000的位置去执行,继续追踪:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226977-9.png&…; alt=“Figure 9 汇编窗口"></center><center><i>Figure 9 汇编窗口</i></center>
问题来了:这个LDR指令需要去取PC指针+4的位置的数据,而此时PC指针肯定是位于PCROP区域的,也就是说这里需要数据总线访问PCROP区域,这肯定是不被允许的,所以才导致了Hard Fault。
那么,编译器为什么会产生这段跳转指令呢?查看map文件后,这段flash区域放置的是veneer section,Veneer又是什么呢?
查看IAR的帮助文档,可以发现以下信息:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226980-10.png…; alt=“Figure 10 IAR帮助文档"></center><center><i>Figure 10 IAR帮助文档</i></center>
原来,编译器为了实现长跳转,会自动生成一段Veneer代码,并且将跳转的地址放在这段区域,而IAR默认将这段代码放在了PCROP的起始区域,所以才导致了该问题。
根据AN4968中关于IAR的配置,只有以下内容需要勾选,目前看来是无法解决这个问题的:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226982-11.png…; alt=“Figure11 IAR设置选项"></center><center><i>Figure11 IAR设置选项</i></center>
在咨询了IAR的支持工程师后,得到信息是可以在Link配置中设置:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226984-12.png…; alt=“Figure 12 IAR设置选项"></center><center><i>Figure 12 IAR设置选项</i></center>
设置完成后,产生的跳转部分汇编代码如下:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226986-13.png…; alt=“Figure13 汇编窗口"></center><center><i>Figure13 汇编窗口</i></center>
可以看到,这里先将0x21b1存放到R12的低地址,再将0x9004存放到R12的高地址,最后BX R12完成跳转。和前面的跳转部分汇编相比,此时不需要访问PCROP保护的flash区域的数据了,所以不会有问题。
<strong><font color="#004a85">问题已经找到</font> </strong>
虽然这种方法可以解决,但图中LINK文件的配置是针对全局的,取消掉literal pool会对全局的代码效率产生影响,有没有更好的办法可以只针对test.c文件做配置呢?
非常遗憾,在当前最新的IAR版本中,无法完美解决该问题。
<strong><font color="#004a85">问题解决</font> </strong>
<center>▼查收解决方法▼ </center>
在发现IAR有这个问题后,尝试使用Keil和CubeIDE来测试该问题。
KEIL中,只需要配置以下内容就可以达到目的.
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226988-14.png…; alt=“Figure14 Kei1汇编窗口"></center><center><i>Figure14 Kei1汇编窗口</i></center>
Keil生成的汇编代码,从图中可以看到,此处Veneer产生的跳转是不会有问题的.
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226990-15.png…; alt=“Figure15 Kei1汇编窗口"></center><center><i>Figure15 Kei1汇编窗口</i></center>
另外,在Keil的官方文档中,有以下提示,这表明Keil已经注意到了类似的问题,只需要勾选XO选项就能避免:
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226992-16.png…; alt=“Figure16 Kei1说明文档"></center><center><i>Figure16 Kei1说明文档</i></center>
CubeIDE中,需要在下图两处地方进行配置:
1.勾选-mslow-falsh-data选项
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226994-17.png…; alt=“Figure17 Cube IDE选项设置"></center><center><i>Figure17 Cube IDE选项设置</i></center>
2.在GCC编译器中,添加-mpure-code选项
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226996-18.png…; alt=“Figure 18 Cube IDE选项设置"></center><center><i>Figure 18 Cube IDE选项设置</i></center>
对比下CubeIDE配置前和配置后的汇编代码如下:
配置前:访问了PCROP区域的flash内容进行跳转。
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-226998-19.png…; alt=“Figure 19 Cube IDE汇编窗口"></center><center><i>Figure 19 Cube IDE汇编窗口</i></center>
配置后,直接修改R12进行跳转。
<center><img src="http://mcu.eetrend.com/files/2021-11/wen_zhang_/100555274-227000-20.png…; alt=“Figure 20 Cube IDE汇编窗口"></center><center><i>Figure 20 Cube IDE汇编窗口</i></center>
<strong><font color="#004a85">问题总结</font> </strong>
PCROP只能保护片内flash区域,无法保护片外flash,在使用PCROP进行保护时,不仅需要配置好Link文件,还需要配置好IDE,注意片外Flash和片内Flash区域相互跳转的地方。
<strong>参考文献</strong>
Keil Veneer说明:https://www.keil.com/support/man/docs/armlink/armlink_pge1406301797482…
IAR Veneer说明:<a href="https://www.iar.com/support/tech-notes/linker/what-is-linker-created-an…;
AN4968: Proprietary code read out protection (PCROP) on STM32F72xxx and STM32F73xxx microcontrollers
<strong>文档中所用到的工具及版本</strong>
测试工具版本信息:
IAR:8.50.1
KEIL:5.24.2.0
CubeIDE:1.5.1
来源:<a href="https://mp.weixin.qq.com/s/PmGS8_DTpyBJ7jykSo4uOA">STM32单片机</a>
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。