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