blob: 8f12da87bb1ba21bdfea1d723c35322c9087bd77 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * Backtrace debugging
3 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07009#ifdef WPA_TRACE_BFD
10#define _GNU_SOURCE
11#include <link.h>
12#endif /* WPA_TRACE_BCD */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070013#include "includes.h"
14
15#include "common.h"
16#include "trace.h"
17
18#ifdef WPA_TRACE
19
20static struct dl_list active_references =
21{ &active_references, &active_references };
22
23#ifdef WPA_TRACE_BFD
24#include <bfd.h>
Dmitry Shmidt7832adb2014-04-29 10:53:02 -070025
26#define DMGL_PARAMS (1 << 0)
27#define DMGL_ANSI (1 << 1)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070028
29static char *prg_fname = NULL;
30static bfd *cached_abfd = NULL;
31static asymbol **syms = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070032static unsigned long start_offset;
33static int start_offset_looked_up;
34
35
36static 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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070054
55static 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)) {
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -080062 wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070063 return;
64 }
65 fname[len] = '\0';
66 prg_fname = strdup(fname);
67}
68
69
70static 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
98static 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
141struct bfd_data {
142 bfd_vma pc;
143 bfd_boolean found;
144 const char *filename;
145 const char *function;
146 unsigned int line;
147};
148
Hai Shalomfdcde762020-04-02 11:19:20 -0700149/*
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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700160
161static 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
189static 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
Hai Shalomc3565922019-10-28 11:58:20 -0700200 data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700201 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);
Dmitry Shmidt7d5c8f22014-03-03 13:53:28 -0800225 aname = NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700226
227 data.found = bfd_find_inliner_info(abfd, &data.filename,
228 &data.function, &data.line);
229 } while (data.found);
230}
231
232
233static 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
Hai Shalomc3565922019-10-28 11:58:20 -0700241 data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700242 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
252static 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 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700273
274 if (!start_offset_looked_up) {
275 dl_iterate_phdr(callback, NULL);
276 start_offset_looked_up = 1;
277 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700278}
279
280
281void 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
Dmitry Shmidt746bde52015-01-12 13:01:47 -0800288
289size_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
Hai Shalomc3565922019-10-28 11:58:20 -0700313 data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset);
Dmitry Shmidt746bde52015-01-12 13:01:47 -0800314 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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700335#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
343void 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
379void 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
389void 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
399void 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
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800411
412void wpa_trace_deinit(void)
413{
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800414#ifdef WPA_TRACE_BFD
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800415 free(syms);
416 syms = NULL;
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800417#endif /* WPA_TRACE_BFD */
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800418}
419
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700420#endif /* WPA_TRACE */