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 16 static 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 25 static char *prg_fname = NULL; 26 static bfd *cached_abfd = NULL; 27 static asymbol **syms = NULL; 28 29 static 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 44 static 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 72 static 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 115 struct 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 124 static 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 152 static 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 196 static 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 215 static 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 239 void 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 247 size_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 301 void 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 337 void 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 347 void 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 357 void 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 370 void 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