在开发bootloader的过程中,为了安全起见,避免flash驱动被错误调用。一般而言,需要将flash 驱动放置在RAM中,同时,在每次执行完重编程后,启用复位操作,程序代码中将无flash驱动的代码存在,从而降低flash被错误改写的风险。因此,剥离flash驱动代码成为了一个基本技能,本文章旨在,用一个简单的例子来说明,制作flash驱动需要的基本知识,即如何将一个函数的code从hex中剥离出来,在另一个工程中调用剥离出来的code。
1、平台、工具介绍
赛普拉斯芯片,IAR开发环境,hexView
2、例子说明
为了简单的说明该问题,使用一个传递两个形参,返回两个形参之和的函数,函数体如下:
uint8 testAdd(uint8 a, uint8 b)
{
uint8 k = 0;
return a + b;
}
我们要做的事情,就是将此函数的code从hex中种提取出来,放置在一个数组中,将该数组强制转换为函数指针,通过函数指针引用,来而验证此方法。完整的例子如下:
typedef uint8 (*TestFUN)(uint8 x, uint8 y);//定义函数指针,类型需要与我们测试函数的类型一致
uint8 testAdd(uint8 a, uint8 b)//定义测试函数
{
uint8 k = 0;
return a + b;
}
#pragma pack (4)//4字节对齐,开始
//const uint8 testAddHex[] = {0x00,0x22, 0x08, 0x18, 0xC0, 0xB2, 0x70, 0x47, 0x80};
const uint8 testAddHex[] = {0x00};//预定义一个空值,该数组用来放testAdd生成的hex
#pragma pack ()//4字节对齐,结束
void TestSleep(void)//调用测试函数的函数,名称请忽略
{
uint8 result = 0;
uint8 a = 0,b = 1;
TestFUN addFUN;
#if 0//测试开关,获取testAdd的code时候,将开关打开,验证时候将此开关关闭
result = testAdd(a,b);
if(result == 1)
{
result = 1;
}
else
{
result = 0;
}
#else
addFUN = (TestFUN)(testAddHex + 1);//因为是thumb指令,需要加1处理,否则程序会跑飞
result = addFUN(a,b);
if(result == 1)
{
result = 1;
}
else
{
result = 0;
}
#endif
}
3、提取code阶段
将测试开关打开,编译程序,然后从map文件中找到testAdd的地址信息,如图
从图中可以看到testAdd的起始地址为0x100e'bcd9,长度为0x8字节,类型为Code。找到这个地址后将hex文件用hexView打开,从HexView中提取出这段长度的代码(地址为奇数,需要将前一个地址的内容也提取出来)。
所以得到:
const uint8 testAddHex[] = {0x00,0x22, 0x08, 0x18, 0xC0, 0xB2, 0x70, 0x47, 0x80};
4、验证阶段
获取testAdd函数的code并放置在testAddHex数组中后,将测试开关关闭,来测试该函数是否可以被调用,是否可以得到我们想要的结果,编译,下载,调试。将testAddHex的地址0x100F2FD1产强制转换为TestFUN类型函数指针,将实参a= 0,b = 1,传递给addFUN函数指针的形参。
可以看到结果,Result == 1。
5、结果与结论
1)可以从hex文件或bin件中的code剥离出来,通过强制类型转换,获得该函数;
2)在定义这个数组的时候,将数组类型定义程伟了const,默认放入到了flash中,这个不加const,及定义在RAM中也是可以的。
3)文中例子加了数据对齐的定义,不使用是否可以,需要去验证;
4)取code的时候,为什么需要从偶数取?该例子是多取了前一个地址的数据,如果起始地址刚好为偶数,是否可以不用夺取?
5)数组强制转换的时候,地址多加了一位,这里是因为指令集的原因?需要去获取资料验证。