power平台访问全局变量出现signal11

本文深入探讨了一个在特定条件下导致Signal 11错误的进程崩溃问题,并详细记录了从现象分析到最终定位问题根源的过程。通过对汇编代码的研究,发现了寄存器被覆盖的现象,最终锁定为栈帧被非法修改导致的问题,并通过代码调整成功解决了这个问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近,遇到一个进程crash的CR(跑十来个小时才会crash,且每次都在同一个点crash)。crash本是很正常的事情,但是这次crash却让我莫名其妙,crash的点代码如下:

ALA_BpdDstore[0] = ((BT_UPM<<8)|(BT_CEI_PUSCH_Decode<<16)|1);  //ALA_BpdDstore[]是一个全局数组,右操作数全是宏。

你告诉我访问一个全局符号如何会产生signal11?

扫了眼出事函数的汇编(r24用来保存这个全局符号的地址):

3f 00 10 fc         lis     r24,4348            //给r24寄存器赋值0x10fcxxxx
3b 18 07 a4      addi    r24,r24,1956          //给r24寄存器赋值0x10fc07a4


90 f8 00 00     stw     r7,0(r24)           //访问r24寄存器,程序在此crash

看到了吧?先把0x10fc07a4赋值给r24寄存器,然后通过r24访问内存。0x10fc07a4是一个合法的.data段地址,访问一个合法的地址怎么会出现signal11呢?

但是,平台log直接表明crash时寄存器被踩!crash时r24的值为0x00170017,而且后面连着几个寄存器的值都为0x00170017:

......

GPR24 = 00170017   GPR25 = 00170017   GPR26 = 00170017   GPR27 = 00170017

......

于是,重新研究这个函数和汇编,最后终于得出root cause。先奉上函数框架:

void CEI_PUSCH_Decode(void *Msg, u32 length, u32 airNum)

{//crash函数

    ......

    for(msg3Idx = 0; msg3Idx < MAX_NB_MSG3; ++msg3Idx)

    {

        ......

        TL_GetULCrnti(){//这里面有一串子函数调用,大约四层}

        ......

        ALA_BpdDstore[0] = ((BT_UPM<<8)|(BT_CEI_PUSCH_Decode<<16)|1);  //crash语句

        ......

     }

     ......

}

再结合该函数的汇编来看,给r24赋值以及访问[r24]之间隔了很多条汇编指令,于是猜想gcc做了优化,循环之前对r24进行一次性赋值,后面每次访问[r24]的时候直接取其内容!

上面一串子函数调用如下:

TL_GetULCrnti() ==> MAC_RLC_UL_GetCrnti() ==> FSO_SBL2_MAC_RLC_UL_GetCrnti==>FSO_SBL2_MACUL_GetCrntiFromDeMuxProcess()

挨个观察他们的汇编,其中发现MAC_RLC_UL_GetCrnti()函数中有如下保存、恢复寄存器的汇编指令:

93 01 00 40 stw     r24,64(r1)             //保存r24到栈帧
93 21 00 44 stw     r25,68(r1)
93 41 00 48 stw     r26,72(r1)
....
83 01 00 a0      lwz     r24,160(r1)           //从栈帧上恢复r24
83 21 00 a4      lwz     r25,164(r1)
83 41 00 a8      lwz     r26,168(r1)
....

于是,发现问题所在:很可能这一串调用过程中栈帧被踩,导致恢复寄存器r24~r26时出错!

但是问题来了,谁会踩这个函数的栈帧?由于栈生长方向向上,所以应该是被子函数踩了(最后一子函数,有一个局部数组,我们的代码的确存在越界风险)。

那么接着问题又来了,子函数栈帧往上一路踩啊踩,怎么没把链接寄存器(lr)给踩了呢?从汇编中找到了答案:最后一个函数不涉及子函数调用,不用保存链接寄存器,而倒数第二个函数非常短小,汇编代码被优化为区区两行,基本就一条跳转指令。

最后,把最后一个子函数FSO_SBL2_MACUL_GetCrntiFromDeMuxProcess()中数组越界加上保护,signal11果然不再出现。

int iUEPCI_mod2 = (cell * 3 + sector) % 2; int tUEPCI_mod2 = (cell * 3 + sector) % 2; if(iUE->UE_SRS_Info.SRSIndex[0] == tUE->UE_SRS_Info.SRSIndex[0] && (iUEPCI_mod2 == tUEPCI_mod2)) { //outFile<< iUE->ServingBSID<< "\t"<<iUE->ServingSectorID<< "\t"<<iUE->CID<< "\t"<<iUE->IsStrongCellAndBeam[cell][sector] << "\t"<< endl; if(iUE->IsStrongCellAndBeam[cell][sector] == true) //strong interference { if(Min_UE_Ant_Port == 1) { iReal = iUE->STChannCoeffperRB_SRS_InterUE_re[cell][sector][i][0][iRB_Idx]; iImag = iUE->STChannCoeffperRB_SRS_InterUE_im[cell][sector][i][0][iRB_Idx]; rx_I_sum =((iReal*iReal) + (iImag*iImag)); } else if(Min_UE_Ant_Port == 2) //rank2 { rx_i_sum0 = ((0.5*iUE->STChannCoeffperRB_SRS_InterUE_re[cell][sector][i][0][iRB_Idx]*iUE->STChannCoeffperRB_SRS_InterUE_re[cell][sector][i][0][iRB_Idx]) //sum of signal power for layer0, power half +(0.5*iUE->STChannCoeffperRB_SRS_InterUE_im[cell][sector][i][0][iRB_Idx]*iUE->STChannCoeffperRB_SRS_InterUE_im[cell][sector][i][0][iRB_Idx])); rx_i_sum1 = ((0.5*iUE->STChannCoeffperRB_SRS_InterUE_re[cell][sector][i][1][iRB_Idx]*iUE->STChannCoeffperRB_SRS_InterUE_re[cell][sector][i][1][iRB_Idx]) //sum of signal power for layer1, power half +(0.5*iUE->STChannCoeffperRB_SRS_InterUE_im[cell][sector][i][1][iRB_Idx]*iUE->STChannCoeffperRB_SRS_InterUE_im[cell][sector][i][1][iRB_Idx])); rx_I_sum = rx_i_sum0 + rx_i_sum1; } } else if(iUE->IsStrongCellAndBeam[cell][sector] == false) //weak interference { rx_I_sum = iSRS_Rx_Power * Min_UE_Ant_Port; } } 请帮我检查下上述代码的问题
最新发布
03-19
<think>好的,我现在需要分析用户提供的C/C++代码中的逻辑错误和潜在问题,重点检查变量初始化、数组访问越界以及条件判断的正确性。用户还提到了几个参考引用,可能需要结合这些内容来辅助分析。 首先,用户提到了引用[1]关于未初始化的全局变量的位置,这可能涉及到变量初始化的检查。全局变量如果未初始化,在C/C++中会被默认初始化为0,但如果局部变量未初始化,值是不确定的,可能导致不可预知的行为。所以需要检查代码中是否有未初始化的局部变量。 接下来,引用[2]提到控制流分析可以检测无效代码,比如孤立的节点。这可能涉及到检查是否有无法到达的代码块,或者条件判断逻辑错误导致某些分支永远无法执行。例如,条件判断中的逻辑运算符误用,或者条件永远为真/假。 引用[3]中的动态数组初始化宏VecInit,这涉及到变量初始化的正确性。如果用户代码中使用类似的结构,需要确认是否正确调用了初始化函数,避免使用未初始化的指针或内存。例如,如果动态数组的data指针未初始化,可能导致访问无效内存。 引用[4]讨论C++指针的误区,包括野指针、内存泄漏等。需要检查指针是否正确初始化,是否在释放后设置为NULL,以及是否有数组越界访问的问题,比如访问数组时索引超出分配的内存范围。 接下来,我需要考虑具体的检查步骤: 1. **变量初始化检查**: - 检查所有局部变量是否在使用前被正确初始化。例如,int a; 直接使用a可能导致未定义行为。 - 检查动态分配的内存是否初始化,如malloc后是否用memset初始化,或calloc是否正确使用。 - 检查结构体或类的成员变量在构造函数中是否初始化,尤其是指针类型。 2. **数组访问越界**: - 检查所有数组访问的索引是否在有效范围内。例如,声明为int arr[10],访问arr[10]会导致越界。 - 动态数组需要确保capacity足够,在添加元素时是否有检查并扩容的逻辑,如引用[3]中的VecInit和可能的扩容函数。 - 检查循环中的终止条件是否正确,例如循环次数是否超过数组长度。 3. **条件判断正确性**: - 检查条件表达式中的逻辑运算符是否正确,比如将赋值=误用为比较==。 - 检查条件是否有可能永远为真或假,例如if (a > 0 && a < 10) 但a在此前的逻辑中已经被限制为只能是正数。 - 检查switch语句是否有break,避免case穿透,或者是否有未处理的default情况。 4. **指针和内存管理**: - 检查指针是否在使用前被正确初始化,避免野指针。 - 检查动态分配的内存是否被正确释放,避免内存泄漏。 - 检查是否有解引用空指针的情况,例如在指针未分配内存或已释放后访问。 5. **控制流分析**: - 使用控制流图检查是否有无法到达的代码块,如return后的代码,或条件永远无法满足的分支。 - 检查函数是否有正确的返回值路径,特别是非void函数的所有可能路径都有返回值。 举个例子,假设用户有如下代码片段: ```c int main() { int *ptr; *ptr = 5; // 野指针,未初始化 int arr[3] = {1,2,3}; for(int i=0; i<=3; i++) { // 越界访问arr[3] arr[i] *= 2; } if (ptr = NULL) { // 误用=代替== printf("Pointer is NULL\n"); } return 0; } ``` 这里的问题包括: - ptr未初始化就被解引用,导致野指针。 - 循环条件i<=3导致访问arr[3],越界。 - 条件判断中误用赋值操作符,导致ptr被赋值为NULL,条件永远为假。 结合引用[2],控制流分析可以发现if条件中的错误,因为ptr被赋值为NULL后,条件永远为假,所以printf可能成为无效代码。同时,引用[4]中的指针误区也适用,这里存在野指针的问题。 另外,如果代码中有动态数组的使用但没有正确初始化,比如未调用VecInit,可能导致size或capacity未正确设置,后续操作引发越界或空指针访问。 总结来说,分析需要逐一检查变量初始化、数组边界、条件逻辑,并结合静态分析工具或手动检查控制流来识别潜在问题。此外,参考引用中的内容提供了具体的检查方向,如动态数组初始化、指针误用等,需要特别关注这些方面。</think>### C/C++代码逻辑错误与潜在问题分析步骤 #### 1. **变量初始化检查** - **局部变量**:确保所有局部变量在使用前被显式初始化。例如: ```c int x; // 未初始化 printf("%d", x); // 未定义行为 ``` 需修正为: ```c int x = 0; // 显式初始化 ``` - **动态内存**:使用 `malloc` 分配内存后,需初始化数据;或使用 `calloc` 自动初始化为零[^3]: ```c int *arr = (int*)malloc(10 * sizeof(int)); memset(arr, 0, 10 * sizeof(int)); // 手动初始化 ``` - **结构体/类成员**:在构造函数中初始化指针为 `NULL`,避免野指针[^4]: ```cpp class Example { public: int *ptr; Example() : ptr(nullptr) {} // 初始化指针 }; ``` #### 2. **数组访问越界检查** - **静态数组**:确保索引在 `[0, size-1]` 范围内: ```c int arr[5]; for (int i=0; i<5; i++) { // 正确:i从0到4 arr[i] = i; } ``` - **动态数组**:检
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值