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 static void find_addr_sect(bfd *abfd, asection *section, void *obj) 151 { 152 struct bfd_data *data = obj; 153 bfd_vma vma; 154 bfd_size_type size; 155 156 if (data->found) 157 return; 158 159 if (!(bfd_get_section_vma(abfd, section))) 160 return; 161 162 vma = bfd_get_section_vma(abfd, section); 163 if (data->pc < vma) 164 return; 165 166 size = bfd_get_section_size(section); 167 if (data->pc >= vma + size) 168 return; 169 170 data->found = bfd_find_nearest_line(abfd, section, syms, 171 data->pc - vma, 172 &data->filename, 173 &data->function, 174 &data->line); 175 } 176 177 178 static void wpa_trace_bfd_addr(void *pc) 179 { 180 bfd *abfd = cached_abfd; 181 struct bfd_data data; 182 const char *name; 183 char *aname = NULL; 184 const char *filename; 185 186 if (abfd == NULL) 187 return; 188 189 data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset); 190 data.found = FALSE; 191 bfd_map_over_sections(abfd, find_addr_sect, &data); 192 193 if (!data.found) 194 return; 195 196 do { 197 if (data.function) 198 aname = bfd_demangle(abfd, data.function, 199 DMGL_ANSI | DMGL_PARAMS); 200 name = aname ? aname : data.function; 201 filename = data.filename; 202 if (filename) { 203 char *end = os_strrchr(filename, '/'); 204 int i = 0; 205 while (*filename && *filename == prg_fname[i] && 206 filename <= end) { 207 filename++; 208 i++; 209 } 210 } 211 wpa_printf(MSG_INFO, " %s() %s:%u", 212 name, filename, data.line); 213 free(aname); 214 aname = NULL; 215 216 data.found = bfd_find_inliner_info(abfd, &data.filename, 217 &data.function, &data.line); 218 } while (data.found); 219 } 220 221 222 static const char * wpa_trace_bfd_addr2func(void *pc) 223 { 224 bfd *abfd = cached_abfd; 225 struct bfd_data data; 226 227 if (abfd == NULL) 228 return NULL; 229 230 data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset); 231 data.found = FALSE; 232 bfd_map_over_sections(abfd, find_addr_sect, &data); 233 234 if (!data.found) 235 return NULL; 236 237 return data.function; 238 } 239 240 241 static void wpa_trace_bfd_init(void) 242 { 243 if (!prg_fname) { 244 get_prg_fname(); 245 if (!prg_fname) 246 return; 247 } 248 249 if (!cached_abfd) { 250 cached_abfd = open_bfd(prg_fname); 251 if (!cached_abfd) { 252 wpa_printf(MSG_INFO, "Failed to open bfd"); 253 return; 254 } 255 } 256 257 read_syms(cached_abfd); 258 if (!syms) { 259 wpa_printf(MSG_INFO, "Failed to read symbols"); 260 return; 261 } 262 263 if (!start_offset_looked_up) { 264 dl_iterate_phdr(callback, NULL); 265 start_offset_looked_up = 1; 266 } 267 } 268 269 270 void wpa_trace_dump_funcname(const char *title, void *pc) 271 { 272 wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc); 273 wpa_trace_bfd_init(); 274 wpa_trace_bfd_addr(pc); 275 } 276 277 278 size_t wpa_trace_calling_func(const char *buf[], size_t len) 279 { 280 bfd *abfd; 281 void *btrace_res[WPA_TRACE_LEN]; 282 int i, btrace_num; 283 size_t pos = 0; 284 285 if (len == 0) 286 return 0; 287 if (len > WPA_TRACE_LEN) 288 len = WPA_TRACE_LEN; 289 290 wpa_trace_bfd_init(); 291 abfd = cached_abfd; 292 if (!abfd) 293 return 0; 294 295 btrace_num = backtrace(btrace_res, len); 296 if (btrace_num < 1) 297 return 0; 298 299 for (i = 0; i < btrace_num; i++) { 300 struct bfd_data data; 301 302 data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset); 303 data.found = FALSE; 304 bfd_map_over_sections(abfd, find_addr_sect, &data); 305 306 while (data.found) { 307 if (data.function && 308 (pos > 0 || 309 os_strcmp(data.function, __func__) != 0)) { 310 buf[pos++] = data.function; 311 if (pos == len) 312 return pos; 313 } 314 315 data.found = bfd_find_inliner_info(abfd, &data.filename, 316 &data.function, 317 &data.line); 318 } 319 } 320 321 return pos; 322 } 323 324 #else /* WPA_TRACE_BFD */ 325 326 #define wpa_trace_bfd_init() do { } while (0) 327 #define wpa_trace_bfd_addr(pc) do { } while (0) 328 #define wpa_trace_bfd_addr2func(pc) NULL 329 330 #endif /* WPA_TRACE_BFD */ 331 332 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) 333 { 334 char **sym; 335 int i; 336 enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; 337 338 wpa_trace_bfd_init(); 339 wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); 340 sym = backtrace_symbols(btrace, btrace_num); 341 state = TRACE_HEAD; 342 for (i = 0; i < btrace_num; i++) { 343 const char *func = wpa_trace_bfd_addr2func(btrace[i]); 344 if (state == TRACE_HEAD && func && 345 (os_strcmp(func, "wpa_trace_add_ref_func") == 0 || 346 os_strcmp(func, "wpa_trace_check_ref") == 0 || 347 os_strcmp(func, "wpa_trace_show") == 0)) 348 continue; 349 if (state == TRACE_TAIL && sym && sym[i] && 350 os_strstr(sym[i], "__libc_start_main")) 351 break; 352 if (state == TRACE_HEAD) 353 state = TRACE_RELEVANT; 354 if (sym) 355 wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]); 356 else 357 wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]); 358 wpa_trace_bfd_addr(btrace[i]); 359 if (state == TRACE_RELEVANT && func && 360 os_strcmp(func, "main") == 0) 361 state = TRACE_TAIL; 362 } 363 free(sym); 364 wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); 365 } 366 367 368 void wpa_trace_show(const char *title) 369 { 370 struct info { 371 WPA_TRACE_INFO 372 } info; 373 wpa_trace_record(&info); 374 wpa_trace_dump(title, &info); 375 } 376 377 378 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr) 379 { 380 if (addr == NULL) 381 return; 382 ref->addr = addr; 383 wpa_trace_record(ref); 384 dl_list_add(&active_references, &ref->list); 385 } 386 387 388 void wpa_trace_check_ref(const void *addr) 389 { 390 struct wpa_trace_ref *ref; 391 dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) { 392 if (addr != ref->addr) 393 continue; 394 wpa_trace_show("Freeing referenced memory"); 395 wpa_trace_dump("Reference registration", ref); 396 abort(); 397 } 398 } 399 400 401 void wpa_trace_deinit(void) 402 { 403 #ifdef WPA_TRACE_BFD 404 free(syms); 405 syms = NULL; 406 #endif /* WPA_TRACE_BFD */ 407 } 408 409 #endif /* WPA_TRACE */ 410