第一句:
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) //推断栈定地址值是否在0x2000 0000 - 0x 2000 2000之间
怎么理解呢?
(1)在程序里 #define ApplicationAddress 0x08004800 ,*(__IO uint32_t*)ApplicationAddress) 即取0x08004800開始到0x080048003 的4个字节的值, 由于我们的应用程序APP中设置把 中断向量表 放置在0x08004800 開始的位置;而中断向量表里第一个放的就是栈顶地址的值
也就是说,这句话即通过推断栈顶地址值是否正确(是否在0x2000 0000 - 0x 2000 2000之间) 来推断是否应用程序已经下载了,由于应用程序的启动文件刚開始就去初始化化栈空间,假设栈顶值对了,说应用程已经下载了启动文件的初始化也运行了;
第二句:
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); [ common.c文件第18行定义了: pFunction Jump_To_Application;]
ApplicationAddress + 4 即为0x0800 3004 ,里面放的是中断向量表的第二项“复位地址” JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); 之后此时JumpAddress
第三句:
Jump_To_Application = (pFunction) JumpAddress;
startup_stm32f10x_md_lv. 文件里别名 typedef void (*pFunction)(void); 这个看上去有点奇怪;正常第一个整型变量 typedef int a; 就是给整型定义一个别名 a
void (*pFunction)(void); 是声明一个函数指针,加上一个typedef 之后 pFunction仅仅只是是类型 void (*)(void) 的一个别名
所以,Jump_To_Application = (pFunction) JumpAddress; 此时Jump_To_Application指向了复位函数所在的地址;
第四 、五句:
__set_MSP(*(__IO uint32_t*) ApplicationAddress); \\设置主函数栈指针
Jump_To_Application(); \\运行复位函数
Jump_To_Application()是把用户代码的复位地址付给PC指针,我看到Jump_To_Application()这句代码debug的时候相应的汇编代码是
LDR r0,[pc,#12] ;相对PC的数据载入,去函数指针的地址
LDR r0,[r0,#00] ;R0做索引,无偏移,数据装载到R0,这个内容就是函数指针指向的内容,也就是函数的地址了,用户程序的起始地址;
BLX r0 ;这个不解释,说了是跳转
APP中配置处理
int main(void)
{
SCB->VTOR = APP_ADDRESS; //重映射向量表
__ASM("CPSIE I"); //使能全局中断
/*用户代码 */
GPIO_Config();
TIMER_Config(1000);
while(1)
{
}
}
VTOR寄存器存放的是中断向量表的起始地址。对于APP,设置为flash基址+偏移量,即0X08005000,所以需要在APP的main函数最开头处添加SCB->VTOR = APP_ADDRESS,实现中断向量表的起始地址的重设。
四、跳转时对中断的处理
ARM芯片可以实现多个APP,通过boot来控制APP,通过不同的地址选择跳转到不同的APP。
常用的为Bootloader跳转到APP,若要实现逆向跳转,通过复位即可实现,因为复位后程序会从flash最开始处执行,即bootloader.
每个APP包括boot loader都有各自的中断向量表,若在相应代码中有使用中断,跳转前需先关闭中断,以免刚跳转过去还没来得及重新设置中断向量表就产生中断,找不到中断入口和执行函数,程序跑飞。
跳转到APP后,第一时间先将中断向量表的地址重新映射,再使能全局中断,就可以正常运行了。
参考: