1 | /* fileline.c -- Get file and line number information in a backtrace. |
2 | Copyright (C) 2012-2023 Free Software Foundation, Inc. |
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 |
10 | notice, this list of conditions and the following disclaimer. |
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 |
15 | distribution. |
16 | |
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> |
37 | #include <errno.h> |
38 | #include <fcntl.h> |
39 | #include <stdlib.h> |
40 | #include <unistd.h> |
41 | |
42 | #if defined (HAVE_KERN_PROC_ARGS) || defined (HAVE_KERN_PROC) |
43 | #include <sys/sysctl.h> |
44 | #endif |
45 | |
46 | #ifdef HAVE_MACH_O_DYLD_H |
47 | #include <mach-o/dyld.h> |
48 | #endif |
49 | |
50 | #include "backtrace.h" |
51 | #include "internal.h" |
52 | |
53 | #ifndef HAVE_GETEXECNAME |
54 | #define getexecname() NULL |
55 | #endif |
56 | |
57 | #if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC) |
58 | |
59 | #define sysctl_exec_name1(state, error_callback, data) NULL |
60 | #define sysctl_exec_name2(state, error_callback, data) NULL |
61 | |
62 | #else /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ |
63 | |
64 | static char * |
65 | sysctl_exec_name (struct backtrace_state *state, |
66 | int mib0, int mib1, int mib2, int mib3, |
67 | backtrace_error_callback error_callback, void *data) |
68 | { |
69 | int mib[4]; |
70 | size_t len; |
71 | char *name; |
72 | size_t rlen; |
73 | |
74 | mib[0] = mib0; |
75 | mib[1] = mib1; |
76 | mib[2] = mib2; |
77 | mib[3] = mib3; |
78 | |
79 | if (sysctl (mib, 4, NULL, &len, NULL, 0) < 0) |
80 | return NULL; |
81 | name = (char *) backtrace_alloc (state, len, error_callback, data); |
82 | if (name == NULL) |
83 | return NULL; |
84 | rlen = len; |
85 | if (sysctl (mib, 4, name, &rlen, NULL, 0) < 0) |
86 | { |
87 | backtrace_free (state, name, len, error_callback, data); |
88 | return NULL; |
89 | } |
90 | return name; |
91 | } |
92 | |
93 | #ifdef HAVE_KERN_PROC_ARGS |
94 | |
95 | static char * |
96 | sysctl_exec_name1 (struct backtrace_state *state, |
97 | backtrace_error_callback error_callback, void *data) |
98 | { |
99 | /* This variant is used on NetBSD. */ |
100 | return sysctl_exec_name (state, CTL_KERN, KERN_PROC_ARGS, -1, |
101 | KERN_PROC_PATHNAME, error_callback, data); |
102 | } |
103 | |
104 | #else |
105 | |
106 | #define sysctl_exec_name1(state, error_callback, data) NULL |
107 | |
108 | #endif |
109 | |
110 | #ifdef HAVE_KERN_PROC |
111 | |
112 | static char * |
113 | sysctl_exec_name2 (struct backtrace_state *state, |
114 | backtrace_error_callback error_callback, void *data) |
115 | { |
116 | /* This variant is used on FreeBSD. */ |
117 | return sysctl_exec_name (state, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, |
118 | error_callback, data); |
119 | } |
120 | |
121 | #else |
122 | |
123 | #define sysctl_exec_name2(state, error_callback, data) NULL |
124 | |
125 | #endif |
126 | |
127 | #endif /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ |
128 | |
129 | #ifdef HAVE_MACH_O_DYLD_H |
130 | |
131 | static char * |
132 | macho_get_executable_path (struct backtrace_state *state, |
133 | backtrace_error_callback error_callback, void *data) |
134 | { |
135 | uint32_t len; |
136 | char *name; |
137 | |
138 | len = 0; |
139 | if (_NSGetExecutablePath (NULL, &len) == 0) |
140 | return NULL; |
141 | name = (char *) backtrace_alloc (state, len, error_callback, data); |
142 | if (name == NULL) |
143 | return NULL; |
144 | if (_NSGetExecutablePath (name, &len) != 0) |
145 | { |
146 | backtrace_free (state, name, len, error_callback, data); |
147 | return NULL; |
148 | } |
149 | return name; |
150 | } |
151 | |
152 | #else /* !defined (HAVE_MACH_O_DYLD_H) */ |
153 | |
154 | #define macho_get_executable_path(state, error_callback, data) NULL |
155 | |
156 | #endif /* !defined (HAVE_MACH_O_DYLD_H) */ |
157 | |
158 | #if HAVE_DECL__PGMPTR |
159 | |
160 | #define windows_executable_filename() _pgmptr |
161 | |
162 | #else /* !HAVE_DECL__PGMPTR */ |
163 | |
164 | #define windows_executable_filename() NULL |
165 | |
166 | #endif /* !HAVE_DECL__PGMPTR */ |
167 | |
168 | /* Initialize the fileline information from the executable. Returns 1 |
169 | on success, 0 on failure. */ |
170 | |
171 | static int |
172 | fileline_initialize (struct backtrace_state *state, |
173 | backtrace_error_callback error_callback, void *data) |
174 | { |
175 | int failed; |
176 | fileline fileline_fn; |
177 | int pass; |
178 | int called_error_callback; |
179 | int descriptor; |
180 | const char *filename; |
181 | char buf[64]; |
182 | |
183 | if (!state->threaded) |
184 | failed = state->fileline_initialization_failed; |
185 | else |
186 | failed = backtrace_atomic_load_int (&state->fileline_initialization_failed); |
187 | |
188 | if (failed) |
189 | { |
190 | error_callback (data, "failed to read executable information" , -1); |
191 | return 0; |
192 | } |
193 | |
194 | if (!state->threaded) |
195 | fileline_fn = state->fileline_fn; |
196 | else |
197 | fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn); |
198 | if (fileline_fn != NULL) |
199 | return 1; |
200 | |
201 | /* We have not initialized the information. Do it now. */ |
202 | |
203 | descriptor = -1; |
204 | called_error_callback = 0; |
205 | for (pass = 0; pass < 9; ++pass) |
206 | { |
207 | int does_not_exist; |
208 | |
209 | switch (pass) |
210 | { |
211 | case 0: |
212 | filename = state->filename; |
213 | break; |
214 | case 1: |
215 | filename = getexecname (); |
216 | break; |
217 | case 2: |
218 | /* Test this before /proc/self/exe, as the latter exists but points |
219 | to the wine binary (and thus doesn't work). */ |
220 | filename = windows_executable_filename (); |
221 | break; |
222 | case 3: |
223 | filename = "/proc/self/exe" ; |
224 | break; |
225 | case 4: |
226 | filename = "/proc/curproc/file" ; |
227 | break; |
228 | case 5: |
229 | snprintf (s: buf, maxlen: sizeof (buf), format: "/proc/%ld/object/a.out" , |
230 | (long) getpid ()); |
231 | filename = buf; |
232 | break; |
233 | case 6: |
234 | filename = sysctl_exec_name1 (state, error_callback, data); |
235 | break; |
236 | case 7: |
237 | filename = sysctl_exec_name2 (state, error_callback, data); |
238 | break; |
239 | case 8: |
240 | filename = macho_get_executable_path (state, error_callback, data); |
241 | break; |
242 | default: |
243 | abort (); |
244 | } |
245 | |
246 | if (filename == NULL) |
247 | continue; |
248 | |
249 | descriptor = backtrace_open (filename, error_callback, data, |
250 | does_not_exist: &does_not_exist); |
251 | if (descriptor < 0 && !does_not_exist) |
252 | { |
253 | called_error_callback = 1; |
254 | break; |
255 | } |
256 | if (descriptor >= 0) |
257 | break; |
258 | } |
259 | |
260 | if (descriptor < 0) |
261 | { |
262 | if (!called_error_callback) |
263 | { |
264 | if (state->filename != NULL) |
265 | error_callback (data, state->filename, ENOENT); |
266 | else |
267 | error_callback (data, |
268 | "libbacktrace could not find executable to open" , |
269 | 0); |
270 | } |
271 | failed = 1; |
272 | } |
273 | |
274 | if (!failed) |
275 | { |
276 | if (!backtrace_initialize (state, filename, descriptor, error_callback, |
277 | data, fileline_fn: &fileline_fn)) |
278 | failed = 1; |
279 | } |
280 | |
281 | if (failed) |
282 | { |
283 | if (!state->threaded) |
284 | state->fileline_initialization_failed = 1; |
285 | else |
286 | backtrace_atomic_store_int (&state->fileline_initialization_failed, 1); |
287 | return 0; |
288 | } |
289 | |
290 | if (!state->threaded) |
291 | state->fileline_fn = fileline_fn; |
292 | else |
293 | { |
294 | backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn); |
295 | |
296 | /* Note that if two threads initialize at once, one of the data |
297 | sets may be leaked. */ |
298 | } |
299 | |
300 | return 1; |
301 | } |
302 | |
303 | /* Given a PC, find the file name, line number, and function name. */ |
304 | |
305 | int |
306 | backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc, |
307 | backtrace_full_callback callback, |
308 | backtrace_error_callback error_callback, void *data) |
309 | { |
310 | if (!fileline_initialize (state, error_callback, data)) |
311 | return 0; |
312 | |
313 | if (state->fileline_initialization_failed) |
314 | return 0; |
315 | |
316 | return state->fileline_fn (state, pc, callback, error_callback, data); |
317 | } |
318 | |
319 | /* Given a PC, find the symbol for it, and its value. */ |
320 | |
321 | int |
322 | backtrace_syminfo (struct backtrace_state *state, uintptr_t pc, |
323 | backtrace_syminfo_callback callback, |
324 | backtrace_error_callback error_callback, void *data) |
325 | { |
326 | if (!fileline_initialize (state, error_callback, data)) |
327 | return 0; |
328 | |
329 | if (state->fileline_initialization_failed) |
330 | return 0; |
331 | |
332 | state->syminfo_fn (state, pc, callback, error_callback, data); |
333 | return 1; |
334 | } |
335 | |
336 | /* A backtrace_syminfo_callback that can call into a |
337 | backtrace_full_callback, used when we have a symbol table but no |
338 | debug info. */ |
339 | |
340 | void |
341 | backtrace_syminfo_to_full_callback (void *data, uintptr_t pc, |
342 | const char *symname, |
343 | uintptr_t symval ATTRIBUTE_UNUSED, |
344 | uintptr_t symsize ATTRIBUTE_UNUSED) |
345 | { |
346 | struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; |
347 | |
348 | bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, symname); |
349 | } |
350 | |
351 | /* An error callback that corresponds to |
352 | backtrace_syminfo_to_full_callback. */ |
353 | |
354 | void |
355 | backtrace_syminfo_to_full_error_callback (void *data, const char *msg, |
356 | int errnum) |
357 | { |
358 | struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; |
359 | |
360 | bdata->full_error_callback (bdata->full_data, msg, errnum); |
361 | } |
362 | |