1214501Srpaulo/*
2214501Srpaulo * Backtrace debugging
3214501Srpaulo * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12214501Srpaulo#include "trace.h"
13214501Srpaulo
14214501Srpaulo#ifdef WPA_TRACE
15214501Srpaulo
16214501Srpaulostatic struct dl_list active_references =
17214501Srpaulo{ &active_references, &active_references };
18214501Srpaulo
19214501Srpaulo#ifdef WPA_TRACE_BFD
20214501Srpaulo#include <bfd.h>
21214501Srpaulo#ifdef __linux__
22214501Srpaulo#include <demangle.h>
23214501Srpaulo#else /* __linux__ */
24214501Srpaulo#include <libiberty/demangle.h>
25214501Srpaulo#endif /* __linux__ */
26214501Srpaulo
27214501Srpaulostatic char *prg_fname = NULL;
28214501Srpaulostatic bfd *cached_abfd = NULL;
29214501Srpaulostatic asymbol **syms = NULL;
30214501Srpaulo
31214501Srpaulostatic void get_prg_fname(void)
32214501Srpaulo{
33214501Srpaulo	char exe[50], fname[512];
34214501Srpaulo	int len;
35214501Srpaulo	os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
36214501Srpaulo	len = readlink(exe, fname, sizeof(fname) - 1);
37214501Srpaulo	if (len < 0 || len >= (int) sizeof(fname)) {
38214501Srpaulo		perror("readlink");
39214501Srpaulo		return;
40214501Srpaulo	}
41214501Srpaulo	fname[len] = '\0';
42214501Srpaulo	prg_fname = strdup(fname);
43214501Srpaulo}
44214501Srpaulo
45214501Srpaulo
46214501Srpaulostatic bfd * open_bfd(const char *fname)
47214501Srpaulo{
48214501Srpaulo	bfd *abfd;
49214501Srpaulo	char **matching;
50214501Srpaulo
51214501Srpaulo	abfd = bfd_openr(prg_fname, NULL);
52214501Srpaulo	if (abfd == NULL) {
53214501Srpaulo		wpa_printf(MSG_INFO, "bfd_openr failed");
54214501Srpaulo		return NULL;
55214501Srpaulo	}
56214501Srpaulo
57214501Srpaulo	if (bfd_check_format(abfd, bfd_archive)) {
58214501Srpaulo		wpa_printf(MSG_INFO, "bfd_check_format failed");
59214501Srpaulo		bfd_close(abfd);
60214501Srpaulo		return NULL;
61214501Srpaulo	}
62214501Srpaulo
63214501Srpaulo	if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
64214501Srpaulo		wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
65214501Srpaulo		free(matching);
66214501Srpaulo		bfd_close(abfd);
67214501Srpaulo		return NULL;
68214501Srpaulo	}
69214501Srpaulo
70214501Srpaulo	return abfd;
71214501Srpaulo}
72214501Srpaulo
73214501Srpaulo
74214501Srpaulostatic void read_syms(bfd *abfd)
75214501Srpaulo{
76214501Srpaulo	long storage, symcount;
77214501Srpaulo	bfd_boolean dynamic = FALSE;
78214501Srpaulo
79214501Srpaulo	if (syms)
80214501Srpaulo		return;
81214501Srpaulo
82214501Srpaulo	if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
83214501Srpaulo		wpa_printf(MSG_INFO, "No symbols");
84214501Srpaulo		return;
85214501Srpaulo	}
86214501Srpaulo
87214501Srpaulo	storage = bfd_get_symtab_upper_bound(abfd);
88214501Srpaulo	if (storage == 0) {
89214501Srpaulo		storage = bfd_get_dynamic_symtab_upper_bound(abfd);
90214501Srpaulo		dynamic = TRUE;
91214501Srpaulo	}
92214501Srpaulo	if (storage < 0) {
93214501Srpaulo		wpa_printf(MSG_INFO, "Unknown symtab upper bound");
94214501Srpaulo		return;
95214501Srpaulo	}
96214501Srpaulo
97214501Srpaulo	syms = malloc(storage);
98214501Srpaulo	if (syms == NULL) {
99214501Srpaulo		wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
100214501Srpaulo			   "(%ld bytes)", storage);
101214501Srpaulo		return;
102214501Srpaulo	}
103214501Srpaulo	if (dynamic)
104214501Srpaulo		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
105214501Srpaulo	else
106214501Srpaulo		symcount = bfd_canonicalize_symtab(abfd, syms);
107214501Srpaulo	if (symcount < 0) {
108214501Srpaulo		wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
109214501Srpaulo			   dynamic ? "dynamic " : "");
110214501Srpaulo		free(syms);
111214501Srpaulo		syms = NULL;
112214501Srpaulo		return;
113214501Srpaulo	}
114214501Srpaulo}
115214501Srpaulo
116214501Srpaulo
117214501Srpaulostruct bfd_data {
118214501Srpaulo	bfd_vma pc;
119214501Srpaulo	bfd_boolean found;
120214501Srpaulo	const char *filename;
121214501Srpaulo	const char *function;
122214501Srpaulo	unsigned int line;
123214501Srpaulo};
124214501Srpaulo
125214501Srpaulo
126214501Srpaulostatic void find_addr_sect(bfd *abfd, asection *section, void *obj)
127214501Srpaulo{
128214501Srpaulo	struct bfd_data *data = obj;
129214501Srpaulo	bfd_vma vma;
130214501Srpaulo	bfd_size_type size;
131214501Srpaulo
132214501Srpaulo	if (data->found)
133214501Srpaulo		return;
134214501Srpaulo
135214501Srpaulo	if (!(bfd_get_section_vma(abfd, section)))
136214501Srpaulo		return;
137214501Srpaulo
138214501Srpaulo	vma = bfd_get_section_vma(abfd, section);
139214501Srpaulo	if (data->pc < vma)
140214501Srpaulo		return;
141214501Srpaulo
142214501Srpaulo	size = bfd_get_section_size(section);
143214501Srpaulo	if (data->pc >= vma + size)
144214501Srpaulo		return;
145214501Srpaulo
146214501Srpaulo	data->found = bfd_find_nearest_line(abfd, section, syms,
147214501Srpaulo					    data->pc - vma,
148214501Srpaulo					    &data->filename,
149214501Srpaulo					    &data->function,
150214501Srpaulo					    &data->line);
151214501Srpaulo}
152214501Srpaulo
153214501Srpaulo
154214501Srpaulostatic void wpa_trace_bfd_addr(void *pc)
155214501Srpaulo{
156214501Srpaulo	bfd *abfd = cached_abfd;
157214501Srpaulo	struct bfd_data data;
158214501Srpaulo	const char *name;
159214501Srpaulo	char *aname = NULL;
160214501Srpaulo	const char *filename;
161214501Srpaulo
162214501Srpaulo	if (abfd == NULL)
163214501Srpaulo		return;
164214501Srpaulo
165214501Srpaulo	data.pc = (bfd_vma) pc;
166214501Srpaulo	data.found = FALSE;
167214501Srpaulo	bfd_map_over_sections(abfd, find_addr_sect, &data);
168214501Srpaulo
169214501Srpaulo	if (!data.found)
170214501Srpaulo		return;
171214501Srpaulo
172214501Srpaulo	do {
173214501Srpaulo		if (data.function)
174214501Srpaulo			aname = bfd_demangle(abfd, data.function,
175214501Srpaulo					     DMGL_ANSI | DMGL_PARAMS);
176214501Srpaulo		name = aname ? aname : data.function;
177214501Srpaulo		filename = data.filename;
178214501Srpaulo		if (filename) {
179214501Srpaulo			char *end = os_strrchr(filename, '/');
180214501Srpaulo			int i = 0;
181214501Srpaulo			while (*filename && *filename == prg_fname[i] &&
182214501Srpaulo			       filename <= end) {
183214501Srpaulo				filename++;
184214501Srpaulo				i++;
185214501Srpaulo			}
186214501Srpaulo		}
187214501Srpaulo		wpa_printf(MSG_INFO, "     %s() %s:%u",
188214501Srpaulo			   name, filename, data.line);
189214501Srpaulo		free(aname);
190214501Srpaulo
191214501Srpaulo		data.found = bfd_find_inliner_info(abfd, &data.filename,
192214501Srpaulo						   &data.function, &data.line);
193214501Srpaulo	} while (data.found);
194214501Srpaulo}
195214501Srpaulo
196214501Srpaulo
197214501Srpaulostatic const char * wpa_trace_bfd_addr2func(void *pc)
198214501Srpaulo{
199214501Srpaulo	bfd *abfd = cached_abfd;
200214501Srpaulo	struct bfd_data data;
201214501Srpaulo
202214501Srpaulo	if (abfd == NULL)
203214501Srpaulo		return NULL;
204214501Srpaulo
205214501Srpaulo	data.pc = (bfd_vma) pc;
206214501Srpaulo	data.found = FALSE;
207214501Srpaulo	bfd_map_over_sections(abfd, find_addr_sect, &data);
208214501Srpaulo
209214501Srpaulo	if (!data.found)
210214501Srpaulo		return NULL;
211214501Srpaulo
212214501Srpaulo	return data.function;
213214501Srpaulo}
214214501Srpaulo
215214501Srpaulo
216214501Srpaulostatic void wpa_trace_bfd_init(void)
217214501Srpaulo{
218214501Srpaulo	if (!prg_fname) {
219214501Srpaulo		get_prg_fname();
220214501Srpaulo		if (!prg_fname)
221214501Srpaulo			return;
222214501Srpaulo	}
223214501Srpaulo
224214501Srpaulo	if (!cached_abfd) {
225214501Srpaulo		cached_abfd = open_bfd(prg_fname);
226214501Srpaulo		if (!cached_abfd) {
227214501Srpaulo			wpa_printf(MSG_INFO, "Failed to open bfd");
228214501Srpaulo			return;
229214501Srpaulo		}
230214501Srpaulo	}
231214501Srpaulo
232214501Srpaulo	read_syms(cached_abfd);
233214501Srpaulo	if (!syms) {
234214501Srpaulo		wpa_printf(MSG_INFO, "Failed to read symbols");
235214501Srpaulo		return;
236214501Srpaulo	}
237214501Srpaulo}
238214501Srpaulo
239214501Srpaulo
240214501Srpaulovoid wpa_trace_dump_funcname(const char *title, void *pc)
241214501Srpaulo{
242214501Srpaulo	wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
243214501Srpaulo	wpa_trace_bfd_init();
244214501Srpaulo	wpa_trace_bfd_addr(pc);
245214501Srpaulo}
246214501Srpaulo
247214501Srpaulo#else /* WPA_TRACE_BFD */
248214501Srpaulo
249214501Srpaulo#define wpa_trace_bfd_init() do { } while (0)
250214501Srpaulo#define wpa_trace_bfd_addr(pc) do { } while (0)
251214501Srpaulo#define wpa_trace_bfd_addr2func(pc) NULL
252214501Srpaulo
253214501Srpaulo#endif /* WPA_TRACE_BFD */
254214501Srpaulo
255214501Srpaulovoid wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
256214501Srpaulo{
257214501Srpaulo	char **sym;
258214501Srpaulo	int i;
259214501Srpaulo	enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
260214501Srpaulo
261214501Srpaulo	wpa_trace_bfd_init();
262214501Srpaulo	wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
263214501Srpaulo	sym = backtrace_symbols(btrace, btrace_num);
264214501Srpaulo	state = TRACE_HEAD;
265214501Srpaulo	for (i = 0; i < btrace_num; i++) {
266214501Srpaulo		const char *func = wpa_trace_bfd_addr2func(btrace[i]);
267214501Srpaulo		if (state == TRACE_HEAD && func &&
268214501Srpaulo		    (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
269214501Srpaulo		     os_strcmp(func, "wpa_trace_check_ref") == 0 ||
270214501Srpaulo		     os_strcmp(func, "wpa_trace_show") == 0))
271214501Srpaulo			continue;
272214501Srpaulo		if (state == TRACE_TAIL && sym && sym[i] &&
273214501Srpaulo		    os_strstr(sym[i], "__libc_start_main"))
274214501Srpaulo			break;
275214501Srpaulo		if (state == TRACE_HEAD)
276214501Srpaulo			state = TRACE_RELEVANT;
277214501Srpaulo		if (sym)
278214501Srpaulo			wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
279214501Srpaulo		else
280214501Srpaulo			wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
281214501Srpaulo		wpa_trace_bfd_addr(btrace[i]);
282214501Srpaulo		if (state == TRACE_RELEVANT && func &&
283214501Srpaulo		    os_strcmp(func, "main") == 0)
284214501Srpaulo			state = TRACE_TAIL;
285214501Srpaulo	}
286214501Srpaulo	free(sym);
287214501Srpaulo	wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
288214501Srpaulo}
289214501Srpaulo
290214501Srpaulo
291214501Srpaulovoid wpa_trace_show(const char *title)
292214501Srpaulo{
293214501Srpaulo	struct info {
294214501Srpaulo		WPA_TRACE_INFO
295214501Srpaulo	} info;
296214501Srpaulo	wpa_trace_record(&info);
297214501Srpaulo	wpa_trace_dump(title, &info);
298214501Srpaulo}
299214501Srpaulo
300214501Srpaulo
301214501Srpaulovoid wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
302214501Srpaulo{
303214501Srpaulo	if (addr == NULL)
304214501Srpaulo		return;
305214501Srpaulo	ref->addr = addr;
306214501Srpaulo	wpa_trace_record(ref);
307214501Srpaulo	dl_list_add(&active_references, &ref->list);
308214501Srpaulo}
309214501Srpaulo
310214501Srpaulo
311214501Srpaulovoid wpa_trace_check_ref(const void *addr)
312214501Srpaulo{
313214501Srpaulo	struct wpa_trace_ref *ref;
314214501Srpaulo	dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
315214501Srpaulo		if (addr != ref->addr)
316214501Srpaulo			continue;
317214501Srpaulo		wpa_trace_show("Freeing referenced memory");
318214501Srpaulo		wpa_trace_dump("Reference registration", ref);
319214501Srpaulo		abort();
320214501Srpaulo	}
321214501Srpaulo}
322214501Srpaulo
323214501Srpaulo#endif /* WPA_TRACE */
324