ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

VTOR的对齐

VTOR的对齐

最近有一个问题会导致MCU不停的reset,经过调试发现是由于中断向量表被意外篡改了,那是什么原因导致中断向量表被意外篡改的呢?主要原因是VTOR的对齐问题,接下来详细介绍一下这个问题。

  • 一开始从call stack中发现,在某个中断触发后,会进入hardfault
  • 回溯hardfault的堆栈信息,发现是一个无效的PC值导致了hardfault
  • 这个时候去查看0x0处中断向量表对应的地址发现,并没有这个无效地址
  • 那么这个无效地址是从哪里来的呢?此时,脑海想到了VTOR这个寄存器,所以去查看了一下这个寄存器的值,果然有问题,VOTR的值为0x20000200,所以中断向量表被重定向到了0x20000200这个区域。并且通过调试器定位到向量表重定向发生在库函数Interrupt_Initialisation(&nvic_servicedata)中。通过去查看0x20000200物理区域,也发现确实存在这个无效地址。所以这个无效地址是怎么来的呢?
  • 并且发现Interrupt_Initialisation(&nvic_servicedata)调用完之后,0x0处的中断向量表被拷贝在了0x20000280开始的表中,这显然与VTOR中显示的0x20000200是有出入的。
  • 此时去查看map文件发现,
    “nvic_servicedata 0x20000280 Data”
    nvic_servicedata占用了0x20000280为起始地址的ram区域,再一看0x20000200-0x20000280这一块区域被分配给其他变量,所以真相就是,MCU认为的中断向量表的起始地址是0x20000200,但是中断向量被拷贝到了预留的0x20000280的nvic_servicedata区域。
  • 但是在这里有产生一个问题:那就是实际的中断向量表在物理地址0x20000280这个区域,但是MCU因为VTOR中为0x20000200,会认为向量表在0x20000200,那么应该所有的中断函数都会有问题,但实际上并没有,这是什么原因呢?原因是Interrupt_Initialisation(&nvic_servicedata)之后,紧接着调用了InterruptRegister(),这个库函数初始化了除16个异常之外的中断服务函数,好在这个InterruptRegister()在进行初始化时,是以VTOR中的20000200为基址初始化的。因此只要你没有触发16个异常中的任何一个,就暂时不会有问题。这里正好也可以解释为什么MCU会reset?因为hardfault的异常入口地址被更改为了0x0,所以hardfault产生时,程序跑飞了,最后导致了watchdog reset。

通过Armv8-M Architecture Reference Manual.pdf可以发现,确实VTOR的最小对齐为0x80,所以这里有两种可能:一,供应商固化在flash里面的库函数Interrupt_Initialisation有bug把后8位mask掉了;二,芯片设计时,硬件上就把后8位mask掉了。

返回列表