about summary refs log tree commit diff
path: root/sysdeps/powerpc/powerpc32/backtrace.c
blob: 857a8aad7b996a4263845f5f8ed2dc538493a99b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* Return backtrace of current program state.
   Copyright (C) 1998-2019 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 <execinfo.h>
#include <stddef.h>
#include <string.h>
#include <signal.h>
#include <libc-vdso.h>

/* This is the stack layout we see with every stack frame.
   Note that every routine is required by the ABI to lay out the stack
   like this.

            +----------------+        +-----------------+
    %r1  -> | %r1 last frame--------> | %r1 last frame--->...  --> NULL
            |                |        |                 |
            | (unused)       |        | return address  |
            +----------------+        +-----------------+
*/
struct layout
{
  struct layout *next;
  void *return_address;
};

#define SIGNAL_FRAMESIZE 64

/* Since the signal handler is just like any other function it needs to
   save/restore its LR and it will save it into callers stack frame.
   Since a signal handler doesn't have a caller, the kernel creates a
   dummy frame to make it look like it has a caller.  */
struct signal_frame_32 {
  char               dummy[SIGNAL_FRAMESIZE];
  struct sigcontext  sctx;
  mcontext_t         mctx;
  /* We don't care about the rest, since IP value is at 'mctx' field.  */
};

static inline int
is_sigtramp_address (void *nip)
{
#ifdef SHARED
  if (nip == VDSO_SYMBOL (sigtramp32))
    return 1;
#endif
  return 0;
}

struct rt_signal_frame_32 {
  char               dummy[SIGNAL_FRAMESIZE + 16];
  siginfo_t          info;
  ucontext_t         uc;
  /* We don't care about the rest, since IP value is at 'uc' field.  */
};

static inline int
is_sigtramp_address_rt (void * nip)
{
#ifdef SHARED
  if (nip == VDSO_SYMBOL (sigtramp_rt32))
    return 1;
#endif
  return 0;
}

int
__backtrace (void **array, int size)
{
  struct layout *current;
  int count;

  /* Force gcc to spill LR.  */
  asm volatile ("" : "=l"(current));

  /* Get the address on top-of-stack.  */
  asm volatile ("lwz %0,0(1)" : "=r"(current));

  for (				count = 0;
       current != NULL && 	count < size;
       current = current->next, count++)
    {
      gregset_t *gregset = NULL;

      array[count] = current->return_address;

      /* Check if the symbol is the signal trampoline and get the interrupted
       * symbol address from the trampoline saved area.  */
      if (is_sigtramp_address (current->return_address))
	{
	  struct signal_frame_32 *sigframe =
	    (struct signal_frame_32*) current;
          gregset = &sigframe->mctx.gregs;
        }
      else if (is_sigtramp_address_rt (current->return_address))
	{
	  struct rt_signal_frame_32 *sigframe =
            (struct rt_signal_frame_32*) current;
          gregset = &sigframe->uc.uc_mcontext.uc_regs->gregs;
        }
      if (gregset)
	{
	  array[++count] = (void*)((*gregset)[PT_NIP]);
	  current = (void*)((*gregset)[PT_R1]);
	}
    }

  /* It's possible the second-last stack frame can't return
     (that is, it's __libc_start_main), in which case
     the CRT startup code will have set its LR to 'NULL'.  */
  if (count > 0 && array[count-1] == NULL)
    count--;

  return count;
}
weak_alias (__backtrace, backtrace)
libc_hidden_def (__backtrace)