]>
Commit | Line | Data |
---|---|---|
847b055c | 1 | /* Return backtrace of current program state. |
b168057a | 2 | Copyright (C) 2000-2015 Free Software Foundation, Inc. |
0e66ade5 | 3 | Contributed by Martin Schwidefsky <schwidefsky@de.ibm.com>. |
847b055c AJ |
4 | This file is part of the GNU C Library. |
5 | ||
6 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
7 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either | |
9 | version 2.1 of the License, or (at your option) any later version. | |
847b055c AJ |
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 | |
41bdb6e2 | 14 | Lesser General Public License for more details. |
847b055c | 15 | |
41bdb6e2 | 16 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 PE |
17 | License along with the GNU C Library; if not, see |
18 | <http://www.gnu.org/licenses/>. */ | |
847b055c | 19 | |
844a34a2 UD |
20 | #include <bits/libc-lock.h> |
21 | #include <dlfcn.h> | |
847b055c AJ |
22 | #include <execinfo.h> |
23 | #include <stddef.h> | |
844a34a2 UD |
24 | #include <stdlib.h> |
25 | #include <unwind.h> | |
847b055c AJ |
26 | |
27 | /* This is a global variable set at program start time. It marks the | |
28 | highest used stack address. */ | |
29 | extern void *__libc_stack_end; | |
30 | ||
31 | ||
32 | /* This is the stack layout we see for every non-leaf function. | |
33 | size offset | |
34 | %r15 -> +------------------+ | |
35 | 4 | back chain | 0 | |
36 | 4 | end of stack | 4 | |
37 | 8 | glue | 8 | |
38 | 8 | scratch | 16 | |
39 | 40 | save area r6-r15 | 24 | |
40 | 16 | save area f4,f6 | 64 | |
41 | 16 | empty | 80 | |
42 | +------------------+ | |
43 | r14 in the save area holds the return address. | |
44 | */ | |
45 | ||
46 | struct layout | |
47 | { | |
48 | int back_chain; | |
49 | int end_of_stack; | |
50 | int glue[2]; | |
51 | int scratch[2]; | |
52 | int save_grps[10]; | |
53 | int save_fp[4]; | |
54 | int empty[2]; | |
55 | }; | |
56 | ||
844a34a2 UD |
57 | struct trace_arg |
58 | { | |
59 | void **array; | |
60 | int cnt, size; | |
61 | }; | |
62 | ||
a9e526e7 | 63 | #ifdef SHARED |
844a34a2 UD |
64 | static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); |
65 | static _Unwind_Ptr (*unwind_getip) (struct _Unwind_Context *); | |
66 | ||
67 | static void | |
68 | init (void) | |
69 | { | |
70 | void *handle = __libc_dlopen ("libgcc_s.so.1"); | |
71 | ||
72 | if (handle == NULL) | |
73 | return; | |
74 | ||
75 | unwind_backtrace = __libc_dlsym (handle, "_Unwind_Backtrace"); | |
76 | unwind_getip = __libc_dlsym (handle, "_Unwind_GetIP"); | |
77 | if (unwind_getip == NULL) | |
78 | unwind_backtrace = NULL; | |
79 | } | |
80 | ||
81 | static int | |
82 | __backchain_backtrace (void **array, int size) | |
847b055c AJ |
83 | { |
84 | /* We assume that all the code is generated with frame pointers set. */ | |
85 | struct layout *stack; | |
86 | int cnt = 0; | |
87 | ||
88 | asm ("LR %0,%%r15" : "=d" (stack) ); | |
89 | /* We skip the call to this function, it makes no sense to record it. */ | |
90 | stack = (struct layout *) stack->back_chain; | |
91 | while (cnt < size) | |
92 | { | |
93 | if (stack == NULL || (void *) stack > __libc_stack_end) | |
94 | /* This means the address is out of range. Note that for the | |
95 | toplevel we see a frame pointer with value NULL which clearly is | |
96 | out of range. */ | |
97 | break; | |
98 | ||
844a34a2 | 99 | array[cnt++] = (void *) (stack->save_grps[8] & 0x7fffffff); |
847b055c AJ |
100 | |
101 | stack = (struct layout *) stack->back_chain; | |
102 | } | |
103 | ||
104 | return cnt; | |
105 | } | |
618cebef SL |
106 | #else |
107 | # define unwind_backtrace _Unwind_Backtrace | |
108 | # define unwind_getip _Unwind_GetIP | |
109 | #endif | |
844a34a2 UD |
110 | |
111 | static _Unwind_Reason_Code | |
112 | backtrace_helper (struct _Unwind_Context *ctx, void *a) | |
113 | { | |
114 | struct trace_arg *arg = a; | |
115 | ||
116 | /* We are first called with address in the __backtrace function. | |
117 | Skip it. */ | |
118 | if (arg->cnt != -1) | |
119 | arg->array[arg->cnt] = (void *) unwind_getip (ctx); | |
120 | if (++arg->cnt == arg->size) | |
121 | return _URC_END_OF_STACK; | |
122 | return _URC_NO_REASON; | |
123 | } | |
124 | ||
125 | int | |
126 | __backtrace (void **array, int size) | |
127 | { | |
128 | struct trace_arg arg = { .array = array, .size = size, .cnt = -1 }; | |
a9e526e7 | 129 | #ifdef SHARED |
844a34a2 UD |
130 | __libc_once_define (static, once); |
131 | ||
132 | __libc_once (once, init); | |
618cebef | 133 | |
844a34a2 UD |
134 | if (unwind_backtrace == NULL) |
135 | return __backchain_backtrace (array, size); | |
618cebef | 136 | #endif |
844a34a2 UD |
137 | |
138 | if (size >= 1) | |
139 | unwind_backtrace (backtrace_helper, &arg); | |
140 | ||
141 | return arg.cnt != -1 ? arg.cnt : 0; | |
142 | } | |
143 | ||
847b055c | 144 | weak_alias (__backtrace, backtrace) |
0e66ade5 | 145 | libc_hidden_def (__backtrace) |