]>
Commit | Line | Data |
---|---|---|
930f704a JM |
1 | /* |
2 | * Backtrace debugging | |
3 | * Copyright (c) 2009, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
930f704a JM |
7 | */ |
8 | ||
51183198 JB |
9 | #ifdef WPA_TRACE_BFD |
10 | #define _GNU_SOURCE | |
11 | #include <link.h> | |
12 | #endif /* WPA_TRACE_BCD */ | |
930f704a JM |
13 | #include "includes.h" |
14 | ||
15 | #include "common.h" | |
16 | #include "trace.h" | |
17 | ||
18 | #ifdef WPA_TRACE | |
19 | ||
a6ff0e08 JM |
20 | static struct dl_list active_references = |
21 | { &active_references, &active_references }; | |
22 | ||
f2f7d965 JM |
23 | #ifdef WPA_TRACE_BFD |
24 | #include <bfd.h> | |
52cb2073 JM |
25 | |
26 | #define DMGL_PARAMS (1 << 0) | |
27 | #define DMGL_ANSI (1 << 1) | |
f2f7d965 JM |
28 | |
29 | static char *prg_fname = NULL; | |
30 | static bfd *cached_abfd = NULL; | |
31 | static asymbol **syms = NULL; | |
51183198 JB |
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 | ||
f2f7d965 JM |
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)) { | |
a193231d | 62 | wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno)); |
f2f7d965 JM |
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 | } | |
f2f7d965 JM |
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 | ||
bd041404 | 189 | data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset); |
f2f7d965 JM |
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); | |
fa0a9f53 | 214 | aname = NULL; |
f2f7d965 JM |
215 | |
216 | data.found = bfd_find_inliner_info(abfd, &data.filename, | |
217 | &data.function, &data.line); | |
218 | } while (data.found); | |
219 | } | |
220 | ||
221 | ||
94caf8cd JM |
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 | ||
bd041404 | 230 | data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset); |
94caf8cd JM |
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 | ||
f2f7d965 JM |
241 | static void wpa_trace_bfd_init(void) |
242 | { | |
243 | if (!prg_fname) { | |
244 | get_prg_fname(); | |
245 | if (!prg_fname) | |
246 | return; | |
f2f7d965 JM |
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 | } | |
51183198 JB |
262 | |
263 | if (!start_offset_looked_up) { | |
264 | dl_iterate_phdr(callback, NULL); | |
265 | start_offset_looked_up = 1; | |
266 | } | |
f2f7d965 JM |
267 | } |
268 | ||
5e5223bf JM |
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 | ||
a156ffda JM |
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 | ||
bd041404 | 302 | data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset); |
a156ffda JM |
303 | data.found = FALSE; |
304 | bfd_map_over_sections(abfd, find_addr_sect, &data); | |
305 | ||
8a42a076 JM |
306 | while (data.found) { |
307 | if (data.function && | |
308 | (pos > 0 || | |
309 | os_strcmp(data.function, __func__) != 0)) { | |
a156ffda JM |
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 | ||
f2f7d965 JM |
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) | |
94caf8cd | 328 | #define wpa_trace_bfd_addr2func(pc) NULL |
f2f7d965 JM |
329 | |
330 | #endif /* WPA_TRACE_BFD */ | |
331 | ||
930f704a JM |
332 | void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) |
333 | { | |
334 | char **sym; | |
335 | int i; | |
94caf8cd | 336 | enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; |
930f704a | 337 | |
f2f7d965 | 338 | wpa_trace_bfd_init(); |
930f704a JM |
339 | wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); |
340 | sym = backtrace_symbols(btrace, btrace_num); | |
94caf8cd | 341 | state = TRACE_HEAD; |
f2f7d965 | 342 | for (i = 0; i < btrace_num; i++) { |
94caf8cd JM |
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; | |
f2f7d965 JM |
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]); | |
94caf8cd JM |
359 | if (state == TRACE_RELEVANT && func && |
360 | os_strcmp(func, "main") == 0) | |
361 | state = TRACE_TAIL; | |
f2f7d965 | 362 | } |
fb4baa68 | 363 | free(sym); |
930f704a JM |
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 | ||
a6ff0e08 JM |
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 | ||
6136d43b JM |
400 | |
401 | void wpa_trace_deinit(void) | |
402 | { | |
4a6e9e55 | 403 | #ifdef WPA_TRACE_BFD |
6136d43b JM |
404 | free(syms); |
405 | syms = NULL; | |
4a6e9e55 | 406 | #endif /* WPA_TRACE_BFD */ |
6136d43b JM |
407 | } |
408 | ||
930f704a | 409 | #endif /* WPA_TRACE */ |