1 /*
2  * Backtrace debugging
3  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #ifdef WPA_TRACE_BFD
10 #define _GNU_SOURCE
11 #include <link.h>
12 #endif /* WPA_TRACE_BCD */
13 #include "includes.h"
14 
15 #include "common.h"
16 #include "trace.h"
17 
18 #ifdef WPA_TRACE
19 
20 static struct dl_list active_references =
21 { &active_references, &active_references };
22 
23 #ifdef WPA_TRACE_BFD
24 #include <bfd.h>
25 
26 #define DMGL_PARAMS      (1 << 0)
27 #define DMGL_ANSI        (1 << 1)
28 
29 static char *prg_fname = NULL;
30 static bfd *cached_abfd = NULL;
31 static asymbol **syms = NULL;
32 static unsigned long start_offset;
33 static int start_offset_looked_up;
34 
35 
callback(struct dl_phdr_info * info,size_t size,void * data)36 static int callback(struct dl_phdr_info *info, size_t size, void *data)
37 {
38           /*
39            * dl_iterate_phdr(3):
40            * "The first object visited by callback is the main program."
41            */
42           start_offset = info->dlpi_addr;
43 
44           /*
45            * dl_iterate_phdr(3):
46            * "The dl_iterate_phdr() function walks through the list of an
47            *  application's shared objects and calls the function callback
48            *  once for each object, until either all shared objects have
49            *  been processed or callback returns a nonzero value."
50            */
51           return 1;
52 }
53 
54 
get_prg_fname(void)55 static void get_prg_fname(void)
56 {
57           char exe[50], fname[512];
58           int len;
59           os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
60           len = readlink(exe, fname, sizeof(fname) - 1);
61           if (len < 0 || len >= (int) sizeof(fname)) {
62                     wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
63                     return;
64           }
65           fname[len] = '\0';
66           prg_fname = strdup(fname);
67 }
68 
69 
open_bfd(const char * fname)70 static bfd * open_bfd(const char *fname)
71 {
72           bfd *abfd;
73           char **matching;
74 
75           abfd = bfd_openr(prg_fname, NULL);
76           if (abfd == NULL) {
77                     wpa_printf(MSG_INFO, "bfd_openr failed");
78                     return NULL;
79           }
80 
81           if (bfd_check_format(abfd, bfd_archive)) {
82                     wpa_printf(MSG_INFO, "bfd_check_format failed");
83                     bfd_close(abfd);
84                     return NULL;
85           }
86 
87           if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
88                     wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
89                     free(matching);
90                     bfd_close(abfd);
91                     return NULL;
92           }
93 
94           return abfd;
95 }
96 
97 
read_syms(bfd * abfd)98 static void read_syms(bfd *abfd)
99 {
100           long storage, symcount;
101           bfd_boolean dynamic = FALSE;
102 
103           if (syms)
104                     return;
105 
106           if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
107                     wpa_printf(MSG_INFO, "No symbols");
108                     return;
109           }
110 
111           storage = bfd_get_symtab_upper_bound(abfd);
112           if (storage == 0) {
113                     storage = bfd_get_dynamic_symtab_upper_bound(abfd);
114                     dynamic = TRUE;
115           }
116           if (storage < 0) {
117                     wpa_printf(MSG_INFO, "Unknown symtab upper bound");
118                     return;
119           }
120 
121           syms = malloc(storage);
122           if (syms == NULL) {
123                     wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
124                                  "(%ld bytes)", storage);
125                     return;
126           }
127           if (dynamic)
128                     symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
129           else
130                     symcount = bfd_canonicalize_symtab(abfd, syms);
131           if (symcount < 0) {
132                     wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
133                                  dynamic ? "dynamic " : "");
134                     free(syms);
135                     syms = NULL;
136                     return;
137           }
138 }
139 
140 
141 struct bfd_data {
142           bfd_vma pc;
143           bfd_boolean found;
144           const char *filename;
145           const char *function;
146           unsigned int line;
147 };
148 
149 /*
150  * binutils removed the bfd parameter and renamed things but
151  * those were macros so we can detect their absence.
152  * Cf. https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=fd3619828e94a24a92cddec42cbc0ab33352eeb4;hp=5dfda3562a69686c43aad4fb0269cc9d5ec010d5
153  */
154 #ifndef bfd_get_section_vma
155 #define bfd_get_section_vma(bfd, section) bfd_section_vma(section)
156 #endif
157 #ifndef bfd_get_section_size
158 #define bfd_get_section_size bfd_section_size
159 #endif
160 
find_addr_sect(bfd * abfd,asection * section,void * obj)161 static void find_addr_sect(bfd *abfd, asection *section, void *obj)
162 {
163           struct bfd_data *data = obj;
164           bfd_vma vma;
165           bfd_size_type size;
166 
167           if (data->found)
168                     return;
169 
170           if (!(bfd_get_section_vma(abfd, section)))
171                     return;
172 
173           vma = bfd_get_section_vma(abfd, section);
174           if (data->pc < vma)
175                     return;
176 
177           size = bfd_get_section_size(section);
178           if (data->pc >= vma + size)
179                     return;
180 
181           data->found = bfd_find_nearest_line(abfd, section, syms,
182                                                       data->pc - vma,
183                                                       &data->filename,
184                                                       &data->function,
185                                                       &data->line);
186 }
187 
188 
wpa_trace_bfd_addr(void * pc)189 static void wpa_trace_bfd_addr(void *pc)
190 {
191           bfd *abfd = cached_abfd;
192           struct bfd_data data;
193           const char *name;
194           char *aname = NULL;
195           const char *filename;
196 
197           if (abfd == NULL)
198                     return;
199 
200           data.pc = (uintptr_t) ((u8 *) pc - start_offset);
201           data.found = FALSE;
202           bfd_map_over_sections(abfd, find_addr_sect, &data);
203 
204           if (!data.found)
205                     return;
206 
207           do {
208                     if (data.function)
209                               aname = bfd_demangle(abfd, data.function,
210                                                        DMGL_ANSI | DMGL_PARAMS);
211                     name = aname ? aname : data.function;
212                     filename = data.filename;
213                     if (filename) {
214                               char *end = os_strrchr(filename, '/');
215                               int i = 0;
216                               while (*filename && *filename == prg_fname[i] &&
217                                      filename <= end) {
218                                         filename++;
219                                         i++;
220                               }
221                     }
222                     wpa_printf(MSG_INFO, "     %s() %s:%u",
223                                  name, filename, data.line);
224                     free(aname);
225                     aname = NULL;
226 
227                     data.found = bfd_find_inliner_info(abfd, &data.filename,
228                                                                &data.function, &data.line);
229           } while (data.found);
230 }
231 
232 
wpa_trace_bfd_addr2func(void * pc)233 static const char * wpa_trace_bfd_addr2func(void *pc)
234 {
235           bfd *abfd = cached_abfd;
236           struct bfd_data data;
237 
238           if (abfd == NULL)
239                     return NULL;
240 
241           data.pc = (uintptr_t) ((u8 *) pc - start_offset);
242           data.found = FALSE;
243           bfd_map_over_sections(abfd, find_addr_sect, &data);
244 
245           if (!data.found)
246                     return NULL;
247 
248           return data.function;
249 }
250 
251 
wpa_trace_bfd_init(void)252 static void wpa_trace_bfd_init(void)
253 {
254           if (!prg_fname) {
255                     get_prg_fname();
256                     if (!prg_fname)
257                               return;
258           }
259 
260           if (!cached_abfd) {
261                     cached_abfd = open_bfd(prg_fname);
262                     if (!cached_abfd) {
263                               wpa_printf(MSG_INFO, "Failed to open bfd");
264                               return;
265                     }
266           }
267 
268           read_syms(cached_abfd);
269           if (!syms) {
270                     wpa_printf(MSG_INFO, "Failed to read symbols");
271                     return;
272           }
273 
274           if (!start_offset_looked_up) {
275                     dl_iterate_phdr(callback, NULL);
276                     start_offset_looked_up = 1;
277           }
278 }
279 
280 
wpa_trace_dump_funcname(const char * title,void * pc)281 void wpa_trace_dump_funcname(const char *title, void *pc)
282 {
283           wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
284           wpa_trace_bfd_init();
285           wpa_trace_bfd_addr(pc);
286 }
287 
288 
wpa_trace_calling_func(const char * buf[],size_t len)289 size_t wpa_trace_calling_func(const char *buf[], size_t len)
290 {
291           bfd *abfd;
292           void *btrace_res[WPA_TRACE_LEN];
293           int i, btrace_num;
294           size_t pos = 0;
295 
296           if (len == 0)
297                     return 0;
298           if (len > WPA_TRACE_LEN)
299                     len = WPA_TRACE_LEN;
300 
301           wpa_trace_bfd_init();
302           abfd = cached_abfd;
303           if (!abfd)
304                     return 0;
305 
306           btrace_num = backtrace(btrace_res, len);
307           if (btrace_num < 1)
308                     return 0;
309 
310           for (i = 0; i < btrace_num; i++) {
311                     struct bfd_data data;
312 
313                     data.pc = (uintptr_t) ((u8 *) btrace_res[i] - start_offset);
314                     data.found = FALSE;
315                     bfd_map_over_sections(abfd, find_addr_sect, &data);
316 
317                     while (data.found) {
318                               if (data.function &&
319                                   (pos > 0 ||
320                                    os_strcmp(data.function, __func__) != 0)) {
321                                         buf[pos++] = data.function;
322                                         if (pos == len)
323                                                   return pos;
324                               }
325 
326                               data.found = bfd_find_inliner_info(abfd, &data.filename,
327                                                                          &data.function,
328                                                                          &data.line);
329                     }
330           }
331 
332           return pos;
333 }
334 
335 #else /* WPA_TRACE_BFD */
336 
337 #define wpa_trace_bfd_init() do { } while (0)
338 #define wpa_trace_bfd_addr(pc) do { } while (0)
339 #define wpa_trace_bfd_addr2func(pc) NULL
340 
341 #endif /* WPA_TRACE_BFD */
342 
wpa_trace_dump_func(const char * title,void ** btrace,int btrace_num)343 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
344 {
345           char **sym;
346           int i;
347           enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
348 
349           wpa_trace_bfd_init();
350           wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
351           sym = backtrace_symbols(btrace, btrace_num);
352           state = TRACE_HEAD;
353           for (i = 0; i < btrace_num; i++) {
354                     const char *func = wpa_trace_bfd_addr2func(btrace[i]);
355                     if (state == TRACE_HEAD && func &&
356                         (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
357                          os_strcmp(func, "wpa_trace_check_ref") == 0 ||
358                          os_strcmp(func, "wpa_trace_show") == 0))
359                               continue;
360                     if (state == TRACE_TAIL && sym && sym[i] &&
361                         os_strstr(sym[i], "__libc_start_main"))
362                               break;
363                     if (state == TRACE_HEAD)
364                               state = TRACE_RELEVANT;
365                     if (sym)
366                               wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
367                     else
368                               wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
369                     wpa_trace_bfd_addr(btrace[i]);
370                     if (state == TRACE_RELEVANT && func &&
371                         os_strcmp(func, "main") == 0)
372                               state = TRACE_TAIL;
373           }
374           free(sym);
375           wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
376 }
377 
378 
wpa_trace_show(const char * title)379 void wpa_trace_show(const char *title)
380 {
381           struct info {
382                     WPA_TRACE_INFO
383           } info;
384           wpa_trace_record(&info);
385           wpa_trace_dump(title, &info);
386 }
387 
388 
wpa_trace_add_ref_func(struct wpa_trace_ref * ref,const void * addr)389 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
390 {
391           if (addr == NULL)
392                     return;
393           ref->addr = addr;
394           wpa_trace_record(ref);
395           dl_list_add(&active_references, &ref->list);
396 }
397 
398 
wpa_trace_check_ref(const void * addr)399 void wpa_trace_check_ref(const void *addr)
400 {
401           struct wpa_trace_ref *ref;
402           dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
403                     if (addr != ref->addr)
404                               continue;
405                     wpa_trace_show("Freeing referenced memory");
406                     wpa_trace_dump("Reference registration", ref);
407                     abort();
408           }
409 }
410 
411 
wpa_trace_deinit(void)412 void wpa_trace_deinit(void)
413 {
414 #ifdef WPA_TRACE_BFD
415           free(syms);
416           syms = NULL;
417 #endif /* WPA_TRACE_BFD */
418 }
419 
420 #endif /* WPA_TRACE */
421