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
find_addr_sect(bfd * abfd,asection * section,void * obj)150 static void find_addr_sect(bfd *abfd, asection *section, void *obj)
151 {
152 struct bfd_data *data = obj;
153 bfd_vma vma;
154 bfd_size_type size;
155
156 if (data->found)
157 return;
158
159 if (!(bfd_get_section_vma(abfd, section)))
160 return;
161
162 vma = bfd_get_section_vma(abfd, section);
163 if (data->pc < vma)
164 return;
165
166 size = bfd_get_section_size(section);
167 if (data->pc >= vma + size)
168 return;
169
170 data->found = bfd_find_nearest_line(abfd, section, syms,
171 data->pc - vma,
172 &data->filename,
173 &data->function,
174 &data->line);
175 }
176
177
wpa_trace_bfd_addr(void * pc)178 static void wpa_trace_bfd_addr(void *pc)
179 {
180 bfd *abfd = cached_abfd;
181 struct bfd_data data;
182 const char *name;
183 char *aname = NULL;
184 const char *filename;
185
186 if (abfd == NULL)
187 return;
188
189 data.pc = (bfd_hostptr_t) (pc - start_offset);
190 data.found = FALSE;
191 bfd_map_over_sections(abfd, find_addr_sect, &data);
192
193 if (!data.found)
194 return;
195
196 do {
197 if (data.function)
198 aname = bfd_demangle(abfd, data.function,
199 DMGL_ANSI | DMGL_PARAMS);
200 name = aname ? aname : data.function;
201 filename = data.filename;
202 if (filename) {
203 char *end = os_strrchr(filename, '/');
204 int i = 0;
205 while (*filename && *filename == prg_fname[i] &&
206 filename <= end) {
207 filename++;
208 i++;
209 }
210 }
211 wpa_printf(MSG_INFO, " %s() %s:%u",
212 name, filename, data.line);
213 free(aname);
214 aname = NULL;
215
216 data.found = bfd_find_inliner_info(abfd, &data.filename,
217 &data.function, &data.line);
218 } while (data.found);
219 }
220
221
wpa_trace_bfd_addr2func(void * pc)222 static const char * wpa_trace_bfd_addr2func(void *pc)
223 {
224 bfd *abfd = cached_abfd;
225 struct bfd_data data;
226
227 if (abfd == NULL)
228 return NULL;
229
230 data.pc = (bfd_hostptr_t) (pc - start_offset);
231 data.found = FALSE;
232 bfd_map_over_sections(abfd, find_addr_sect, &data);
233
234 if (!data.found)
235 return NULL;
236
237 return data.function;
238 }
239
240
wpa_trace_bfd_init(void)241 static void wpa_trace_bfd_init(void)
242 {
243 if (!prg_fname) {
244 get_prg_fname();
245 if (!prg_fname)
246 return;
247 }
248
249 if (!cached_abfd) {
250 cached_abfd = open_bfd(prg_fname);
251 if (!cached_abfd) {
252 wpa_printf(MSG_INFO, "Failed to open bfd");
253 return;
254 }
255 }
256
257 read_syms(cached_abfd);
258 if (!syms) {
259 wpa_printf(MSG_INFO, "Failed to read symbols");
260 return;
261 }
262
263 if (!start_offset_looked_up) {
264 dl_iterate_phdr(callback, NULL);
265 start_offset_looked_up = 1;
266 }
267 }
268
269
wpa_trace_dump_funcname(const char * title,void * pc)270 void wpa_trace_dump_funcname(const char *title, void *pc)
271 {
272 wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
273 wpa_trace_bfd_init();
274 wpa_trace_bfd_addr(pc);
275 }
276
277
wpa_trace_calling_func(const char * buf[],size_t len)278 size_t wpa_trace_calling_func(const char *buf[], size_t len)
279 {
280 bfd *abfd;
281 void *btrace_res[WPA_TRACE_LEN];
282 int i, btrace_num;
283 size_t pos = 0;
284
285 if (len == 0)
286 return 0;
287 if (len > WPA_TRACE_LEN)
288 len = WPA_TRACE_LEN;
289
290 wpa_trace_bfd_init();
291 abfd = cached_abfd;
292 if (!abfd)
293 return 0;
294
295 btrace_num = backtrace(btrace_res, len);
296 if (btrace_num < 1)
297 return 0;
298
299 for (i = 0; i < btrace_num; i++) {
300 struct bfd_data data;
301
302 data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset);
303 data.found = FALSE;
304 bfd_map_over_sections(abfd, find_addr_sect, &data);
305
306 while (data.found) {
307 if (data.function &&
308 (pos > 0 ||
309 os_strcmp(data.function, __func__) != 0)) {
310 buf[pos++] = data.function;
311 if (pos == len)
312 return pos;
313 }
314
315 data.found = bfd_find_inliner_info(abfd, &data.filename,
316 &data.function,
317 &data.line);
318 }
319 }
320
321 return pos;
322 }
323
324 #else /* WPA_TRACE_BFD */
325
326 #define wpa_trace_bfd_init() do { } while (0)
327 #define wpa_trace_bfd_addr(pc) do { } while (0)
328 #define wpa_trace_bfd_addr2func(pc) NULL
329
330 #endif /* WPA_TRACE_BFD */
331
wpa_trace_dump_func(const char * title,void ** btrace,int btrace_num)332 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
333 {
334 char **sym;
335 int i;
336 enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
337
338 wpa_trace_bfd_init();
339 wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
340 sym = backtrace_symbols(btrace, btrace_num);
341 state = TRACE_HEAD;
342 for (i = 0; i < btrace_num; i++) {
343 const char *func = wpa_trace_bfd_addr2func(btrace[i]);
344 if (state == TRACE_HEAD && func &&
345 (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
346 os_strcmp(func, "wpa_trace_check_ref") == 0 ||
347 os_strcmp(func, "wpa_trace_show") == 0))
348 continue;
349 if (state == TRACE_TAIL && sym && sym[i] &&
350 os_strstr(sym[i], "__libc_start_main"))
351 break;
352 if (state == TRACE_HEAD)
353 state = TRACE_RELEVANT;
354 if (sym)
355 wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
356 else
357 wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
358 wpa_trace_bfd_addr(btrace[i]);
359 if (state == TRACE_RELEVANT && func &&
360 os_strcmp(func, "main") == 0)
361 state = TRACE_TAIL;
362 }
363 free(sym);
364 wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
365 }
366
367
wpa_trace_show(const char * title)368 void wpa_trace_show(const char *title)
369 {
370 struct info {
371 WPA_TRACE_INFO
372 } info;
373 wpa_trace_record(&info);
374 wpa_trace_dump(title, &info);
375 }
376
377
wpa_trace_add_ref_func(struct wpa_trace_ref * ref,const void * addr)378 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
379 {
380 if (addr == NULL)
381 return;
382 ref->addr = addr;
383 wpa_trace_record(ref);
384 dl_list_add(&active_references, &ref->list);
385 }
386
387
wpa_trace_check_ref(const void * addr)388 void wpa_trace_check_ref(const void *addr)
389 {
390 struct wpa_trace_ref *ref;
391 dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
392 if (addr != ref->addr)
393 continue;
394 wpa_trace_show("Freeing referenced memory");
395 wpa_trace_dump("Reference registration", ref);
396 abort();
397 }
398 }
399
400
wpa_trace_deinit(void)401 void wpa_trace_deinit(void)
402 {
403 #ifdef WPA_TRACE_BFD
404 free(syms);
405 syms = NULL;
406 #endif /* WPA_TRACE_BFD */
407 }
408
409 #endif /* WPA_TRACE */
410