diff options
Diffstat (limited to 'sysdeps/microblaze/backtrace.c')
-rw-r--r-- | sysdeps/microblaze/backtrace.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/sysdeps/microblaze/backtrace.c b/sysdeps/microblaze/backtrace.c new file mode 100644 index 0000000000..6b0c617b0f --- /dev/null +++ b/sysdeps/microblaze/backtrace.c @@ -0,0 +1,139 @@ +/* Copyright (C) 2005-2014 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; + + __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) |