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
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 __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__
74static char *
75hpux_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
107static char *
108sysctl_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
138static char *
139sysctl_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
155static char *
156sysctl_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
174static char *
175macho_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
215static char *
216windows_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
245static int
246fileline_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
385int
386backtrace_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
401int
402backtrace_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
420void
421backtrace_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
434void
435backtrace_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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of libbacktrace/fileline.c