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