18dbcf02cSchristos /* 28dbcf02cSchristos * Backtrace debugging 38dbcf02cSchristos * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 48dbcf02cSchristos * 5e604d861Schristos * This software may be distributed under the terms of the BSD license. 6e604d861Schristos * See README for more details. 78dbcf02cSchristos */ 88dbcf02cSchristos 90a73ee0aSchristos #ifdef WPA_TRACE_BFD 100a73ee0aSchristos #define _GNU_SOURCE 110a73ee0aSchristos #include <link.h> 120a73ee0aSchristos #endif /* WPA_TRACE_BCD */ 138dbcf02cSchristos #include "includes.h" 148dbcf02cSchristos 158dbcf02cSchristos #include "common.h" 168dbcf02cSchristos #include "trace.h" 178dbcf02cSchristos 188dbcf02cSchristos #ifdef WPA_TRACE 198dbcf02cSchristos 208dbcf02cSchristos static struct dl_list active_references = 218dbcf02cSchristos { &active_references, &active_references }; 228dbcf02cSchristos 238dbcf02cSchristos #ifdef WPA_TRACE_BFD 248dbcf02cSchristos #include <bfd.h> 253c260e60Schristos 263c260e60Schristos #define DMGL_PARAMS (1 << 0) 273c260e60Schristos #define DMGL_ANSI (1 << 1) 288dbcf02cSchristos 298dbcf02cSchristos static char *prg_fname = NULL; 308dbcf02cSchristos static bfd *cached_abfd = NULL; 318dbcf02cSchristos static asymbol **syms = NULL; 320a73ee0aSchristos static unsigned long start_offset; 330a73ee0aSchristos static int start_offset_looked_up; 340a73ee0aSchristos 350a73ee0aSchristos 360a73ee0aSchristos static int callback(struct dl_phdr_info *info, size_t size, void *data) 370a73ee0aSchristos { 380a73ee0aSchristos /* 390a73ee0aSchristos * dl_iterate_phdr(3): 400a73ee0aSchristos * "The first object visited by callback is the main program." 410a73ee0aSchristos */ 420a73ee0aSchristos start_offset = info->dlpi_addr; 430a73ee0aSchristos 440a73ee0aSchristos /* 450a73ee0aSchristos * dl_iterate_phdr(3): 460a73ee0aSchristos * "The dl_iterate_phdr() function walks through the list of an 470a73ee0aSchristos * application's shared objects and calls the function callback 480a73ee0aSchristos * once for each object, until either all shared objects have 490a73ee0aSchristos * been processed or callback returns a nonzero value." 500a73ee0aSchristos */ 510a73ee0aSchristos return 1; 520a73ee0aSchristos } 530a73ee0aSchristos 548dbcf02cSchristos 558dbcf02cSchristos static void get_prg_fname(void) 568dbcf02cSchristos { 578dbcf02cSchristos char exe[50], fname[512]; 588dbcf02cSchristos int len; 598dbcf02cSchristos os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); 608dbcf02cSchristos len = readlink(exe, fname, sizeof(fname) - 1); 618dbcf02cSchristos if (len < 0 || len >= (int) sizeof(fname)) { 62bb610346Schristos wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno)); 638dbcf02cSchristos return; 648dbcf02cSchristos } 658dbcf02cSchristos fname[len] = '\0'; 668dbcf02cSchristos prg_fname = strdup(fname); 678dbcf02cSchristos } 688dbcf02cSchristos 698dbcf02cSchristos 708dbcf02cSchristos static bfd * open_bfd(const char *fname) 718dbcf02cSchristos { 728dbcf02cSchristos bfd *abfd; 738dbcf02cSchristos char **matching; 748dbcf02cSchristos 758dbcf02cSchristos abfd = bfd_openr(prg_fname, NULL); 768dbcf02cSchristos if (abfd == NULL) { 778dbcf02cSchristos wpa_printf(MSG_INFO, "bfd_openr failed"); 788dbcf02cSchristos return NULL; 798dbcf02cSchristos } 808dbcf02cSchristos 818dbcf02cSchristos if (bfd_check_format(abfd, bfd_archive)) { 828dbcf02cSchristos wpa_printf(MSG_INFO, "bfd_check_format failed"); 838dbcf02cSchristos bfd_close(abfd); 848dbcf02cSchristos return NULL; 858dbcf02cSchristos } 868dbcf02cSchristos 878dbcf02cSchristos if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { 888dbcf02cSchristos wpa_printf(MSG_INFO, "bfd_check_format_matches failed"); 898dbcf02cSchristos free(matching); 908dbcf02cSchristos bfd_close(abfd); 918dbcf02cSchristos return NULL; 928dbcf02cSchristos } 938dbcf02cSchristos 948dbcf02cSchristos return abfd; 958dbcf02cSchristos } 968dbcf02cSchristos 978dbcf02cSchristos 988dbcf02cSchristos static void read_syms(bfd *abfd) 998dbcf02cSchristos { 1008dbcf02cSchristos long storage, symcount; 1018dbcf02cSchristos bfd_boolean dynamic = FALSE; 1028dbcf02cSchristos 1038dbcf02cSchristos if (syms) 1048dbcf02cSchristos return; 1058dbcf02cSchristos 1068dbcf02cSchristos if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) { 1078dbcf02cSchristos wpa_printf(MSG_INFO, "No symbols"); 1088dbcf02cSchristos return; 1098dbcf02cSchristos } 1108dbcf02cSchristos 1118dbcf02cSchristos storage = bfd_get_symtab_upper_bound(abfd); 1128dbcf02cSchristos if (storage == 0) { 1138dbcf02cSchristos storage = bfd_get_dynamic_symtab_upper_bound(abfd); 1148dbcf02cSchristos dynamic = TRUE; 1158dbcf02cSchristos } 1168dbcf02cSchristos if (storage < 0) { 1178dbcf02cSchristos wpa_printf(MSG_INFO, "Unknown symtab upper bound"); 1188dbcf02cSchristos return; 1198dbcf02cSchristos } 1208dbcf02cSchristos 1218dbcf02cSchristos syms = malloc(storage); 1228dbcf02cSchristos if (syms == NULL) { 1238dbcf02cSchristos wpa_printf(MSG_INFO, "Failed to allocate memory for symtab " 1248dbcf02cSchristos "(%ld bytes)", storage); 1258dbcf02cSchristos return; 1268dbcf02cSchristos } 1278dbcf02cSchristos if (dynamic) 1288dbcf02cSchristos symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); 1298dbcf02cSchristos else 1308dbcf02cSchristos symcount = bfd_canonicalize_symtab(abfd, syms); 1318dbcf02cSchristos if (symcount < 0) { 1328dbcf02cSchristos wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab", 1338dbcf02cSchristos dynamic ? "dynamic " : ""); 1348dbcf02cSchristos free(syms); 1358dbcf02cSchristos syms = NULL; 1368dbcf02cSchristos return; 1378dbcf02cSchristos } 1388dbcf02cSchristos } 1398dbcf02cSchristos 1408dbcf02cSchristos 1418dbcf02cSchristos struct bfd_data { 1428dbcf02cSchristos bfd_vma pc; 1438dbcf02cSchristos bfd_boolean found; 1448dbcf02cSchristos const char *filename; 1458dbcf02cSchristos const char *function; 1468dbcf02cSchristos unsigned int line; 1478dbcf02cSchristos }; 1488dbcf02cSchristos 149*bb618362Schristos /* 150*bb618362Schristos * binutils removed the bfd parameter and renamed things but 151*bb618362Schristos * those were macros so we can detect their absence. 152*bb618362Schristos * Cf. https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=fd3619828e94a24a92cddec42cbc0ab33352eeb4;hp=5dfda3562a69686c43aad4fb0269cc9d5ec010d5 153*bb618362Schristos */ 154*bb618362Schristos #ifndef bfd_get_section_vma 155*bb618362Schristos #define bfd_get_section_vma(bfd, section) bfd_section_vma(section) 156*bb618362Schristos #endif 157*bb618362Schristos #ifndef bfd_get_section_size 158*bb618362Schristos #define bfd_get_section_size bfd_section_size 159*bb618362Schristos #endif 1608dbcf02cSchristos 1618dbcf02cSchristos static void find_addr_sect(bfd *abfd, asection *section, void *obj) 1628dbcf02cSchristos { 1638dbcf02cSchristos struct bfd_data *data = obj; 1648dbcf02cSchristos bfd_vma vma; 1658dbcf02cSchristos bfd_size_type size; 1668dbcf02cSchristos 1678dbcf02cSchristos if (data->found) 1688dbcf02cSchristos return; 1698dbcf02cSchristos 1708dbcf02cSchristos if (!(bfd_get_section_vma(abfd, section))) 1718dbcf02cSchristos return; 1728dbcf02cSchristos 1738dbcf02cSchristos vma = bfd_get_section_vma(abfd, section); 1748dbcf02cSchristos if (data->pc < vma) 1758dbcf02cSchristos return; 1768dbcf02cSchristos 1778dbcf02cSchristos size = bfd_get_section_size(section); 1788dbcf02cSchristos if (data->pc >= vma + size) 1798dbcf02cSchristos return; 1808dbcf02cSchristos 1818dbcf02cSchristos data->found = bfd_find_nearest_line(abfd, section, syms, 1828dbcf02cSchristos data->pc - vma, 1838dbcf02cSchristos &data->filename, 1848dbcf02cSchristos &data->function, 1858dbcf02cSchristos &data->line); 1868dbcf02cSchristos } 1878dbcf02cSchristos 1888dbcf02cSchristos 1898dbcf02cSchristos static void wpa_trace_bfd_addr(void *pc) 1908dbcf02cSchristos { 1918dbcf02cSchristos bfd *abfd = cached_abfd; 1928dbcf02cSchristos struct bfd_data data; 1938dbcf02cSchristos const char *name; 1948dbcf02cSchristos char *aname = NULL; 1958dbcf02cSchristos const char *filename; 1968dbcf02cSchristos 1978dbcf02cSchristos if (abfd == NULL) 1988dbcf02cSchristos return; 1998dbcf02cSchristos 200*bb618362Schristos data.pc = (uintptr_t) ((u8 *) pc - start_offset); 2018dbcf02cSchristos data.found = FALSE; 2028dbcf02cSchristos bfd_map_over_sections(abfd, find_addr_sect, &data); 2038dbcf02cSchristos 2048dbcf02cSchristos if (!data.found) 2058dbcf02cSchristos return; 2068dbcf02cSchristos 2078dbcf02cSchristos do { 2088dbcf02cSchristos if (data.function) 2098dbcf02cSchristos aname = bfd_demangle(abfd, data.function, 2108dbcf02cSchristos DMGL_ANSI | DMGL_PARAMS); 2118dbcf02cSchristos name = aname ? aname : data.function; 2128dbcf02cSchristos filename = data.filename; 2138dbcf02cSchristos if (filename) { 2148dbcf02cSchristos char *end = os_strrchr(filename, '/'); 2158dbcf02cSchristos int i = 0; 2168dbcf02cSchristos while (*filename && *filename == prg_fname[i] && 2178dbcf02cSchristos filename <= end) { 2188dbcf02cSchristos filename++; 2198dbcf02cSchristos i++; 2208dbcf02cSchristos } 2218dbcf02cSchristos } 2228dbcf02cSchristos wpa_printf(MSG_INFO, " %s() %s:%u", 2238dbcf02cSchristos name, filename, data.line); 2248dbcf02cSchristos free(aname); 2253c260e60Schristos aname = NULL; 2268dbcf02cSchristos 2278dbcf02cSchristos data.found = bfd_find_inliner_info(abfd, &data.filename, 2288dbcf02cSchristos &data.function, &data.line); 2298dbcf02cSchristos } while (data.found); 2308dbcf02cSchristos } 2318dbcf02cSchristos 2328dbcf02cSchristos 2338dbcf02cSchristos static const char * wpa_trace_bfd_addr2func(void *pc) 2348dbcf02cSchristos { 2358dbcf02cSchristos bfd *abfd = cached_abfd; 2368dbcf02cSchristos struct bfd_data data; 2378dbcf02cSchristos 2388dbcf02cSchristos if (abfd == NULL) 2398dbcf02cSchristos return NULL; 2408dbcf02cSchristos 241*bb618362Schristos data.pc = (uintptr_t) ((u8 *) pc - start_offset); 2428dbcf02cSchristos data.found = FALSE; 2438dbcf02cSchristos bfd_map_over_sections(abfd, find_addr_sect, &data); 2448dbcf02cSchristos 2458dbcf02cSchristos if (!data.found) 2468dbcf02cSchristos return NULL; 2478dbcf02cSchristos 2488dbcf02cSchristos return data.function; 2498dbcf02cSchristos } 2508dbcf02cSchristos 2518dbcf02cSchristos 2528dbcf02cSchristos static void wpa_trace_bfd_init(void) 2538dbcf02cSchristos { 2548dbcf02cSchristos if (!prg_fname) { 2558dbcf02cSchristos get_prg_fname(); 2568dbcf02cSchristos if (!prg_fname) 2578dbcf02cSchristos return; 2588dbcf02cSchristos } 2598dbcf02cSchristos 2608dbcf02cSchristos if (!cached_abfd) { 2618dbcf02cSchristos cached_abfd = open_bfd(prg_fname); 2628dbcf02cSchristos if (!cached_abfd) { 2638dbcf02cSchristos wpa_printf(MSG_INFO, "Failed to open bfd"); 2648dbcf02cSchristos return; 2658dbcf02cSchristos } 2668dbcf02cSchristos } 2678dbcf02cSchristos 2688dbcf02cSchristos read_syms(cached_abfd); 2698dbcf02cSchristos if (!syms) { 2708dbcf02cSchristos wpa_printf(MSG_INFO, "Failed to read symbols"); 2718dbcf02cSchristos return; 2728dbcf02cSchristos } 2730a73ee0aSchristos 2740a73ee0aSchristos if (!start_offset_looked_up) { 2750a73ee0aSchristos dl_iterate_phdr(callback, NULL); 2760a73ee0aSchristos start_offset_looked_up = 1; 2770a73ee0aSchristos } 2788dbcf02cSchristos } 2798dbcf02cSchristos 2808dbcf02cSchristos 2818dbcf02cSchristos void wpa_trace_dump_funcname(const char *title, void *pc) 2828dbcf02cSchristos { 2838dbcf02cSchristos wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc); 2848dbcf02cSchristos wpa_trace_bfd_init(); 2858dbcf02cSchristos wpa_trace_bfd_addr(pc); 2868dbcf02cSchristos } 2878dbcf02cSchristos 288bb610346Schristos 289bb610346Schristos size_t wpa_trace_calling_func(const char *buf[], size_t len) 290bb610346Schristos { 291bb610346Schristos bfd *abfd; 292bb610346Schristos void *btrace_res[WPA_TRACE_LEN]; 293bb610346Schristos int i, btrace_num; 294bb610346Schristos size_t pos = 0; 295bb610346Schristos 296bb610346Schristos if (len == 0) 297bb610346Schristos return 0; 298bb610346Schristos if (len > WPA_TRACE_LEN) 299bb610346Schristos len = WPA_TRACE_LEN; 300bb610346Schristos 301bb610346Schristos wpa_trace_bfd_init(); 302bb610346Schristos abfd = cached_abfd; 303bb610346Schristos if (!abfd) 304bb610346Schristos return 0; 305bb610346Schristos 306bb610346Schristos btrace_num = backtrace(btrace_res, len); 307bb610346Schristos if (btrace_num < 1) 308bb610346Schristos return 0; 309bb610346Schristos 310bb610346Schristos for (i = 0; i < btrace_num; i++) { 311bb610346Schristos struct bfd_data data; 312bb610346Schristos 313*bb618362Schristos data.pc = (uintptr_t) ((u8 *) btrace_res[i] - start_offset); 314bb610346Schristos data.found = FALSE; 315bb610346Schristos bfd_map_over_sections(abfd, find_addr_sect, &data); 316bb610346Schristos 317bb610346Schristos while (data.found) { 318bb610346Schristos if (data.function && 319bb610346Schristos (pos > 0 || 320bb610346Schristos os_strcmp(data.function, __func__) != 0)) { 321bb610346Schristos buf[pos++] = data.function; 322bb610346Schristos if (pos == len) 323bb610346Schristos return pos; 324bb610346Schristos } 325bb610346Schristos 326bb610346Schristos data.found = bfd_find_inliner_info(abfd, &data.filename, 327bb610346Schristos &data.function, 328bb610346Schristos &data.line); 329bb610346Schristos } 330bb610346Schristos } 331bb610346Schristos 332bb610346Schristos return pos; 333bb610346Schristos } 334bb610346Schristos 3358dbcf02cSchristos #else /* WPA_TRACE_BFD */ 3368dbcf02cSchristos 3378dbcf02cSchristos #define wpa_trace_bfd_init() do { } while (0) 3388dbcf02cSchristos #define wpa_trace_bfd_addr(pc) do { } while (0) 3398dbcf02cSchristos #define wpa_trace_bfd_addr2func(pc) NULL 3408dbcf02cSchristos 3418dbcf02cSchristos #endif /* WPA_TRACE_BFD */ 3428dbcf02cSchristos 3438dbcf02cSchristos void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) 3448dbcf02cSchristos { 3458dbcf02cSchristos char **sym; 3468dbcf02cSchristos int i; 3478dbcf02cSchristos enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; 3488dbcf02cSchristos 3498dbcf02cSchristos wpa_trace_bfd_init(); 3508dbcf02cSchristos wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); 3518dbcf02cSchristos sym = backtrace_symbols(btrace, btrace_num); 3528dbcf02cSchristos state = TRACE_HEAD; 3538dbcf02cSchristos for (i = 0; i < btrace_num; i++) { 3548dbcf02cSchristos const char *func = wpa_trace_bfd_addr2func(btrace[i]); 3558dbcf02cSchristos if (state == TRACE_HEAD && func && 3568dbcf02cSchristos (os_strcmp(func, "wpa_trace_add_ref_func") == 0 || 3578dbcf02cSchristos os_strcmp(func, "wpa_trace_check_ref") == 0 || 3588dbcf02cSchristos os_strcmp(func, "wpa_trace_show") == 0)) 3598dbcf02cSchristos continue; 3608dbcf02cSchristos if (state == TRACE_TAIL && sym && sym[i] && 3618dbcf02cSchristos os_strstr(sym[i], "__libc_start_main")) 3628dbcf02cSchristos break; 3638dbcf02cSchristos if (state == TRACE_HEAD) 3648dbcf02cSchristos state = TRACE_RELEVANT; 3658dbcf02cSchristos if (sym) 3668dbcf02cSchristos wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]); 3678dbcf02cSchristos else 3688dbcf02cSchristos wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]); 3698dbcf02cSchristos wpa_trace_bfd_addr(btrace[i]); 3708dbcf02cSchristos if (state == TRACE_RELEVANT && func && 3718dbcf02cSchristos os_strcmp(func, "main") == 0) 3728dbcf02cSchristos state = TRACE_TAIL; 3738dbcf02cSchristos } 3748dbcf02cSchristos free(sym); 3758dbcf02cSchristos wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); 3768dbcf02cSchristos } 3778dbcf02cSchristos 3788dbcf02cSchristos 3798dbcf02cSchristos void wpa_trace_show(const char *title) 3808dbcf02cSchristos { 3818dbcf02cSchristos struct info { 3828dbcf02cSchristos WPA_TRACE_INFO 3838dbcf02cSchristos } info; 3848dbcf02cSchristos wpa_trace_record(&info); 3858dbcf02cSchristos wpa_trace_dump(title, &info); 3868dbcf02cSchristos } 3878dbcf02cSchristos 3888dbcf02cSchristos 3898dbcf02cSchristos void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr) 3908dbcf02cSchristos { 3918dbcf02cSchristos if (addr == NULL) 3928dbcf02cSchristos return; 3938dbcf02cSchristos ref->addr = addr; 3948dbcf02cSchristos wpa_trace_record(ref); 3958dbcf02cSchristos dl_list_add(&active_references, &ref->list); 3968dbcf02cSchristos } 3978dbcf02cSchristos 3988dbcf02cSchristos 3998dbcf02cSchristos void wpa_trace_check_ref(const void *addr) 4008dbcf02cSchristos { 4018dbcf02cSchristos struct wpa_trace_ref *ref; 4028dbcf02cSchristos dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) { 4038dbcf02cSchristos if (addr != ref->addr) 4048dbcf02cSchristos continue; 4058dbcf02cSchristos wpa_trace_show("Freeing referenced memory"); 4068dbcf02cSchristos wpa_trace_dump("Reference registration", ref); 4078dbcf02cSchristos abort(); 4088dbcf02cSchristos } 4098dbcf02cSchristos } 4108dbcf02cSchristos 41136ebd06eSchristos 41236ebd06eSchristos void wpa_trace_deinit(void) 41336ebd06eSchristos { 41436ebd06eSchristos #ifdef WPA_TRACE_BFD 41536ebd06eSchristos free(syms); 41636ebd06eSchristos syms = NULL; 41736ebd06eSchristos #endif /* WPA_TRACE_BFD */ 41836ebd06eSchristos } 41936ebd06eSchristos 4208dbcf02cSchristos #endif /* WPA_TRACE */ 421