踩坑记 BSS段的初始化

前言

接手一个项目,调试全靠串口日志,测试同事测试产品的时候无法拿到日志,刚好产品RAM够大,且刚好有SD卡。所以就诞生了将日志缓存在RAM上,在特定条件下将它写到SD卡上的想法。

开工。

写完代码之后发现机器偶尔会无法启动,无法开机,无任何日志。

有的时候是烧录完成代码后重启无法启动,有些情况下是烧录完成代码后运行正常,放置一段时间后无法启动

有时候有些无法启动的机器放置一段时间又成功启动

一段一段代码,屏蔽,编译,验证,发现一个非常无法理解的事,TFCardLogBufferPut函数注释后设备可以正常启动。

看代码:

1
2
3
4
5
6
7
8
9
10
11
#define TF_CARD_LOG_BUFFER_SIZE     (64*1024ul)
uint8_t tfCardLogBuffer[TF_CARD_LOG_BUFFER_SIZE] = {0};
uint32_t tfCardLogBufferIndex = 0;

void TFCardLogBufferPut(uint8_t data) {
tfCardLogBuffer[tfCardLogBufferIndex] = data;
tfCardLogBufferIndex++;
if( tfCardLogBufferIndex>=(sizeof(tfCardLogBuffer)/sizeof(tfCardLogBuffer[0]))-1 ) {
tfCardLogBufferIndex = 0;
}
}

我实在无法理解,这段代码是如何导致设备无法启动的,后面搁置了一段时间,遂归结于小众芯片不完善导致。

但是后面这个需求实在是过于旺盛,遂重新开始验证代码。

当时代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// main.c
int main(int argc, char **argv) {

hal_uartInit();
ax32xx_uart0SendByte('a');
ax32xx_uart0SendByte('b');
hal_uartSendData('1');
hal_uartSendData('2');
ax32xx_uart0SendByte('c');

hal_sysInit();

//.... some code here
}

// uart.c
void hal_uartSendData(u8 data)
{
ax32xx_uart0SendByte(data);

TFCardLogBufferPut(data);
}

void ax32xx_uart0SendByte(u8 data)
{
R_UART_DATA0 = data;
while((R_UART_PEND0 & 0x2)==0);
R_UART_PEND0 |= 1;
}

#define TF_CARD_LOG_BUFFER_SIZE (64*1024ul)
uint8_t tfCardLogBuffer[TF_CARD_LOG_BUFFER_SIZE] = {0};
uint32_t tfCardLogBufferIndex = 0;

void TFCardLogBufferPut(uint8_t data) {
tfCardLogBuffer[tfCardLogBufferIndex] = data;
tfCardLogBufferIndex = (tfCardLogBufferIndex>=(sizeof(tfCardLogBuffer)/sizeof(tfCardLogBuffer[0]))-1) ? 0 : tfCardLogBufferIndex+1;
}

运行后日志是这样的:

1
2
3
4
5
# 正常运行 的日志
ab12c ...

# 无法启动 的日志
ab1

后面我就猜想了很多:

  • 这个soc不能支持这么大的数值 64KB
  • 这个位置的内存被改写了
  • 和冷启动热启动有关
  • 芯片异常

后面我猜想,试试把TFCardLogBufferPut函数里面的tfCardLogBufferIndex的数值和data数值输出出来看看。

这一看不得了,越界了!

代码:

image-20231020190520640

输出日志:

1
ab1z31m00327300

这个tfCardLogBufferIndex越界了啊!而且初始化赋0并没有成功!这就非常坑爹了!

后面尝试在调用这个函数之前再次赋0,发现程序运行正常,设备也正常启动。

查看map文件这个数组和变量也是存放在BSS段的,这就非常令人费解了。

看到这里我直接就怀疑,就是这个芯片的锅。

但是别急这还不是让人最震惊的!后面还有让人更加鼻血飙升的。

最后找FAE排查问题很久,最后发现BSS段在hal_sysInit函数内初始化。

看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void ax32xx_sysInit(u32 *saddr,u32 *eaddr)
{
debg("sys init\n"); // 这个debg会调用 hal_uartSendData 函数输出日志

//...

ax32xx_wdtClear();

//-----cache set
ax32xx_sysIcacheInit();
ax32xx_sysDcacheInit();
//-----bss clear
ax32xx_sysBSSClear();

// ...
}

实在是震惊,无法理解这个RAM的初始化为什么放在main函数里面,实在无法理解进入main函数了,c语言环境都没有OK。

最烦的就是这种xx芯片,总是会有一些让人血压飙升的操作。

参考ARM MDK在编译的时候BSS初始化的操作是放在__main函数里面,需要注意的是这个__main不是c语言里面的main函数,它是从MDK自带的库文件里链接的一个函数。这个__main函数会初始化这些段,并调用__rt_entry,最终在__rt_entry中将会调用c语言里面的main函数。

教训总结

在对数组进行访问的时候一定要先检查范围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 原函数
void TFCardLogBufferPut(uint8_t data) {
tfCardLogBuffer[tfCardLogBufferIndex] = data;
tfCardLogBufferIndex = (tfCardLogBufferIndex>=(sizeof(tfCardLogBuffer)/sizeof(tfCardLogBuffer[0]))-1) ? 0 : tfCardLogBufferIndex+1;
}

// 改后
void TFCardLogBufferPut(uint8_t data) {
if( tfCardLogBufferIndex>=(sizeof(tfCardLogBuffer)/sizeof(tfCardLogBuffer[0]))-1 ) {
tfCardLogBufferIndex = 0;
}
tfCardLogBuffer[tfCardLogBufferIndex] = data;
tfCardLogBufferIndex++;
}

踩坑记 BSS段的初始化
https://www.oikiou.top/2023/96364bb6/
作者
Oikiou
发布于
2023年10月20日
许可协议