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