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