关键词:PCROP,Hard Fault, STM32H750
引言
PCROP全称为Proprietary code read out protection(专用代码保护),它提供了一种新的代码保护机制,在PCROP区域的内容只能为可执行,不能读取或写入。这种机制可以为OEM厂商提供保护,方便保护自己IP的代码。本文主要记录在使用PCROP上遇到的Hardfault问题。
如何使用PCROP呢?
参考AN4968中PCROP的描述,PCROP的使用大致有以下几个步骤:
1. 将需要保护的文件,在IDE中标识为仅可执行,IAR和KEIL,CubeIDE都有此类标识。
2.需要修改Link文件,将受保护文件中的rw data, ro data, ro code进行分区存放。
3. 通过修改选项字,将受保护文件中的ro code中的内容进行PCROP保护。
4.编译工程产生保护文件的.o文件,并把符号导出给实际应用工程使用。
问题描述
在我使用IAR进行PCROP的测试时,发生了如下错误。
Figure1 IAR中的Hard Fault提示窗口
问题分析与定位
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状态,查看文档后有以下信息:
Figure 2 UFSR寄存器描述
把PCROP取消掉以后,查看汇编代码如下图3:
Figure 3 汇编窗口
0x9003开头的是外部flash地址,首先会跳转到0x9003’6fd8去执行,如下图4。
Figure 4 汇编窗口
红色框出来的部分就是0x9003’6fd8的内容了,这里会取下一个PC指针指向区域的内容,赋给PC指针,也就是PC会跳转到0x0801’f0b0去执行,LSB=1代表的是下一条为thumb指令,如下图5。
Figure 5 汇编窗口
然后就会进行压栈操作,到此下面自己写的汇编测试指令还没有走到就已经出现Hard Fault了,目前看来,要么是跳转的问题,要么是压栈操作导致了Hard Fault。
为了方便继续测试,又写了一个测试函数,如下图6:
Figure 6 测试代码
该函数加入到PCROP区域后,发现没有问题,和之前的汇编代码对比,该函数由于局部变量很少所以并没有压栈的操作,那么我可以尝试把局部变量加大,让编译器产生PUSH等压栈操作。修改后的代码如下图7:
Figure 7 测试代码
修改完成后,再次测试,确认已经产生了PUSH操作,果然Hard Fault又出现了。
但是PUSH操作不应该导致hard fault啊,继续查看汇编代码,查找可疑的指令:
Figure 8 汇编窗口
在0x801’f01e这个位置,有一段跳转指令,代码会跳转到0x801’f000的位置去执行,继续追踪:
Figure 9 汇编窗口
问题来了:这个LDR指令需要去取PC指针+4的位置的数据,而此时PC指针肯定是位于PCROP区域的,也就是说这里需要数据总线访问PCROP区域,这肯定是不被允许的,所以才导致了Hard Fault。
那么,编译器为什么会产生这段跳转指令呢?查看map文件后,这段flash区域放置的是veneer section,Veneer又是什么呢?
查看IAR的帮助文档,可以发现以下信息:
Figure 10 IAR帮助文档
原来,编译器为了实现长跳转,会自动生成一段Veneer代码,并且将跳转的地址放在这段区域,而IAR默认将这段代码放在了PCROP的起始区域,所以才导致了该问题。
根据AN4968中关于IAR的配置,只有以下内容需要勾选,目前看来是无法解决这个问题的:
Figure11 IAR设置选项
在咨询了IAR的支持工程师后,得到信息是可以在Link配置中设置:
Figure 12 IAR设置选项
设置完成后,产生的跳转部分汇编代码如下:
Figure13 汇编窗口
可以看到,这里先将0x21b1存放到R12的低地址,再将0x9004存放到R12的高地址,最后BX R12完成跳转。和前面的跳转部分汇编相比,此时不需要访问PCROP保护的flash区域的数据了,所以不会有问题。
问题已经找到
虽然这种方法可以解决,但图中LINK文件的配置是针对全局的,取消掉literal pool会对全局的代码效率产生影响,有没有更好的办法可以只针对test.c文件做配置呢?
非常遗憾,在当前最新的IAR版本中,无法完美解决该问题。
问题解决
▼查收解决方法▼
在发现IAR有这个问题后,尝试使用Keil和CubeIDE来测试该问题。
KEIL中,只需要配置以下内容就可以达到目的.
Figure14 Kei1汇编窗口
Keil生成的汇编代码,从图中可以看到,此处Veneer产生的跳转是不会有问题的.
Figure15 Kei1汇编窗口
另外,在Keil的官方文档中,有以下提示,这表明Keil已经注意到了类似的问题,只需要勾选XO选项就能避免:
Figure16 Kei1说明文档
CubeIDE中,需要在下图两处地方进行配置:
1.勾选-mslow-falsh-data选项
2.在GCC编译器中,添加-mpure-code选项
Figure 18 Cube IDE选项设置
对比下CubeIDE配置前和配置后的汇编代码如下:
配置前:访问了PCROP区域的flash内容进行跳转。
Figure 19 Cube IDE汇编窗口
配置后,直接修改R12进行跳转。
Figure 20 Cube IDE汇编窗口
问题总结
PCROP只能保护片内flash区域,无法保护片外flash,在使用PCROP进行保护时,不仅需要配置好Link文件,还需要配置好IDE,注意片外Flash和片内Flash区域相互跳转的地方。
参考文献
🔷Keil Veneer说明:https://www.keil.com/support/man/docs/armlink/armlink_pge1406301797482.htm
🔷IAR Veneer说明:https://www.iar.com/support/tech-notes/linker/what-is-linker-created-and-lcgbwk-in-the-.map-file/
🔷AN4968: Proprietary code read out protection (PCROP) on STM32F72xxx and STM32F73xxx microcontrollers
·
文档中所用到的工具及版本
测试工具版本信息:
🔷 IAR:8.50.1
🔷KEIL:5.24.2.0
🔷CubeIDE:1.5.1
© THE END
▽点击“阅读原文”,可下载原文档