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		perror("readlink");
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_vma) 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_vma) 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#else /* WPA_TRACE_BFD */
247
248#define wpa_trace_bfd_init() do { } while (0)
249#define wpa_trace_bfd_addr(pc) do { } while (0)
250#define wpa_trace_bfd_addr2func(pc) NULL
251
252#endif /* WPA_TRACE_BFD */
253
254void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
255{
256	char **sym;
257	int i;
258	enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
259
260	wpa_trace_bfd_init();
261	wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
262	sym = backtrace_symbols(btrace, btrace_num);
263	state = TRACE_HEAD;
264	for (i = 0; i < btrace_num; i++) {
265		const char *func = wpa_trace_bfd_addr2func(btrace[i]);
266		if (state == TRACE_HEAD && func &&
267		    (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
268		     os_strcmp(func, "wpa_trace_check_ref") == 0 ||
269		     os_strcmp(func, "wpa_trace_show") == 0))
270			continue;
271		if (state == TRACE_TAIL && sym && sym[i] &&
272		    os_strstr(sym[i], "__libc_start_main"))
273			break;
274		if (state == TRACE_HEAD)
275			state = TRACE_RELEVANT;
276		if (sym)
277			wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
278		else
279			wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
280		wpa_trace_bfd_addr(btrace[i]);
281		if (state == TRACE_RELEVANT && func &&
282		    os_strcmp(func, "main") == 0)
283			state = TRACE_TAIL;
284	}
285	free(sym);
286	wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
287}
288
289
290void wpa_trace_show(const char *title)
291{
292	struct info {
293		WPA_TRACE_INFO
294	} info;
295	wpa_trace_record(&info);
296	wpa_trace_dump(title, &info);
297}
298
299
300void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
301{
302	if (addr == NULL)
303		return;
304	ref->addr = addr;
305	wpa_trace_record(ref);
306	dl_list_add(&active_references, &ref->list);
307}
308
309
310void wpa_trace_check_ref(const void *addr)
311{
312	struct wpa_trace_ref *ref;
313	dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
314		if (addr != ref->addr)
315			continue;
316		wpa_trace_show("Freeing referenced memory");
317		wpa_trace_dump("Reference registration", ref);
318		abort();
319	}
320}
321
322#endif /* WPA_TRACE */
323