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