vfs: check userland buffers before reading them.
[haiku.git] / src / libs / libunwind / dwarf / Gfind_proc_info-lsb.c
blob857b85c48c16795c11951d5f8cc52c3d6d5948d6
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/). */
29 #include <stddef.h>
30 #include <stdio.h>
31 #include <limits.h>
33 #include "dwarf_i.h"
34 #include "dwarf-eh.h"
35 #include "libunwind_i.h"
37 struct table_entry
39 int32_t start_ip_offset;
40 int32_t fde_offset;
44 static inline const struct table_entry *
45 lookup (const struct table_entry *table, size_t table_size, int32_t rel_ip);
47 static int
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
55 #ifdef __linux
56 #include "os-linux.h"
57 #endif
59 static int
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,
62 unw_word_t fde_count,
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;
67 unw_proc_info_t pi;
68 int ret;
70 while (i++ < fde_count && addr < eh_frame_end)
72 *fde_addr = addr;
73 if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, &pi,
74 eh_frame_start,
75 0, 0, arg)) < 0)
76 return ret;
78 if (ip >= pi.start_ip && ip < pi.end_ip)
80 *fde_addr = faddr;
82 if (!need_unwind_info)
83 return 0;
85 addr = faddr;
86 if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, &pi,
87 eh_frame_start,
88 need_unwind_info, 0,
89 arg))
90 < 0)
91 return ret;
92 return 0;
95 return -UNW_ENOINFO;
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
106 .debug_frame. */
107 /* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */
109 static int
110 load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
112 FILE *f;
113 Elf_W (Ehdr) ehdr;
114 Elf_W (Half) shstrndx;
115 Elf_W (Shdr) *sec_hdrs = NULL;
116 char *stringtab = NULL;
117 unsigned int i;
118 size_t linksize = 0;
119 char *linkbuf = NULL;
121 *buf = NULL;
122 *bufsize = 0;
124 f = fopen (file, "r");
126 if (!f)
127 return 1;
129 if (fread (&ehdr, sizeof (Elf_W (Ehdr)), 1, f) != 1)
130 goto file_error;
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)
140 goto file_error;
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)
147 goto file_error;
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)
160 goto file_error;
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)
172 goto file_error;
174 Debug (4, "read %zd bytes of .gnu_debuglink from offset %zd\n",
175 linksize, sec_hdrs[i].sh_offset);
179 free (stringtab);
180 free (sec_hdrs);
182 fclose (f);
184 /* Ignore separate debug files which contain a .gnu_debuglink section. */
185 if (linkbuf && is_local == -1)
187 free (linkbuf);
188 return 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";
195 int ret;
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, '/');
203 if (p != NULL)
205 memcpy (basedir, file, p - file);
206 basedir[p - file] = '\0';
208 else
209 basedir[0] = 0;
211 strcpy (newname, basedir);
212 strcat (newname, "/");
213 strcat (newname, linkbuf);
214 ret = load_debug_frame (newname, buf, bufsize, -1);
216 if (ret == 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);
233 free (basedir);
234 free (newname);
236 free (linkbuf);
238 return 0;
240 /* An error reading image file. Release resources and return error code */
241 file_error:
242 free(stringtab);
243 free(sec_hdrs);
244 free(linkbuf);
245 fclose(f);
247 return 1;
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. */
254 static int
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;
259 int found = 0;
260 int pid = getpid ();
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);
272 found = 1;
274 break;
276 maps_close (&mi);
277 return !found;
278 #endif
280 return 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;
291 char path[PATH_MAX];
292 char *name = path;
293 int err;
294 char *buf;
295 size_t bufsize;
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)
303 return w;
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));
312 if (err)
314 Debug (15, "tried to locate binary for 0x%" PRIx64 ", but no luck\n",
315 (uint64_t) addr);
316 return 0;
319 else
320 name = (char*) dlname;
322 err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space);
324 if (!err)
326 fdesc = malloc (sizeof (struct unw_debug_frame_list));
328 fdesc->start = start;
329 fdesc->end = end;
330 fdesc->debug_frame = buf;
331 fdesc->debug_frame_size = bufsize;
332 fdesc->index = NULL;
333 fdesc->next = as->debug_frames;
335 as->debug_frames = fdesc;
338 return fdesc;
341 struct debug_frame_tab
343 struct table_entry *tab;
344 uint32_t length;
345 uint32_t size;
348 static void
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)
356 tab->size *= 2;
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;
366 static void
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;
376 static int
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)
382 return 1;
383 else if (fa->start_ip_offset < fb->start_ip_offset)
384 return -1;
385 else
386 return 0;
389 PROTECTED int
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)
394 unw_dyn_info_t *di;
395 struct unw_debug_frame_list *fdesc = 0;
396 unw_accessors_t *a;
397 unw_word_t addr;
399 Debug (15, "Trying to find .debug_frame for %s\n", obj_name);
400 di = di_debug;
402 fdesc = locate_debug_info (unw_local_addr_space, ip, obj_name, start, end);
404 if (!fdesc)
406 Debug (15, "couldn't load .debug_frame\n");
407 return found;
409 else
411 char *buf;
412 size_t bufsize;
413 unw_word_t item_start, item_end = 0;
414 uint32_t u32val = 0;
415 uint64_t cie_id = 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;
423 if (bufsize == 0)
425 Debug (15, "zero-length .debug_frame\n");
426 return found;
429 /* Now create a binary-search table, if it does not already exist. */
430 if (!fdesc->index)
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
437 index. */
439 tab.length = 0;
440 tab.size = 16;
441 tab.tab = calloc (tab.size, sizeof (struct table_entry));
443 while (addr < (unw_word_t) (uintptr_t) (buf + bufsize))
445 uint64_t id_for_cie;
446 item_start = addr;
448 dwarf_readu32 (unw_local_addr_space, a, &addr, &u32val, NULL);
450 if (u32val == 0)
451 break;
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,
457 NULL);
458 cie_id = cie_id32;
459 id_for_cie = 0xffffffff;
461 else
463 uint64_t u64val = 0;
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);*/
477 else
479 unw_word_t fde_addr = item_start;
480 unw_proc_info_t this_pi;
481 int err;
483 /*Debug (1, "Found FDE at %.8x\n", item_start);*/
485 err = dwarf_extract_proc_info_from_fde (unw_local_addr_space,
486 a, &fde_addr,
487 &this_pi,
488 (uintptr_t) buf, 0, 1,
489 NULL);
490 if (err == 0)
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,
496 this_pi.start_ip);
498 /*else
499 Debug (1, "FDE parse failed\n");*/
502 addr = item_end;
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;
526 found = 1;
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);
533 return found;
536 #endif /* CONFIG_DEBUG_FRAME */
538 #ifndef UNW_REMOTE_ONLY
540 static int
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,
546 void *arg)
548 const struct table_entry *e = NULL;
549 unw_accessors_t *a;
550 #ifndef UNW_LOCAL_ONLY
551 struct table_entry ent;
552 #endif
553 int ret;
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);
566 else
567 #endif
569 #ifndef UNW_LOCAL_ONLY
570 if ((ret = remote_lookup (as, (uintptr_t) table, table_len,
571 ip - segbase, &ent, arg)) < 0)
572 return ret;
573 if (ret)
574 e = &ent;
575 else
576 e = NULL; /* no info found */
577 #endif
579 if (!e)
581 Debug (1, "IP %x: no explicit unwind info found\n",
582 (int) ip);
583 /* IP is inside this table's range, but there is no explicit
584 unwind info. */
585 return -UNW_ENOINFO;
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;
593 else
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);
600 return 0;
604 /* Searches for FDE for specific ip in eh_frame_hdr.
605 Returns 0 if success. */
606 static int
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,
610 const char * name)
612 unw_accessors_t *a;
613 unw_word_t addr, eh_frame_start, fde_count;
614 int ret;
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",
620 name, hdr->version);
621 return -UNW_ENOINFO;
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)
631 return ret;
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)
637 return ret;
639 if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
641 int err;
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",
647 name);
648 else
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)
653 fde_count = ~0UL;
654 if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
655 abort ();
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);
663 else
665 int err;
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,
678 sbase, table_len,
679 (const struct table_entry*)addr, 0,
680 fde_addr, arg);
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
688 for. */
689 HIDDEN int
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;
698 unw_accessors_t *a;
699 long n;
700 int found = 0;
701 #ifdef CONFIG_DEBUG_FRAME
702 unw_word_t start, end;
703 #endif /* CONFIG_DEBUG_FRAME*/
705 ip = cb_data->ip;
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))
710 return -1;
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;
717 p_text = NULL;
718 p_eh_hdr = NULL;
719 p_dynamic = NULL;
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)
730 p_text = phdr;
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)
736 p_eh_hdr = phdr;
737 else if (phdr->p_type == PT_DYNAMIC)
738 p_dynamic = phdr;
741 if (!p_text)
742 return 0;
744 if (p_eh_hdr)
746 if (p_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;
758 break;
761 else
762 /* Otherwise this is a static executable with no _DYNAMIC. Assume
763 that data-relative addresses are relative to 0, i.e.,
764 absolute. */
765 cb_data->gp = 0;
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);
771 if (ret < 0)
772 return ret;
774 cb_data->fde_base = 0;
775 cb_data->ip_offset = 0;
777 return 1;
780 #ifdef CONFIG_DEBUG_FRAME
781 found = dwarf_find_debug_frame (found, &cb_data->di_debug, info, ip);
782 #endif /* CONFIG_DEBUG_FRAME */
784 return found;
787 static int
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;
793 int ret;
795 memset (&cb_data, 0, sizeof (cb_data));
796 cb_data.ip = ip;
797 cb_data.arg = arg;
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);
803 if (ret < 0)
804 return ret;
806 if (ret == 0)
808 ret = -UNW_ENOINFO;
809 return ret;
812 *fde_addr = cb_data.fde_addr;
813 *gp = cb_data.gp;
814 *fde_base = cb_data.fde_base;
815 *ip_offset = cb_data.ip_offset;
817 return 0;
820 #else // HAVE_DL_ITERATE_PHDR
822 static int
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;
827 void* data;
828 void* data_end;
829 int fp_enc, fc_enc, ft_enc;
830 unsigned char *pi, *pj;
831 ptrdiff_t reloc;
832 uintptr_t base;
833 int ret;
835 dlef.dlui_version = 1;
837 /* Locate the appropiate exception_range_entry table first */
838 if (0 == dlamd64getunwind((void*)ip, &dlef)) {
839 return -UNW_ENOINFO;
843 * you now know size and position of block of data needed for
844 * binary search ??REMOTE??
846 data = dlef.dlui_unwindstart;
847 if (0 == data)
848 return -UNW_ENOINFO;
850 base = (uintptr_t)data;
851 data_end = dlef.dlui_unwindend;
852 reloc = 0;
853 /* ??REMOTE?? */
855 *gp = 0;
856 *fde_base = 0;
857 *ip_offset = 0;
859 return search_fde_in_eh_frame(ip, (unw_word_t)data, 0,
860 (unw_word_t)data_end, fde_addr, arg, 0,
861 dlef.dlui_objname);
864 #endif // HAVE_DL_ITERATE_PHDR
866 HIDDEN int
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;
872 int ret;
873 unw_word_t fde_addr;
874 unw_word_t gp;
875 unw_word_t fde_base;
876 unw_word_t ip_offset;
877 unw_accessors_t *a;
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);
882 if (ret < 0)
884 Debug (14, "IP=0x%lx not found\n", (long) ip);
885 return ret;
888 pi->gp = gp;
890 a = unw_get_accessors (unw_local_addr_space);
891 ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi,
892 fde_base,
893 need_unwind_info, 0,
894 arg);
895 if (ret < 0)
896 return ret;
898 pi->start_ip += ip_offset;
899 pi->end_ip += ip_offset;
901 if (ip < pi->start_ip || ip >= pi->end_ip)
902 return -UNW_ENOINFO;
904 return 0;
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;)
917 mid = (lo + hi) / 2;
918 e = table + mid;
919 Debug (15, "e->start_ip_offset = %lx\n", (long) e->start_ip_offset);
920 if (rel_ip < e->start_ip_offset)
921 hi = mid;
922 else
923 lo = mid + 1;
925 if (hi <= 0)
926 return NULL;
927 e = table + hi - 1;
928 return e;
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. */
938 static int
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;
947 int32_t start;
948 int ret;
950 /* do a binary search for right entry: */
951 for (lo = 0, hi = table_len; lo < hi;)
953 mid = (lo + hi) / 2;
954 e_addr = table + mid * sizeof (struct table_entry);
955 if ((ret = dwarf_reads32 (as, a, &e_addr, &start, arg)) < 0)
956 return ret;
958 if (rel_ip < start)
959 hi = mid;
960 else
961 lo = mid + 1;
963 if (hi <= 0)
964 return 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)
968 return ret;
969 return 1;
972 #endif /* !UNW_LOCAL_ONLY */
974 PROTECTED int
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;
981 unw_accessors_t *a;
982 #ifndef UNW_LOCAL_ONLY
983 struct table_entry ent;
984 #endif
985 int ret;
986 unw_word_t debug_frame_base;
987 size_t table_len;
989 #ifdef UNW_REMOTE_ONLY
990 assert (di->format == UNW_INFO_FORMAT_REMOTE_TABLE);
991 #else
992 assert (di->format == UNW_INFO_FORMAT_REMOTE_TABLE
993 || di->format == UNW_INFO_FORMAT_TABLE);
994 #endif
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;
1003 else
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;
1016 #endif
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);
1027 else
1028 #endif
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)
1034 return ret;
1035 if (ret)
1036 e = &ent;
1037 else
1038 e = NULL; /* no info found */
1039 #endif
1041 if (!e)
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
1046 unwind info. */
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;
1053 else
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,
1059 debug_frame_base ?
1060 debug_frame_base : segbase,
1061 need_unwind_info,
1062 debug_frame_base != 0, arg)) < 0)
1063 return ret;
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;
1077 return 0;
1080 HIDDEN void
1081 dwarf_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg)
1083 return; /* always a nop */