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