]>
Commit | Line | Data |
---|---|---|
7c1e01aa | 1 | /* Return backtrace of current program state. |
b168057a | 2 | Copyright (C) 2013-2015 Free Software Foundation, Inc. |
7c1e01aa DM |
3 | This file is part of the GNU C Library. |
4 | Contributed by David S. Miller <davem@davemloft.net> | |
5 | ||
6 | The GNU C Library is free software; you can redistribute it and/or | |
7 | modify it under the terms of the GNU Library General Public License as | |
8 | published by the Free Software Foundation; either version 2 of the | |
9 | License, or (at your option) any later version. | |
10 | ||
11 | The GNU C Library is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Library General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Library General Public | |
17 | License along with the GNU C Library; see the file COPYING.LIB. If | |
18 | not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include <execinfo.h> | |
21 | #include <stddef.h> | |
7c1e01aa DM |
22 | #include <sysdep.h> |
23 | #include <sys/trap.h> | |
24 | #include <dlfcn.h> | |
25 | #include <unwind.h> | |
26 | #include <backtrace.h> | |
27 | ||
28 | struct layout | |
29 | { | |
30 | unsigned long locals[8]; | |
31 | unsigned long ins[6]; | |
32 | unsigned long next; | |
70d9946a | 33 | void *return_address; |
7c1e01aa DM |
34 | }; |
35 | ||
36 | struct trace_arg | |
37 | { | |
38 | void **array; | |
39 | _Unwind_Word cfa; | |
40 | int cnt; | |
41 | int size; | |
42 | }; | |
43 | ||
44 | #ifdef SHARED | |
45 | static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); | |
46 | static _Unwind_Ptr (*unwind_getip) (struct _Unwind_Context *); | |
47 | static _Unwind_Word (*unwind_getcfa) (struct _Unwind_Context *); | |
48 | static void *libgcc_handle; | |
49 | ||
50 | /* Dummy version in case libgcc_s does not contain the real code. */ | |
51 | static _Unwind_Word | |
52 | dummy_getcfa (struct _Unwind_Context *ctx __attribute__ ((unused))) | |
53 | { | |
54 | return 0; | |
55 | } | |
56 | ||
57 | static void | |
58 | init (void) | |
59 | { | |
60 | libgcc_handle = __libc_dlopen ("libgcc_s.so.1"); | |
61 | ||
62 | if (libgcc_handle == NULL) | |
63 | return; | |
64 | ||
65 | unwind_backtrace = __libc_dlsym (libgcc_handle, "_Unwind_Backtrace"); | |
66 | unwind_getip = __libc_dlsym (libgcc_handle, "_Unwind_GetIP"); | |
67 | if (unwind_getip == NULL) | |
68 | unwind_backtrace = NULL; | |
69 | unwind_getcfa = (__libc_dlsym (libgcc_handle, "_Unwind_GetCFA") | |
70 | ?: dummy_getcfa); | |
71 | } | |
72 | #else | |
73 | # define unwind_backtrace _Unwind_Backtrace | |
74 | # define unwind_getip _Unwind_GetIP | |
75 | # define unwind_getcfa _Unwind_GetCFA | |
76 | #endif | |
77 | ||
78 | static _Unwind_Reason_Code | |
79 | backtrace_helper (struct _Unwind_Context *ctx, void *a) | |
80 | { | |
81 | struct trace_arg *arg = a; | |
82 | _Unwind_Ptr ip; | |
83 | ||
84 | /* We are first called with address in the __backtrace function. | |
85 | Skip it. */ | |
86 | if (arg->cnt != -1) | |
87 | { | |
88 | ip = unwind_getip (ctx); | |
89 | arg->array[arg->cnt] = (void *) ip; | |
90 | ||
91 | /* Check whether we make any progress. */ | |
92 | _Unwind_Word cfa = unwind_getcfa (ctx); | |
93 | ||
94 | if (arg->cnt > 0 && arg->array[arg->cnt - 1] == arg->array[arg->cnt] | |
95 | && cfa == arg->cfa) | |
96 | return _URC_END_OF_STACK; | |
97 | arg->cfa = cfa; | |
98 | } | |
99 | if (++arg->cnt == arg->size) | |
100 | return _URC_END_OF_STACK; | |
101 | return _URC_NO_REASON; | |
102 | } | |
103 | ||
104 | int | |
105 | __backtrace (void **array, int size) | |
106 | { | |
107 | struct trace_arg arg = { .array = array, .cfa = 0, .size = size, .cnt = -1 }; | |
108 | bool use_unwinder; | |
109 | int count; | |
110 | ||
111 | if (!size) | |
112 | return 0; | |
113 | ||
114 | use_unwinder = true; | |
115 | #ifdef SHARED | |
116 | __libc_once_define (static, once); | |
117 | ||
118 | __libc_once (once, init); | |
119 | if (unwind_backtrace == NULL) | |
120 | use_unwinder = false; | |
121 | #endif | |
122 | ||
123 | if (use_unwinder == false) | |
124 | { | |
125 | struct layout *current; | |
126 | unsigned long fp, i7; | |
127 | ||
128 | asm volatile ("mov %%fp, %0" : "=r"(fp)); | |
129 | asm volatile ("mov %%i7, %0" : "=r"(i7)); | |
70d9946a | 130 | current = (struct layout *) (fp + BACKTRACE_STACK_BIAS); |
7c1e01aa | 131 | |
70d9946a | 132 | array[0] = (void *) i7; |
7c1e01aa DM |
133 | |
134 | if (size == 1) | |
135 | return 1; | |
136 | ||
137 | backtrace_flush_register_windows(); | |
138 | for (count = 1; count < size; count++) | |
139 | { | |
140 | array[count] = current->return_address; | |
141 | if (!current->next) | |
142 | break; | |
70d9946a | 143 | current = (struct layout *) (current->next + BACKTRACE_STACK_BIAS); |
7c1e01aa DM |
144 | } |
145 | } | |
146 | else | |
147 | { | |
148 | unwind_backtrace (backtrace_helper, &arg); | |
149 | ||
150 | /* _Unwind_Backtrace seems to put NULL address above | |
151 | _start. Fix it up here. */ | |
152 | if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL) | |
153 | --arg.cnt; | |
154 | count = arg.cnt != -1 ? arg.cnt : 0; | |
155 | } | |
156 | return count; | |
157 | } | |
158 | weak_alias (__backtrace, backtrace) | |
159 | libc_hidden_def (__backtrace) |