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