1 /* fileline.c -- Get file and line number information in a backtrace.
2    Copyright (C) 2012-2018 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 #include "backtrace.h"
43 #include "internal.h"
44 
45 #ifndef HAVE_GETEXECNAME
46 #define getexecname() NULL
47 #endif
48 
49 /* Initialize the fileline information from the executable.  Returns 1
50    on success, 0 on failure.  */
51 
52 static int
fileline_initialize(struct backtrace_state * state,backtrace_error_callback error_callback,void * data)53 fileline_initialize (struct backtrace_state *state,
54 		     backtrace_error_callback error_callback, void *data)
55 {
56   int failed;
57   fileline fileline_fn;
58   int pass;
59   int called_error_callback;
60   int descriptor;
61   const char *filename;
62   char buf[64];
63 
64   if (!state->threaded)
65     failed = state->fileline_initialization_failed;
66   else
67     failed = backtrace_atomic_load_int (&state->fileline_initialization_failed);
68 
69   if (failed)
70     {
71       error_callback (data, "failed to read executable information", -1);
72       return 0;
73     }
74 
75   if (!state->threaded)
76     fileline_fn = state->fileline_fn;
77   else
78     fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
79   if (fileline_fn != NULL)
80     return 1;
81 
82   /* We have not initialized the information.  Do it now.  */
83 
84   descriptor = -1;
85   called_error_callback = 0;
86   for (pass = 0; pass < 5; ++pass)
87     {
88       int does_not_exist;
89 
90       switch (pass)
91 	{
92 	case 0:
93 	  filename = state->filename;
94 	  break;
95 	case 1:
96 	  filename = getexecname ();
97 	  break;
98 	case 2:
99 	  filename = "/proc/self/exe";
100 	  break;
101 	case 3:
102 	  filename = "/proc/curproc/file";
103 	  break;
104 	case 4:
105 	  snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out",
106 		    (long) getpid ());
107 	  filename = buf;
108 	  break;
109 	default:
110 	  abort ();
111 	}
112 
113       if (filename == NULL)
114 	continue;
115 
116       descriptor = backtrace_open (filename, error_callback, data,
117 				   &does_not_exist);
118       if (descriptor < 0 && !does_not_exist)
119 	{
120 	  called_error_callback = 1;
121 	  break;
122 	}
123       if (descriptor >= 0)
124 	break;
125     }
126 
127   if (descriptor < 0)
128     {
129       if (!called_error_callback)
130 	{
131 	  if (state->filename != NULL)
132 	    error_callback (data, state->filename, ENOENT);
133 	  else
134 	    error_callback (data,
135 			    "libbacktrace could not find executable to open",
136 			    0);
137 	}
138       failed = 1;
139     }
140 
141   if (!failed)
142     {
143       if (!backtrace_initialize (state, filename, descriptor, error_callback,
144 				 data, &fileline_fn))
145 	failed = 1;
146     }
147 
148   if (failed)
149     {
150       if (!state->threaded)
151 	state->fileline_initialization_failed = 1;
152       else
153 	backtrace_atomic_store_int (&state->fileline_initialization_failed, 1);
154       return 0;
155     }
156 
157   if (!state->threaded)
158     state->fileline_fn = fileline_fn;
159   else
160     {
161       backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn);
162 
163       /* Note that if two threads initialize at once, one of the data
164 	 sets may be leaked.  */
165     }
166 
167   return 1;
168 }
169 
170 /* Given a PC, find the file name, line number, and function name.  */
171 
172 int
backtrace_pcinfo(struct backtrace_state * state,uintptr_t pc,backtrace_full_callback callback,backtrace_error_callback error_callback,void * data)173 backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
174 		  backtrace_full_callback callback,
175 		  backtrace_error_callback error_callback, void *data)
176 {
177   if (!fileline_initialize (state, error_callback, data))
178     return 0;
179 
180   if (state->fileline_initialization_failed)
181     return 0;
182 
183   return state->fileline_fn (state, pc, callback, error_callback, data);
184 }
185 
186 /* Given a PC, find the symbol for it, and its value.  */
187 
188 int
backtrace_syminfo(struct backtrace_state * state,uintptr_t pc,backtrace_syminfo_callback callback,backtrace_error_callback error_callback,void * data)189 backtrace_syminfo (struct backtrace_state *state, uintptr_t pc,
190 		   backtrace_syminfo_callback callback,
191 		   backtrace_error_callback error_callback, void *data)
192 {
193   if (!fileline_initialize (state, error_callback, data))
194     return 0;
195 
196   if (state->fileline_initialization_failed)
197     return 0;
198 
199   state->syminfo_fn (state, pc, callback, error_callback, data);
200   return 1;
201 }
202