1 /* Copyright (C) 2011 Monty Program Ab
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
15 
16 #include "mysys_priv.h"
17 #include <m_string.h>
18 #include <my_sys.h>
19 #include <my_stacktrace.h>
20 
21 /**
22   strip the path, leave the file name and the last dirname
23 */
24 static const char *strip_path(const char *s) __attribute__((unused));
strip_path(const char * s)25 static const char *strip_path(const char *s)
26 {
27   const char *prev, *last;
28   for(prev= last= s; *s; s++)
29     if (*s == '/' || *s == '\\')
30     {
31       prev= last;
32       last= s + 1;
33     }
34   return prev;
35 }
36 
37 /*
38   The following is very much single-threaded code and it's only supposed
39   to be used on shutdown or for a crash report
40   Or the caller should take care and use mutexes.
41 
42   Also it does not free any its memory. For the same reason -
43   it's only used for crash reports or on shutdown when we already
44   have a memory leak.
45 */
46 
47 #ifdef HAVE_BFD_H
48 #include <bfd.h>
49 static bfd *bfdh= 0;
50 static asymbol **symtable= 0;
51 
52 #if defined(HAVE_LINK_H) && defined(HAVE_DLOPEN)
53 #include <link.h>
54 static ElfW(Addr) offset= 0;
55 #else
56 #define offset 0
57 #endif
58 
59 #ifndef bfd_get_section_flags
60 #define bfd_get_section_flags(H, S) bfd_section_flags(S)
61 #endif /* bfd_get_section_flags */
62 
63 #ifndef bfd_get_section_size
64 #define bfd_get_section_size(S) bfd_section_size(S)
65 #endif /* bfd_get_section_size */
66 
67 #ifndef bfd_get_section_vma
68 #define bfd_get_section_vma(H, S) bfd_section_vma(S)
69 #endif /* bfd_get_section_vma */
70 
71 /**
72   finds a file name, a line number, and a function name corresponding to addr.
73 
74   the function name is demangled.
75   the file name is stripped of its path, only the two last components are kept
76   the resolving logic is mostly based on addr2line of binutils-2.17
77 
78   @return 0 on success, 1 on failure
79 */
my_addr_resolve(void * ptr,my_addr_loc * loc)80 int my_addr_resolve(void *ptr, my_addr_loc *loc)
81 {
82   bfd_vma addr= (intptr)ptr - offset;
83   asection *sec;
84 
85   for (sec= bfdh->sections; sec; sec= sec->next)
86   {
87     bfd_vma start;
88 
89     if ((bfd_get_section_flags(bfdh, sec) & SEC_ALLOC) == 0)
90       continue;
91 
92     start = bfd_get_section_vma(bfdh, sec);
93     if (addr < start || addr >= start + bfd_get_section_size(sec))
94       continue;
95 
96     if (bfd_find_nearest_line(bfdh, sec, symtable, addr - start,
97                               &loc->file, &loc->func, &loc->line))
98     {
99       if (loc->file)
100         loc->file= strip_path(loc->file);
101       else
102         loc->file= "";
103 
104       if (loc->func)
105       {
106         const char *str= bfd_demangle(bfdh, loc->func, 3);
107         if (str)
108           loc->func= str;
109       }
110 
111       return 0;
112     }
113   }
114 
115   return 1;
116 }
117 
my_addr_resolve_init()118 const char *my_addr_resolve_init()
119 {
120   if (!bfdh)
121   {
122     uint unused;
123     char **matching;
124 
125 #if defined(HAVE_LINK_H) && defined(HAVE_DLOPEN)
126     struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW);
127     if (lm)
128       offset= lm->l_addr;
129 #endif
130 
131     bfdh= bfd_openr(my_progname, NULL);
132     if (!bfdh)
133       goto err;
134 
135     if (bfd_check_format(bfdh, bfd_archive))
136       goto err;
137     if (!bfd_check_format_matches (bfdh, bfd_object, &matching))
138       goto err;
139 
140     if (bfd_read_minisymbols(bfdh, FALSE, (void *)&symtable, &unused) < 0)
141       goto err;
142   }
143   return 0;
144 
145 err:
146   return bfd_errmsg(bfd_get_error());
147 }
148 #elif defined(HAVE_LIBELF_H)
149 /*
150   another possible implementation.
151 */
152 #elif defined(MY_ADDR_RESOLVE_FORK)
153 /*
154   yet another - just execute addr2line pipe the addresses to it, and parse the
155   output
156 */
157 
158 #include <m_string.h>
159 #include <ctype.h>
160 #include <sys/wait.h>
161 
162 #if defined(HAVE_POLL_H)
163 #include <poll.h>
164 #elif defined(HAVE_SYS_POLL_H)
165 #include <sys/poll.h>
166 #endif /* defined(HAVE_POLL_H) */
167 
168 static int in[2], out[2];
169 static pid_t pid;
170 static char addr2line_binary[1024];
171 static char output[1024];
172 static struct pollfd poll_fds;
173 static void *addr_offset;
174 
start_addr2line_fork(const char * binary_path)175 int start_addr2line_fork(const char *binary_path)
176 {
177 
178   if (pid > 0)
179   {
180     /* Don't leak FDs */
181     close(in[1]);
182     close(out[0]);
183     /* Don't create zombie processes. */
184     waitpid(pid, NULL, 0);
185   }
186 
187   if (pipe(in) < 0)
188     return 1;
189   if (pipe(out) < 0)
190     return 1;
191 
192   pid = fork();
193   if (pid == -1)
194     return 1;
195 
196   if (!pid) /* child */
197   {
198     dup2(in[0], 0);
199     dup2(out[1], 1);
200     close(in[0]);
201     close(in[1]);
202     close(out[0]);
203     close(out[1]);
204     execlp("addr2line", "addr2line", "-C", "-f", "-e", binary_path, NULL);
205     exit(1);
206   }
207 
208   close(in[0]);
209   close(out[1]);
210 
211   return 0;
212 }
213 
214 static int first_error= 0;
215 
addr_resolve(void * ptr,my_addr_loc * loc)216 static int addr_resolve(void *ptr, my_addr_loc *loc)
217 {
218   char input[32];
219   size_t len;
220 
221   ssize_t total_bytes_read = 0;
222   ssize_t extra_bytes_read = 0;
223   ssize_t parsed = 0;
224 
225   int ret;
226 
227   int filename_start = -1;
228   int line_number_start = -1;
229 
230   poll_fds.fd = out[0];
231   poll_fds.events = POLLIN | POLLRDBAND;
232 
233   len= my_snprintf(input, sizeof(input), "%p\n", ptr);
234   if (write(in[1], input, len) <= 0)
235   {
236     if (!first_error++)
237       fputs("Printing to addr2line failed\n", stderr);
238     return 3;
239   }
240 
241 
242   /* 500 ms should be plenty of time for addr2line to issue a response. */
243   /* Read in a loop till all the output from addr2line is complete. */
244   while (parsed == total_bytes_read &&
245          (ret= poll(&poll_fds, 1, 500)))
246   {
247     /* error during poll */
248     if (ret < 0)
249       return 1;
250 
251     extra_bytes_read= read(out[0], output + total_bytes_read,
252                            sizeof(output) - total_bytes_read);
253     if (extra_bytes_read < 0)
254       return 4;
255     /* Timeout or max bytes read. */
256     if (extra_bytes_read == 0)
257       break;
258 
259     total_bytes_read += extra_bytes_read;
260 
261     /* Go through the addr2line response and get the required data.
262        The response is structured in 2 lines. The first line contains the function
263        name, while the second one contains <filename>:<line number> */
264     for (; parsed < total_bytes_read; parsed++)
265     {
266       if (output[parsed] == '\n')
267       {
268         filename_start = parsed + 1;
269         output[parsed] = '\0';
270       }
271       if (filename_start != -1 && output[parsed] == ':')
272       {
273         line_number_start = parsed + 1;
274         output[parsed] = '\0';
275         break;
276       }
277     }
278   }
279 
280   /* Response is malformed. */
281   if (filename_start == -1 || line_number_start == -1)
282    return 5;
283 
284   loc->func= output;
285   loc->file= output + filename_start;
286   loc->line= atoi(output + line_number_start);
287 
288   /* Addr2line was unable to extract any meaningful information. */
289   if (strcmp(loc->file, "??") == 0 && loc->func[0] == '?')
290     return 6;
291 
292   loc->file= strip_path(loc->file);
293 
294   return 0;
295 }
296 
297 
my_addr_resolve(void * ptr,my_addr_loc * loc)298 int my_addr_resolve(void *ptr, my_addr_loc *loc)
299 {
300   Dl_info info;
301 
302   if (!dladdr(ptr, &info))
303     return 1;
304 
305   if (strcmp(addr2line_binary, info.dli_fname))
306   {
307     /*
308       We use dli_fname in case the path is longer than the length of
309       our static string. We don't want to allocate anything
310       dynamically here as we are in a "crashed" state.
311     */
312     if (start_addr2line_fork(info.dli_fname))
313     {
314       if (!first_error++)
315         fputs("Can't start addr2line\n", stderr);
316       addr2line_binary[0] = '\0';
317       return 2;
318     }
319     /* Save result for future comparisons. */
320     strnmov(addr2line_binary, info.dli_fname, sizeof(addr2line_binary));
321 
322     /*
323       Check if we should use info.dli_fbase as an offset or not
324       for the base program. This is depending on if the compilation is
325       done with PIE or not.
326     */
327     addr_offset= (void*) info.dli_fbase;
328 #ifndef __PIE__
329     if (strcmp(info.dli_fname, my_progname) == 0 &&
330         addr_resolve((void*) my_addr_resolve, loc) == 0 &&
331 	strcmp(loc->func, "my_addr_resolve") == 0)
332       addr_offset= 0;
333 #endif
334   }
335 
336   return addr_resolve((void*) (ptr - addr_offset), loc);
337 }
338 
339 
my_addr_resolve_init()340 const char *my_addr_resolve_init()
341 {
342   return 0;
343 }
344 #endif
345