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