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