stm32程序跑起来后,擦除指定的函数?

写在前面

前两天在一个讨论群里面,一位同行提了一个问题,就是能不能程序跑起来后擦除某个指定的函数。诶,这问题可以啊,虽然我没有使用这个功能的需求,但是有人问那肯定就有人用,正好公司也没啥事,就花了两个小时鼓捣了一下,成功的实现了,下面记录一下操作的方法。

我的想法

看到这个问题,我的第一反应就是通过分散加载脚本来实现(具体是什么,自行网上搜索),因为之前搞过ubootuboot里面一般称作链接脚本),根据之前的经验,觉得smt32应该也是可行的,只需要知道函数存储的起始地址和结束地址就行了(但实际上操作起来是不需要就可以完成的,实在想要起始地址和结束地址,那得使用汇编了),而链接脚本可以得到这些信息。

第一步

准备一个简单的能运行的工程,搭建工程这些就不讲了,我直接打开了一个示例工程,直接奔主题,到底怎么做。
下图步骤2去掉勾选后才能使用指定的分散加载文件,否则会使用默认的(工程需要先编译一次才会生成这个脚本)。
修改分散加载文件

第二步

打开分散加载文件后,新增一段对于ROM的描述,我这里主控芯片是stm32f429,选择第三个扇区来存储我需要擦除的函数,为什么这样选择呢?因为stm32 ROM区的擦除是以扇区为最小单位,直接用整个扇区来存储我想擦除的内容,这样就比较简单,如果不设置为一个扇区,那可能擦除是比较麻烦的,需要先读回然后修改再写入。

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00200000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00200000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00030000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

;新增描述
LR_IROM2 0x0800C000 0x00004000  {    ; load region size_region
  ER_IROM2 0x0800C000 0x00004000  {  ; load address = execution address
   test_func.o (+RO)
  }
}

如上,我新增了一段描述,将test_func.c(对应test_func.o)文件内的所有函数及只读内容全部放到指定的0x0800C000地址开始的一片ROM空间。
现在重新编译程序,打开生成的.map文件就可以看到test_func.c里面的函数地址都落在了指定的那一片区域,你也可以直接运行程序,把函数地址给打印出来查看。
map文件

另外一种方式

上面的方式已经能够实现将函数放到指定的地址了,但还是不够人性化,因为必须得将所有需要擦除的函数都放到一个指定的.c文件内,这样就破坏了程序原有的结构和可读性,下面介绍另一种方式,那就是通过attribute属性来指定函数存放的段,同一个段属性的内容会被放到一起。

;新增描述
LR_IROM2 0x0800C000 0x00004000  {    ; load region size_region
  ER_IROM2 0x0800C000 0x00004000  {  ; load address = execution address
   *(.TEST)
  }
}

如上方式就是新增了一个TEST段,具有这个段属性的内容就会被放到一起,而且是在指定的那一片ROM区域内,示例用法:

__attribute__((section (".TEST")))
void test_func(void)
{
    printf("%s\r\n", __FUNCTION__);
}

这种方式也是可行的,而且不需要指定文件,可以是任意.c文件内的任意函数,和前面介绍的指定文件的方式是可以混用的。

另另一种方式

嗯,还有一种方式,这种方式不需要修改分散加载脚本就可以实现,不过也有其局限性,方法就是直接通过attribute属性指定函数存放的地址,示例代码:

__attribute__((section (".ARM.__at_0x0800C000")))
void test_func(void)
{
    printf("%s\r\n", __FUNCTION__);
}

和第二种方式是不是很像,但这种方式不好的地方就是,只指定了起始地址,你要是有很多个函数那就不好搞了,你还要自己去算各个函数的起始地址该设置为多少才不会冲突,如果冲突,编译时会报错。

结尾

好了,上面介绍了几种将函数放到指定位置的方法,那程序跑起来后,删除指定的函数就很简单了,就调下操作flash的接口就行了,此处不作介绍。

思考

存放函数块的那一片区域被擦除了,那函数还能运行吗?
肯定不能了,所以需要在每个调用的地方判断一下,示例代码:

if(*(volatile uint32_t *)test_func != 0xffffffff)
{
    test_func();
}

如果程序不是放在IROM然后本地执行的,那上面的方法还有效吗?
不一定了,对于不支持XIP的存储器来说,想要实现这个操作,那就不是这几步这么简单就能实现的了。


顺便提一下,我把之前很多分享的源码整理成了独立的项目,并且会一直维护,有需要的可以点击下方阅读原文访问文中超链接。(点我跳转
我的开源

欢迎扫码关注我的微信公众号

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值