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