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