1 | /* Copyright (C) 2005-2022 Free Software Foundation, Inc. |
2 | |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public License as |
7 | published by the Free Software Foundation; either version 2.1 of the |
8 | License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <stdio.h> |
20 | #include <stdlib.h> |
21 | #include <string.h> |
22 | #include <sysdep.h> |
23 | #include <signal.h> |
24 | #include <execinfo.h> |
25 | |
26 | extern int |
27 | _identify_sighandler (unsigned long fp, unsigned long pc, |
28 | unsigned long *pprev_fp, unsigned long *pprev_pc, |
29 | unsigned long *retaddr); |
30 | |
31 | static inline long |
32 | get_frame_size (unsigned long instr) |
33 | { |
34 | return abs (x: (short signed) (instr & 0xFFFF)); |
35 | } |
36 | |
37 | static unsigned long * |
38 | find_frame_creation (unsigned long *pc) |
39 | { |
40 | int i; |
41 | |
42 | /* NOTE: Distance to search is arbitrary. |
43 | 250 works well for most things, |
44 | 750 picks up things like tcp_recvmsg, |
45 | 1000 needed for fat_fill_super. */ |
46 | for (i = 0; i < 1000; i++, pc--) |
47 | { |
48 | unsigned long instr; |
49 | unsigned long frame_size; |
50 | |
51 | instr = *pc; |
52 | |
53 | /* Is the instruction of the form |
54 | addik r1, r1, foo ? */ |
55 | if ((instr & 0xFFFF0000) != 0x30210000) |
56 | continue; |
57 | |
58 | frame_size = get_frame_size (instr); |
59 | |
60 | if ((frame_size < 8) || (frame_size & 3)) |
61 | return NULL; |
62 | |
63 | return pc; |
64 | } |
65 | return NULL; |
66 | } |
67 | |
68 | static int |
69 | lookup_prev_stack_frame (unsigned long fp, unsigned long pc, |
70 | unsigned long *pprev_fp, unsigned long *pprev_pc, |
71 | unsigned long *retaddr) |
72 | { |
73 | unsigned long *prologue = NULL; |
74 | |
75 | int is_signalhandler = _identify_sighandler (fp, pc, pprev_fp, |
76 | pprev_pc, retaddr); |
77 | |
78 | if (!is_signalhandler) |
79 | { |
80 | prologue = find_frame_creation (pc: (unsigned long *) pc); |
81 | |
82 | if (prologue) |
83 | { |
84 | long frame_size = get_frame_size (instr: *prologue); |
85 | *pprev_fp = fp + frame_size; |
86 | if (*retaddr != 0) |
87 | *pprev_pc = *retaddr; |
88 | else |
89 | *pprev_pc = *(unsigned long *) fp; |
90 | |
91 | *retaddr = 0; |
92 | if (!*pprev_pc || (*pprev_pc & 3)) |
93 | prologue=0; |
94 | } |
95 | else |
96 | { |
97 | *pprev_pc = 0; |
98 | *pprev_fp = fp; |
99 | *retaddr = 0; |
100 | } |
101 | } |
102 | return (!*pprev_pc || (*pprev_pc & 3)) ? -1 : 0; |
103 | } |
104 | |
105 | int |
106 | __backtrace (void **array, int size) |
107 | { |
108 | unsigned long pc, fp; |
109 | unsigned long ppc, pfp; |
110 | /* Return address(r15) is required in the signal handler case, since the |
111 | return address of the function which causes the signal may not be |
112 | recorded in the stack. */ |
113 | unsigned long retaddr; |
114 | |
115 | int count; |
116 | int rc = 0; |
117 | |
118 | if (size <= 0) |
119 | return 0; |
120 | |
121 | __asm__ __volatile__ ("mfs %0, rpc" |
122 | : "=r" (pc)); |
123 | |
124 | __asm__ __volatile__ ("add %0, r1, r0" |
125 | : "=r" (fp)); |
126 | |
127 | array[0] = (void *) pc; |
128 | retaddr = 0; |
129 | for (count = 1; count < size; count++) |
130 | { |
131 | rc = lookup_prev_stack_frame (fp, pc, pprev_fp: &pfp, pprev_pc: &ppc, retaddr: &retaddr); |
132 | |
133 | fp = pfp; |
134 | pc = ppc; |
135 | array[count] = (void *) pc; |
136 | if (rc) |
137 | return count; |
138 | } |
139 | return count; |
140 | } |
141 | |
142 | weak_alias (__backtrace, backtrace) |
143 | libc_hidden_def (__backtrace) |
144 | |