I’m using an ARM Cortex-A9 processor ZYNQ 7000. The processor raises an data abort exception if invalid address is accessed. The exception handler captures the data abort address, but if the program runs freely, the captured data abort address is wrong.
To find the data abort address, the link register is used. In the exception handler, lr - 8
is assigned to the DataAbortAddr
global variable, as in the bsp library.
While this captured address is correct if the program runs step by step via a debugger, or if there is a breakpoint before the invalid address accessing instruction, it is wrong if the program runs freely.
Code for demo:
extern uint32_t DataAbortAddr;
void MyDataAbortHandler(void *arg) {
(void)arg;
printf("DataAbort at %pn", (void *)(uintptr_t)DataAbortAddr);
PrintBacktrace();
SystemReset();
}
int TriggerDataAbort() {
PrintBacktrace();
printf("Accessing invalid addressn");
int *invalidAddr = (int *)0x30001;
int value = *invalidAddr;
return value;
}
int main() {
Init();
TriggerDataAbort();
...
}
The assembly:
int TriggerDataAbort() {
1010a0: e92d4800 push {fp, lr}
1010a4: e28db004 add fp, sp, #4
1010a8: e24dd060 sub sp, sp, #96 ; 0x60
...
10112c: e51b3010 ldr r3, [fp, #-16]
101130: e5933000 ldr r3, [r3]
101134: e50b3014 str r3, [fp, #-20] ; 0xffffffec ; the exact instruction that causes data abort exception
...
}
int main() {
101154: e3a02000 mov r2, #0
101158: e3001ff0 movw r1, #4080 ; 0xff0
...
101180: ebffffc6 bl 1010a0 <TriggerDataAbort>
101184: e3040378 movw r0, #17272 ; 0x4378
101188: e3400010 movt r0, #16
...
}
If the program runs freely, the captured DataAbortAddr is always 0x101188, which is wrong. If it runs step by step, or hits a breakpoint at int value = *invalidAddr
, DataAbortAddr is 0x101134, which is correct.
What might be the reason of the wrong DataAbortAddr? How to correctly capture the DataAbortAddr when the program runs freely?