1214501Srpaulo/* 2214501Srpaulo * Backtrace debugging 3214501Srpaulo * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "includes.h" 10214501Srpaulo 11214501Srpaulo#include "common.h" 12214501Srpaulo#include "trace.h" 13214501Srpaulo 14214501Srpaulo#ifdef WPA_TRACE 15214501Srpaulo 16214501Srpaulostatic struct dl_list active_references = 17214501Srpaulo{ &active_references, &active_references }; 18214501Srpaulo 19214501Srpaulo#ifdef WPA_TRACE_BFD 20214501Srpaulo#include <bfd.h> 21214501Srpaulo#ifdef __linux__ 22214501Srpaulo#include <demangle.h> 23214501Srpaulo#else /* __linux__ */ 24214501Srpaulo#include <libiberty/demangle.h> 25214501Srpaulo#endif /* __linux__ */ 26214501Srpaulo 27214501Srpaulostatic char *prg_fname = NULL; 28214501Srpaulostatic bfd *cached_abfd = NULL; 29214501Srpaulostatic asymbol **syms = NULL; 30214501Srpaulo 31214501Srpaulostatic void get_prg_fname(void) 32214501Srpaulo{ 33214501Srpaulo char exe[50], fname[512]; 34214501Srpaulo int len; 35214501Srpaulo os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); 36214501Srpaulo len = readlink(exe, fname, sizeof(fname) - 1); 37214501Srpaulo if (len < 0 || len >= (int) sizeof(fname)) { 38214501Srpaulo perror("readlink"); 39214501Srpaulo return; 40214501Srpaulo } 41214501Srpaulo fname[len] = '\0'; 42214501Srpaulo prg_fname = strdup(fname); 43214501Srpaulo} 44214501Srpaulo 45214501Srpaulo 46214501Srpaulostatic bfd * open_bfd(const char *fname) 47214501Srpaulo{ 48214501Srpaulo bfd *abfd; 49214501Srpaulo char **matching; 50214501Srpaulo 51214501Srpaulo abfd = bfd_openr(prg_fname, NULL); 52214501Srpaulo if (abfd == NULL) { 53214501Srpaulo wpa_printf(MSG_INFO, "bfd_openr failed"); 54214501Srpaulo return NULL; 55214501Srpaulo } 56214501Srpaulo 57214501Srpaulo if (bfd_check_format(abfd, bfd_archive)) { 58214501Srpaulo wpa_printf(MSG_INFO, "bfd_check_format failed"); 59214501Srpaulo bfd_close(abfd); 60214501Srpaulo return NULL; 61214501Srpaulo } 62214501Srpaulo 63214501Srpaulo if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { 64214501Srpaulo wpa_printf(MSG_INFO, "bfd_check_format_matches failed"); 65214501Srpaulo free(matching); 66214501Srpaulo bfd_close(abfd); 67214501Srpaulo return NULL; 68214501Srpaulo } 69214501Srpaulo 70214501Srpaulo return abfd; 71214501Srpaulo} 72214501Srpaulo 73214501Srpaulo 74214501Srpaulostatic void read_syms(bfd *abfd) 75214501Srpaulo{ 76214501Srpaulo long storage, symcount; 77214501Srpaulo bfd_boolean dynamic = FALSE; 78214501Srpaulo 79214501Srpaulo if (syms) 80214501Srpaulo return; 81214501Srpaulo 82214501Srpaulo if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) { 83214501Srpaulo wpa_printf(MSG_INFO, "No symbols"); 84214501Srpaulo return; 85214501Srpaulo } 86214501Srpaulo 87214501Srpaulo storage = bfd_get_symtab_upper_bound(abfd); 88214501Srpaulo if (storage == 0) { 89214501Srpaulo storage = bfd_get_dynamic_symtab_upper_bound(abfd); 90214501Srpaulo dynamic = TRUE; 91214501Srpaulo } 92214501Srpaulo if (storage < 0) { 93214501Srpaulo wpa_printf(MSG_INFO, "Unknown symtab upper bound"); 94214501Srpaulo return; 95214501Srpaulo } 96214501Srpaulo 97214501Srpaulo syms = malloc(storage); 98214501Srpaulo if (syms == NULL) { 99214501Srpaulo wpa_printf(MSG_INFO, "Failed to allocate memory for symtab " 100214501Srpaulo "(%ld bytes)", storage); 101214501Srpaulo return; 102214501Srpaulo } 103214501Srpaulo if (dynamic) 104214501Srpaulo symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); 105214501Srpaulo else 106214501Srpaulo symcount = bfd_canonicalize_symtab(abfd, syms); 107214501Srpaulo if (symcount < 0) { 108214501Srpaulo wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab", 109214501Srpaulo dynamic ? "dynamic " : ""); 110214501Srpaulo free(syms); 111214501Srpaulo syms = NULL; 112214501Srpaulo return; 113214501Srpaulo } 114214501Srpaulo} 115214501Srpaulo 116214501Srpaulo 117214501Srpaulostruct bfd_data { 118214501Srpaulo bfd_vma pc; 119214501Srpaulo bfd_boolean found; 120214501Srpaulo const char *filename; 121214501Srpaulo const char *function; 122214501Srpaulo unsigned int line; 123214501Srpaulo}; 124214501Srpaulo 125214501Srpaulo 126214501Srpaulostatic void find_addr_sect(bfd *abfd, asection *section, void *obj) 127214501Srpaulo{ 128214501Srpaulo struct bfd_data *data = obj; 129214501Srpaulo bfd_vma vma; 130214501Srpaulo bfd_size_type size; 131214501Srpaulo 132214501Srpaulo if (data->found) 133214501Srpaulo return; 134214501Srpaulo 135214501Srpaulo if (!(bfd_get_section_vma(abfd, section))) 136214501Srpaulo return; 137214501Srpaulo 138214501Srpaulo vma = bfd_get_section_vma(abfd, section); 139214501Srpaulo if (data->pc < vma) 140214501Srpaulo return; 141214501Srpaulo 142214501Srpaulo size = bfd_get_section_size(section); 143214501Srpaulo if (data->pc >= vma + size) 144214501Srpaulo return; 145214501Srpaulo 146214501Srpaulo data->found = bfd_find_nearest_line(abfd, section, syms, 147214501Srpaulo data->pc - vma, 148214501Srpaulo &data->filename, 149214501Srpaulo &data->function, 150214501Srpaulo &data->line); 151214501Srpaulo} 152214501Srpaulo 153214501Srpaulo 154214501Srpaulostatic void wpa_trace_bfd_addr(void *pc) 155214501Srpaulo{ 156214501Srpaulo bfd *abfd = cached_abfd; 157214501Srpaulo struct bfd_data data; 158214501Srpaulo const char *name; 159214501Srpaulo char *aname = NULL; 160214501Srpaulo const char *filename; 161214501Srpaulo 162214501Srpaulo if (abfd == NULL) 163214501Srpaulo return; 164214501Srpaulo 165214501Srpaulo data.pc = (bfd_vma) pc; 166214501Srpaulo data.found = FALSE; 167214501Srpaulo bfd_map_over_sections(abfd, find_addr_sect, &data); 168214501Srpaulo 169214501Srpaulo if (!data.found) 170214501Srpaulo return; 171214501Srpaulo 172214501Srpaulo do { 173214501Srpaulo if (data.function) 174214501Srpaulo aname = bfd_demangle(abfd, data.function, 175214501Srpaulo DMGL_ANSI | DMGL_PARAMS); 176214501Srpaulo name = aname ? aname : data.function; 177214501Srpaulo filename = data.filename; 178214501Srpaulo if (filename) { 179214501Srpaulo char *end = os_strrchr(filename, '/'); 180214501Srpaulo int i = 0; 181214501Srpaulo while (*filename && *filename == prg_fname[i] && 182214501Srpaulo filename <= end) { 183214501Srpaulo filename++; 184214501Srpaulo i++; 185214501Srpaulo } 186214501Srpaulo } 187214501Srpaulo wpa_printf(MSG_INFO, " %s() %s:%u", 188214501Srpaulo name, filename, data.line); 189214501Srpaulo free(aname); 190214501Srpaulo 191214501Srpaulo data.found = bfd_find_inliner_info(abfd, &data.filename, 192214501Srpaulo &data.function, &data.line); 193214501Srpaulo } while (data.found); 194214501Srpaulo} 195214501Srpaulo 196214501Srpaulo 197214501Srpaulostatic const char * wpa_trace_bfd_addr2func(void *pc) 198214501Srpaulo{ 199214501Srpaulo bfd *abfd = cached_abfd; 200214501Srpaulo struct bfd_data data; 201214501Srpaulo 202214501Srpaulo if (abfd == NULL) 203214501Srpaulo return NULL; 204214501Srpaulo 205214501Srpaulo data.pc = (bfd_vma) pc; 206214501Srpaulo data.found = FALSE; 207214501Srpaulo bfd_map_over_sections(abfd, find_addr_sect, &data); 208214501Srpaulo 209214501Srpaulo if (!data.found) 210214501Srpaulo return NULL; 211214501Srpaulo 212214501Srpaulo return data.function; 213214501Srpaulo} 214214501Srpaulo 215214501Srpaulo 216214501Srpaulostatic void wpa_trace_bfd_init(void) 217214501Srpaulo{ 218214501Srpaulo if (!prg_fname) { 219214501Srpaulo get_prg_fname(); 220214501Srpaulo if (!prg_fname) 221214501Srpaulo return; 222214501Srpaulo } 223214501Srpaulo 224214501Srpaulo if (!cached_abfd) { 225214501Srpaulo cached_abfd = open_bfd(prg_fname); 226214501Srpaulo if (!cached_abfd) { 227214501Srpaulo wpa_printf(MSG_INFO, "Failed to open bfd"); 228214501Srpaulo return; 229214501Srpaulo } 230214501Srpaulo } 231214501Srpaulo 232214501Srpaulo read_syms(cached_abfd); 233214501Srpaulo if (!syms) { 234214501Srpaulo wpa_printf(MSG_INFO, "Failed to read symbols"); 235214501Srpaulo return; 236214501Srpaulo } 237214501Srpaulo} 238214501Srpaulo 239214501Srpaulo 240214501Srpaulovoid wpa_trace_dump_funcname(const char *title, void *pc) 241214501Srpaulo{ 242214501Srpaulo wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc); 243214501Srpaulo wpa_trace_bfd_init(); 244214501Srpaulo wpa_trace_bfd_addr(pc); 245214501Srpaulo} 246214501Srpaulo 247214501Srpaulo#else /* WPA_TRACE_BFD */ 248214501Srpaulo 249214501Srpaulo#define wpa_trace_bfd_init() do { } while (0) 250214501Srpaulo#define wpa_trace_bfd_addr(pc) do { } while (0) 251214501Srpaulo#define wpa_trace_bfd_addr2func(pc) NULL 252214501Srpaulo 253214501Srpaulo#endif /* WPA_TRACE_BFD */ 254214501Srpaulo 255214501Srpaulovoid wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) 256214501Srpaulo{ 257214501Srpaulo char **sym; 258214501Srpaulo int i; 259214501Srpaulo enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; 260214501Srpaulo 261214501Srpaulo wpa_trace_bfd_init(); 262214501Srpaulo wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); 263214501Srpaulo sym = backtrace_symbols(btrace, btrace_num); 264214501Srpaulo state = TRACE_HEAD; 265214501Srpaulo for (i = 0; i < btrace_num; i++) { 266214501Srpaulo const char *func = wpa_trace_bfd_addr2func(btrace[i]); 267214501Srpaulo if (state == TRACE_HEAD && func && 268214501Srpaulo (os_strcmp(func, "wpa_trace_add_ref_func") == 0 || 269214501Srpaulo os_strcmp(func, "wpa_trace_check_ref") == 0 || 270214501Srpaulo os_strcmp(func, "wpa_trace_show") == 0)) 271214501Srpaulo continue; 272214501Srpaulo if (state == TRACE_TAIL && sym && sym[i] && 273214501Srpaulo os_strstr(sym[i], "__libc_start_main")) 274214501Srpaulo break; 275214501Srpaulo if (state == TRACE_HEAD) 276214501Srpaulo state = TRACE_RELEVANT; 277214501Srpaulo if (sym) 278214501Srpaulo wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]); 279214501Srpaulo else 280214501Srpaulo wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]); 281214501Srpaulo wpa_trace_bfd_addr(btrace[i]); 282214501Srpaulo if (state == TRACE_RELEVANT && func && 283214501Srpaulo os_strcmp(func, "main") == 0) 284214501Srpaulo state = TRACE_TAIL; 285214501Srpaulo } 286214501Srpaulo free(sym); 287214501Srpaulo wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); 288214501Srpaulo} 289214501Srpaulo 290214501Srpaulo 291214501Srpaulovoid wpa_trace_show(const char *title) 292214501Srpaulo{ 293214501Srpaulo struct info { 294214501Srpaulo WPA_TRACE_INFO 295214501Srpaulo } info; 296214501Srpaulo wpa_trace_record(&info); 297214501Srpaulo wpa_trace_dump(title, &info); 298214501Srpaulo} 299214501Srpaulo 300214501Srpaulo 301214501Srpaulovoid wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr) 302214501Srpaulo{ 303214501Srpaulo if (addr == NULL) 304214501Srpaulo return; 305214501Srpaulo ref->addr = addr; 306214501Srpaulo wpa_trace_record(ref); 307214501Srpaulo dl_list_add(&active_references, &ref->list); 308214501Srpaulo} 309214501Srpaulo 310214501Srpaulo 311214501Srpaulovoid wpa_trace_check_ref(const void *addr) 312214501Srpaulo{ 313214501Srpaulo struct wpa_trace_ref *ref; 314214501Srpaulo dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) { 315214501Srpaulo if (addr != ref->addr) 316214501Srpaulo continue; 317214501Srpaulo wpa_trace_show("Freeing referenced memory"); 318214501Srpaulo wpa_trace_dump("Reference registration", ref); 319214501Srpaulo abort(); 320214501Srpaulo } 321214501Srpaulo} 322214501Srpaulo 323214501Srpaulo#endif /* WPA_TRACE */ 324