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 Dl_info info;
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
my_addr_resolve(void * ptr,my_addr_loc * loc)214 int my_addr_resolve(void *ptr, my_addr_loc *loc)
215 {
216 char input[32];
217 size_t len;
218
219 ssize_t total_bytes_read = 0;
220 ssize_t extra_bytes_read = 0;
221 ssize_t parsed = 0;
222
223 int ret;
224
225 int filename_start = -1;
226 int line_number_start = -1;
227
228 void *offset;
229
230 poll_fds.fd = out[0];
231 poll_fds.events = POLLIN | POLLRDBAND;
232
233 if (!dladdr(ptr, &info))
234 return 1;
235
236 if (strcmp(addr2line_binary, info.dli_fname))
237 {
238 /* We use dli_fname in case the path is longer than the length of our static
239 string. We don't want to allocate anything dynamicaly here as we are in
240 a "crashed" state. */
241 if (start_addr2line_fork(info.dli_fname))
242 {
243 addr2line_binary[0] = '\0';
244 return 2;
245 }
246 /* Save result for future comparisons. */
247 strnmov(addr2line_binary, info.dli_fname, sizeof(addr2line_binary));
248 }
249 offset = info.dli_fbase;
250 len= my_snprintf(input, sizeof(input), "%08x\n", (ulonglong)(ptr - offset));
251 if (write(in[1], input, len) <= 0)
252 return 3;
253
254
255 /* 500 ms should be plenty of time for addr2line to issue a response. */
256 /* Read in a loop till all the output from addr2line is complete. */
257 while (parsed == total_bytes_read &&
258 (ret= poll(&poll_fds, 1, 500)))
259 {
260 /* error during poll */
261 if (ret < 0)
262 return 1;
263
264 extra_bytes_read= read(out[0], output + total_bytes_read,
265 sizeof(output) - total_bytes_read);
266 if (extra_bytes_read < 0)
267 return 4;
268 /* Timeout or max bytes read. */
269 if (extra_bytes_read == 0)
270 break;
271
272 total_bytes_read += extra_bytes_read;
273
274 /* Go through the addr2line response and get the required data.
275 The response is structured in 2 lines. The first line contains the function
276 name, while the second one contains <filename>:<line number> */
277 for (; parsed < total_bytes_read; parsed++)
278 {
279 if (output[parsed] == '\n')
280 {
281 filename_start = parsed + 1;
282 output[parsed] = '\0';
283 }
284 if (filename_start != -1 && output[parsed] == ':')
285 {
286 line_number_start = parsed + 1;
287 output[parsed] = '\0';
288 break;
289 }
290 }
291 }
292
293 /* Response is malformed. */
294 if (filename_start == -1 || line_number_start == -1)
295 return 5;
296
297 loc->func= output;
298 loc->file= output + filename_start;
299 loc->line= atoi(output + line_number_start);
300
301 /* Addr2line was unable to extract any meaningful information. */
302 if (strcmp(loc->file, "??") == 0)
303 return 6;
304
305 loc->file= strip_path(loc->file);
306
307 return 0;
308 }
309
my_addr_resolve_init()310 const char *my_addr_resolve_init()
311 {
312 return 0;
313 }
314 #endif
315