ARM Technical Support Knowledge Articles

Trapping and identifying divide-by-zero errors

Applies to: RealView Development Suite (RVDS)

Answer

It is often important to be able to eliminate any possible (accidental) division by zero errors in code, especially for embedded systems, which may not be able to recover easily. The following shows how to catch division by zero errors in:

  • integer division
  • (software) floating point division

and also shows how to identify which line of your source code caused the error.

  1. Integer division

    Integer division by zero throws a signal by default, so you must either:

    1. re-implement __rt_raise() to deal with the signal in a thread-safe manner
    2. re-implement __aeabi_idiv0 so that division by zero returns some standard result (such as 0) rather than raising a signal.

    Integer division is implemented by calling the C library functions __aeabi_idiv and __aeabi_uidiv. These check for divison by zero. If integer division by zero is detected, a branch to __aeabi_idiv0 is made. So to trap the division by zero, all you need to do is place a breakpoint on __aeabi_idiv0.

    N.B. As a result of changes from the ABI for the ARM Architecture, __rt_div0 is now called __aeabi_idiv0__rt_sdiv is now called __aeabi_idiv  and __rt_udiv is now called __aeabi_uidiv.

    These aeabi functions such as __aeabi_idiv0 are documented in the Run-time ABI for the ARM Architecture.

    This can be found at: http://www.arm.com/products/DevTools/ABI.html

    N.B. These names could change in a future release.

    On entry into __aeabi_idiv0, the Link Register (LR,R14) will contain the address of the instruction _after_ the call to the__aeabi_uidiv division routine in your application code. Therefore, to identify the offending line in your source code, you can simply look up the line of C code in the debugger at that address given by LR.

    If you need to examine parameters, etc, and save them for post-mortem debugging, you can trap __aeabi_idiv0. You can intervene in all calls to __aeabi_idiv0 by using the $Super$$ and $Sub$$ mechanism. This is documented in chapter 4 of the Linker and Utilities Guide of your tools (e.g. ADS or RVDS).

    $Super$$__aeabi_idiv0 identifies the original unpatched function __aeabi_idiv0(). Use this to call the original function directly.
    $Sub$$__aeabi_idiv0 - identifies the new function that will be called instead of the original function __aeabi_idiv0(). Use this to add processing before or after the original function.

    Example:

    extern void $Super$$__aeabi_idiv0(void);

    /* this function will be called instead of the original __aeabi_idiv0() */
    void $Sub$$__aeabi_idiv0()
    {
       // insert code to process a divide by zero
       ...

       // call the original __aeabi_idiv0 function
       $Super$$__aeabi_idiv0();
    }

    An alternative place to intercept divide by zero is in __rt_raise(). As documented in the Compilers and Libraries Guide, the interface to __rt_raise looks like this:

    void __rt_raise(int signal, int type)

    In the case of __aeabi_idiv0(), it simply calls __rt_raise(2, 2). Thus, in your implementation of __rt_raise, you could check for (signal == 2) && (type == 2) to determine if a divide by zero has occurred.

  2. Floating point division (using the software floating point libraries)

    To find the address of the function in your application code which contains the arithmetic operation which resulted in the exception, simply place a breakpoint on the function "_fp_trapveneer", then look at LR.

    This is a function within the C library which is called if an exception occurs. On entry into this function, the registers are in the state they were when the exception occurred. So you can then simply view the registers when the breakpoint is hit.

    Example:

    ---- main.c ----
    #include <stdio.h>
    #include <fenv.h>

    int main(void)
    {
       float a, b, c;

       // Trap the Invalid Operation exception and untrap all other exceptions:
       __ieee_status(FE_IEEE_MASK_ALL_EXCEPT, FE_IEEE_MASK_DIVBYZERO);

       c = 0;
       a = b / c;
       printf("b / c = %f, ", a);

       return 0;
    }
    ---- end main.c ----

    Built/executed with:

    • armcc -g main.c -o main.axf (for RVCT 1.2 and earlier) or:
    • armcc -g main.c -o main.axf --fpmode ieee_full (for RVCT 2.0 and later)

    armsd main.axf
    armsd: break @_fp_trapveneer
    armsd: go
    Breakpoint #1 at PC = 0x0000c77c (_fp_trapveneer + 0)
    _fp_trapveneer
    +0000 0x0000c77c: 0xe92d5000  .P-. :  * stmfd    r13!,{r12,r14}
    armsd: reg
      r0  = 0x00000000  r1  = 0x00000000  r2  = 0x00000000  r3  = 0x00000000
      r4  = 0x0000cb14  r5  = 0x00000000  r6  = 0x00000000  r7  = 0x00000000
      r8  = 0x00000000  r9  = 0x00000000  r10 = 0x00000000  r11 = 0x00000000
      r12 = 0x04000004  r13 = 0x07ffffe8  r14 = 0x000080c8
      pc  = 0x0000c77c  cpsr = %nzcvqIFt_SVC  spsr = %nzcvqift_Reserved_00

    where r14 = LR = 0x80c8 = the address of the instruction after the "bl _f2d".

    If you need to examine parameters, etc, and save them for post-mortem debugging, you can intercept _fp_trapveneer like this:

             AREA foo, CODE

             IMPORT |$Super$$_fp_trapveneer|
             EXPORT |$Sub$$_fp_trapveneer|
     |$Sub$$_fp_trapveneer|

     ;; Add code to save whatever registers you need here
     ;; Take care not to corrupt any needed registers

             B |$Super$$_fp_trapveneer|

             END

Article last edited on: 2008-09-09 15:47:47

Rate this article

[Bad]
|
|
[Good]
Disagree? Move your mouse over the bar and click

Did you find this article helpful? Yes No

How can we improve this article?

Link to this article
Copyright © 2011 ARM Limited. All rights reserved. External (Open), Non-Confidential