Home | History | Annotate | Line # | Download | only in utils
      1 /*
      2  * Backtrace debugging
      3  * Copyright (c) 2009, Jouni Malinen <j (at) 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  * binutils removed the bfd parameter and renamed things but
    151  * those were macros so we can detect their absence.
    152  * Cf. https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=fd3619828e94a24a92cddec42cbc0ab33352eeb4;hp=5dfda3562a69686c43aad4fb0269cc9d5ec010d5
    153  */
    154 #ifndef bfd_get_section_vma
    155 #define bfd_get_section_vma(bfd, section) bfd_section_vma(section)
    156 #endif
    157 #ifndef bfd_get_section_size
    158 #define bfd_get_section_size bfd_section_size
    159 #endif
    160 
    161 static void find_addr_sect(bfd *abfd, asection *section, void *obj)
    162 {
    163 	struct bfd_data *data = obj;
    164 	bfd_vma vma;
    165 	bfd_size_type size;
    166 
    167 	if (data->found)
    168 		return;
    169 
    170 	if (!(bfd_get_section_vma(abfd, section)))
    171 		return;
    172 
    173 	vma = bfd_get_section_vma(abfd, section);
    174 	if (data->pc < vma)
    175 		return;
    176 
    177 	size = bfd_get_section_size(section);
    178 	if (data->pc >= vma + size)
    179 		return;
    180 
    181 	data->found = bfd_find_nearest_line(abfd, section, syms,
    182 					    data->pc - vma,
    183 					    &data->filename,
    184 					    &data->function,
    185 					    &data->line);
    186 }
    187 
    188 
    189 static void wpa_trace_bfd_addr(void *pc)
    190 {
    191 	bfd *abfd = cached_abfd;
    192 	struct bfd_data data;
    193 	const char *name;
    194 	char *aname = NULL;
    195 	const char *filename;
    196 
    197 	if (abfd == NULL)
    198 		return;
    199 
    200 	data.pc = (uintptr_t) ((u8 *) pc - start_offset);
    201 	data.found = FALSE;
    202 	bfd_map_over_sections(abfd, find_addr_sect, &data);
    203 
    204 	if (!data.found)
    205 		return;
    206 
    207 	do {
    208 		if (data.function)
    209 			aname = bfd_demangle(abfd, data.function,
    210 					     DMGL_ANSI | DMGL_PARAMS);
    211 		name = aname ? aname : data.function;
    212 		filename = data.filename;
    213 		if (filename) {
    214 			char *end = os_strrchr(filename, '/');
    215 			int i = 0;
    216 			while (*filename && *filename == prg_fname[i] &&
    217 			       filename <= end) {
    218 				filename++;
    219 				i++;
    220 			}
    221 		}
    222 		wpa_printf(MSG_INFO, "     %s() %s:%u",
    223 			   name, filename, data.line);
    224 		free(aname);
    225 		aname = NULL;
    226 
    227 		data.found = bfd_find_inliner_info(abfd, &data.filename,
    228 						   &data.function, &data.line);
    229 	} while (data.found);
    230 }
    231 
    232 
    233 static const char * wpa_trace_bfd_addr2func(void *pc)
    234 {
    235 	bfd *abfd = cached_abfd;
    236 	struct bfd_data data;
    237 
    238 	if (abfd == NULL)
    239 		return NULL;
    240 
    241 	data.pc = (uintptr_t) ((u8 *) pc - start_offset);
    242 	data.found = FALSE;
    243 	bfd_map_over_sections(abfd, find_addr_sect, &data);
    244 
    245 	if (!data.found)
    246 		return NULL;
    247 
    248 	return data.function;
    249 }
    250 
    251 
    252 static void wpa_trace_bfd_init(void)
    253 {
    254 	if (!prg_fname) {
    255 		get_prg_fname();
    256 		if (!prg_fname)
    257 			return;
    258 	}
    259 
    260 	if (!cached_abfd) {
    261 		cached_abfd = open_bfd(prg_fname);
    262 		if (!cached_abfd) {
    263 			wpa_printf(MSG_INFO, "Failed to open bfd");
    264 			return;
    265 		}
    266 	}
    267 
    268 	read_syms(cached_abfd);
    269 	if (!syms) {
    270 		wpa_printf(MSG_INFO, "Failed to read symbols");
    271 		return;
    272 	}
    273 
    274 	if (!start_offset_looked_up) {
    275 		dl_iterate_phdr(callback, NULL);
    276 		start_offset_looked_up = 1;
    277 	}
    278 }
    279 
    280 
    281 void wpa_trace_dump_funcname(const char *title, void *pc)
    282 {
    283 	wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
    284 	wpa_trace_bfd_init();
    285 	wpa_trace_bfd_addr(pc);
    286 }
    287 
    288 
    289 size_t wpa_trace_calling_func(const char *buf[], size_t len)
    290 {
    291 	bfd *abfd;
    292 	void *btrace_res[WPA_TRACE_LEN];
    293 	int i, btrace_num;
    294 	size_t pos = 0;
    295 
    296 	if (len == 0)
    297 		return 0;
    298 	if (len > WPA_TRACE_LEN)
    299 		len = WPA_TRACE_LEN;
    300 
    301 	wpa_trace_bfd_init();
    302 	abfd = cached_abfd;
    303 	if (!abfd)
    304 		return 0;
    305 
    306 	btrace_num = backtrace(btrace_res, len);
    307 	if (btrace_num < 1)
    308 		return 0;
    309 
    310 	for (i = 0; i < btrace_num; i++) {
    311 		struct bfd_data data;
    312 
    313 		data.pc = (uintptr_t) ((u8 *) btrace_res[i] - start_offset);
    314 		data.found = FALSE;
    315 		bfd_map_over_sections(abfd, find_addr_sect, &data);
    316 
    317 		while (data.found) {
    318 			if (data.function &&
    319 			    (pos > 0 ||
    320 			     os_strcmp(data.function, __func__) != 0)) {
    321 				buf[pos++] = data.function;
    322 				if (pos == len)
    323 					return pos;
    324 			}
    325 
    326 			data.found = bfd_find_inliner_info(abfd, &data.filename,
    327 							   &data.function,
    328 							   &data.line);
    329 		}
    330 	}
    331 
    332 	return pos;
    333 }
    334 
    335 #else /* WPA_TRACE_BFD */
    336 
    337 #define wpa_trace_bfd_init() do { } while (0)
    338 #define wpa_trace_bfd_addr(pc) do { } while (0)
    339 #define wpa_trace_bfd_addr2func(pc) NULL
    340 
    341 #endif /* WPA_TRACE_BFD */
    342 
    343 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
    344 {
    345 	char **sym;
    346 	int i;
    347 	enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
    348 
    349 	wpa_trace_bfd_init();
    350 	wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
    351 	sym = backtrace_symbols(btrace, btrace_num);
    352 	state = TRACE_HEAD;
    353 	for (i = 0; i < btrace_num; i++) {
    354 		const char *func = wpa_trace_bfd_addr2func(btrace[i]);
    355 		if (state == TRACE_HEAD && func &&
    356 		    (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
    357 		     os_strcmp(func, "wpa_trace_check_ref") == 0 ||
    358 		     os_strcmp(func, "wpa_trace_show") == 0))
    359 			continue;
    360 		if (state == TRACE_TAIL && sym && sym[i] &&
    361 		    os_strstr(sym[i], "__libc_start_main"))
    362 			break;
    363 		if (state == TRACE_HEAD)
    364 			state = TRACE_RELEVANT;
    365 		if (sym)
    366 			wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
    367 		else
    368 			wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
    369 		wpa_trace_bfd_addr(btrace[i]);
    370 		if (state == TRACE_RELEVANT && func &&
    371 		    os_strcmp(func, "main") == 0)
    372 			state = TRACE_TAIL;
    373 	}
    374 	free(sym);
    375 	wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
    376 }
    377 
    378 
    379 void wpa_trace_show(const char *title)
    380 {
    381 	struct info {
    382 		WPA_TRACE_INFO
    383 	} info;
    384 	wpa_trace_record(&info);
    385 	wpa_trace_dump(title, &info);
    386 }
    387 
    388 
    389 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
    390 {
    391 	if (addr == NULL)
    392 		return;
    393 	ref->addr = addr;
    394 	wpa_trace_record(ref);
    395 	dl_list_add(&active_references, &ref->list);
    396 }
    397 
    398 
    399 void wpa_trace_check_ref(const void *addr)
    400 {
    401 	struct wpa_trace_ref *ref;
    402 	dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
    403 		if (addr != ref->addr)
    404 			continue;
    405 		wpa_trace_show("Freeing referenced memory");
    406 		wpa_trace_dump("Reference registration", ref);
    407 		abort();
    408 	}
    409 }
    410 
    411 
    412 void wpa_trace_deinit(void)
    413 {
    414 #ifdef WPA_TRACE_BFD
    415 	free(syms);
    416 	syms = NULL;
    417 #endif /* WPA_TRACE_BFD */
    418 }
    419 
    420 #endif /* WPA_TRACE */
    421