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