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