ARM(XScale)转i386(Vortex86)后for循环ChanId异常,疑与malloc有关
问题背景与现象
将代码从ARM架构的Intel XScale移植到i386架构的Vortex86 CPU后,启动阶段的初始化循环出现异常:
- 需为4个通道(CH0-CH3)初始化对应采样频率的缓冲区,各缓冲区大小:
- 100Hz:6000个int类型样本 →
6000 * sizeof(int) - 20Hz:1200个int类型样本 →
1200 * sizeof(int) - 1Hz:3600个int类型样本 →
3600 * sizeof(int) - 10Min:144个int类型样本 →
144 * sizeof(int)
- 100Hz:6000个int类型样本 →
- 循环初始化时,ChanId=0正常;ChanId=1执行成功,但日志输出
Ch0 Initialised,后续通道均出现类似ChanId被异常覆盖的问题 - 改用硬编码逐个调用
Initialise函数则完全正常,相同代码在ARM平台运行无问题
异常循环代码
// THIS DOESN'T WORK.. I don't know why... // Initialise() performs a malloc x 4 for each channel for the sizeof(buffer) // Initialise() also opens a serial port DeviceNames[ChanId], with the FD returned for (ChanId=0; ChanId < NUM_CHANNELS; ChanId++) { if ((fds[ChanId] = Initialise(ChanId, DeviceNames[ChanId])) == -1) { syslog(LOG_INFO, "Failed to initialise RT Ch%d", ChanId); return -1; } else { syslog(LOG_INFO, "Ch%d Initialised", ChanId); } }
正常的硬编码代码
if ((fds[0] = Initialise(0, DeviceNames[0])) == -1) { syslog(LOG_INFO, "Failed to initialise RT Ch0"); return -1; } if ((fds[1] = Initialise(1, DeviceNames[1])) == -1) { syslog(LOG_INFO, "Failed to initialise RT Ch1"); return -1; } if ((fds[2] = Initialise(2, DeviceNames[2])) == -1) { syslog(LOG_INFO, "Failed to initialise RT Ch2"); return -1; } if ((fds[3] = Initialise(3, DeviceNames[3])) == -1) { syslog(LOG_INFO, "Failed to initialise RT Ch3"); return -1; }
排查与解决方案
核心怀疑点:内存越界破坏栈变量
你的猜测完全正确——ChanId作为栈上的循环变量,大概率被Initialise函数中的内存操作破坏了。ARM与i386架构的栈布局、内存对齐规则存在差异,导致原本在ARM上未触发的越界问题,在i386平台直接暴露。
具体排查方向
检查
Initialise函数的缓冲区操作- 核对每个通道的缓冲区大小计算:确认是否误将
sizeof(sample)写成sizeof(sample*),或是在缓冲区初始化时出现下标越界(写入超过malloc分配大小的内容) - 排查
Initialise内的局部数组溢出、指针错误解引用等可能越写内存的行为
- 核对每个通道的缓冲区大小计算:确认是否误将
验证
ChanId的篡改情况- 在循环内每次调用
Initialise前后打印ChanId的地址和值,确认是否被篡改:for (ChanId=0; ChanId < NUM_CHANNELS; ChanId++) { syslog(LOG_INFO, "Before Initialise: ChanId=%d, addr=%p", ChanId, &ChanId); if ((fds[ChanId] = Initialise(ChanId, DeviceNames[ChanId])) == -1) { syslog(LOG_INFO, "Failed to initialise RT Ch%d", ChanId); return -1; } syslog(LOG_INFO, "After Initialise: ChanId=%d, addr=%p", ChanId, &ChanId); syslog(LOG_INFO, "Ch%d Initialised", ChanId); } - 如果调用后
ChanId值被修改,直接坐实内存越界篡改栈的问题
- 在循环内每次调用
排查编译器与内存对齐差异
- 对比ARM与i386平台的编译器优化选项、内存对齐参数:比如ARM默认栈对齐8字节,i386为4字节,变量栈位置偏移不同,导致越界操作刚好命中
ChanId - 尝试关闭编译器优化(如
-O0),若异常消失,说明优化后的栈布局触发了越界命中
- 对比ARM与i386平台的编译器优化选项、内存对齐参数:比如ARM默认栈对齐8字节,i386为4字节,变量栈位置偏移不同,导致越界操作刚好命中
修复建议
- 优先根治
Initialise内的内存越界:仔细核对malloc的缓冲区大小,确保所有写入操作都在分配的内存范围内 - 临时规避方案:将
ChanId声明为全局变量,或用volatile修饰(仅绕过问题,无法根治越界) - 启用内存检测工具:在i386平台用
valgrind或GCC的-fsanitize=address选项,直接定位越界的具体位置
内容的提问来源于stack exchange,提问作者AndyOudyn
相关产品推荐
相关产品推荐

