【Bootloader学习理解----跳转优化异常】

news/2024/7/10 4:46:40 标签: Bootloader, 跳转, 中断, 设置MSP, 优化, GCC优化

笔者接着来介绍一下Bootloader跳转代码以及优化

1、跳转代码理解

跳转代码可能要涉及到芯片架构的知识,要跳转到对应的位置,还要设置相关的SP 堆栈指针,具体可以参考笔者这篇文章BootLoader的理解与实现。
STM32的跳转代码如下所示:

u32 ApplicationAddress = 0x08008000;  //app 地址 
typedef  void (*pFunction)(void);     //函数指针
void Jump_Used_Main(void)
{
	printf("\r\nboot2-------- Jump APP --------- \r\n");
	/*	判断栈顶 是否位于 0x20000000  128K 	  */
   	if (  ((*(__IO uint32_t*)ApplicationAddress) >= 0x20000000) &&((*(__IO uint32_t*)ApplicationAddress) <= 0x20010000))
   	{ 	
     		JumpAddress = *(__IO uint32_t*) (ApplicationAddress +4);
     		Jump_To_Application = (pFunction) JumpAddress;
     		/*close the interrupt*/
     		_disable_interrupr();
     		__set_MSP(*(__IO uint32_t*) ApplicationAddress);
     		Jump_To_Application();
   	}
	else
	{			
		while(1)
		{
			printf("\r\nboot2  error\r\n");
		}
	}
}

可以看到__set_MSP(* (__IO uint32_t *) ApplicationAddress);这行代码中,在取地址里面的内容时,增加了__IO的选项,

#define __IO volatile

保证是从内存里面读出来的SP栈指针的数据,然后设置到MSP,否则可能导致SP设置错误,程序跑飞。

2、跳转代码编译优化

笔者在实际开发的过程中,遇到了一个跳转过去就崩掉的情况,单步调试,发现到Set_MSP就崩掉了,很是奇怪,通过汇编一查看,就很明显了。

  • NXP LPC的单片机,
  • arm-noen-eabi-gcc的编译器。
typedef struct addr_manager_struct
{
	xxxxxxx;
	u32 image_load_addr;
	u32 image_exec_addr;
}addr_manager_t;
u8 main_jump(addr_manager_t* info)
{
	u32 *image_addr = (u32*)info->image_load_addr;
	u32 *sp = (u32*) image_addr [0];
	u32 *jump = (u32*) image_addr [1];
	disable_interrupt();
	set_msp((u32)sp);
	(*jump)();
	return 0;
}

在这里插入图片描述

  • 上述在设置完SP之后,放到r2寄存器里面,
  • 然后获取jump地址,放到r3里面,
  • 之后禁止中断修改了r2,然后禁止中断
  • 然后就把r2传到SP里面,
  • 造成跑飞,可能是一个不存在的地址

有人说sp设置的时候需要加volatile,即使改成下面的函数,也没用效果

void jump_main(addr_manager_t* info)
{
	u32 *image_addr = (u32*)info->image_load_addr;
	u32 *sp = (u32*) image_addr [0];
	u32 *jump = (u32*) image_addr [1];
	disable_interrupt();
	set_msp((volatile u32)sp);
	(*jump)();
}

在这里插入图片描述
接着再继续改,直

  • 接通过Image指针取内容,然后去获取到SP值,然后就可以了,
  • 看来是禁止中断的这个函数所影响,
  • 禁止中断如果不报存返回值,则不会对SP的值产生影响
void jump_main(addr_manager_t* info)
{
	u32 *image_addr = (u32*)info->image_load_addr;
	u32 *sp = (u32*) image_addr [0];
	u32 *jump = (u32*) image_addr [1];
	disable_interrupt();
	set_msp(*((volatile u32*)image_addr));
	(*jump)();
}

在这里插入图片描述
接着我们再尝试一种方法,就是将禁止中断函数移动位置,看一下情况,发现也是可以的。

void jump_main(addr_manager_t* info)
{
	disable_interrupt();
	u32 *image_addr = (u32*)info->image_load_addr;
	u32 *sp = (u32*) image_addr [0];
	u32 *jump = (u32*) image_addr [1];
	set_msp((u32)sp);
	(*jump)();
}

在这里插入图片描述
然后我们看一下set sp的汇编函数,也并没有指明操作的寄存器,很可能是GCC编译器优化的bug。

inline void set_sp(u32 sp_value)
{
	asm volatile ("msr msp, %0" : "=r"(sp_value))
}

然后我们再看看armcc编译器的结果,没有任何问题。

  • armcc对于禁止中断的返回值没有处理,
  • 而GCC处理了然后导致了问题(将其中断保存值填到r2里面,然后r2的sp值被覆盖),
  • 其实应用层如果没有对返回值进行处理,即使函数有返回值,编译器优化会将其处理掉。显然GCC的编译器优化做的还是差一点。

在这里插入图片描述


http://www.niftyadmin.cn/n/5251404.html

相关文章

python 协程asyncio详解

协程 协程就是告诉Cpython解释器&#xff0c;你不是nb吗&#xff0c;不是搞了个GIL锁吗&#xff0c;那好&#xff0c;我就自己搞成一个线程让你去执行&#xff0c;省去你切换线程的时间&#xff0c;我自己切换比你切换要快很多&#xff0c;避免了很多的开销。 协程的本质就是…

VisualSVN Server的安装全过程

目录 背景: 安装过程&#xff1a; 步骤1&#xff1a; 步骤2&#xff1a; 步骤3&#xff1a; 步骤4&#xff1a; 步骤5&#xff1a; 安装出现的bug&#xff1a; 问题: 解决办法: 总结: 背景: VisualSVN Server 是一款免费的 SVN (Subversion) 服务器软件&#xff0c…

Redis小知识

1.Redis可以用于以下场景&#xff1a; 缓存: Redis 接受并处理缓存数据请求&#xff0c;将数据存储在内存中&#xff0c;从而提高数据获取速度。 分布式锁: Redis提供了分布式锁的能力&#xff0c;可以很容易地解决线程安全问题。 消息队列: Redis提供了消息队列功能&#xf…

电源小白入门学习3——电源系统常见元件选型MOS管、二极管、电感篇

电源小白入门学习3——电源系统常见元件选型MOS管、二极管、电感篇 MOS管二极管电感 书接上文&#xff0c;上一期我们讲了电阻、电容选型中需要注意的事项&#xff0c;下面我们接着来介绍MOS管和二极管。 MOS管 关于MOS管的基本原理和内部的一些结构&#xff0c;PN结、半导体的…

深度学习 时间序列回归学习笔记

目录 常用的深度学习时间序列回归模型: ARIMA模型 ETS模型 效果评估

第二十一章网络通信总结

网络程序设计基础 局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机。如下图所示 网络协议 1.IP协议 IP是Internet Protocol的简称&#xff0c;是一种网络协议。Internet 网络采用的协议是TCP/IP协议&#xff0c;其全称是Transmission …

C语言面试之旅:掌握基础,探索深度(面试实战之单片机指令系统)

一、概述 单片机指令系统是单片机硬件设计的重要组成部分&#xff0c;它决定了单片机能够执行什么样的操作。指令系统通常由一系列二进制代码指令组成&#xff0c;这些指令可用于对单片机内部的各个寄存器进行操作&#xff0c;或者对内存中的数据进行读写。了解单片机的指令系统…

第 119 场 LeetCode 双周赛题解

A 找到两个数组中的公共元素 模拟 class Solution { public:vector<int> findIntersectionValues(vector<int> &nums1, vector<int> &nums2) {unordered_set<int> s1(nums1.begin(), nums1.end()), s2(nums2.begin(), nums2.end());vector<…