我正在尝试调试TI am 3358 MCU中的一个非常低级别的数据故障。它来自浮点数学。
该系统使用TI RTOS、GNU 7.3.1编译器和VFPv3(VFP是编译器设置吗?FP数学库吗?我不清楚浮点代码的生成)。因此,尽管我有列出片段的反汇编,但修复需要在C代码级别。
这是一个由两部分组成的问题:
首先我能正确理解助记符吗?为什么有些没有列出?
我注意到反汇编的操作码没有助记符。这是一个列表片段,现在还不需要详细介绍。只需注意助记符丢失了,我不认为它们是即时数据(我在对编译的代码进行逆向工程时添加的注释):
8003ced0: EEF1FA10 vmrs apsr_nzcv, fpscr ; Pull STAT reg to ARM MCU
8003ced4: DA000041 ble #0x8003cfe0 ; branch less-equal to x0x...3cfe0
8003ced8: EEFD7BE0 .word 0xeefd7be0 ; ??? What is this
8003cedc: EDC47A0A vstr s15, [r4, #0x28] ; Store S15 <- r4+28 = st->f2.z
8003cee0: E584702C str r7, [r4, #0x2c] ; Store r7 <- r4+2c = st->f2.a
8003cee4: E3A03000 mov r3, #0
8003cee8: E5843030 str r3, [r4, #0x30]
8003ceec: EE07CA90 vmov s15, r12 ; ( I decode this below)
8003cef0: EEF80BE7 .word 0xeef80be7 ; ???
8003cef4: EE702BA2 .word 0xee702ba2 ; ???
8003cef8: EEFD7BE2 .word 0xeefd7be2 ; ???
8003cefc: EDC47A0D vstr s15, [r4, #0x34]
8003cf00: E5845018 str r5, [r4, #0x18]
8003cf04: EE701BA1 .word 0xee701ba1
8003cf08: EEFD7BE1 .word 0xeefd7be1
为了确保我能理解VFPv3助记符,我将地址8002ceec解码为以下内容:
8003ceec: EE07CA90 vmov s15, r12
VMOV (between ARM core register and single-precision register)
1110 unconditional
1110
0000 opt = 0: so this is TO the VFP
0111 Vn = 7 (but still need one more bit from nibble 1)
1100 Rt = 12
1010
1001 N = 1 (so n = 01111 =S15)
0000
它来自https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/VMOV--between-ARM-core-register-and-single-precision-register-?lang=en,(我很确定我得到了正确的,如果没有,欢迎任何更正)
那么,操作码0xeef80be7、0xee702ba2等是什么。?我无法在ARM书籍或网站中解读它们。遵循VFP/NEON模式,这是某种"无条件移动",但除此之外,我无法将比特模式与任何东西相匹配(而且网站对这种搜索非常不友好,我只能下载PDF并进行一点搜索)。
至于第二个问题,如果有一个简单明了的答案,我会很高兴被引导到正确的方向。
这是一个经过编译的C函数,它传递一个指向结构的指针。然后把成员从里面拉出来,做一些浮点运算。我确定结构地址存储在R4中。
示例原型是
int Function(int x, int y, struct *a);
并被称为(虚构的例子)
Function (5,5,&st[0]);
稍后在上
Function (5,7,&st[1]);
当访问第二个结构时,只有才会发生数据中止崩溃。访问第一个时从不使用。当VFP/Neon访问时,仅,而不是常规ARM寄存器。
进入代码的泥潭,R4是传入的结构的地址:
8003cfe0: EEFD7BE0 .word 0xeefd7be0 ; branch lands here
8003cfe4: EDC47A06 vstr s15, [r4, #0x18] ; CRASH Store S15 <- r4+24 = st->f1.x
8003cfe8: E584C01C str r12, [r4, #0x1c] ; r12 = st->f1.y
8003cfec: E3A03000 mov r3, #0
8003cff0: E5843020 str r3, [r4, #0x20]
我从指针中验证了成员的所有偏移,一切都是正确的。
重复,崩溃发生在地址8003cfe4,但仅当R4指针指向st[1]时,而从不在指向st[0]时。
我知道一个";数据中止";来自于尝试访问MMU未配置权限的内存。然而,其他一切都可以访问st[1]的所有成员。这只是当VFP代码试图访问is时。
事实上,在地址8003cedc、8003cee0和8003cee8,它们都在地址8003cfe4之前执行,可以愉快地访问该结构的成员。这让我相信这不是MMU访问问题?
这可能是缓存未命中的结果吗?或者是否有其他VFP问题试图在VFP系统和内存之间移动?或者是否存在协处理器尚未准备好的问题?
我能够通过删除所有浮点数学来避免这次崩溃。但这确实损害了应用程序的功能。我更希望浮点运算正确。
任何想法都将受到欢迎。
-Scotty
虽然我没有未知操作码的答案,但在第二部分的答案中,VFP协处理器必须在适当的边界上(在本例中为4个字节)将数据传输到它中。
虽然结构中的偏移正确对齐,但结构本身的底部却没有对齐。它在地址0x开始(由于打包)。。。2931。因此(+0x28)中40字节处的偏移位于奇数地址上。
只需添加
} __attribute__ ((aligned (4)))
在结构的最后声明解决了问题。
***更新***
我尝试了许多方法在代码片段中复制这个问题。在所有情况下,编译器都会生成代码,在将32位值移动到neon处理器之前,将其从内存移动到寄存器。
我能够通过一个内联汇编语句强行导致数据故障,该语句试图将未对齐的数据直接从奇数地址移动到neon处理器。
asm("vstr s15, [r0, #0x4] ");
(R0包含以x1结尾的基地址)
因此,这可能是GNU编译器中的一个优化错误。
如果其他人被这个问题咬了,我会发布这个。
由于我目前正在编写一个反汇编程序,我很快就抛出了问题的十六进制,得到了以下结果-没有保证或道歉!
EEF1FA10h,fmstat
DA000041h,ble #000083AC
EEFD7BE0h,ftosizd s15,d0
EDC47A0Ah,fsts s15,[r4+40]
E584702Ch,str r7, [r4+44]
E3A03000h,mov r3, 0
E5843030h,str r3, [r4+48]
EE07CA90h,fmsr s15,r12
EEF80BE7h,fsitod d0,s15
EE702BA2h,faddd d2,d0,d2
EEFD7BE2h,ftosizd s15,d2
EDC47A0Dh,fsts s15,[r4+52]
E5845018h,str r5, [r4+24]
EE701BA1h,faddd d1,d0,d1
EEFD7BE1h,ftosizd s15,d1