1 | /* libunwind - a platform-independent unwind library |
2 | Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. |
3 | Contributed by David Mosberger-Tang <davidm@hpl.hp.com> |
4 | |
5 | This file is part of libunwind. |
6 | |
7 | Permission is hereby granted, free of charge, to any person obtaining |
8 | a copy of this software and associated documentation files (the |
9 | "Software"), to deal in the Software without restriction, including |
10 | without limitation the rights to use, copy, modify, merge, publish, |
11 | distribute, sublicense, and/or sell copies of the Software, and to |
12 | permit persons to whom the Software is furnished to do so, subject to |
13 | the following conditions: |
14 | |
15 | The above copyright notice and this permission notice shall be |
16 | included in all copies or substantial portions of the Software. |
17 | |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
25 | |
26 | /* Locate an FDE via the ELF data-structures defined by LSB v1.3 |
27 | (http://www.linuxbase.org/spec/). */ |
28 | |
29 | #include <stddef.h> |
30 | #include <stdio.h> |
31 | #include <limits.h> |
32 | |
33 | #include "dwarf_i.h" |
34 | #include "dwarf-eh.h" |
35 | #include "libunwind_i.h" |
36 | |
37 | struct table_entry |
38 | { |
39 | int32_t start_ip_offset; |
40 | int32_t fde_offset; |
41 | }; |
42 | |
43 | #ifndef UNW_REMOTE_ONLY |
44 | |
45 | #ifdef __linux |
46 | #include "os-linux.h" |
47 | #endif |
48 | |
49 | static ALIAS(dwarf_search_unwind_table) int |
50 | dwarf_search_unwind_table_int (unw_addr_space_t as, |
51 | unw_word_t ip, |
52 | unw_dyn_info_t *di, |
53 | unw_proc_info_t *pi, |
54 | int need_unwind_info, void *arg); |
55 | static int |
56 | linear_search (unw_addr_space_t as, unw_word_t ip, |
57 | unw_word_t eh_frame_start, unw_word_t eh_frame_end, |
58 | unw_word_t fde_count, |
59 | unw_proc_info_t *pi, int need_unwind_info, void *arg) |
60 | { |
61 | unw_accessors_t *a = unw_get_accessors_int (unw_local_addr_space); |
62 | unw_word_t i = 0, fde_addr, addr = eh_frame_start; |
63 | int ret; |
64 | |
65 | while (i++ < fde_count && addr < eh_frame_end) |
66 | { |
67 | fde_addr = addr; |
68 | if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, |
69 | eh_frame_start, |
70 | 0, 0, arg)) < 0) |
71 | return ret; |
72 | |
73 | if (ip >= pi->start_ip && ip < pi->end_ip) |
74 | { |
75 | if (!need_unwind_info) |
76 | return 1; |
77 | addr = fde_addr; |
78 | if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, |
79 | eh_frame_start, |
80 | need_unwind_info, 0, |
81 | arg)) |
82 | < 0) |
83 | return ret; |
84 | return 1; |
85 | } |
86 | } |
87 | return -UNW_ENOINFO; |
88 | } |
89 | #endif /* !UNW_REMOTE_ONLY */ |
90 | |
91 | #ifdef CONFIG_DEBUG_FRAME |
92 | /* Load .debug_frame section from FILE. Allocates and returns space |
93 | in *BUF, and sets *BUFSIZE to its size. IS_LOCAL is 1 if using the |
94 | local process, in which case we can search the system debug file |
95 | directory; 0 for other address spaces, in which case we do |
96 | not. Returns 0 on success, 1 on error. Succeeds even if the file |
97 | contains no .debug_frame. */ |
98 | /* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */ |
99 | |
100 | static int |
101 | load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) |
102 | { |
103 | struct elf_image ei; |
104 | Elf_W (Shdr) *shdr; |
105 | int ret; |
106 | |
107 | ei.image = NULL; |
108 | |
109 | ret = elf_w (load_debuglink) (file, &ei, is_local); |
110 | if (ret != 0) |
111 | return ret; |
112 | |
113 | shdr = elf_w (find_section) (&ei, ".debug_frame" ); |
114 | if (!shdr || |
115 | (shdr->sh_offset + shdr->sh_size > ei.size)) |
116 | { |
117 | munmap(ei.image, ei.size); |
118 | return 1; |
119 | } |
120 | |
121 | *bufsize = shdr->sh_size; |
122 | *buf = malloc (*bufsize); |
123 | |
124 | memcpy(*buf, shdr->sh_offset + ei.image, *bufsize); |
125 | |
126 | Debug (4, "read %zd bytes of .debug_frame from offset %zd\n" , |
127 | *bufsize, shdr->sh_offset); |
128 | |
129 | munmap(ei.image, ei.size); |
130 | return 0; |
131 | } |
132 | |
133 | /* Locate the binary which originated the contents of address ADDR. Return |
134 | the name of the binary in *name (space is allocated by the caller) |
135 | Returns 0 if a binary is successfully found, or 1 if an error occurs. */ |
136 | |
137 | static int |
138 | find_binary_for_address (unw_word_t ip, char *name, size_t name_size) |
139 | { |
140 | #if defined(__linux) && (!UNW_REMOTE_ONLY) |
141 | struct map_iterator mi; |
142 | int found = 0; |
143 | int pid = getpid (); |
144 | unsigned long segbase, mapoff, hi; |
145 | |
146 | if (maps_init (&mi, pid) != 0) |
147 | return 1; |
148 | |
149 | while (maps_next (&mi, &segbase, &hi, &mapoff)) |
150 | if (ip >= segbase && ip < hi) |
151 | { |
152 | size_t len = strlen (mi.path); |
153 | |
154 | if (len + 1 <= name_size) |
155 | { |
156 | memcpy (name, mi.path, len + 1); |
157 | found = 1; |
158 | } |
159 | break; |
160 | } |
161 | maps_close (&mi); |
162 | return !found; |
163 | #endif |
164 | |
165 | return 1; |
166 | } |
167 | |
168 | /* Locate and/or try to load a debug_frame section for address ADDR. Return |
169 | pointer to debug frame descriptor, or zero if not found. */ |
170 | |
171 | static struct unw_debug_frame_list * |
172 | locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname, |
173 | unw_word_t start, unw_word_t end) |
174 | { |
175 | struct unw_debug_frame_list *w, *fdesc = 0; |
176 | char path[PATH_MAX]; |
177 | char *name = path; |
178 | int err; |
179 | char *buf; |
180 | size_t bufsize; |
181 | |
182 | /* First, see if we loaded this frame already. */ |
183 | |
184 | for (w = as->debug_frames; w; w = w->next) |
185 | { |
186 | Debug (4, "checking %p: %lx-%lx\n" , w, (long)w->start, (long)w->end); |
187 | if (addr >= w->start && addr < w->end) |
188 | return w; |
189 | } |
190 | |
191 | /* If the object name we receive is blank, there's still a chance of locating |
192 | the file by parsing /proc/self/maps. */ |
193 | |
194 | if (strcmp (dlname, "" ) == 0) |
195 | { |
196 | err = find_binary_for_address (addr, name, sizeof(path)); |
197 | if (err) |
198 | { |
199 | Debug (15, "tried to locate binary for 0x%" PRIx64 ", but no luck\n" , |
200 | (uint64_t) addr); |
201 | return 0; |
202 | } |
203 | } |
204 | else |
205 | name = (char*) dlname; |
206 | |
207 | err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space); |
208 | |
209 | if (!err) |
210 | { |
211 | fdesc = malloc (sizeof (struct unw_debug_frame_list)); |
212 | |
213 | fdesc->start = start; |
214 | fdesc->end = end; |
215 | fdesc->debug_frame = buf; |
216 | fdesc->debug_frame_size = bufsize; |
217 | fdesc->index = NULL; |
218 | fdesc->next = as->debug_frames; |
219 | |
220 | as->debug_frames = fdesc; |
221 | } |
222 | |
223 | return fdesc; |
224 | } |
225 | |
226 | struct debug_frame_tab |
227 | { |
228 | struct table_entry *tab; |
229 | uint32_t length; |
230 | uint32_t size; |
231 | }; |
232 | |
233 | static void |
234 | debug_frame_tab_append (struct debug_frame_tab *tab, |
235 | unw_word_t fde_offset, unw_word_t start_ip) |
236 | { |
237 | unsigned int length = tab->length; |
238 | |
239 | if (length == tab->size) |
240 | { |
241 | tab->size *= 2; |
242 | tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->size); |
243 | } |
244 | |
245 | tab->tab[length].fde_offset = fde_offset; |
246 | tab->tab[length].start_ip_offset = start_ip; |
247 | |
248 | tab->length = length + 1; |
249 | } |
250 | |
251 | static void |
252 | debug_frame_tab_shrink (struct debug_frame_tab *tab) |
253 | { |
254 | if (tab->size > tab->length) |
255 | { |
256 | tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->length); |
257 | tab->size = tab->length; |
258 | } |
259 | } |
260 | |
261 | static int |
262 | debug_frame_tab_compare (const void *a, const void *b) |
263 | { |
264 | const struct table_entry *fa = a, *fb = b; |
265 | |
266 | if (fa->start_ip_offset > fb->start_ip_offset) |
267 | return 1; |
268 | else if (fa->start_ip_offset < fb->start_ip_offset) |
269 | return -1; |
270 | else |
271 | return 0; |
272 | } |
273 | |
274 | HIDDEN int |
275 | dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug, unw_word_t ip, |
276 | unw_word_t segbase, const char* obj_name, |
277 | unw_word_t start, unw_word_t end) |
278 | { |
279 | unw_dyn_info_t *di; |
280 | struct unw_debug_frame_list *fdesc = 0; |
281 | unw_accessors_t *a; |
282 | unw_word_t addr; |
283 | |
284 | Debug (15, "Trying to find .debug_frame for %s\n" , obj_name); |
285 | di = di_debug; |
286 | |
287 | fdesc = locate_debug_info (unw_local_addr_space, ip, obj_name, start, end); |
288 | |
289 | if (!fdesc) |
290 | { |
291 | Debug (15, "couldn't load .debug_frame\n" ); |
292 | return found; |
293 | } |
294 | else |
295 | { |
296 | char *buf; |
297 | size_t bufsize; |
298 | unw_word_t item_start, item_end = 0; |
299 | uint32_t u32val = 0; |
300 | uint64_t cie_id = 0; |
301 | struct debug_frame_tab tab; |
302 | |
303 | Debug (15, "loaded .debug_frame\n" ); |
304 | |
305 | buf = fdesc->debug_frame; |
306 | bufsize = fdesc->debug_frame_size; |
307 | |
308 | if (bufsize == 0) |
309 | { |
310 | Debug (15, "zero-length .debug_frame\n" ); |
311 | return found; |
312 | } |
313 | |
314 | /* Now create a binary-search table, if it does not already exist. */ |
315 | if (!fdesc->index) |
316 | { |
317 | addr = (unw_word_t) (uintptr_t) buf; |
318 | |
319 | a = unw_get_accessors_int (unw_local_addr_space); |
320 | |
321 | /* Find all FDE entries in debug_frame, and make into a sorted |
322 | index. */ |
323 | |
324 | tab.length = 0; |
325 | tab.size = 16; |
326 | tab.tab = calloc (tab.size, sizeof (struct table_entry)); |
327 | |
328 | while (addr < (unw_word_t) (uintptr_t) (buf + bufsize)) |
329 | { |
330 | uint64_t id_for_cie; |
331 | item_start = addr; |
332 | |
333 | dwarf_readu32 (unw_local_addr_space, a, &addr, &u32val, NULL); |
334 | |
335 | if (u32val == 0) |
336 | break; |
337 | else if (u32val != 0xffffffff) |
338 | { |
339 | uint32_t cie_id32 = 0; |
340 | item_end = addr + u32val; |
341 | dwarf_readu32 (unw_local_addr_space, a, &addr, &cie_id32, |
342 | NULL); |
343 | cie_id = cie_id32; |
344 | id_for_cie = 0xffffffff; |
345 | } |
346 | else |
347 | { |
348 | uint64_t u64val = 0; |
349 | /* Extended length. */ |
350 | dwarf_readu64 (unw_local_addr_space, a, &addr, &u64val, NULL); |
351 | item_end = addr + u64val; |
352 | |
353 | dwarf_readu64 (unw_local_addr_space, a, &addr, &cie_id, NULL); |
354 | id_for_cie = 0xffffffffffffffffull; |
355 | } |
356 | |
357 | /*Debug (1, "CIE/FDE id = %.8x\n", (int) cie_id);*/ |
358 | |
359 | if (cie_id == id_for_cie) |
360 | ; |
361 | /*Debug (1, "Found CIE at %.8x.\n", item_start);*/ |
362 | else |
363 | { |
364 | unw_word_t fde_addr = item_start; |
365 | unw_proc_info_t this_pi; |
366 | int err; |
367 | |
368 | /*Debug (1, "Found FDE at %.8x\n", item_start);*/ |
369 | |
370 | err = dwarf_extract_proc_info_from_fde (unw_local_addr_space, |
371 | a, &fde_addr, |
372 | &this_pi, |
373 | (uintptr_t) buf, 0, 1, |
374 | NULL); |
375 | if (err == 0) |
376 | { |
377 | Debug (15, "start_ip = %lx, end_ip = %lx\n" , |
378 | (long) this_pi.start_ip, (long) this_pi.end_ip); |
379 | debug_frame_tab_append (&tab, |
380 | item_start - (unw_word_t) (uintptr_t) buf, |
381 | this_pi.start_ip); |
382 | } |
383 | /*else |
384 | Debug (1, "FDE parse failed\n");*/ |
385 | } |
386 | |
387 | addr = item_end; |
388 | } |
389 | |
390 | debug_frame_tab_shrink (&tab); |
391 | qsort (tab.tab, tab.length, sizeof (struct table_entry), |
392 | debug_frame_tab_compare); |
393 | /* for (i = 0; i < tab.length; i++) |
394 | { |
395 | fprintf (stderr, "ip %x, fde offset %x\n", |
396 | (int) tab.tab[i].start_ip_offset, |
397 | (int) tab.tab[i].fde_offset); |
398 | }*/ |
399 | fdesc->index = tab.tab; |
400 | fdesc->index_size = tab.length; |
401 | } |
402 | |
403 | di->format = UNW_INFO_FORMAT_TABLE; |
404 | di->start_ip = fdesc->start; |
405 | di->end_ip = fdesc->end; |
406 | di->u.ti.name_ptr = (unw_word_t) (uintptr_t) obj_name; |
407 | di->u.ti.table_data = (unw_word_t *) fdesc; |
408 | di->u.ti.table_len = sizeof (*fdesc) / sizeof (unw_word_t); |
409 | di->u.ti.segbase = segbase; |
410 | |
411 | found = 1; |
412 | Debug (15, "found debug_frame table `%s': segbase=0x%lx, len=%lu, " |
413 | "gp=0x%lx, table_data=0x%lx\n" , |
414 | (char *) (uintptr_t) di->u.ti.name_ptr, |
415 | (long) di->u.ti.segbase, (long) di->u.ti.table_len, |
416 | (long) di->gp, (long) di->u.ti.table_data); |
417 | } |
418 | return found; |
419 | } |
420 | |
421 | #endif /* CONFIG_DEBUG_FRAME */ |
422 | |
423 | #ifndef UNW_REMOTE_ONLY |
424 | |
425 | static Elf_W (Addr) |
426 | dwarf_find_eh_frame_section(struct dl_phdr_info *info) |
427 | { |
428 | int rc; |
429 | struct elf_image ei; |
430 | Elf_W (Addr) eh_frame = 0; |
431 | Elf_W (Shdr)* shdr; |
432 | const char *file = info->dlpi_name; |
433 | char exepath[PATH_MAX]; |
434 | |
435 | if (strlen(file) == 0) |
436 | { |
437 | tdep_get_exe_image_path(exepath); |
438 | file = exepath; |
439 | } |
440 | |
441 | Debug (1, "looking for .eh_frame section in %s\n" , |
442 | file); |
443 | |
444 | rc = elf_map_image (&ei, file); |
445 | if (rc != 0) |
446 | return 0; |
447 | |
448 | shdr = elf_w (find_section) (&ei, ".eh_frame" ); |
449 | if (!shdr) |
450 | goto out; |
451 | |
452 | eh_frame = shdr->sh_addr + info->dlpi_addr; |
453 | Debug (4, "found .eh_frame at address %lx\n" , |
454 | eh_frame); |
455 | |
456 | out: |
457 | munmap (ei.image, ei.size); |
458 | |
459 | return eh_frame; |
460 | } |
461 | |
462 | struct dwarf_callback_data |
463 | { |
464 | /* in: */ |
465 | unw_word_t ip; /* instruction-pointer we're looking for */ |
466 | unw_proc_info_t *pi; /* proc-info pointer */ |
467 | int need_unwind_info; |
468 | /* out: */ |
469 | int single_fde; /* did we find a single FDE? (vs. a table) */ |
470 | unw_dyn_info_t di; /* table info (if single_fde is false) */ |
471 | unw_dyn_info_t di_debug; /* additional table info for .debug_frame */ |
472 | }; |
473 | |
474 | /* ptr is a pointer to a dwarf_callback_data structure and, on entry, |
475 | member ip contains the instruction-pointer we're looking |
476 | for. */ |
477 | HIDDEN int |
478 | dwarf_callback (struct dl_phdr_info *info, size_t size, void *ptr) |
479 | { |
480 | struct dwarf_callback_data *cb_data = ptr; |
481 | unw_dyn_info_t *di = &cb_data->di; |
482 | const Elf_W(Phdr) *phdr, *p_eh_hdr, *p_dynamic, *p_text; |
483 | unw_word_t addr, eh_frame_start, eh_frame_end, fde_count, ip; |
484 | Elf_W(Addr) load_base, max_load_addr = 0; |
485 | int ret, need_unwind_info = cb_data->need_unwind_info; |
486 | unw_proc_info_t *pi = cb_data->pi; |
487 | struct dwarf_eh_frame_hdr *hdr = NULL; |
488 | unw_accessors_t *a; |
489 | long n; |
490 | int found = 0; |
491 | struct dwarf_eh_frame_hdr synth_eh_frame_hdr; |
492 | #ifdef CONFIG_DEBUG_FRAME |
493 | unw_word_t start, end; |
494 | #endif /* CONFIG_DEBUG_FRAME*/ |
495 | |
496 | ip = cb_data->ip; |
497 | |
498 | /* Make sure struct dl_phdr_info is at least as big as we need. */ |
499 | if (size < offsetof (struct dl_phdr_info, dlpi_phnum) |
500 | + sizeof (info->dlpi_phnum)) |
501 | return -1; |
502 | |
503 | Debug (15, "checking %s, base=0x%lx)\n" , |
504 | info->dlpi_name, (long) info->dlpi_addr); |
505 | |
506 | phdr = info->dlpi_phdr; |
507 | load_base = info->dlpi_addr; |
508 | p_text = NULL; |
509 | p_eh_hdr = NULL; |
510 | p_dynamic = NULL; |
511 | |
512 | /* See if PC falls into one of the loaded segments. Find the |
513 | eh-header segment at the same time. */ |
514 | for (n = info->dlpi_phnum; --n >= 0; phdr++) |
515 | { |
516 | if (phdr->p_type == PT_LOAD) |
517 | { |
518 | Elf_W(Addr) vaddr = phdr->p_vaddr + load_base; |
519 | |
520 | if (ip >= vaddr && ip < vaddr + phdr->p_memsz) |
521 | p_text = phdr; |
522 | |
523 | if (vaddr + phdr->p_filesz > max_load_addr) |
524 | max_load_addr = vaddr + phdr->p_filesz; |
525 | } |
526 | else if (phdr->p_type == PT_GNU_EH_FRAME) |
527 | p_eh_hdr = phdr; |
528 | else if (phdr->p_type == PT_DYNAMIC) |
529 | p_dynamic = phdr; |
530 | } |
531 | |
532 | if (!p_text) |
533 | return 0; |
534 | |
535 | if (p_eh_hdr) |
536 | { |
537 | hdr = (struct dwarf_eh_frame_hdr *) (p_eh_hdr->p_vaddr + load_base); |
538 | } |
539 | else |
540 | { |
541 | Elf_W (Addr) eh_frame; |
542 | Debug (1, "no .eh_frame_hdr section found\n" ); |
543 | eh_frame = dwarf_find_eh_frame_section (info); |
544 | if (eh_frame) |
545 | { |
546 | Debug (1, "using synthetic .eh_frame_hdr section for %s\n" , |
547 | info->dlpi_name); |
548 | synth_eh_frame_hdr.version = DW_EH_VERSION; |
549 | synth_eh_frame_hdr.eh_frame_ptr_enc = DW_EH_PE_absptr | |
550 | ((sizeof(Elf_W (Addr)) == 4) ? DW_EH_PE_udata4 : DW_EH_PE_udata8); |
551 | synth_eh_frame_hdr.fde_count_enc = DW_EH_PE_omit; |
552 | synth_eh_frame_hdr.table_enc = DW_EH_PE_omit; |
553 | synth_eh_frame_hdr.eh_frame = eh_frame; |
554 | hdr = &synth_eh_frame_hdr; |
555 | } |
556 | } |
557 | |
558 | if (hdr) |
559 | { |
560 | if (p_dynamic) |
561 | { |
562 | /* For dynamicly linked executables and shared libraries, |
563 | DT_PLTGOT is the value that data-relative addresses are |
564 | relative to for that object. We call this the "gp". */ |
565 | Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(p_dynamic->p_vaddr + load_base); |
566 | for (; dyn->d_tag != DT_NULL; ++dyn) |
567 | if (dyn->d_tag == DT_PLTGOT) |
568 | { |
569 | /* Assume that _DYNAMIC is writable and GLIBC has |
570 | relocated it (true for x86 at least). */ |
571 | di->gp = dyn->d_un.d_ptr; |
572 | break; |
573 | } |
574 | } |
575 | else |
576 | /* Otherwise this is a static executable with no _DYNAMIC. Assume |
577 | that data-relative addresses are relative to 0, i.e., |
578 | absolute. */ |
579 | di->gp = 0; |
580 | pi->gp = di->gp; |
581 | |
582 | if (hdr->version != DW_EH_VERSION) |
583 | { |
584 | Debug (1, "table `%s' has unexpected version %d\n" , |
585 | info->dlpi_name, hdr->version); |
586 | return 0; |
587 | } |
588 | |
589 | a = unw_get_accessors_int (unw_local_addr_space); |
590 | addr = (unw_word_t) (uintptr_t) (&hdr->eh_frame); |
591 | |
592 | /* (Optionally) read eh_frame_ptr: */ |
593 | if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, |
594 | &addr, hdr->eh_frame_ptr_enc, pi, |
595 | &eh_frame_start, NULL)) < 0) |
596 | return ret; |
597 | |
598 | /* (Optionally) read fde_count: */ |
599 | if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, |
600 | &addr, hdr->fde_count_enc, pi, |
601 | &fde_count, NULL)) < 0) |
602 | return ret; |
603 | |
604 | if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) |
605 | { |
606 | /* If there is no search table or it has an unsupported |
607 | encoding, fall back on linear search. */ |
608 | if (hdr->table_enc == DW_EH_PE_omit) |
609 | Debug (4, "table `%s' lacks search table; doing linear search\n" , |
610 | info->dlpi_name); |
611 | else |
612 | Debug (4, "table `%s' has encoding 0x%x; doing linear search\n" , |
613 | info->dlpi_name, hdr->table_enc); |
614 | |
615 | eh_frame_end = max_load_addr; /* XXX can we do better? */ |
616 | |
617 | if (hdr->fde_count_enc == DW_EH_PE_omit) |
618 | fde_count = ~0UL; |
619 | if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit) |
620 | abort (); |
621 | |
622 | Debug (1, "eh_frame_start = %lx eh_frame_end = %lx\n" , |
623 | eh_frame_start, eh_frame_end); |
624 | |
625 | /* XXX we know how to build a local binary search table for |
626 | .debug_frame, so we could do that here too. */ |
627 | found = linear_search (unw_local_addr_space, ip, |
628 | eh_frame_start, eh_frame_end, fde_count, |
629 | pi, need_unwind_info, NULL); |
630 | if (found != 1) |
631 | found = 0; |
632 | else |
633 | cb_data->single_fde = 1; |
634 | } |
635 | else |
636 | { |
637 | di->format = UNW_INFO_FORMAT_REMOTE_TABLE; |
638 | di->start_ip = p_text->p_vaddr + load_base; |
639 | di->end_ip = p_text->p_vaddr + load_base + p_text->p_memsz; |
640 | di->u.rti.name_ptr = (unw_word_t) (uintptr_t) info->dlpi_name; |
641 | di->u.rti.table_data = addr; |
642 | assert (sizeof (struct table_entry) % sizeof (unw_word_t) == 0); |
643 | di->u.rti.table_len = (fde_count * sizeof (struct table_entry) |
644 | / sizeof (unw_word_t)); |
645 | /* For the binary-search table in the eh_frame_hdr, data-relative |
646 | means relative to the start of that section... */ |
647 | di->u.rti.segbase = (unw_word_t) (uintptr_t) hdr; |
648 | |
649 | found = 1; |
650 | Debug (15, "found table `%s': segbase=0x%lx, len=%lu, gp=0x%lx, " |
651 | "table_data=0x%lx\n" , (char *) (uintptr_t) di->u.rti.name_ptr, |
652 | (long) di->u.rti.segbase, (long) di->u.rti.table_len, |
653 | (long) di->gp, (long) di->u.rti.table_data); |
654 | } |
655 | } |
656 | |
657 | #ifdef CONFIG_DEBUG_FRAME |
658 | /* Find the start/end of the described region by parsing the phdr_info |
659 | structure. */ |
660 | start = (unw_word_t) -1; |
661 | end = 0; |
662 | |
663 | for (n = 0; n < info->dlpi_phnum; n++) |
664 | { |
665 | if (info->dlpi_phdr[n].p_type == PT_LOAD) |
666 | { |
667 | unw_word_t seg_start = info->dlpi_addr + info->dlpi_phdr[n].p_vaddr; |
668 | unw_word_t seg_end = seg_start + info->dlpi_phdr[n].p_memsz; |
669 | |
670 | if (seg_start < start) |
671 | start = seg_start; |
672 | |
673 | if (seg_end > end) |
674 | end = seg_end; |
675 | } |
676 | } |
677 | |
678 | found = dwarf_find_debug_frame (found, &cb_data->di_debug, ip, |
679 | info->dlpi_addr, info->dlpi_name, start, |
680 | end); |
681 | #endif /* CONFIG_DEBUG_FRAME */ |
682 | |
683 | return found; |
684 | } |
685 | |
686 | HIDDEN int |
687 | dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, |
688 | unw_proc_info_t *pi, int need_unwind_info, void *arg) |
689 | { |
690 | struct dwarf_callback_data cb_data; |
691 | intrmask_t saved_mask; |
692 | int ret; |
693 | |
694 | Debug (14, "looking for IP=0x%lx\n" , (long) ip); |
695 | |
696 | memset (&cb_data, 0, sizeof (cb_data)); |
697 | cb_data.ip = ip; |
698 | cb_data.pi = pi; |
699 | cb_data.need_unwind_info = need_unwind_info; |
700 | cb_data.di.format = -1; |
701 | cb_data.di_debug.format = -1; |
702 | |
703 | SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); |
704 | ret = dl_iterate_phdr (dwarf_callback, &cb_data); |
705 | SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); |
706 | |
707 | if (ret > 0) |
708 | { |
709 | if (cb_data.single_fde) |
710 | /* already got the result in *pi */ |
711 | return 0; |
712 | |
713 | /* search the table: */ |
714 | if (cb_data.di.format != -1) |
715 | ret = dwarf_search_unwind_table_int (as, ip, &cb_data.di, |
716 | pi, need_unwind_info, arg); |
717 | else |
718 | ret = -UNW_ENOINFO; |
719 | |
720 | if (ret == -UNW_ENOINFO && cb_data.di_debug.format != -1) |
721 | ret = dwarf_search_unwind_table_int (as, ip, &cb_data.di_debug, pi, |
722 | need_unwind_info, arg); |
723 | } |
724 | else |
725 | ret = -UNW_ENOINFO; |
726 | |
727 | return ret; |
728 | } |
729 | |
730 | static inline const struct table_entry * |
731 | lookup (const struct table_entry *table, size_t table_size, int32_t rel_ip) |
732 | { |
733 | unsigned long table_len = table_size / sizeof (struct table_entry); |
734 | const struct table_entry *e = NULL; |
735 | unsigned long lo, hi, mid; |
736 | |
737 | /* do a binary search for right entry: */ |
738 | for (lo = 0, hi = table_len; lo < hi;) |
739 | { |
740 | mid = (lo + hi) / 2; |
741 | e = table + mid; |
742 | Debug (15, "e->start_ip_offset = %lx\n" , (long) e->start_ip_offset); |
743 | if (rel_ip < e->start_ip_offset) |
744 | hi = mid; |
745 | else |
746 | lo = mid + 1; |
747 | } |
748 | if (hi <= 0) |
749 | return NULL; |
750 | e = table + hi - 1; |
751 | return e; |
752 | } |
753 | |
754 | #endif /* !UNW_REMOTE_ONLY */ |
755 | |
756 | #ifndef UNW_LOCAL_ONLY |
757 | |
758 | /* Lookup an unwind-table entry in remote memory. Returns 1 if an |
759 | entry is found, 0 if no entry is found, negative if an error |
760 | occurred reading remote memory. */ |
761 | static int |
762 | remote_lookup (unw_addr_space_t as, |
763 | unw_word_t table, size_t table_size, int32_t rel_ip, |
764 | struct table_entry *e, int32_t *last_ip_offset, void *arg) |
765 | { |
766 | unsigned long table_len = table_size / sizeof (struct table_entry); |
767 | unw_accessors_t *a = unw_get_accessors_int (as); |
768 | unsigned long lo, hi, mid; |
769 | unw_word_t e_addr = 0; |
770 | int32_t start = 0; |
771 | int ret; |
772 | |
773 | /* do a binary search for right entry: */ |
774 | for (lo = 0, hi = table_len; lo < hi;) |
775 | { |
776 | mid = (lo + hi) / 2; |
777 | e_addr = table + mid * sizeof (struct table_entry); |
778 | if ((ret = dwarf_reads32 (as, a, &e_addr, &start, arg)) < 0) |
779 | return ret; |
780 | |
781 | if (rel_ip < start) |
782 | hi = mid; |
783 | else |
784 | lo = mid + 1; |
785 | } |
786 | if (hi <= 0) |
787 | return 0; |
788 | e_addr = table + (hi - 1) * sizeof (struct table_entry); |
789 | if ((ret = dwarf_reads32 (as, a, &e_addr, &e->start_ip_offset, arg)) < 0 |
790 | || (ret = dwarf_reads32 (as, a, &e_addr, &e->fde_offset, arg)) < 0 |
791 | || (hi < table_len && |
792 | (ret = dwarf_reads32 (as, a, &e_addr, last_ip_offset, arg)) < 0)) |
793 | return ret; |
794 | return 1; |
795 | } |
796 | |
797 | #endif /* !UNW_LOCAL_ONLY */ |
798 | |
799 | static int is_remote_table(int format) |
800 | { |
801 | return (format == UNW_INFO_FORMAT_REMOTE_TABLE || |
802 | format == UNW_INFO_FORMAT_IP_OFFSET); |
803 | } |
804 | |
805 | int |
806 | dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, |
807 | unw_dyn_info_t *di, unw_proc_info_t *pi, |
808 | int need_unwind_info, void *arg) |
809 | { |
810 | const struct table_entry *e = NULL, *table; |
811 | unw_word_t ip_base = 0, segbase = 0, last_ip, fde_addr; |
812 | unw_accessors_t *a; |
813 | #ifndef UNW_LOCAL_ONLY |
814 | struct table_entry ent; |
815 | #endif |
816 | int ret; |
817 | unw_word_t debug_frame_base; |
818 | size_t table_len; |
819 | |
820 | #ifdef UNW_REMOTE_ONLY |
821 | assert (is_remote_table(di->format)); |
822 | #else |
823 | assert (is_remote_table(di->format) |
824 | || di->format == UNW_INFO_FORMAT_TABLE); |
825 | #endif |
826 | assert (ip >= di->start_ip && ip < di->end_ip); |
827 | |
828 | if (is_remote_table(di->format)) |
829 | { |
830 | table = (const struct table_entry *) (uintptr_t) di->u.rti.table_data; |
831 | table_len = di->u.rti.table_len * sizeof (unw_word_t); |
832 | debug_frame_base = 0; |
833 | } |
834 | else |
835 | { |
836 | assert(di->format == UNW_INFO_FORMAT_TABLE); |
837 | #ifndef UNW_REMOTE_ONLY |
838 | struct unw_debug_frame_list *fdesc = (void *) di->u.ti.table_data; |
839 | |
840 | /* UNW_INFO_FORMAT_TABLE (i.e. .debug_frame) is read from local address |
841 | space. Both the index and the unwind tables live in local memory, but |
842 | the address space to check for properties like the address size and |
843 | endianness is the target one. */ |
844 | as = unw_local_addr_space; |
845 | table = fdesc->index; |
846 | table_len = fdesc->index_size * sizeof (struct table_entry); |
847 | debug_frame_base = (uintptr_t) fdesc->debug_frame; |
848 | #endif |
849 | } |
850 | |
851 | a = unw_get_accessors_int (as); |
852 | |
853 | segbase = di->u.rti.segbase; |
854 | if (di->format == UNW_INFO_FORMAT_IP_OFFSET) { |
855 | ip_base = di->start_ip; |
856 | } else { |
857 | ip_base = segbase; |
858 | } |
859 | |
860 | #ifndef UNW_REMOTE_ONLY |
861 | if (as == unw_local_addr_space) |
862 | { |
863 | e = lookup (table, table_len, ip - ip_base); |
864 | if (e && &e[1] < &table[table_len]) |
865 | last_ip = e[1].start_ip_offset + ip_base; |
866 | else |
867 | last_ip = di->end_ip; |
868 | } |
869 | else |
870 | #endif |
871 | { |
872 | #ifndef UNW_LOCAL_ONLY |
873 | int32_t last_ip_offset = di->end_ip - ip_base; |
874 | segbase = di->u.rti.segbase; |
875 | if ((ret = remote_lookup (as, (uintptr_t) table, table_len, |
876 | ip - ip_base, &ent, &last_ip_offset, arg)) < 0) |
877 | return ret; |
878 | if (ret) |
879 | { |
880 | e = &ent; |
881 | last_ip = last_ip_offset + ip_base; |
882 | } |
883 | else |
884 | e = NULL; /* no info found */ |
885 | #endif |
886 | } |
887 | if (!e) |
888 | { |
889 | Debug (1, "IP %lx inside range %lx-%lx, but no explicit unwind info found\n" , |
890 | (long) ip, (long) di->start_ip, (long) di->end_ip); |
891 | /* IP is inside this table's range, but there is no explicit |
892 | unwind info. */ |
893 | return -UNW_ENOINFO; |
894 | } |
895 | Debug (15, "ip=0x%lx, start_ip=0x%lx\n" , |
896 | (long) ip, (long) (e->start_ip_offset)); |
897 | if (debug_frame_base) |
898 | fde_addr = e->fde_offset + debug_frame_base; |
899 | else |
900 | fde_addr = e->fde_offset + segbase; |
901 | Debug (1, "e->fde_offset = %lx, segbase = %lx, debug_frame_base = %lx, " |
902 | "fde_addr = %lx\n" , (long) e->fde_offset, (long) segbase, |
903 | (long) debug_frame_base, (long) fde_addr); |
904 | if ((ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi, |
905 | debug_frame_base ? |
906 | debug_frame_base : segbase, |
907 | need_unwind_info, |
908 | debug_frame_base != 0, arg)) < 0) |
909 | return ret; |
910 | |
911 | /* .debug_frame uses an absolute encoding that does not know about any |
912 | shared library relocation. */ |
913 | if (di->format == UNW_INFO_FORMAT_TABLE) |
914 | { |
915 | pi->start_ip += segbase; |
916 | pi->end_ip += segbase; |
917 | pi->flags = UNW_PI_FLAG_DEBUG_FRAME; |
918 | } |
919 | |
920 | #if defined(NEED_LAST_IP) |
921 | pi->last_ip = last_ip; |
922 | #else |
923 | (void)last_ip; |
924 | #endif |
925 | if (ip < pi->start_ip || ip >= pi->end_ip) |
926 | return -UNW_ENOINFO; |
927 | |
928 | return 0; |
929 | } |
930 | |
931 | HIDDEN void |
932 | dwarf_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg) |
933 | { |
934 | return; /* always a nop */ |
935 | } |
936 | |