Home | History | Annotate | Download | only in ia64
      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 (at) 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
     48 is_local_addr_space (unw_addr_space_t as)
     49 {
     50   return 1;
     51 }
     52 
     53 static inline int
     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
     63 is_local_addr_space (unw_addr_space_t as)
     64 {
     65   return as == unw_local_addr_space;
     66 }
     67 
     68 static inline int
     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
     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
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    639 validate_cache (unw_addr_space_t as)
    640 {
    641   return 1;
    642 }
    643 
    644 # endif /* !HAVE_DLMODINFO */
    645 
    646 HIDDEN int
    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
    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