/* * arch/arm/kernel/unwind.c * * Copyright (C) 2008 ARM Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Stack unwinding support for ARM * * An ARM EABI version of gcc is required to generate the unwind * tables. For information about the structure of the unwind tables, * see "Exception Handling ABI for the ARM Architecture" at: * * http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html */ #ifndef __CHECKER__ #if !defined (__ARM_EABI__) #warning Your compiler does not have EABI support. #warning ARM unwind is known to compile only with EABI compilers. #warning Change compiler or disable ARM_UNWIND option. #elif (__GNUC__ == 4 && __GNUC_MINOR__ <= 2) #warning Your compiler is too buggy; it is known to not compile ARM unwind support. #warning Change compiler or disable ARM_UNWIND option. #endif #endif /* __CHECKER__ */ #include #include #include #include #include #include #include #include #include #include /* Dummy functions to avoid linker complaints */ void __aeabi_unwind_cpp_pr0(void) { }; EXPORT_SYMBOL(__aeabi_unwind_cpp_pr0); void __aeabi_unwind_cpp_pr1(void) { }; EXPORT_SYMBOL(__aeabi_unwind_cpp_pr1); void __aeabi_unwind_cpp_pr2(void) { }; EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2); struct unwind_ctrl_block { unsigned long vrs[16]; /* virtual register set */ const unsigned long *insn; /* pointer to the current instructions word */ int entries; /* number of entries left to interpret */ int byte; /* current byte number in the instructions word */ }; enum regs { #ifdef CONFIG_THUMB2_KERNEL FP = 7, #else FP = 11, #endif SP = 13, LR = 14, PC = 15 }; extern const struct unwind_idx __start_unwind_idx[]; static const struct unwind_idx *__origin_unwind_idx; extern const struct unwind_idx __stop_unwind_idx[]; static DEFINE_SPINLOCK(unwind_lock); static LIST_HEAD(unwind_tables); /* Convert a prel31 symbol to an absolute address */ #define prel31_to_addr(ptr) \ ({ \ /* sign-extend to 32 bits */ \ long offset = (((long)*(ptr)) << 1) >> 1; \ (unsigned long)(ptr) + offset; \ }) /* * Binary search in the unwind index. The entries are * guaranteed to be sorted in ascending order by the linker. * * start = first entry * origin = first entry with positive offset (or stop if there is no such entry) * stop - 1 = last entry */ static const struct unwind_idx *search_index(unsigned long addr, const struct unwind_idx *start, const struct unwind_idx *origin, const struct unwind_idx *stop) { unsigned long addr_prel31; pr_debug("%s(%08lx, %p, %p, %p)\n", __func__, addr, start, origin, stop); /* * only search in the section with the matching sign. This way the * prel31 numbers can be compared as unsigned longs. */ if (addr < (unsigned long)start) /* negative offsets: [start; origin) */ stop = origin; else /* positive offsets: [origin; stop) */ start = origin; /* prel31 for address relavive to start */ addr_prel31 = (addr - (unsigned long)start) & 0x7fffffff; while (start < stop - 1) { const struct unwind_idx *mid = start + ((stop - start) >> 1); /* * As addr_prel31 is relative to start an offset is needed to * make it relative to mid. */ if (addr_prel31 - ((unsigned long)mid - (unsigned long)start) < mid->addr_offset) stop = mid; else { /* keep addr_prel31 relative to start */ addr_prel31 -= ((unsigned long)mid - (unsigned long)start); start = mid; } }