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