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
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH 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
37struct 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
49static ALIAS(dwarf_search_unwind_table) int
50dwarf_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);
55static int
56linear_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
100static int
101load_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
137static int
138find_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
171static struct unw_debug_frame_list *
172locate_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
226struct debug_frame_tab
227 {
228 struct table_entry *tab;
229 uint32_t length;
230 uint32_t size;
231 };
232
233static void
234debug_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
251static void
252debug_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
261static int
262debug_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
274HIDDEN int
275dwarf_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
425static Elf_W (Addr)
426dwarf_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
456out:
457 munmap (ei.image, ei.size);
458
459 return eh_frame;
460}
461
462struct 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. */
477HIDDEN int
478dwarf_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
686HIDDEN int
687dwarf_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
730static inline const struct table_entry *
731lookup (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. */
761static int
762remote_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
799static int is_remote_table(int format)
800{
801 return (format == UNW_INFO_FORMAT_REMOTE_TABLE ||
802 format == UNW_INFO_FORMAT_IP_OFFSET);
803}
804
805int
806dwarf_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
931HIDDEN void
932dwarf_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg)
933{
934 return; /* always a nop */
935}
936