xref: /netbsd-src/external/bsd/wpa/dist/src/utils/trace.c (revision bb6183629cf165db498d8e1f4e2de129f7efb21c)
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