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 #ifdef WPA_TRACE_BFD 10 #define _GNU_SOURCE 11 #include <link.h> 12 #endif /* WPA_TRACE_BCD */ 13 #include "includes.h" 14 15 #include "common.h" 16 #include "trace.h" 17 18 #ifdef WPA_TRACE 19 20 const char *current_btrace_ctx; 21 22 static struct dl_list active_references = 23 { &active_references, &active_references }; 24 25 #ifdef WPA_TRACE_BFD 26 #include <bfd.h> 27 28 #define DMGL_PARAMS (1 << 0) 29 #define DMGL_ANSI (1 << 1) 30 31 static char *prg_fname = NULL; 32 static bfd *cached_abfd = NULL; 33 static asymbol **syms = NULL; 34 static unsigned long start_offset; 35 static int start_offset_looked_up; 36 37 38 static int callback(struct dl_phdr_info *info, size_t size, void *data) 39 { 40 /* 41 * dl_iterate_phdr(3): 42 * "The first object visited by callback is the main program." 43 */ 44 start_offset = info->dlpi_addr; 45 46 /* 47 * dl_iterate_phdr(3): 48 * "The dl_iterate_phdr() function walks through the list of an 49 * application's shared objects and calls the function callback 50 * once for each object, until either all shared objects have 51 * been processed or callback returns a nonzero value." 52 */ 53 return 1; 54 } 55 56 57 static void get_prg_fname(void) 58 { 59 char exe[50], fname[512]; 60 int len; 61 os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); 62 len = readlink(exe, fname, sizeof(fname) - 1); 63 if (len < 0 || len >= (int) sizeof(fname)) { 64 wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno)); 65 return; 66 } 67 fname[len] = '\0'; 68 prg_fname = strdup(fname); 69 } 70 71 72 static bfd * open_bfd(const char *fname) 73 { 74 bfd *abfd; 75 char **matching; 76 77 abfd = bfd_openr(prg_fname, NULL); 78 if (abfd == NULL) { 79 wpa_printf(MSG_INFO, "bfd_openr failed"); 80 return NULL; 81 } 82 83 if (bfd_check_format(abfd, bfd_archive)) { 84 wpa_printf(MSG_INFO, "bfd_check_format failed"); 85 bfd_close(abfd); 86 return NULL; 87 } 88 89 if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { 90 wpa_printf(MSG_INFO, "bfd_check_format_matches failed"); 91 free(matching); 92 bfd_close(abfd); 93 return NULL; 94 } 95 96 return abfd; 97 } 98 99 100 static void read_syms(bfd *abfd) 101 { 102 long storage, symcount; 103 bfd_boolean dynamic = FALSE; 104 105 if (syms) 106 return; 107 108 if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) { 109 wpa_printf(MSG_INFO, "No symbols"); 110 return; 111 } 112 113 storage = bfd_get_symtab_upper_bound(abfd); 114 if (storage == 0) { 115 storage = bfd_get_dynamic_symtab_upper_bound(abfd); 116 dynamic = TRUE; 117 } 118 if (storage < 0) { 119 wpa_printf(MSG_INFO, "Unknown symtab upper bound"); 120 return; 121 } 122 123 syms = malloc(storage); 124 if (syms == NULL) { 125 wpa_printf(MSG_INFO, "Failed to allocate memory for symtab " 126 "(%ld bytes)", storage); 127 return; 128 } 129 if (dynamic) 130 symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); 131 else 132 symcount = bfd_canonicalize_symtab(abfd, syms); 133 if (symcount < 0) { 134 wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab", 135 dynamic ? "dynamic " : ""); 136 free(syms); 137 syms = NULL; 138 return; 139 } 140 } 141 142 143 struct bfd_data { 144 bfd_vma pc; 145 bfd_boolean found; 146 const char *filename; 147 const char *function; 148 unsigned int line; 149 }; 150 151 /* 152 * binutils removed the bfd parameter and renamed things but 153 * those were macros so we can detect their absence. 154 * Cf. https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=fd3619828e94a24a92cddec42cbc0ab33352eeb4;hp=5dfda3562a69686c43aad4fb0269cc9d5ec010d5 155 */ 156 #ifndef bfd_get_section_vma 157 #define bfd_get_section_vma(bfd, section) bfd_section_vma(section) 158 #endif 159 #ifndef bfd_get_section_size 160 #define bfd_get_section_size bfd_section_size 161 #endif 162 163 static void find_addr_sect(bfd *abfd, asection *section, void *obj) 164 { 165 struct bfd_data *data = obj; 166 bfd_vma vma; 167 bfd_size_type size; 168 169 if (data->found) 170 return; 171 172 if (!(bfd_get_section_vma(abfd, section))) 173 return; 174 175 vma = bfd_get_section_vma(abfd, section); 176 if (data->pc < vma) 177 return; 178 179 size = bfd_get_section_size(section); 180 if (data->pc >= vma + size) 181 return; 182 183 data->found = bfd_find_nearest_line(abfd, section, syms, 184 data->pc - vma, 185 &data->filename, 186 &data->function, 187 &data->line); 188 } 189 190 191 static void wpa_trace_bfd_addr(void *pc) 192 { 193 bfd *abfd = cached_abfd; 194 struct bfd_data data; 195 const char *name; 196 char *aname = NULL; 197 const char *filename; 198 199 if (abfd == NULL) 200 return; 201 202 if (start_offset > (uintptr_t) pc) 203 return; 204 data.pc = (uintptr_t) ((u8 *) pc - start_offset); 205 data.found = FALSE; 206 bfd_map_over_sections(abfd, find_addr_sect, &data); 207 208 if (!data.found) 209 return; 210 211 do { 212 if (data.function) 213 aname = bfd_demangle(abfd, data.function, 214 DMGL_ANSI | DMGL_PARAMS); 215 name = aname ? aname : data.function; 216 filename = data.filename; 217 if (filename) { 218 char *end = os_strrchr(filename, '/'); 219 int i = 0; 220 while (*filename && *filename == prg_fname[i] && 221 filename <= end) { 222 filename++; 223 i++; 224 } 225 } 226 wpa_printf(MSG_INFO, " %s() %s:%u", 227 name, filename, data.line); 228 free(aname); 229 aname = NULL; 230 231 data.found = bfd_find_inliner_info(abfd, &data.filename, 232 &data.function, &data.line); 233 } while (data.found); 234 } 235 236 237 static const char * wpa_trace_bfd_addr2func(void *pc) 238 { 239 bfd *abfd = cached_abfd; 240 struct bfd_data data; 241 242 if (abfd == NULL) 243 return NULL; 244 245 if (start_offset > (uintptr_t) pc) 246 return NULL; 247 data.pc = (uintptr_t) ((u8 *) pc - start_offset); 248 data.found = FALSE; 249 bfd_map_over_sections(abfd, find_addr_sect, &data); 250 251 if (!data.found) 252 return NULL; 253 254 return data.function; 255 } 256 257 258 static void wpa_trace_bfd_init(void) 259 { 260 if (!prg_fname) { 261 get_prg_fname(); 262 if (!prg_fname) 263 return; 264 } 265 266 if (!cached_abfd) { 267 cached_abfd = open_bfd(prg_fname); 268 if (!cached_abfd) { 269 wpa_printf(MSG_INFO, "Failed to open bfd"); 270 return; 271 } 272 } 273 274 read_syms(cached_abfd); 275 if (!syms) { 276 wpa_printf(MSG_INFO, "Failed to read symbols"); 277 return; 278 } 279 280 if (!start_offset_looked_up) { 281 dl_iterate_phdr(callback, NULL); 282 start_offset_looked_up = 1; 283 } 284 } 285 286 287 void wpa_trace_dump_funcname(const char *title, void *pc) 288 { 289 wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc); 290 wpa_trace_bfd_init(); 291 wpa_trace_bfd_addr(pc); 292 } 293 294 295 size_t wpa_trace_calling_func(const char *buf[], size_t len) 296 { 297 bfd *abfd; 298 void *btrace_res[WPA_TRACE_LEN]; 299 int i, btrace_num; 300 size_t pos = 0; 301 302 if (len == 0) 303 return 0; 304 if (len > WPA_TRACE_LEN) 305 len = WPA_TRACE_LEN; 306 307 wpa_trace_bfd_init(); 308 abfd = cached_abfd; 309 if (!abfd) 310 return 0; 311 312 btrace_num = backtrace(btrace_res, len); 313 if (btrace_num < 1) 314 return 0; 315 316 for (i = 0; i < btrace_num; i++) { 317 struct bfd_data data; 318 319 if (start_offset > (uintptr_t) btrace_res[i]) 320 continue; 321 data.pc = (uintptr_t) ((u8 *) btrace_res[i] - start_offset); 322 data.found = FALSE; 323 bfd_map_over_sections(abfd, find_addr_sect, &data); 324 325 while (data.found) { 326 if (data.function && 327 (pos > 0 || 328 os_strcmp(data.function, __func__) != 0)) { 329 buf[pos++] = data.function; 330 if (pos == len) 331 return pos; 332 } 333 334 data.found = bfd_find_inliner_info(abfd, &data.filename, 335 &data.function, 336 &data.line); 337 } 338 } 339 340 return pos; 341 } 342 343 #else /* WPA_TRACE_BFD */ 344 345 #define wpa_trace_bfd_init() do { } while (0) 346 #define wpa_trace_bfd_addr(pc) do { } while (0) 347 #define wpa_trace_bfd_addr2func(pc) NULL 348 349 #endif /* WPA_TRACE_BFD */ 350 351 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num, 352 const char *btrace_ctx) 353 { 354 char **sym; 355 int i; 356 enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; 357 358 wpa_trace_bfd_init(); 359 wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); 360 sym = backtrace_symbols(btrace, btrace_num); 361 state = TRACE_HEAD; 362 for (i = 0; i < btrace_num; i++) { 363 const char *func = wpa_trace_bfd_addr2func(btrace[i]); 364 if (state == TRACE_HEAD && func && 365 (os_strcmp(func, "wpa_trace_add_ref_func") == 0 || 366 os_strcmp(func, "wpa_trace_check_ref") == 0 || 367 os_strcmp(func, "wpa_trace_show") == 0)) 368 continue; 369 if (state == TRACE_TAIL && sym && sym[i] && 370 os_strstr(sym[i], "__libc_start_main")) 371 break; 372 if (state == TRACE_HEAD) 373 state = TRACE_RELEVANT; 374 if (sym) 375 wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]); 376 else 377 wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]); 378 wpa_trace_bfd_addr(btrace[i]); 379 if (state == TRACE_RELEVANT && func && 380 os_strcmp(func, "main") == 0) 381 state = TRACE_TAIL; 382 } 383 free(sym); 384 if (btrace_ctx) 385 wpa_printf(MSG_INFO, "WPA_TRACE: External context: %s", 386 btrace_ctx); 387 wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); 388 } 389 390 391 void wpa_trace_show(const char *title) 392 { 393 struct info { 394 WPA_TRACE_INFO 395 } info; 396 wpa_trace_record(&info); 397 wpa_trace_dump(title, &info); 398 } 399 400 401 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr) 402 { 403 if (addr == NULL) 404 return; 405 ref->addr = addr; 406 wpa_trace_record(ref); 407 dl_list_add(&active_references, &ref->list); 408 } 409 410 411 void wpa_trace_check_ref(const void *addr) 412 { 413 struct wpa_trace_ref *ref; 414 dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) { 415 if (addr != ref->addr) 416 continue; 417 wpa_trace_show("Freeing referenced memory"); 418 wpa_trace_dump("Reference registration", ref); 419 abort(); 420 } 421 } 422 423 424 void wpa_trace_deinit(void) 425 { 426 #ifdef WPA_TRACE_BFD 427 free(prg_fname); 428 prg_fname = NULL; 429 430 free(syms); 431 syms = NULL; 432 if (cached_abfd) { 433 bfd_close(cached_abfd); 434 cached_abfd = NULL; 435 } 436 #endif /* WPA_TRACE_BFD */ 437 } 438 439 struct btrace_ctx_entry { 440 struct dl_list list; 441 char ctx[]; 442 }; 443 static struct dl_list old_btrace_ctxs = DL_LIST_HEAD_INIT(old_btrace_ctxs); 444 445 void wpa_trace_set_context(const char *ctx) 446 { 447 #ifdef WPA_TRACE_BFD 448 /* 449 * Old values might still be referenced, keep them in a list so that 450 * they are not freed and so that a reference exists for the sanitizer. 451 */ 452 if (ctx) { 453 struct btrace_ctx_entry *entry; 454 455 entry = malloc(sizeof(*entry) + os_strlen(ctx) + 1); 456 strcpy(entry->ctx, ctx); 457 dl_list_add(&old_btrace_ctxs, &entry->list); 458 459 current_btrace_ctx = entry->ctx; 460 } else 461 current_btrace_ctx = NULL; 462 #endif /* WPA_TRACE_BFD */ 463 } 464 465 #endif /* WPA_TRACE */ 466