Pergunta

I'm trying to debug a program running on a bare-metal ARM platform using gdb. At some point an SWI (Software Interrupt) exception is generated. However the back-trace doesn't show what generated the exception as you can see:

(gdb) c

Continuing. 
^C 
Program received signal SIGTRAP, Trace/breakpoint trap. 0xffff0008 in ?? ()
(gdb) bt
#0  0xffff0008 in ?? ()

The program running on the ARM was compiled with arm-none-linux-gnueabi-gcc -O2 -c -ggdb, I also tried using -O0 with the same result.

How can I get a meaningful call stack? Is there another way to find what is generating this exception?

Foi útil?

Solução

To understand the issue gdb has with producing a stack trace, you first have to understand how gdb produces a stack trace. A compiler use a standard prologue and epilogue when it creates a C function, this is assembler code at function entry and exit. Part of this is to save the lr on the stack, reserve space for local variables and link the previous frame pointer or fp. These stack frames provide a type of linked list that is root-ed with the fp and normally terminated with zero. This depends on the ABI (see -mabi). The specifics are slightly different for each ARM ABI type, but the concepts are similar.

So, when the SWI (or any exception happens), it completely interrupts the flow of the C compiled code and the frame pointer list can be difficult to decode, especially in the case of a stack corruption. Also, gdb does not decode the context of the program. Some systems may change the frame pointer and immediately switch from an exception mode to system/supervisor mode. The sp of the exception may even be used as a scratch register. When the SWI is entered, part of the job of the handler will be to save the user registers (r0-r12). In the case of a multi-tasking O/S, this may result in a complete change of the user stack, from one task to another.

You can always determine the faulting/causing instruction by examining the lr in the exception mode. This specified in the ARM Architechure, and is the same for any ARM CPU. 0xfff0008 is the default SWI handler address (when using a high memory vector table). Excepts from the SWI in the ARM ARM (architechure reference manual) follows,


A2.6.4 Software Interrupt exception

The Software Interrupt instruction (SWI) enters Supervisor mode to request a particular supervisor (operating system) function. When a SWI is executed, the following actions are performed:

   R14_svc    = address of next instruction after the SWI instruction
   SPSR_svc   = CPSR
   CPSR[4:0]  = 0b10011                 /* Enter Supervisor mode */
   CPSR[5]    = 0                       /* Execute in ARM state */
                                        /* CPSR[6] is unchanged */
   CPSR[7]    = 1                       /* Disable normal interrupts */
                                        /* CPSR[8] is unchanged */
   CPSR[9]    = CP15_reg1_EEbit         /* Endianness on exception entry */
   if high vectors configured then
       PC     = 0xFFFF0008
   else
       PC     = 0x00000008

To return after performing the SWI operation, use the following instruction to restore the PC (from R14_svc) and CPSR (from SPSR_svc) and return to the instruction following the SWI:

       MOVS PC,R14

As you can see, R14_svc which is the lr when in supervisor mode, is set to the SWI instruction+4. This is so that the normal return will restart the following instruction. By examining where the lr is, you can determine where the SWI occurred. If the user stack is not corrupted, you can link your supervisor stack to the user stack using the ABI the compiler is using via the fp. If you do this, then gdb can give a stack trace. However, in the system under discussion, there is no SWI support code.

You could also write gdb macros to give a stack trace in this situation. However, I hope it is clear that gdb would have a difficult task to do this generically.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top