ARMv8初始化调用memset清BSS段时PC指针异常跳转求助
从现象看,执行str q0, [x0]指令后PC跳转到spi_send,这是异常触发后的错误跳转,而非正常代码流,核心原因大概率是内存访问异常+异常向量表被破坏,下面是具体分析和排查步骤:
核心原因推测
1. BSS段范围错误,覆盖了中断向量表
如果链接脚本中__bss_start__/__bss_end__的定义错误,把中断向量表所在的内存区域包含进了BSS段,那么memset清零时会覆盖向量表中的异常入口地址。当后续触发异常时,CPU会读取被覆盖后的地址(正好是spi_send的0x7e1a00),从而跳转到该函数。
2. 未对齐内存访问触发Data Abort,且异常入口被破坏
ARMv8的NEON指令(比如str q0)要求内存地址16字节对齐,如果__bss_start__的地址未对齐到16字节,执行str q0, [x0]会直接触发Data Abort异常。此时CPU会从向量表中读取Data Abort的处理入口,若该入口已被memset清零或覆盖为spi_send的地址,就会出现跳转到spi_send的现象。
具体排查步骤
步骤1:验证BSS段范围是否合法
在执行memset前,用gdb打印BSS段的起止地址和长度:
p/x __bss_start__ p/x __bss_end__ p/x (__bss_end__ - __bss_start__)
同时查看ARMv8的向量表基地址(VBAR寄存器),确认BSS段是否覆盖了向量表区域:
p/x $vbar
如果__bss_start__小于$vbar且__bss_end__大于$vbar + 0x400(AArch64向量表长度固定为0x400),说明BSS段包含了向量表,需要修改链接脚本修正BSS段的范围。
步骤2:检查BSS起始地址的对齐性
执行以下指令查看__bss_start__是否16字节对齐:
p/x __bss_start__ & 0xf
如果结果不为0,说明地址未对齐,会触发NEON指令的未对齐访问异常。此时需要修改链接脚本,确保BSS段起始地址对齐到16字节(比如在链接脚本中添加.align 4)。
步骤3:检查异常向量表的原始内容
在执行memset前,查看向量表中Data Abort异常的入口地址(AArch64 EL1下,Data Abort入口为$vbar + 0x200):
x/i $vbar + 0x200
如果该地址的内容已经是spi_send的0x7e1a00,说明向量表在memset执行前就已被破坏;如果是正常的异常处理函数,那就是memset执行时覆盖了向量表。
临时修复方案:使用手动对齐的清零代码
暂时替换掉memset调用,用手动处理对齐的汇编代码清零BSS段,避免未对齐访问和误覆盖向量表:
// 手动清零BSS段(处理对齐) ldr x0, =__bss_start__ // BSS起始地址 mov x1, #0 // 清零值 ldr x2, =__bss_end__ // BSS结束地址 sub x2, x2, x0 // 总长度 // 处理头部未对齐的字节 and x3, x0, #0xf // 计算偏移量(0-15) cbz x3, 2f // 已对齐则跳过 neg x3, x3 // 计算需要补齐的字节数 add x3, x3, #16 cmp x3, x2 csel x3, x2, x3, ls // 取较小值(避免超过总长度) 1: strb w1, [x0], #1 // 逐字节清零 subs x3, x3, #1 b.ne 1b sub x2, x2, x3 // 更新剩余长度 // 批量清零16字节(对齐后) 2: mov q0.16b, x1 // 将清零值加载到NEON寄存器 3: str q0, [x0], #16 // 批量写16字节 subs x2, x2, #16 b.hs 3b // 剩余长度≥16则继续 // 处理尾部剩余的字节 cbz x2, 5f 4: strb w1, [x0], #1 subs x2, x2, #1 b.ne 4b 5:
内容的提问来源于stack exchange,提问作者asabxkh74142057 pgww18176

