]>
Commit | Line | Data |
---|---|---|
eff02e4f | 1 | /* fileline.c -- Get file and line number information in a backtrace. |
8d9254fc | 2 | Copyright (C) 2012-2020 Free Software Foundation, Inc. |
eff02e4f ILT |
3 | Written by Ian Lance Taylor, Google. |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions are | |
7 | met: | |
8 | ||
9 | (1) Redistributions of source code must retain the above copyright | |
84ebf639 | 10 | notice, this list of conditions and the following disclaimer. |
eff02e4f ILT |
11 | |
12 | (2) Redistributions in binary form must reproduce the above copyright | |
13 | notice, this list of conditions and the following disclaimer in | |
14 | the documentation and/or other materials provided with the | |
84ebf639 CL |
15 | distribution. |
16 | ||
eff02e4f ILT |
17 | (3) The name of the author may not be used to |
18 | endorse or promote products derived from this software without | |
19 | specific prior written permission. | |
20 | ||
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |
25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
31 | POSSIBILITY OF SUCH DAMAGE. */ | |
32 | ||
33 | #include "config.h" | |
34 | ||
35 | #include <sys/types.h> | |
36 | #include <sys/stat.h> | |
33521509 | 37 | #include <errno.h> |
eff02e4f | 38 | #include <fcntl.h> |
c0558468 | 39 | #include <stdlib.h> |
b3530b94 | 40 | #include <unistd.h> |
eff02e4f | 41 | |
26135684 ILT |
42 | #if defined (HAVE_KERN_PROC_ARGS) || defined (HAVE_KERN_PROC) |
43 | #include <sys/sysctl.h> | |
44 | #endif | |
45 | ||
eff02e4f ILT |
46 | #include "backtrace.h" |
47 | #include "internal.h" | |
48 | ||
33521509 ILT |
49 | #ifndef HAVE_GETEXECNAME |
50 | #define getexecname() NULL | |
51 | #endif | |
52 | ||
26135684 ILT |
53 | #if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC) |
54 | ||
55 | #define sysctl_exec_name1(state, error_callback, data) NULL | |
56 | #define sysctl_exec_name2(state, error_callback, data) NULL | |
57 | ||
58 | #else /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ | |
59 | ||
60 | static char * | |
61 | sysctl_exec_name (struct backtrace_state *state, | |
62 | int mib0, int mib1, int mib2, int mib3, | |
63 | backtrace_error_callback error_callback, void *data) | |
64 | { | |
65 | int mib[4]; | |
66 | size_t len; | |
67 | char *name; | |
68 | size_t rlen; | |
69 | ||
70 | mib[0] = mib0; | |
71 | mib[1] = mib1; | |
72 | mib[2] = mib2; | |
73 | mib[3] = mib3; | |
74 | ||
75 | if (sysctl (mib, 4, NULL, &len, NULL, 0) < 0) | |
76 | return NULL; | |
77 | name = (char *) backtrace_alloc (state, len, error_callback, data); | |
78 | if (name == NULL) | |
79 | return NULL; | |
80 | rlen = len; | |
81 | if (sysctl (mib, 4, name, &rlen, NULL, 0) < 0) | |
82 | { | |
83 | backtrace_free (state, name, len, error_callback, data); | |
84 | return NULL; | |
85 | } | |
86 | return name; | |
87 | } | |
88 | ||
89 | #ifdef HAVE_KERN_PROC_ARGS | |
90 | ||
91 | static char * | |
92 | sysctl_exec_name1 (struct backtrace_state *state, | |
93 | backtrace_error_callback error_callback, void *data) | |
94 | { | |
95 | /* This variant is used on NetBSD. */ | |
96 | return sysctl_exec_name (state, CTL_KERN, KERN_PROC_ARGS, -1, | |
97 | KERN_PROC_PATHNAME, error_callback, data); | |
98 | } | |
99 | ||
100 | #else | |
101 | ||
102 | #define sysctl_exec_name1(state, error_callback, data) NULL | |
103 | ||
104 | #endif | |
105 | ||
106 | #ifdef HAVE_KERN_PROC | |
107 | ||
108 | static char * | |
109 | sysctl_exec_name2 (struct backtrace_state *state, | |
110 | backtrace_error_callback error_callback, void *data) | |
111 | { | |
112 | /* This variant is used on FreeBSD. */ | |
113 | return sysctl_exec_name (state, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, | |
114 | error_callback, data); | |
115 | } | |
116 | ||
117 | #else | |
118 | ||
119 | #define sysctl_exec_name2(state, error_callback, data) NULL | |
120 | ||
121 | #endif | |
122 | ||
123 | #endif /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ | |
124 | ||
eff02e4f ILT |
125 | /* Initialize the fileline information from the executable. Returns 1 |
126 | on success, 0 on failure. */ | |
127 | ||
128 | static int | |
129 | fileline_initialize (struct backtrace_state *state, | |
130 | backtrace_error_callback error_callback, void *data) | |
131 | { | |
132 | int failed; | |
133 | fileline fileline_fn; | |
33521509 ILT |
134 | int pass; |
135 | int called_error_callback; | |
eff02e4f | 136 | int descriptor; |
9283471b | 137 | const char *filename; |
b3530b94 | 138 | char buf[64]; |
eff02e4f | 139 | |
49579c7e ILT |
140 | if (!state->threaded) |
141 | failed = state->fileline_initialization_failed; | |
142 | else | |
143 | failed = backtrace_atomic_load_int (&state->fileline_initialization_failed); | |
eff02e4f ILT |
144 | |
145 | if (failed) | |
146 | { | |
8a447b3d | 147 | error_callback (data, "failed to read executable information", -1); |
eff02e4f ILT |
148 | return 0; |
149 | } | |
150 | ||
49579c7e ILT |
151 | if (!state->threaded) |
152 | fileline_fn = state->fileline_fn; | |
153 | else | |
154 | fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn); | |
eff02e4f ILT |
155 | if (fileline_fn != NULL) |
156 | return 1; | |
157 | ||
158 | /* We have not initialized the information. Do it now. */ | |
159 | ||
33521509 ILT |
160 | descriptor = -1; |
161 | called_error_callback = 0; | |
26135684 | 162 | for (pass = 0; pass < 7; ++pass) |
33521509 | 163 | { |
33521509 ILT |
164 | int does_not_exist; |
165 | ||
166 | switch (pass) | |
167 | { | |
168 | case 0: | |
169 | filename = state->filename; | |
170 | break; | |
171 | case 1: | |
172 | filename = getexecname (); | |
173 | break; | |
174 | case 2: | |
175 | filename = "/proc/self/exe"; | |
176 | break; | |
177 | case 3: | |
178 | filename = "/proc/curproc/file"; | |
179 | break; | |
b3530b94 | 180 | case 4: |
cf311b03 RO |
181 | snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out", |
182 | (long) getpid ()); | |
b3530b94 TR |
183 | filename = buf; |
184 | break; | |
26135684 ILT |
185 | case 5: |
186 | filename = sysctl_exec_name1 (state, error_callback, data); | |
187 | break; | |
188 | case 6: | |
189 | filename = sysctl_exec_name2 (state, error_callback, data); | |
190 | break; | |
33521509 ILT |
191 | default: |
192 | abort (); | |
193 | } | |
194 | ||
195 | if (filename == NULL) | |
196 | continue; | |
197 | ||
198 | descriptor = backtrace_open (filename, error_callback, data, | |
199 | &does_not_exist); | |
200 | if (descriptor < 0 && !does_not_exist) | |
201 | { | |
202 | called_error_callback = 1; | |
203 | break; | |
204 | } | |
205 | if (descriptor >= 0) | |
206 | break; | |
207 | } | |
208 | ||
eff02e4f | 209 | if (descriptor < 0) |
33521509 ILT |
210 | { |
211 | if (!called_error_callback) | |
212 | { | |
213 | if (state->filename != NULL) | |
214 | error_callback (data, state->filename, ENOENT); | |
215 | else | |
216 | error_callback (data, | |
217 | "libbacktrace could not find executable to open", | |
218 | 0); | |
219 | } | |
220 | failed = 1; | |
221 | } | |
eff02e4f ILT |
222 | |
223 | if (!failed) | |
224 | { | |
9283471b ILT |
225 | if (!backtrace_initialize (state, filename, descriptor, error_callback, |
226 | data, &fileline_fn)) | |
eff02e4f ILT |
227 | failed = 1; |
228 | } | |
229 | ||
230 | if (failed) | |
231 | { | |
232 | if (!state->threaded) | |
233 | state->fileline_initialization_failed = 1; | |
234 | else | |
49579c7e | 235 | backtrace_atomic_store_int (&state->fileline_initialization_failed, 1); |
eff02e4f ILT |
236 | return 0; |
237 | } | |
238 | ||
239 | if (!state->threaded) | |
240 | state->fileline_fn = fileline_fn; | |
241 | else | |
242 | { | |
49579c7e ILT |
243 | backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn); |
244 | ||
245 | /* Note that if two threads initialize at once, one of the data | |
246 | sets may be leaked. */ | |
eff02e4f ILT |
247 | } |
248 | ||
249 | return 1; | |
250 | } | |
251 | ||
252 | /* Given a PC, find the file name, line number, and function name. */ | |
253 | ||
254 | int | |
255 | backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc, | |
256 | backtrace_full_callback callback, | |
257 | backtrace_error_callback error_callback, void *data) | |
258 | { | |
259 | if (!fileline_initialize (state, error_callback, data)) | |
260 | return 0; | |
261 | ||
262 | if (state->fileline_initialization_failed) | |
263 | return 0; | |
264 | ||
265 | return state->fileline_fn (state, pc, callback, error_callback, data); | |
266 | } | |
267 | ||
268 | /* Given a PC, find the symbol for it, and its value. */ | |
269 | ||
270 | int | |
271 | backtrace_syminfo (struct backtrace_state *state, uintptr_t pc, | |
272 | backtrace_syminfo_callback callback, | |
273 | backtrace_error_callback error_callback, void *data) | |
274 | { | |
275 | if (!fileline_initialize (state, error_callback, data)) | |
276 | return 0; | |
277 | ||
278 | if (state->fileline_initialization_failed) | |
279 | return 0; | |
280 | ||
281 | state->syminfo_fn (state, pc, callback, error_callback, data); | |
282 | return 1; | |
283 | } |