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