Keil调试笔记:数组越界

概要:一个例子,两个思路,一些思考。

问题现象

struct bmm150_dev {
	/*! Chip Id */
	uint8_t chip_id;
	/*! Device Id */
	uint8_t dev_id;
	/*! SPI/I2C Interface */
	enum bmm150_intf intf;
	/*! Bus read function pointer */
	bmm150_com_fptr_t read;
	…//此处为省略
};

//定义一个结构体变量
struct bmm150_dev MagDev ;
//检查不BUG的地方
static int8_t null_ptr_check(const struct bmm150_dev *dev)
{
	int8_t rslt;

	if ((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_ms == NULL) ) {
		/* Device structure pointer is not valid */
		rslt = BMM150_E_NULL_PTR;
	} else {
		/* Device structure is fine */
		rslt = BMM150_OK;
	}

	return rslt;
}

注:以上代码是由博世的BMM150的开源驱动。程序是在Stm32上运行的。

注:推荐使用NotePad,然后用Ctrl+F进行查找

在传感器通过MagDev句柄读取传感器数据,在调用API函数周期性读取传感器函数时,通常都会判断句柄是否有效。而出现的现象是程序运行一会儿后,发现“(dev->read == NULL)”为真了,而MagDev.read是在程序初始化时就不再改变的值。也就是说该值被意外篡改了。

查找问题引发源的方式1

程序还没调用操作系统,但此时已经有开启了几个中断了。

1、首先我先把所有中断都屏蔽了,只运行主函数,现象不再现。

2、每次只打开一个中断,在调试模式下依次排查。问题复现并找到原因。

我定义了如下变量

static double AGdataSum[6] = {0};

但由于需求改变,实际上使用了7个double值,于是由于数组越界,意外改变了下一个静态/全局变量的值。

查找问题引发源的方式2

==============================================================================

Image Symbol Table

    Local Symbols

    Symbol Name                              Value     Ov Type        Size  Object(Section)

    adcBuf                                   0x240041a2   Data          14  cfileObject1.o(.bss)

    AGdataSum                                0x240041b0   Data          56 cfileObject1.o(.bss)

    .bss                                     0x240041e8   Section       72  magnetrun.o(.bss)

    .bss                                     0x24004230   Section      192  cfileObject2.o(.bss)

    .bss                                     0x240042f0   Section     1044  cfileObject3.o(.bss)

    Global Symbols

    Symbol Name                              Value     Ov Type        Size  Object(Section)

    sensorData_value                         0x24004068   Data         112  cfileObject4.o(.bss)

    MagDev                                   0x240041e8   Data          72  magnetrun.o(.bss)

    next_value                           0x24004230   Data          64  cfileObject2.o(.bss)

==============================================================================

注:此表是在BUG还没修改前的表,表中部分内容已经匿名处理。“”为省略部分。

 

其实我一开始就怀疑是数组越界,于是到map文件中查找。但以为编译器将全局变量一次存在RAM中,找到变量MagDev后面的 变量next_value 的相关程序,并未发现有什么问题。后来才知道,编译器分配全局变量和静态变量的内存时,编译器内存不是先分配静态变量内存后分配全局变量的方式进行的,而是按文件搜索顺序分配内存。

变量 AGdataSum的地址加上其内存大小后就是变量MagDev 的首地址了。显然如果变量 AGdataSum操作第7位时正好改变了变量MagDev的值。

0x240041b0+56=0x240041e8

 

MAP文件的说明

1、0x2400xxxx表明存储空间在RAM;若为0x0800xxxx,则表明存储空间为Flash。

2、静态变量区的符号名为“.bss”的部分可能会在全局变量区写出具体变量名。

 

思考:

1、局部变量使用的是栈,若出现错误,一般会使局部逻辑出错,也一般不会导致夸逻辑区域的错处。

2、计算资源不紧张的话,函数尽量每次使用函数指针时都判断一下指针是否为空,当然能让本文例子那样判断出BUG来也是要有一定运气的。

3、谨慎使用数组,谨慎使用指针,多写些检验程序,提高程序鲁棒性。

4、虽然函数指针能够使程序的结构更加合理,但在不考虑外界因素(如电磁干扰)的影响下,程序跑飞的原因主要来自于函数指针。然而,并不推荐因为函数指针这一缺点而不使用函数指针。

5、在可确定函数指针指向的函数地址的具体范围时,在该范围判断指针指向的地址是否在范围内,比判断指针是否指向NULL更可靠。(一般NULL判断只能避免未初始化时使用函数指针。)

Keil调试笔记:数组越界_第1张图片

#include 
#define SYS_RAM_ADR_MIN 0x20000000UL
#define SYS_RAM_ADR_MAX 0x24080000UL
static bool ptr_check(const void* pntr)
{
	bool rslt;

	if ((uint32_t)pntr >= SYS_RAM_ADR_MIN && (uint32_t)pntr <= SYS_RAM_ADR_MAX) 
	{
		rslt = true;
	} 
	else 
	{
		rslt = false;
	}

	return rslt;
}

 

你可能感兴趣的:(嵌入式)