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 | |
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 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 | |
76 | static char * |
77 | sysctl_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 | |
107 | static char * |
108 | sysctl_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 | |
124 | static char * |
125 | sysctl_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 | |
143 | static char * |
144 | macho_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 | |
184 | static char * |
185 | windows_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 | |
214 | static int |
215 | fileline_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 | |
351 | int |
352 | backtrace_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 | |
367 | int |
368 | backtrace_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 | |
386 | void |
387 | backtrace_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 | |
400 | void |
401 | backtrace_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 | |