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

source code of libbacktrace/fileline.c