1/* 2 * Backtrace debugging 3 * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15#include "includes.h" 16 17#include "common.h" 18#include "trace.h" 19 20#ifdef WPA_TRACE 21 22static struct dl_list active_references = 23{ &active_references, &active_references }; 24 25#ifdef WPA_TRACE_BFD 26#include <bfd.h> 27#ifdef __linux__ 28#include <demangle.h> 29#else /* __linux__ */ 30#include <libiberty/demangle.h> 31#endif /* __linux__ */ 32 33static char *prg_fname = NULL; 34static bfd *cached_abfd = NULL; 35static asymbol **syms = NULL; 36 37static void get_prg_fname(void) 38{ 39 char exe[50], fname[512]; 40 int len; 41 os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); 42 len = readlink(exe, fname, sizeof(fname) - 1); 43 if (len < 0 || len >= (int) sizeof(fname)) { 44 perror("readlink"); 45 return; 46 } 47 fname[len] = '\0'; 48 prg_fname = strdup(fname); 49} 50 51 52static bfd * open_bfd(const char *fname) 53{ 54 bfd *abfd; 55 char **matching; 56 57 abfd = bfd_openr(prg_fname, NULL); 58 if (abfd == NULL) { 59 wpa_printf(MSG_INFO, "bfd_openr failed"); 60 return NULL; 61 } 62 63 if (bfd_check_format(abfd, bfd_archive)) { 64 wpa_printf(MSG_INFO, "bfd_check_format failed"); 65 bfd_close(abfd); 66 return NULL; 67 } 68 69 if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { 70 wpa_printf(MSG_INFO, "bfd_check_format_matches failed"); 71 free(matching); 72 bfd_close(abfd); 73 return NULL; 74 } 75 76 return abfd; 77} 78 79 80static void read_syms(bfd *abfd) 81{ 82 long storage, symcount; 83 bfd_boolean dynamic = FALSE; 84 85 if (syms) 86 return; 87 88 if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) { 89 wpa_printf(MSG_INFO, "No symbols"); 90 return; 91 } 92 93 storage = bfd_get_symtab_upper_bound(abfd); 94 if (storage == 0) { 95 storage = bfd_get_dynamic_symtab_upper_bound(abfd); 96 dynamic = TRUE; 97 } 98 if (storage < 0) { 99 wpa_printf(MSG_INFO, "Unknown symtab upper bound"); 100 return; 101 } 102 103 syms = malloc(storage); 104 if (syms == NULL) { 105 wpa_printf(MSG_INFO, "Failed to allocate memory for symtab " 106 "(%ld bytes)", storage); 107 return; 108 } 109 if (dynamic) 110 symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); 111 else 112 symcount = bfd_canonicalize_symtab(abfd, syms); 113 if (symcount < 0) { 114 wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab", 115 dynamic ? "dynamic " : ""); 116 free(syms); 117 syms = NULL; 118 return; 119 } 120} 121 122 123struct bfd_data { 124 bfd_vma pc; 125 bfd_boolean found; 126 const char *filename; 127 const char *function; 128 unsigned int line; 129}; 130 131 132static void find_addr_sect(bfd *abfd, asection *section, void *obj) 133{ 134 struct bfd_data *data = obj; 135 bfd_vma vma; 136 bfd_size_type size; 137 138 if (data->found) 139 return; 140 141 if (!(bfd_get_section_vma(abfd, section))) 142 return; 143 144 vma = bfd_get_section_vma(abfd, section); 145 if (data->pc < vma) 146 return; 147 148 size = bfd_get_section_size(section); 149 if (data->pc >= vma + size) 150 return; 151 152 data->found = bfd_find_nearest_line(abfd, section, syms, 153 data->pc - vma, 154 &data->filename, 155 &data->function, 156 &data->line); 157} 158 159 160static void wpa_trace_bfd_addr(void *pc) 161{ 162 bfd *abfd = cached_abfd; 163 struct bfd_data data; 164 const char *name; 165 char *aname = NULL; 166 const char *filename; 167 168 if (abfd == NULL) 169 return; 170 171 data.pc = (bfd_vma) pc; 172 data.found = FALSE; 173 bfd_map_over_sections(abfd, find_addr_sect, &data); 174 175 if (!data.found) 176 return; 177 178 do { 179 if (data.function) 180 aname = bfd_demangle(abfd, data.function, 181 DMGL_ANSI | DMGL_PARAMS); 182 name = aname ? aname : data.function; 183 filename = data.filename; 184 if (filename) { 185 char *end = os_strrchr(filename, '/'); 186 int i = 0; 187 while (*filename && *filename == prg_fname[i] && 188 filename <= end) { 189 filename++; 190 i++; 191 } 192 } 193 wpa_printf(MSG_INFO, " %s() %s:%u", 194 name, filename, data.line); 195 free(aname); 196 197 data.found = bfd_find_inliner_info(abfd, &data.filename, 198 &data.function, &data.line); 199 } while (data.found); 200} 201 202 203static const char * wpa_trace_bfd_addr2func(void *pc) 204{ 205 bfd *abfd = cached_abfd; 206 struct bfd_data data; 207 208 if (abfd == NULL) 209 return NULL; 210 211 data.pc = (bfd_vma) pc; 212 data.found = FALSE; 213 bfd_map_over_sections(abfd, find_addr_sect, &data); 214 215 if (!data.found) 216 return NULL; 217 218 return data.function; 219} 220 221 222static void wpa_trace_bfd_init(void) 223{ 224 if (!prg_fname) { 225 get_prg_fname(); 226 if (!prg_fname) 227 return; 228 } 229 230 if (!cached_abfd) { 231 cached_abfd = open_bfd(prg_fname); 232 if (!cached_abfd) { 233 wpa_printf(MSG_INFO, "Failed to open bfd"); 234 return; 235 } 236 } 237 238 read_syms(cached_abfd); 239 if (!syms) { 240 wpa_printf(MSG_INFO, "Failed to read symbols"); 241 return; 242 } 243} 244 245 246void wpa_trace_dump_funcname(const char *title, void *pc) 247{ 248 wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc); 249 wpa_trace_bfd_init(); 250 wpa_trace_bfd_addr(pc); 251} 252 253#else /* WPA_TRACE_BFD */ 254 255#define wpa_trace_bfd_init() do { } while (0) 256#define wpa_trace_bfd_addr(pc) do { } while (0) 257#define wpa_trace_bfd_addr2func(pc) NULL 258 259#endif /* WPA_TRACE_BFD */ 260 261void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) 262{ 263 char **sym; 264 int i; 265 enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; 266 267 wpa_trace_bfd_init(); 268 wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); 269 sym = backtrace_symbols(btrace, btrace_num); 270 state = TRACE_HEAD; 271 for (i = 0; i < btrace_num; i++) { 272 const char *func = wpa_trace_bfd_addr2func(btrace[i]); 273 if (state == TRACE_HEAD && func && 274 (os_strcmp(func, "wpa_trace_add_ref_func") == 0 || 275 os_strcmp(func, "wpa_trace_check_ref") == 0 || 276 os_strcmp(func, "wpa_trace_show") == 0)) 277 continue; 278 if (state == TRACE_TAIL && sym && sym[i] && 279 os_strstr(sym[i], "__libc_start_main")) 280 break; 281 if (state == TRACE_HEAD) 282 state = TRACE_RELEVANT; 283 if (sym) 284 wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]); 285 else 286 wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]); 287 wpa_trace_bfd_addr(btrace[i]); 288 if (state == TRACE_RELEVANT && func && 289 os_strcmp(func, "main") == 0) 290 state = TRACE_TAIL; 291 } 292 free(sym); 293 wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); 294} 295 296 297void wpa_trace_show(const char *title) 298{ 299 struct info { 300 WPA_TRACE_INFO 301 } info; 302 wpa_trace_record(&info); 303 wpa_trace_dump(title, &info); 304} 305 306 307void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr) 308{ 309 if (addr == NULL) 310 return; 311 ref->addr = addr; 312 wpa_trace_record(ref); 313 dl_list_add(&active_references, &ref->list); 314} 315 316 317void wpa_trace_check_ref(const void *addr) 318{ 319 struct wpa_trace_ref *ref; 320 dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) { 321 if (addr != ref->addr) 322 continue; 323 wpa_trace_show("Freeing referenced memory"); 324 wpa_trace_dump("Reference registration", ref); 325 abort(); 326 } 327} 328 329#endif /* WPA_TRACE */ 330