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>
5 This file is part of libunwind.
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:
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
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. */
26 /* Locate an FDE via the ELF data-structures defined by LSB v1.3
27 (http://www.linuxbase.org/spec/). */
35 #include "libunwind_i.h"
39 int32_t start_ip_offset
;
44 static inline const struct table_entry
*
45 lookup (const struct table_entry
*table
, size_t table_size
, int32_t rel_ip
);
48 remote_lookup (unw_addr_space_t as
,
49 unw_word_t table
, size_t table_size
, int32_t rel_ip
,
50 struct table_entry
*e
, void *arg
);
53 #ifndef UNW_REMOTE_ONLY
60 linear_search (unw_addr_space_t as
, unw_word_t ip
,
61 unw_word_t eh_frame_start
, unw_word_t eh_frame_end
,
63 unw_word_t
*fde_addr
, int need_unwind_info
, void *arg
)
65 unw_accessors_t
*a
= unw_get_accessors (unw_local_addr_space
);
66 unw_word_t i
= 0, faddr
, addr
= eh_frame_start
;
70 while (i
++ < fde_count
&& addr
< eh_frame_end
)
73 if ((ret
= dwarf_extract_proc_info_from_fde (as
, a
, &addr
, &pi
,
78 if (ip
>= pi
.start_ip
&& ip
< pi
.end_ip
)
82 if (!need_unwind_info
)
86 if ((ret
= dwarf_extract_proc_info_from_fde (as
, a
, &addr
, &pi
,
97 #endif /* !UNW_REMOTE_ONLY */
99 #ifdef CONFIG_DEBUG_FRAME
100 /* Load .debug_frame section from FILE. Allocates and returns space
101 in *BUF, and sets *BUFSIZE to its size. IS_LOCAL is 1 if using the
102 local process, in which case we can search the system debug file
103 directory; 0 for other address spaces, in which case we do not; or
104 -1 for recursive calls following .gnu_debuglink. Returns 0 on
105 success, 1 on error. Succeeds even if the file contains no
107 /* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */
110 load_debug_frame (const char *file
, char **buf
, size_t *bufsize
, int is_local
)
114 Elf_W (Half
) shstrndx
;
115 Elf_W (Shdr
) *sec_hdrs
= NULL
;
116 char *stringtab
= NULL
;
119 char *linkbuf
= NULL
;
124 f
= fopen (file
, "r");
129 if (fread (&ehdr
, sizeof (Elf_W (Ehdr
)), 1, f
) != 1)
132 shstrndx
= ehdr
.e_shstrndx
;
134 Debug (4, "opened file '%s'. Section header at offset %d\n",
135 file
, (int) ehdr
.e_shoff
);
137 fseek (f
, ehdr
.e_shoff
, SEEK_SET
);
138 sec_hdrs
= calloc (ehdr
.e_shnum
, sizeof (Elf_W (Shdr
)));
139 if (fread (sec_hdrs
, sizeof (Elf_W (Shdr
)), ehdr
.e_shnum
, f
) != ehdr
.e_shnum
)
142 Debug (4, "loading string table of size %zd\n",
143 sec_hdrs
[shstrndx
].sh_size
);
144 stringtab
= malloc (sec_hdrs
[shstrndx
].sh_size
);
145 fseek (f
, sec_hdrs
[shstrndx
].sh_offset
, SEEK_SET
);
146 if (fread (stringtab
, 1, sec_hdrs
[shstrndx
].sh_size
, f
) != sec_hdrs
[shstrndx
].sh_size
)
149 for (i
= 1; i
< ehdr
.e_shnum
&& *buf
== NULL
; i
++)
151 char *secname
= &stringtab
[sec_hdrs
[i
].sh_name
];
153 if (strcmp (secname
, ".debug_frame") == 0)
155 *bufsize
= sec_hdrs
[i
].sh_size
;
156 *buf
= malloc (*bufsize
);
158 fseek (f
, sec_hdrs
[i
].sh_offset
, SEEK_SET
);
159 if (fread (*buf
, 1, *bufsize
, f
) != *bufsize
)
162 Debug (4, "read %zd bytes of .debug_frame from offset %zd\n",
163 *bufsize
, sec_hdrs
[i
].sh_offset
);
165 else if (strcmp (secname
, ".gnu_debuglink") == 0)
167 linksize
= sec_hdrs
[i
].sh_size
;
168 linkbuf
= malloc (linksize
);
170 fseek (f
, sec_hdrs
[i
].sh_offset
, SEEK_SET
);
171 if (fread (linkbuf
, 1, linksize
, f
) != linksize
)
174 Debug (4, "read %zd bytes of .gnu_debuglink from offset %zd\n",
175 linksize
, sec_hdrs
[i
].sh_offset
);
184 /* Ignore separate debug files which contain a .gnu_debuglink section. */
185 if (linkbuf
&& is_local
== -1)
191 if (*buf
== NULL
&& linkbuf
!= NULL
&& memchr (linkbuf
, 0, linksize
) != NULL
)
193 char *newname
, *basedir
, *p
;
194 static const char *debugdir
= "/usr/lib/debug";
197 /* XXX: Don't bother with the checksum; just search for the file. */
198 basedir
= malloc (strlen (file
) + 1);
199 newname
= malloc (strlen (linkbuf
) + strlen (debugdir
)
200 + strlen (file
) + 9);
202 p
= strrchr (file
, '/');
205 memcpy (basedir
, file
, p
- file
);
206 basedir
[p
- file
] = '\0';
211 strcpy (newname
, basedir
);
212 strcat (newname
, "/");
213 strcat (newname
, linkbuf
);
214 ret
= load_debug_frame (newname
, buf
, bufsize
, -1);
218 strcpy (newname
, basedir
);
219 strcat (newname
, "/.debug/");
220 strcat (newname
, linkbuf
);
221 ret
= load_debug_frame (newname
, buf
, bufsize
, -1);
224 if (ret
== 1 && is_local
== 1)
226 strcpy (newname
, debugdir
);
227 strcat (newname
, basedir
);
228 strcat (newname
, "/");
229 strcat (newname
, linkbuf
);
230 ret
= load_debug_frame (newname
, buf
, bufsize
, -1);
240 /* An error reading image file. Release resources and return error code */
250 /* Locate the binary which originated the contents of address ADDR. Return
251 the name of the binary in *name (space is allocated by the caller)
252 Returns 0 if a binary is successfully found, or 1 if an error occurs. */
255 find_binary_for_address (unw_word_t ip
, char *name
, size_t name_size
)
257 #if defined(__linux) && (!UNW_REMOTE_ONLY)
258 struct map_iterator mi
;
261 unsigned long segbase
, mapoff
, hi
;
263 maps_init (&mi
, pid
);
264 while (maps_next (&mi
, &segbase
, &hi
, &mapoff
))
265 if (ip
>= segbase
&& ip
< hi
)
267 size_t len
= strlen (mi
.path
);
269 if (len
+ 1 <= name_size
)
271 memcpy (name
, mi
.path
, len
+ 1);
283 /* Locate and/or try to load a debug_frame section for address ADDR. Return
284 pointer to debug frame descriptor, or zero if not found. */
286 static struct unw_debug_frame_list
*
287 locate_debug_info (unw_addr_space_t as
, unw_word_t addr
, const char *dlname
,
288 unw_word_t start
, unw_word_t end
)
290 struct unw_debug_frame_list
*w
, *fdesc
= 0;
297 /* First, see if we loaded this frame already. */
299 for (w
= as
->debug_frames
; w
; w
= w
->next
)
301 Debug (4, "checking %p: %lx-%lx\n", w
, (long)w
->start
, (long)w
->end
);
302 if (addr
>= w
->start
&& addr
< w
->end
)
306 /* If the object name we receive is blank, there's still a chance of locating
307 the file by parsing /proc/self/maps. */
309 if (strcmp (dlname
, "") == 0)
311 err
= find_binary_for_address (addr
, name
, sizeof(path
));
314 Debug (15, "tried to locate binary for 0x%" PRIx64
", but no luck\n",
320 name
= (char*) dlname
;
322 err
= load_debug_frame (name
, &buf
, &bufsize
, as
== unw_local_addr_space
);
326 fdesc
= malloc (sizeof (struct unw_debug_frame_list
));
328 fdesc
->start
= start
;
330 fdesc
->debug_frame
= buf
;
331 fdesc
->debug_frame_size
= bufsize
;
333 fdesc
->next
= as
->debug_frames
;
335 as
->debug_frames
= fdesc
;
341 struct debug_frame_tab
343 struct table_entry
*tab
;
349 debug_frame_tab_append (struct debug_frame_tab
*tab
,
350 unw_word_t fde_offset
, unw_word_t start_ip
)
352 unsigned int length
= tab
->length
;
354 if (length
== tab
->size
)
357 tab
->tab
= realloc (tab
->tab
, sizeof (struct table_entry
) * tab
->size
);
360 tab
->tab
[length
].fde_offset
= fde_offset
;
361 tab
->tab
[length
].start_ip_offset
= start_ip
;
363 tab
->length
= length
+ 1;
367 debug_frame_tab_shrink (struct debug_frame_tab
*tab
)
369 if (tab
->size
> tab
->length
)
371 tab
->tab
= realloc (tab
->tab
, sizeof (struct table_entry
) * tab
->length
);
372 tab
->size
= tab
->length
;
377 debug_frame_tab_compare (const void *a
, const void *b
)
379 const struct table_entry
*fa
= a
, *fb
= b
;
381 if (fa
->start_ip_offset
> fb
->start_ip_offset
)
383 else if (fa
->start_ip_offset
< fb
->start_ip_offset
)
390 dwarf_find_debug_frame (int found
, unw_dyn_info_t
*di_debug
, unw_word_t ip
,
391 unw_word_t segbase
, const char* obj_name
,
392 unw_word_t start
, unw_word_t end
)
395 struct unw_debug_frame_list
*fdesc
= 0;
399 Debug (15, "Trying to find .debug_frame for %s\n", obj_name
);
402 fdesc
= locate_debug_info (unw_local_addr_space
, ip
, obj_name
, start
, end
);
406 Debug (15, "couldn't load .debug_frame\n");
413 unw_word_t item_start
, item_end
= 0;
416 struct debug_frame_tab tab
;
418 Debug (15, "loaded .debug_frame\n");
420 buf
= fdesc
->debug_frame
;
421 bufsize
= fdesc
->debug_frame_size
;
425 Debug (15, "zero-length .debug_frame\n");
429 /* Now create a binary-search table, if it does not already exist. */
432 addr
= (unw_word_t
) (uintptr_t) buf
;
434 a
= unw_get_accessors (unw_local_addr_space
);
436 /* Find all FDE entries in debug_frame, and make into a sorted
441 tab
.tab
= calloc (tab
.size
, sizeof (struct table_entry
));
443 while (addr
< (unw_word_t
) (uintptr_t) (buf
+ bufsize
))
448 dwarf_readu32 (unw_local_addr_space
, a
, &addr
, &u32val
, NULL
);
452 else if (u32val
!= 0xffffffff)
454 uint32_t cie_id32
= 0;
455 item_end
= addr
+ u32val
;
456 dwarf_readu32 (unw_local_addr_space
, a
, &addr
, &cie_id32
,
459 id_for_cie
= 0xffffffff;
464 /* Extended length. */
465 dwarf_readu64 (unw_local_addr_space
, a
, &addr
, &u64val
, NULL
);
466 item_end
= addr
+ u64val
;
468 dwarf_readu64 (unw_local_addr_space
, a
, &addr
, &cie_id
, NULL
);
469 id_for_cie
= 0xffffffffffffffffull
;
472 /*Debug (1, "CIE/FDE id = %.8x\n", (int) cie_id);*/
474 if (cie_id
== id_for_cie
)
476 /*Debug (1, "Found CIE at %.8x.\n", item_start);*/
479 unw_word_t fde_addr
= item_start
;
480 unw_proc_info_t this_pi
;
483 /*Debug (1, "Found FDE at %.8x\n", item_start);*/
485 err
= dwarf_extract_proc_info_from_fde (unw_local_addr_space
,
488 (uintptr_t) buf
, 0, 1,
492 Debug (15, "start_ip = %lx, end_ip = %lx\n",
493 (long) this_pi
.start_ip
, (long) this_pi
.end_ip
);
494 debug_frame_tab_append (&tab
,
495 item_start
- (unw_word_t
) (uintptr_t) buf
,
499 Debug (1, "FDE parse failed\n");*/
505 debug_frame_tab_shrink (&tab
);
506 qsort (tab
.tab
, tab
.length
, sizeof (struct table_entry
),
507 debug_frame_tab_compare
);
508 /* for (i = 0; i < tab.length; i++)
510 fprintf (stderr, "ip %x, fde offset %x\n",
511 (int) tab.tab[i].start_ip_offset,
512 (int) tab.tab[i].fde_offset);
514 fdesc
->index
= tab
.tab
;
515 fdesc
->index_size
= tab
.length
;
518 di
->format
= UNW_INFO_FORMAT_TABLE
;
519 di
->start_ip
= fdesc
->start
;
520 di
->end_ip
= fdesc
->end
;
521 di
->u
.ti
.name_ptr
= (unw_word_t
) (uintptr_t) obj_name
;
522 di
->u
.ti
.table_data
= (unw_word_t
*) fdesc
;
523 di
->u
.ti
.table_len
= sizeof (*fdesc
) / sizeof (unw_word_t
);
524 di
->u
.ti
.segbase
= segbase
;
527 Debug (15, "found debug_frame table `%s': segbase=0x%lx, len=%lu, "
528 "gp=0x%lx, table_data=0x%lx\n",
529 (char *) (uintptr_t) di
->u
.ti
.name_ptr
,
530 (long) di
->u
.ti
.segbase
, (long) di
->u
.ti
.table_len
,
531 (long) di
->gp
, (long) di
->u
.ti
.table_data
);
536 #endif /* CONFIG_DEBUG_FRAME */
538 #ifndef UNW_REMOTE_ONLY
541 dwarf_search_fde_in_unwind_table (unw_addr_space_t as
, unw_word_t ip
,
542 unw_word_t segbase
, size_t table_len
,
543 const struct table_entry
*table
,
544 unw_word_t debug_frame_base
,
545 unw_word_t
*fde_addr
,
548 const struct table_entry
*e
= NULL
;
550 #ifndef UNW_LOCAL_ONLY
551 struct table_entry ent
;
555 Debug(15, "dwarf_search_fde_in_unwind_table: ip=0x%lx, segbase=%lx, "
556 "table_len=%zu, table=%p, debug_frame_base=%lx",
557 ip
, segbase
, table_len
, table
, debug_frame_base
);
559 a
= unw_get_accessors (as
);
561 #ifndef UNW_REMOTE_ONLY
562 if (as
== unw_local_addr_space
)
564 e
= lookup (table
, table_len
, ip
- segbase
);
569 #ifndef UNW_LOCAL_ONLY
570 if ((ret
= remote_lookup (as
, (uintptr_t) table
, table_len
,
571 ip
- segbase
, &ent
, arg
)) < 0)
576 e
= NULL
; /* no info found */
581 Debug (1, "IP %x: no explicit unwind info found\n",
583 /* IP is inside this table's range, but there is no explicit
588 Debug (15, "ip=0x%lx, start_ip=0x%lx\n",
589 (long) ip
, (long) (e
->start_ip_offset
));
591 if (debug_frame_base
)
592 *fde_addr
= e
->fde_offset
+ debug_frame_base
;
594 *fde_addr
= e
->fde_offset
+ segbase
;
596 Debug (1, "e->fde_offset = %x, segbase = %x, debug_frame_base = %x, "
597 "fde_addr = %x\n", (int) e
->fde_offset
, (int) segbase
,
598 (int) debug_frame_base
, (int) fde_addr
);
604 /* Searches for FDE for specific ip in eh_frame_hdr.
605 Returns 0 if success. */
607 search_fde_in_eh_frame(unw_word_t ip
, unw_word_t hdr_addr
, unw_word_t gp
,
608 unw_word_t eh_frame_end
, unw_word_t
*fde_addr
,
609 int need_unwind_info
, void *arg
,
613 unw_word_t addr
, eh_frame_start
, fde_count
;
615 struct dwarf_eh_frame_hdr
*hdr
= (struct dwarf_eh_frame_hdr
*)hdr_addr
;
617 if (hdr
->version
!= DW_EH_VERSION
)
619 Debug (1, "table `%s' has unexpected version %d\n",
624 a
= unw_get_accessors (unw_local_addr_space
);
625 addr
= (unw_word_t
) (uintptr_t) (hdr
+ 1);
627 /* (Optionally) read eh_frame_ptr: */
628 if ((ret
= dwarf_read_encoded_pointer (unw_local_addr_space
, a
,
629 &addr
, hdr
->eh_frame_ptr_enc
, gp
, 0,
630 &eh_frame_start
, NULL
)) < 0)
633 /* (Optionally) read fde_count: */
634 if ((ret
= dwarf_read_encoded_pointer (unw_local_addr_space
, a
,
635 &addr
, hdr
->fde_count_enc
, gp
, 0,
636 &fde_count
, NULL
)) < 0)
639 if (hdr
->table_enc
!= (DW_EH_PE_datarel
| DW_EH_PE_sdata4
))
643 /* If there is no search table or it has an unsupported
644 encoding, fall back on linear search. */
645 if (hdr
->table_enc
== DW_EH_PE_omit
)
646 Debug (4, "table `%s' lacks search table; doing linear search\n",
649 Debug (4, "table `%s' has encoding 0x%x; doing linear search\n",
650 name
, hdr
->table_enc
);
652 if (hdr
->fde_count_enc
== DW_EH_PE_omit
)
654 if (hdr
->eh_frame_ptr_enc
== DW_EH_PE_omit
)
657 /* XXX we know how to build a local binary search table for
658 .debug_frame, so we could do that here too. */
659 return linear_search (unw_local_addr_space
, ip
,
660 eh_frame_start
, eh_frame_end
, fde_count
,
661 fde_addr
, need_unwind_info
, NULL
);
667 size_t table_len
= (fde_count
* sizeof (struct table_entry
));
668 /* For the binary-search table in the eh_frame_hdr, data-relative
669 means relative to the start of that section... */
670 unw_word_t sbase
= (unw_word_t
) (uintptr_t) hdr
;
672 Debug (15, "found table `%s': sbase=0x%lx, len=%lu, gp=0x%lx, "
673 "table_data=0x%lx\n", (char *) (uintptr_t) name
,
674 (long) sbase
, (long) table_len
,
675 (long) gp
, (long) addr
);
677 return dwarf_search_fde_in_unwind_table (unw_local_addr_space
, ip
,
679 (const struct table_entry
*)addr
, 0,
684 #ifdef HAVE_DL_ITERATE_PHDR
686 /* ptr is a pointer to a dwarf_callback_data structure and, on entry,
687 member ip contains the instruction-pointer we're looking
690 dwarf_callback (struct dl_phdr_info
*info
, size_t size
, void *ptr
)
692 struct dwarf_callback_data
*cb_data
= ptr
;
693 const Elf_W(Phdr
) *phdr
, *p_eh_hdr
, *p_dynamic
, *p_text
;
694 unw_word_t addr
, eh_frame_start
, eh_frame_end
, fde_count
, ip
;
695 Elf_W(Addr
) load_base
, max_load_addr
= 0;
696 int ret
, need_unwind_info
= cb_data
->need_unwind_info
;
697 struct dwarf_eh_frame_hdr
*hdr
;
701 #ifdef CONFIG_DEBUG_FRAME
702 unw_word_t start
, end
;
703 #endif /* CONFIG_DEBUG_FRAME*/
707 /* Make sure struct dl_phdr_info is at least as big as we need. */
708 if (size
< offsetof (struct dl_phdr_info
, dlpi_phnum
)
709 + sizeof (info
->dlpi_phnum
))
712 Debug (15, "checking %s, base=0x%lx)\n",
713 info
->dlpi_name
, (long) info
->dlpi_addr
);
715 phdr
= info
->dlpi_phdr
;
716 load_base
= info
->dlpi_addr
;
721 /* See if PC falls into one of the loaded segments. Find the
722 eh-header segment at the same time. */
723 for (n
= info
->dlpi_phnum
; --n
>= 0; phdr
++)
725 if (phdr
->p_type
== PT_LOAD
)
727 Elf_W(Addr
) vaddr
= phdr
->p_vaddr
+ load_base
;
729 if (ip
>= vaddr
&& ip
< vaddr
+ phdr
->p_memsz
)
732 if (vaddr
+ phdr
->p_filesz
> max_load_addr
)
733 max_load_addr
= vaddr
+ phdr
->p_filesz
;
735 else if (phdr
->p_type
== PT_GNU_EH_FRAME
)
737 else if (phdr
->p_type
== PT_DYNAMIC
)
748 /* For dynamicly linked executables and shared libraries,
749 DT_PLTGOT is the value that data-relative addresses are
750 relative to for that object. We call this the "gp". */
751 Elf_W(Dyn
) *dyn
= (Elf_W(Dyn
) *)(p_dynamic
->p_vaddr
+ load_base
);
752 for (; dyn
->d_tag
!= DT_NULL
; ++dyn
)
753 if (dyn
->d_tag
== DT_PLTGOT
)
755 /* Assume that _DYNAMIC is writable and GLIBC has
756 relocated it (true for x86 at least). */
757 cb_data
->gp
= dyn
->d_un
.d_ptr
;
762 /* Otherwise this is a static executable with no _DYNAMIC. Assume
763 that data-relative addresses are relative to 0, i.e.,
767 eh_frame_end
= (unw_word_t
)max_load_addr
; // Can we do better?
768 ret
= search_fde_in_eh_frame(ip
, (unw_word_t
)(p_eh_hdr
->p_vaddr
+ load_base
),
769 cb_data
->gp
, eh_frame_end
, &cb_data
->fde_addr
,
770 cb_data
->need_unwind_info
, cb_data
->arg
, info
->dlpi_name
);
774 cb_data
->fde_base
= 0;
775 cb_data
->ip_offset
= 0;
780 #ifdef CONFIG_DEBUG_FRAME
781 found
= dwarf_find_debug_frame (found
, &cb_data
->di_debug
, info
, ip
);
782 #endif /* CONFIG_DEBUG_FRAME */
788 find_proc_fde (unw_word_t ip
, unw_word_t
*fde_addr
,
789 unw_word_t
*gp
, unw_word_t
*fde_base
,
790 unw_word_t
*ip_offset
, void *arg
) {
791 struct dwarf_callback_data cb_data
;
792 intrmask_t saved_mask
;
795 memset (&cb_data
, 0, sizeof (cb_data
));
799 SIGPROCMASK (SIG_SETMASK
, &unwi_full_mask
, &saved_mask
);
800 ret
= dl_iterate_phdr (dwarf_callback
, &cb_data
);
801 SIGPROCMASK (SIG_SETMASK
, &saved_mask
, NULL
);
812 *fde_addr
= cb_data
.fde_addr
;
814 *fde_base
= cb_data
.fde_base
;
815 *ip_offset
= cb_data
.ip_offset
;
820 #else // HAVE_DL_ITERATE_PHDR
823 find_proc_fde(unw_word_t ip
, unw_word_t
*fde_addr
,
824 unw_word_t
*gp
, unw_word_t
*fde_base
,
825 unw_word_t
*ip_offset
, void *arg
) {
826 Dl_amd64_unwindinfo dlef
;
829 int fp_enc
, fc_enc
, ft_enc
;
830 unsigned char *pi
, *pj
;
835 dlef
.dlui_version
= 1;
837 /* Locate the appropiate exception_range_entry table first */
838 if (0 == dlamd64getunwind((void*)ip
, &dlef
)) {
843 * you now know size and position of block of data needed for
844 * binary search ??REMOTE??
846 data
= dlef
.dlui_unwindstart
;
850 base
= (uintptr_t)data
;
851 data_end
= dlef
.dlui_unwindend
;
859 return search_fde_in_eh_frame(ip
, (unw_word_t
)data
, 0,
860 (unw_word_t
)data_end
, fde_addr
, arg
, 0,
864 #endif // HAVE_DL_ITERATE_PHDR
867 dwarf_find_proc_info (unw_addr_space_t as
, unw_word_t ip
,
868 unw_proc_info_t
*pi
, int need_unwind_info
, void *arg
)
870 struct dwarf_callback_data cb_data
;
871 intrmask_t saved_mask
;
876 unw_word_t ip_offset
;
879 Debug (14, "looking for IP=0x%lx\n", (long) ip
);
881 ret
= find_proc_fde (ip
, &fde_addr
, &gp
, &fde_base
, &ip_offset
, arg
);
884 Debug (14, "IP=0x%lx not found\n", (long) ip
);
890 a
= unw_get_accessors (unw_local_addr_space
);
891 ret
= dwarf_extract_proc_info_from_fde (as
, a
, &fde_addr
, pi
,
898 pi
->start_ip
+= ip_offset
;
899 pi
->end_ip
+= ip_offset
;
901 if (ip
< pi
->start_ip
|| ip
>= pi
->end_ip
)
907 static inline const struct table_entry
*
908 lookup (const struct table_entry
*table
, size_t table_size
, int32_t rel_ip
)
910 unsigned long table_len
= table_size
/ sizeof (struct table_entry
);
911 const struct table_entry
*e
= NULL
;
912 unsigned long lo
, hi
, mid
;
914 /* do a binary search for right entry: */
915 for (lo
= 0, hi
= table_len
; lo
< hi
;)
919 Debug (15, "e->start_ip_offset = %lx\n", (long) e
->start_ip_offset
);
920 if (rel_ip
< e
->start_ip_offset
)
931 #endif /* !UNW_REMOTE_ONLY */
933 #ifndef UNW_LOCAL_ONLY
935 /* Lookup an unwind-table entry in remote memory. Returns 1 if an
936 entry is found, 0 if no entry is found, negative if an error
937 occurred reading remote memory. */
939 remote_lookup (unw_addr_space_t as
,
940 unw_word_t table
, size_t table_size
, int32_t rel_ip
,
941 struct table_entry
*e
, void *arg
)
943 unsigned long table_len
= table_size
/ sizeof (struct table_entry
);
944 unw_accessors_t
*a
= unw_get_accessors (as
);
945 unsigned long lo
, hi
, mid
;
946 unw_word_t e_addr
= 0;
950 /* do a binary search for right entry: */
951 for (lo
= 0, hi
= table_len
; lo
< hi
;)
954 e_addr
= table
+ mid
* sizeof (struct table_entry
);
955 if ((ret
= dwarf_reads32 (as
, a
, &e_addr
, &start
, arg
)) < 0)
965 e_addr
= table
+ (hi
- 1) * sizeof (struct table_entry
);
966 if ((ret
= dwarf_reads32 (as
, a
, &e_addr
, &e
->start_ip_offset
, arg
)) < 0
967 || (ret
= dwarf_reads32 (as
, a
, &e_addr
, &e
->fde_offset
, arg
)) < 0)
972 #endif /* !UNW_LOCAL_ONLY */
975 dwarf_search_unwind_table (unw_addr_space_t as
, unw_word_t ip
,
976 unw_dyn_info_t
*di
, unw_proc_info_t
*pi
,
977 int need_unwind_info
, void *arg
)
979 const struct table_entry
*e
= NULL
, *table
;
980 unw_word_t segbase
= 0, fde_addr
;
982 #ifndef UNW_LOCAL_ONLY
983 struct table_entry ent
;
986 unw_word_t debug_frame_base
;
989 #ifdef UNW_REMOTE_ONLY
990 assert (di
->format
== UNW_INFO_FORMAT_REMOTE_TABLE
);
992 assert (di
->format
== UNW_INFO_FORMAT_REMOTE_TABLE
993 || di
->format
== UNW_INFO_FORMAT_TABLE
);
995 assert (ip
>= di
->start_ip
&& ip
< di
->end_ip
);
997 if (di
->format
== UNW_INFO_FORMAT_REMOTE_TABLE
)
999 table
= (const struct table_entry
*) (uintptr_t) di
->u
.rti
.table_data
;
1000 table_len
= di
->u
.rti
.table_len
* sizeof (unw_word_t
);
1001 debug_frame_base
= 0;
1005 #ifndef UNW_REMOTE_ONLY
1006 struct unw_debug_frame_list
*fdesc
= (void *) di
->u
.ti
.table_data
;
1008 /* UNW_INFO_FORMAT_TABLE (i.e. .debug_frame) is read from local address
1009 space. Both the index and the unwind tables live in local memory, but
1010 the address space to check for properties like the address size and
1011 endianness is the target one. */
1012 as
= unw_local_addr_space
;
1013 table
= fdesc
->index
;
1014 table_len
= fdesc
->index_size
* sizeof (struct table_entry
);
1015 debug_frame_base
= (uintptr_t) fdesc
->debug_frame
;
1019 a
= unw_get_accessors (as
);
1021 #ifndef UNW_REMOTE_ONLY
1022 if (as
== unw_local_addr_space
)
1024 segbase
= di
->u
.rti
.segbase
;
1025 e
= lookup (table
, table_len
, ip
- segbase
);
1030 #ifndef UNW_LOCAL_ONLY
1031 segbase
= di
->u
.rti
.segbase
;
1032 if ((ret
= remote_lookup (as
, (uintptr_t) table
, table_len
,
1033 ip
- segbase
, &ent
, arg
)) < 0)
1038 e
= NULL
; /* no info found */
1043 Debug (1, "IP %lx inside range %lx-%lx, but no explicit unwind info found\n",
1044 (long) ip
, (long) di
->start_ip
, (long) di
->end_ip
);
1045 /* IP is inside this table's range, but there is no explicit
1047 return -UNW_ENOINFO
;
1049 Debug (15, "ip=0x%lx, start_ip=0x%lx\n",
1050 (long) ip
, (long) (e
->start_ip_offset
));
1051 if (debug_frame_base
)
1052 fde_addr
= e
->fde_offset
+ debug_frame_base
;
1054 fde_addr
= e
->fde_offset
+ segbase
;
1055 Debug (1, "e->fde_offset = %lx, segbase = %lx, debug_frame_base = %lx, "
1056 "fde_addr = %lx\n", (long) e
->fde_offset
, (long) segbase
,
1057 (long) debug_frame_base
, (long) fde_addr
);
1058 if ((ret
= dwarf_extract_proc_info_from_fde (as
, a
, &fde_addr
, pi
,
1060 debug_frame_base
: segbase
,
1062 debug_frame_base
!= 0, arg
)) < 0)
1065 /* .debug_frame uses an absolute encoding that does not know about any
1066 shared library relocation. */
1067 if (di
->format
== UNW_INFO_FORMAT_TABLE
)
1069 pi
->start_ip
+= segbase
;
1070 pi
->end_ip
+= segbase
;
1071 pi
->flags
= UNW_PI_FLAG_DEBUG_FRAME
;
1074 if (ip
< pi
->start_ip
|| ip
>= pi
->end_ip
)
1075 return -UNW_ENOINFO
;
1081 dwarf_put_unwind_info (unw_addr_space_t as
, unw_proc_info_t
*pi
, void *arg
)
1083 return; /* always a nop */