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