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