Blame sysdeps/microblaze/backtrace.c

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