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