• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libunwind - a platform-independent unwind library
2    Copyright (c) 2001-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 #include <assert.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 
30 #include "unwind_i.h"
31 
32 #ifdef HAVE_IA64INTRIN_H
33 # include <ia64intrin.h>
34 #endif
35 
36 extern unw_addr_space_t _ULia64_local_addr_space;
37 
38 struct ia64_table_entry
39   {
40     uint64_t start_offset;
41     uint64_t end_offset;
42     uint64_t info_offset;
43   };
44 
45 #ifdef UNW_LOCAL_ONLY
46 
47 static inline int
is_local_addr_space(unw_addr_space_t as)48 is_local_addr_space (unw_addr_space_t as)
49 {
50   return 1;
51 }
52 
53 static inline int
read_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * valp,void * arg)54 read_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, void *arg)
55 {
56   *valp = *(unw_word_t *) addr;
57   return 0;
58 }
59 
60 #else /* !UNW_LOCAL_ONLY */
61 
62 static inline int
is_local_addr_space(unw_addr_space_t as)63 is_local_addr_space (unw_addr_space_t as)
64 {
65   return as == unw_local_addr_space;
66 }
67 
68 static inline int
read_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * valp,void * arg)69 read_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, void *arg)
70 {
71   unw_accessors_t *a = unw_get_accessors (as);
72 
73   return (*a->access_mem) (as, addr, valp, 0, arg);
74 }
75 
76 /* Helper macro for reading an ia64_table_entry from remote memory.  */
77 #define remote_read(addr, member)					     \
78 	(*a->access_mem) (as, (addr) + offsetof (struct ia64_table_entry,    \
79 						 member), &member, 0, arg)
80 
81 /* Lookup an unwind-table entry in remote memory.  Returns 1 if an
82    entry is found, 0 if no entry is found, negative if an error
83    occurred reading remote memory.  */
84 static int
remote_lookup(unw_addr_space_t as,unw_word_t table,size_t table_size,unw_word_t rel_ip,struct ia64_table_entry * e,void * arg)85 remote_lookup (unw_addr_space_t as,
86 	       unw_word_t table, size_t table_size, unw_word_t rel_ip,
87 	       struct ia64_table_entry *e, void *arg)
88 {
89   unw_word_t e_addr = 0, start_offset, end_offset, info_offset;
90   unw_accessors_t *a = unw_get_accessors (as);
91   unsigned long lo, hi, mid;
92   int ret;
93 
94   /* do a binary search for right entry: */
95   for (lo = 0, hi = table_size / sizeof (struct ia64_table_entry); lo < hi;)
96     {
97       mid = (lo + hi) / 2;
98       e_addr = table + mid * sizeof (struct ia64_table_entry);
99       if ((ret = remote_read (e_addr, start_offset)) < 0)
100 	return ret;
101 
102       if (rel_ip < start_offset)
103 	hi = mid;
104       else
105 	{
106 	  if ((ret = remote_read (e_addr, end_offset)) < 0)
107 	    return ret;
108 
109 	  if (rel_ip >= end_offset)
110 	    lo = mid + 1;
111 	  else
112 	    break;
113 	}
114     }
115   if (rel_ip < start_offset || rel_ip >= end_offset)
116     return 0;
117   e->start_offset = start_offset;
118   e->end_offset = end_offset;
119 
120   if ((ret = remote_read (e_addr, info_offset)) < 0)
121     return ret;
122   e->info_offset = info_offset;
123   return 1;
124 }
125 
126 HIDDEN void
tdep_put_unwind_info(unw_addr_space_t as,unw_proc_info_t * pi,void * arg)127 tdep_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg)
128 {
129   if (!pi->unwind_info)
130     return;
131 
132   if (is_local_addr_space (as))
133     {
134       free (pi->unwind_info);
135       pi->unwind_info = NULL;
136     }
137 }
138 
139 PROTECTED unw_word_t
_Uia64_find_dyn_list(unw_addr_space_t as,unw_dyn_info_t * di,void * arg)140 _Uia64_find_dyn_list (unw_addr_space_t as, unw_dyn_info_t *di, void *arg)
141 {
142   unw_word_t hdr_addr, info_addr, hdr, directives, pers, cookie, off;
143   unw_word_t start_offset, end_offset, info_offset, segbase;
144   struct ia64_table_entry *e;
145   size_t table_size;
146   unw_word_t gp = di->gp;
147   int ret;
148 
149   switch (di->format)
150     {
151     case UNW_INFO_FORMAT_DYNAMIC:
152     default:
153       return 0;
154 
155     case UNW_INFO_FORMAT_TABLE:
156       e = (struct ia64_table_entry *) di->u.ti.table_data;
157       table_size = di->u.ti.table_len * sizeof (di->u.ti.table_data[0]);
158       segbase = di->u.ti.segbase;
159       if (table_size < sizeof (struct ia64_table_entry))
160 	return 0;
161       start_offset = e[0].start_offset;
162       end_offset = e[0].end_offset;
163       info_offset = e[0].info_offset;
164       break;
165 
166     case UNW_INFO_FORMAT_REMOTE_TABLE:
167       {
168 	unw_accessors_t *a = unw_get_accessors (as);
169 	unw_word_t e_addr = di->u.rti.table_data;
170 
171 	table_size = di->u.rti.table_len * sizeof (unw_word_t);
172 	segbase = di->u.rti.segbase;
173 	if (table_size < sizeof (struct ia64_table_entry))
174 	  return 0;
175 
176 	if (   (ret = remote_read (e_addr, start_offset) < 0)
177 	    || (ret = remote_read (e_addr, end_offset) < 0)
178 	    || (ret = remote_read (e_addr, info_offset) < 0))
179 	  return ret;
180       }
181       break;
182     }
183 
184   if (start_offset != end_offset)
185     /* dyn-list entry cover a zero-length "procedure" and should be
186        first entry (note: technically a binary could contain code
187        below the segment base, but this doesn't happen for normal
188        binaries and certainly doesn't happen when libunwind is a
189        separate shared object.  For weird cases, the application may
190        have to provide its own (slower) version of this routine.  */
191     return 0;
192 
193   hdr_addr = info_offset + segbase;
194   info_addr = hdr_addr + 8;
195 
196   /* read the header word: */
197   if ((ret = read_mem (as, hdr_addr, &hdr, arg)) < 0)
198     return ret;
199 
200   if (IA64_UNW_VER (hdr) != 1
201       || IA64_UNW_FLAG_EHANDLER (hdr) || IA64_UNW_FLAG_UHANDLER (hdr))
202     /* dyn-list entry must be version 1 and doesn't have ehandler
203        or uhandler */
204     return 0;
205 
206   if (IA64_UNW_LENGTH (hdr) != 1)
207     /* dyn-list entry must consist of a single word of NOP directives */
208     return 0;
209 
210   if (   ((ret = read_mem (as, info_addr, &directives, arg)) < 0)
211       || ((ret = read_mem (as, info_addr + 0x08, &pers, arg)) < 0)
212       || ((ret = read_mem (as, info_addr + 0x10, &cookie, arg)) < 0)
213       || ((ret = read_mem (as, info_addr + 0x18, &off, arg)) < 0))
214     return 0;
215 
216   if (directives != 0 || pers != 0
217       || (!as->big_endian && cookie != 0x7473696c2d6e7964ULL)
218       || ( as->big_endian && cookie != 0x64796e2d6c697374ULL))
219     return 0;
220 
221   /* OK, we ran the gauntlet and found it: */
222   return off + gp;
223 }
224 
225 #endif /* !UNW_LOCAL_ONLY */
226 
227 static inline const struct ia64_table_entry *
lookup(struct ia64_table_entry * table,size_t table_size,unw_word_t rel_ip)228 lookup (struct ia64_table_entry *table, size_t table_size, unw_word_t rel_ip)
229 {
230   const struct ia64_table_entry *e = 0;
231   unsigned long lo, hi, mid;
232 
233   /* do a binary search for right entry: */
234   for (lo = 0, hi = table_size / sizeof (struct ia64_table_entry); lo < hi;)
235     {
236       mid = (lo + hi) / 2;
237       e = table + mid;
238       if (rel_ip < e->start_offset)
239 	hi = mid;
240       else if (rel_ip >= e->end_offset)
241 	lo = mid + 1;
242       else
243 	break;
244     }
245   if (rel_ip < e->start_offset || rel_ip >= e->end_offset)
246     return NULL;
247   return e;
248 }
249 
250 PROTECTED int
unw_search_ia64_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)251 unw_search_ia64_unwind_table (unw_addr_space_t as, unw_word_t ip,
252 			      unw_dyn_info_t *di, unw_proc_info_t *pi,
253 			      int need_unwind_info, void *arg)
254 {
255   unw_word_t addr, hdr_addr, info_addr, info_end_addr, hdr, *wp;
256   const struct ia64_table_entry *e = NULL;
257   unw_word_t handler_offset, segbase = 0;
258   int ret, is_local;
259 #ifndef UNW_LOCAL_ONLY
260   struct ia64_table_entry ent;
261 #endif
262 
263   assert ((di->format == UNW_INFO_FORMAT_TABLE
264 	   || di->format == UNW_INFO_FORMAT_REMOTE_TABLE)
265 	  && (ip >= di->start_ip && ip < di->end_ip));
266 
267   pi->flags = 0;
268   pi->unwind_info = 0;
269   pi->handler = 0;
270 
271   if (likely (di->format == UNW_INFO_FORMAT_TABLE))
272     {
273       segbase = di->u.ti.segbase;
274       e = lookup ((struct ia64_table_entry *) di->u.ti.table_data,
275 		  di->u.ti.table_len * sizeof (unw_word_t),
276 		  ip - segbase);
277     }
278 #ifndef UNW_LOCAL_ONLY
279   else
280     {
281       segbase = di->u.rti.segbase;
282       if ((ret = remote_lookup (as, di->u.rti.table_data,
283 				di->u.rti.table_len * sizeof (unw_word_t),
284 				ip - segbase, &ent, arg)) < 0)
285 	return ret;
286       if (ret)
287 	e = &ent;
288     }
289 #endif
290   if (!e)
291     {
292       /* IP is inside this table's range, but there is no explicit
293 	 unwind info => use default conventions (i.e., this is NOT an
294 	 error).  */
295       memset (pi, 0, sizeof (*pi));
296       pi->start_ip = 0;
297       pi->end_ip = 0;
298       pi->gp = di->gp;
299       pi->lsda = 0;
300       return 0;
301     }
302 
303   pi->start_ip = e->start_offset + segbase;
304   pi->end_ip = e->end_offset + segbase;
305 
306   hdr_addr = e->info_offset + segbase;
307   info_addr = hdr_addr + 8;
308 
309   /* Read the header word.  Note: the actual unwind-info is always
310      assumed to reside in memory, independent of whether di->format is
311      UNW_INFO_FORMAT_TABLE or UNW_INFO_FORMAT_REMOTE_TABLE.  */
312 
313   if ((ret = read_mem (as, hdr_addr, &hdr, arg)) < 0)
314     return ret;
315 
316   if (IA64_UNW_VER (hdr) != 1)
317     {
318       Debug (1, "Unknown header version %ld (hdr word=0x%lx @ 0x%lx)\n",
319 	     IA64_UNW_VER (hdr), (unsigned long) hdr,
320 	     (unsigned long) hdr_addr);
321       return -UNW_EBADVERSION;
322     }
323 
324   info_end_addr = info_addr + 8 * IA64_UNW_LENGTH (hdr);
325 
326   is_local = is_local_addr_space (as);
327 
328   /* If we must have the unwind-info, return it.  Also, if we are in
329      the local address-space, return the unwind-info because it's so
330      cheap to do so and it may come in handy later on.  */
331   if (need_unwind_info || is_local)
332     {
333       pi->unwind_info_size = 8 * IA64_UNW_LENGTH (hdr);
334 
335       if (is_local)
336 	pi->unwind_info = (void *) (uintptr_t) info_addr;
337       else
338 	{
339 	  /* Internalize unwind info.  Note: since we're doing this
340 	     only for non-local address spaces, there is no
341 	     signal-safety issue and it is OK to use malloc()/free().  */
342 	  pi->unwind_info = malloc (8 * IA64_UNW_LENGTH (hdr));
343 	  if (!pi->unwind_info)
344 	    return -UNW_ENOMEM;
345 
346 	  wp = (unw_word_t *) pi->unwind_info;
347 	  for (addr = info_addr; addr < info_end_addr; addr += 8, ++wp)
348 	    {
349 	      if ((ret = read_mem (as, addr, wp, arg)) < 0)
350 		{
351 		  free (pi->unwind_info);
352 		  return ret;
353 		}
354 	    }
355 	}
356     }
357 
358   if (IA64_UNW_FLAG_EHANDLER (hdr) || IA64_UNW_FLAG_UHANDLER (hdr))
359     {
360       /* read the personality routine address (address is gp-relative): */
361       if ((ret = read_mem (as, info_end_addr, &handler_offset, arg)) < 0)
362 	return ret;
363       Debug (4, "handler ptr @ offset=%lx, gp=%lx\n", handler_offset, di->gp);
364       if ((read_mem (as, handler_offset + di->gp, &pi->handler, arg)) < 0)
365 	return ret;
366     }
367   pi->lsda = info_end_addr + 8;
368   pi->gp = di->gp;
369   pi->format = di->format;
370   return 0;
371 }
372 
373 #ifndef UNW_REMOTE_ONLY
374 
375 # if defined(HAVE_DL_ITERATE_PHDR)
376 #  include <link.h>
377 #  include <stdlib.h>
378 
379 #  if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) \
380       || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && !defined(DT_CONFIG))
381 #    error You need GLIBC 2.2.4 or later on IA-64 Linux
382 #  endif
383 
384 #  if defined(HAVE_GETUNWIND)
385      extern unsigned long getunwind (void *buf, size_t len);
386 #  else /* HAVE_GETUNWIND */
387 #   include <unistd.h>
388 #   include <sys/syscall.h>
389 #   ifndef __NR_getunwind
390 #     define __NR_getunwind	1215
391 #   endif
392 
393 static unsigned long
getunwind(void * buf,size_t len)394 getunwind (void *buf, size_t len)
395 {
396   return syscall (SYS_getunwind, buf, len);
397 }
398 
399 #  endif /* HAVE_GETUNWIND */
400 
401 static unw_dyn_info_t kernel_table;
402 
403 static int
get_kernel_table(unw_dyn_info_t * di)404 get_kernel_table (unw_dyn_info_t *di)
405 {
406   struct ia64_table_entry *ktab, *etab;
407   size_t size;
408 
409   Debug (16, "getting kernel table");
410 
411   size = getunwind (NULL, 0);
412   ktab = sos_alloc (size);
413   if (!ktab)
414     {
415       Dprintf (__FILE__".%s: failed to allocate %zu bytes",
416 	       __FUNCTION__, size);
417       return -UNW_ENOMEM;
418     }
419   getunwind (ktab, size);
420 
421   /* Determine length of kernel's unwind table & relocate its entries.  */
422   for (etab = ktab; etab->start_offset; ++etab)
423     etab->info_offset += (uint64_t) ktab;
424 
425   di->format = UNW_INFO_FORMAT_TABLE;
426   di->gp = 0;
427   di->start_ip = ktab[0].start_offset;
428   di->end_ip = etab[-1].end_offset;
429   di->u.ti.name_ptr = (unw_word_t) "<kernel>";
430   di->u.ti.segbase = 0;
431   di->u.ti.table_len = ((char *) etab - (char *) ktab) / sizeof (unw_word_t);
432   di->u.ti.table_data = (unw_word_t *) ktab;
433 
434   Debug (16, "found table `%s': [%lx-%lx) segbase=%lx len=%lu\n",
435 	 (char *) di->u.ti.name_ptr, di->start_ip, di->end_ip,
436 	 di->u.ti.segbase, di->u.ti.table_len);
437   return 0;
438 }
439 
440 #  ifndef UNW_LOCAL_ONLY
441 
442 /* This is exported for the benefit of libunwind-ptrace.a.  */
443 PROTECTED int
_Uia64_get_kernel_table(unw_dyn_info_t * di)444 _Uia64_get_kernel_table (unw_dyn_info_t *di)
445 {
446   int ret;
447 
448   if (!kernel_table.u.ti.table_data)
449     if ((ret = get_kernel_table (&kernel_table)) < 0)
450       return ret;
451 
452   memcpy (di, &kernel_table, sizeof (*di));
453   return 0;
454 }
455 
456 #  endif /* !UNW_LOCAL_ONLY */
457 
458 static inline unsigned long
current_gp(void)459 current_gp (void)
460 {
461 #  if defined(__GNUC__) && !defined(__INTEL_COMPILER)
462       register unsigned long gp __asm__("gp");
463       return gp;
464 #  elif HAVE_IA64INTRIN_H
465       return __getReg (_IA64_REG_GP);
466 #  else
467 #    error Implement me.
468 #  endif
469 }
470 
471 static int
callback(struct dl_phdr_info * info,size_t size,void * ptr)472 callback (struct dl_phdr_info *info, size_t size, void *ptr)
473 {
474   unw_dyn_info_t *di = ptr;
475   const Elf64_Phdr *phdr, *p_unwind, *p_dynamic, *p_text;
476   long n;
477   Elf64_Addr load_base, segbase = 0;
478 
479   /* Make sure struct dl_phdr_info is at least as big as we need.  */
480   if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
481 	     + sizeof (info->dlpi_phnum))
482     return -1;
483 
484   Debug (16, "checking `%s' (load_base=%lx)\n",
485 	 info->dlpi_name, info->dlpi_addr);
486 
487   phdr = info->dlpi_phdr;
488   load_base = info->dlpi_addr;
489   p_text = NULL;
490   p_unwind = NULL;
491   p_dynamic = NULL;
492 
493   /* See if PC falls into one of the loaded segments.  Find the unwind
494      segment at the same time.  */
495   for (n = info->dlpi_phnum; --n >= 0; phdr++)
496     {
497       if (phdr->p_type == PT_LOAD)
498 	{
499 	  Elf64_Addr vaddr = phdr->p_vaddr + load_base;
500 	  if (di->u.ti.segbase >= vaddr
501 	      && di->u.ti.segbase < vaddr + phdr->p_memsz)
502 	    p_text = phdr;
503 	}
504       else if (phdr->p_type == PT_IA_64_UNWIND)
505 	p_unwind = phdr;
506       else if (phdr->p_type == PT_DYNAMIC)
507 	p_dynamic = phdr;
508     }
509   if (!p_text || !p_unwind)
510     return 0;
511 
512   if (likely (p_unwind->p_vaddr >= p_text->p_vaddr
513 	      && p_unwind->p_vaddr < p_text->p_vaddr + p_text->p_memsz))
514     /* normal case: unwind table is inside text segment */
515     segbase = p_text->p_vaddr + load_base;
516   else
517     {
518       /* Special case: unwind table is in some other segment; this
519 	 happens for the Linux kernel's gate DSO, for example.  */
520       phdr = info->dlpi_phdr;
521       for (n = info->dlpi_phnum; --n >= 0; phdr++)
522 	{
523 	  if (phdr->p_type == PT_LOAD && p_unwind->p_vaddr >= phdr->p_vaddr
524 	      && p_unwind->p_vaddr < phdr->p_vaddr + phdr->p_memsz)
525 	    {
526 	      segbase = phdr->p_vaddr + load_base;
527 	      break;
528 	    }
529 	}
530     }
531 
532   if (p_dynamic)
533     {
534       /* For dynamicly linked executables and shared libraries,
535 	 DT_PLTGOT is the gp value for that object.  */
536       Elf64_Dyn *dyn = (Elf64_Dyn *)(p_dynamic->p_vaddr + load_base);
537       for (; dyn->d_tag != DT_NULL; ++dyn)
538 	if (dyn->d_tag == DT_PLTGOT)
539 	  {
540 	    /* On IA-64, _DYNAMIC is writable and GLIBC has relocated it.  */
541 	    di->gp = dyn->d_un.d_ptr;
542 	    break;
543 	  }
544     }
545   else
546     /* Otherwise this is a static executable with no _DYNAMIC.
547        The gp is constant program-wide.  */
548     di->gp = current_gp();
549   di->format = UNW_INFO_FORMAT_TABLE;
550   di->start_ip = p_text->p_vaddr + load_base;
551   di->end_ip = p_text->p_vaddr + load_base + p_text->p_memsz;
552   di->u.ti.name_ptr = (unw_word_t) info->dlpi_name;
553   di->u.ti.table_data = (void *) (p_unwind->p_vaddr + load_base);
554   di->u.ti.table_len = p_unwind->p_memsz / sizeof (unw_word_t);
555   di->u.ti.segbase = segbase;
556 
557   Debug (16, "found table `%s': segbase=%lx, len=%lu, gp=%lx, "
558 	 "table_data=%p\n", (char *) di->u.ti.name_ptr, di->u.ti.segbase,
559 	 di->u.ti.table_len, di->gp, di->u.ti.table_data);
560   return 1;
561 }
562 
563 #  ifdef HAVE_DL_PHDR_REMOVALS_COUNTER
564 
565 static inline int
validate_cache(unw_addr_space_t as)566 validate_cache (unw_addr_space_t as)
567 {
568   /* Note: we don't need to serialize here with respect to
569      dl_iterate_phdr() because if somebody were to remove an object
570      that is required to complete the unwind on whose behalf we're
571      validating the cache here, we'd be hosed anyhow.  What we're
572      guarding against here is the case where library FOO gets mapped,
573      unwind info for FOO gets cached, FOO gets unmapped, BAR gets
574      mapped in the place where FOO was and then we unwind across a
575      function in FOO.  Since no thread can execute in BAR before FOO
576      has been removed, we are guaranteed that
577      dl_phdr_removals_counter() would have been incremented before we
578      get here.  */
579   unsigned long long removals = dl_phdr_removals_counter ();
580 
581   if (removals == as->shared_object_removals)
582     return 1;
583 
584   as->shared_object_removals = removals;
585   unw_flush_cache (as, 0, 0);
586   return -1;
587 }
588 
589 #  else /* !HAVE_DL_PHDR_REMOVALS_COUNTER */
590 
591 /* Check whether any phdrs have been removed since we last flushed the
592    cache.  If so we flush the cache and return -1, if not, we do
593    nothing and return 1.  */
594 
595 static int
check_callback(struct dl_phdr_info * info,size_t size,void * ptr)596 check_callback (struct dl_phdr_info *info, size_t size, void *ptr)
597 {
598 #   ifdef HAVE_STRUCT_DL_PHDR_INFO_DLPI_SUBS
599   unw_addr_space_t as = ptr;
600 
601   if (size <
602       offsetof (struct dl_phdr_info, dlpi_subs) + sizeof (info->dlpi_subs))
603     /* It would be safer to flush the cache here, but that would
604        disable caching for older libc's which would be incompatible
605        with the behavior of older versions of libunwind so we return 1
606        instead and hope nobody runs into stale cache info...  */
607     return 1;
608 
609   if (info->dlpi_subs == as->shared_object_removals)
610     return 1;
611 
612   as->shared_object_removals = info->dlpi_subs;
613   unw_flush_cache (as, 0, 0);
614   return -1;		/* indicate that there were removals */
615 #   else
616   return 1;
617 #   endif
618 }
619 
620 static inline int
validate_cache(unw_addr_space_t as)621 validate_cache (unw_addr_space_t as)
622 {
623   intrmask_t saved_mask;
624   int ret;
625 
626   SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
627   ret = dl_iterate_phdr (check_callback, as);
628   SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
629   return ret;
630 }
631 
632 #  endif /* HAVE_DL_PHDR_REMOVALS_COUNTER */
633 
634 # elif defined(HAVE_DLMODINFO)
635   /* Support for HP-UX-style dlmodinfo() */
636 #  include <dlfcn.h>
637 
638 static inline int
validate_cache(unw_addr_space_t as)639 validate_cache (unw_addr_space_t as)
640 {
641   return 1;
642 }
643 
644 # endif /* !HAVE_DLMODINFO */
645 
646 HIDDEN int
tdep_find_proc_info(unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)647 tdep_find_proc_info (unw_addr_space_t as, unw_word_t ip,
648 		     unw_proc_info_t *pi, int need_unwind_info, void *arg)
649 {
650 # if defined(HAVE_DL_ITERATE_PHDR)
651   unw_dyn_info_t di, *dip = &di;
652   intrmask_t saved_mask;
653   int ret;
654 
655   di.u.ti.segbase = ip;	/* this is cheap... */
656 
657   SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
658   ret = dl_iterate_phdr (callback, &di);
659   SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
660 
661   if (ret <= 0)
662     {
663       if (!kernel_table.u.ti.table_data)
664 	{
665 	  if ((ret = get_kernel_table (&kernel_table)) < 0)
666 	    return ret;
667 	}
668       if (ip < kernel_table.start_ip || ip >= kernel_table.end_ip)
669 	return -UNW_ENOINFO;
670       dip = &kernel_table;
671     }
672 # elif defined(HAVE_DLMODINFO)
673 # define UNWIND_TBL_32BIT	0x8000000000000000
674   struct load_module_desc lmd;
675   unw_dyn_info_t di, *dip = &di;
676   struct unwind_header
677     {
678       uint64_t header_version;
679       uint64_t start_offset;
680       uint64_t end_offset;
681     }
682   *uhdr;
683 
684   if (!dlmodinfo (ip, &lmd, sizeof (lmd), NULL, 0, 0))
685     return -UNW_ENOINFO;
686 
687   di.format = UNW_INFO_FORMAT_TABLE;
688   di.start_ip = lmd.text_base;
689   di.end_ip = lmd.text_base + lmd.text_size;
690   di.gp = lmd.linkage_ptr;
691   di.u.ti.name_ptr = 0;	/* no obvious table-name available */
692   di.u.ti.segbase = lmd.text_base;
693 
694   uhdr = (struct unwind_header *) lmd.unwind_base;
695 
696   if ((uhdr->header_version & ~UNWIND_TBL_32BIT) != 1
697       && (uhdr->header_version & ~UNWIND_TBL_32BIT) != 2)
698     {
699       Debug (1, "encountered unknown unwind header version %ld\n",
700  	     (long) (uhdr->header_version & ~UNWIND_TBL_32BIT));
701       return -UNW_EBADVERSION;
702     }
703   if (uhdr->header_version & UNWIND_TBL_32BIT)
704     {
705       Debug (1, "32-bit unwind tables are not supported yet\n");
706       return -UNW_EINVAL;
707     }
708 
709   di.u.ti.table_data = (unw_word_t *) (di.u.ti.segbase + uhdr->start_offset);
710   di.u.ti.table_len = ((uhdr->end_offset - uhdr->start_offset)
711 		       / sizeof (unw_word_t));
712 
713   Debug (16, "found table `%s': segbase=%lx, len=%lu, gp=%lx, "
714  	 "table_data=%p\n", (char *) di.u.ti.name_ptr, di.u.ti.segbase,
715  	 di.u.ti.table_len, di.gp, di.u.ti.table_data);
716 # endif
717 
718   /* now search the table: */
719   return tdep_search_unwind_table (as, ip, dip, pi, need_unwind_info, arg);
720 }
721 
722 /* Returns 1 if the cache is up-to-date or -1 if the cache contained
723    stale data and had to be flushed.  */
724 
725 HIDDEN int
ia64_local_validate_cache(unw_addr_space_t as,void * arg)726 ia64_local_validate_cache (unw_addr_space_t as, void *arg)
727 {
728   return validate_cache (as);
729 }
730 
731 #endif /* !UNW_REMOTE_ONLY */
732