|  | /* Copyright (C) 2005-2016 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of the GNU C Library. | 
|  |  | 
|  | The GNU C Library is free software; you can redistribute it and/or | 
|  | modify it under the terms of the GNU Lesser General Public License as | 
|  | published by the Free Software Foundation; either version 2.1 of the | 
|  | License, or (at your option) any later version. | 
|  |  | 
|  | The GNU C Library 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 | 
|  | Lesser General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU Lesser General Public | 
|  | License along with the GNU C Library; if not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <sysdep.h> | 
|  | #include <signal.h> | 
|  | #include <execinfo.h> | 
|  |  | 
|  | extern int | 
|  | _identify_sighandler (unsigned long fp, unsigned long pc, | 
|  | unsigned long *pprev_fp, unsigned long *pprev_pc, | 
|  | unsigned long *retaddr); | 
|  |  | 
|  | inline long | 
|  | get_frame_size (unsigned long instr) | 
|  | { | 
|  | return abs ((short signed) (instr & 0xFFFF)); | 
|  | } | 
|  |  | 
|  | static unsigned long * | 
|  | find_frame_creation (unsigned long *pc) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* NOTE: Distance to search is arbitrary. | 
|  | 250 works well for most things, | 
|  | 750 picks up things like tcp_recvmsg, | 
|  | 1000 needed for fat_fill_super.  */ | 
|  | for (i = 0; i < 1000; i++, pc--) | 
|  | { | 
|  | unsigned long instr; | 
|  | unsigned long frame_size; | 
|  |  | 
|  | instr = *pc; | 
|  |  | 
|  | /* Is the instruction of the form | 
|  | addik r1, r1, foo ? */ | 
|  | if ((instr & 0xFFFF0000) != 0x30210000) | 
|  | continue; | 
|  |  | 
|  | frame_size = get_frame_size (instr); | 
|  |  | 
|  | if ((frame_size < 8) || (frame_size & 3)) | 
|  | return NULL; | 
|  |  | 
|  | return pc; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static int | 
|  | lookup_prev_stack_frame (unsigned long fp, unsigned long pc, | 
|  | unsigned long *pprev_fp, unsigned long *pprev_pc, | 
|  | unsigned long *retaddr) | 
|  | { | 
|  | unsigned long *prologue = NULL; | 
|  |  | 
|  | int is_signalhandler = _identify_sighandler (fp, pc, pprev_fp, | 
|  | pprev_pc, retaddr); | 
|  |  | 
|  | if (!is_signalhandler) | 
|  | { | 
|  | prologue = find_frame_creation ((unsigned long *) pc); | 
|  |  | 
|  | if (prologue) | 
|  | { | 
|  | long frame_size = get_frame_size (*prologue); | 
|  | *pprev_fp = fp + frame_size; | 
|  | if (*retaddr != 0) | 
|  | *pprev_pc = *retaddr; | 
|  | else | 
|  | *pprev_pc = *(unsigned long *) fp; | 
|  |  | 
|  | *retaddr = 0; | 
|  | if (!*pprev_pc || (*pprev_pc & 3)) | 
|  | prologue=0; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pprev_pc = 0; | 
|  | *pprev_fp = fp; | 
|  | *retaddr = 0; | 
|  | } | 
|  | } | 
|  | return (!*pprev_pc || (*pprev_pc & 3)) ? -1 : 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | __backtrace (void **array, int size) | 
|  | { | 
|  | unsigned long pc, fp; | 
|  | unsigned long ppc, pfp; | 
|  | /* Return address(r15) is required in the signal handler case, since the | 
|  | return address of the function which causes the signal may not be | 
|  | recorded in the stack.  */ | 
|  | unsigned long retaddr; | 
|  |  | 
|  | int count; | 
|  | int rc = 0; | 
|  |  | 
|  | if (size <= 0) | 
|  | return 0; | 
|  |  | 
|  | __asm__ __volatile__ ("mfs %0, rpc" | 
|  | : "=r"(pc)); | 
|  |  | 
|  | __asm__ __volatile__ ("add %0, r1, r0" | 
|  | : "=r"(fp)); | 
|  |  | 
|  | array[0] = (void *) pc; | 
|  | retaddr = 0; | 
|  | for (count = 1; count < size; count++) | 
|  | { | 
|  | rc = lookup_prev_stack_frame (fp, pc, &pfp, &ppc, &retaddr); | 
|  |  | 
|  | fp = pfp; | 
|  | pc = ppc; | 
|  | array[count] = (void *) pc; | 
|  | if (rc) | 
|  | return count; | 
|  | } | 
|  | return count; | 
|  | } | 
|  |  | 
|  | weak_alias (__backtrace, backtrace) | 
|  | libc_hidden_def (__backtrace) |