Home | History | Annotate | Download | only in mi
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2001-2002, 2005 Hewlett-Packard Co
      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 <stdlib.h>
     27 
     28 #include "libunwind_i.h"
     29 #include "remote.h"
     30 
     31 static void
     32 free_regions (unw_dyn_region_info_t *region)
     33 {
     34   if (region->next)
     35     free_regions (region->next);
     36   free (region);
     37 }
     38 
     39 static int
     40 intern_op (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr,
     41 	   unw_dyn_op_t *op, void *arg)
     42 {
     43   int ret;
     44 
     45   if ((ret = fetch8 (as, a, addr, &op->tag, arg)) < 0
     46       || (ret = fetch8 (as, a, addr, &op->qp, arg)) < 0
     47       || (ret = fetch16 (as, a, addr, &op->reg, arg)) < 0
     48       || (ret = fetch32 (as, a, addr, &op->when, arg)) < 0
     49       || (ret = fetchw  (as, a, addr, &op->val, arg)) < 0)
     50     return ret;
     51   return 0;
     52 }
     53 
     54 static int
     55 intern_regions (unw_addr_space_t as, unw_accessors_t *a,
     56 		unw_word_t *addr, unw_dyn_region_info_t **regionp, void *arg)
     57 {
     58   uint32_t insn_count, op_count, i;
     59   unw_dyn_region_info_t *region;
     60   unw_word_t next_addr;
     61   int ret;
     62 
     63   *regionp = NULL;
     64 
     65   if (!*addr)
     66     return 0;	/* NULL region-list */
     67 
     68   if ((ret = fetchw (as, a, addr, &next_addr, arg)) < 0
     69       || (ret = fetch32 (as, a, addr, (int32_t *) &insn_count, arg)) < 0
     70       || (ret = fetch32 (as, a, addr, (int32_t *) &op_count, arg)) < 0)
     71     return ret;
     72 
     73   region = calloc (1, _U_dyn_region_info_size (op_count));
     74   if (!region)
     75     {
     76       ret = -UNW_ENOMEM;
     77       goto out;
     78     }
     79 
     80   region->insn_count = insn_count;
     81   region->op_count = op_count;
     82   for (i = 0; i < op_count; ++i)
     83     if ((ret = intern_op (as, a, addr, region->op + i, arg)) < 0)
     84       goto out;
     85 
     86   if (next_addr)
     87     if ((ret = intern_regions (as, a, &next_addr, &region->next, arg)) < 0)
     88       goto out;
     89 
     90   *regionp = region;
     91   return 0;
     92 
     93  out:
     94   if (region)
     95     free_regions (region);
     96   return ret;
     97 }
     98 
     99 static int
    100 intern_array (unw_addr_space_t as, unw_accessors_t *a,
    101 	      unw_word_t *addr, unw_word_t table_len, unw_word_t **table_data,
    102 	      void *arg)
    103 {
    104   unw_word_t i, *data = calloc (table_len, WSIZE);
    105   int ret = 0;
    106 
    107   if (!data)
    108     {
    109       ret = -UNW_ENOMEM;
    110       goto out;
    111     }
    112 
    113   for (i = 0; i < table_len; ++i)
    114     if (fetchw (as, a, addr, data + i, arg) < 0)
    115       goto out;
    116 
    117   *table_data = data;
    118   return 0;
    119 
    120  out:
    121   if (data)
    122     free (data);
    123   return ret;
    124 }
    125 
    126 static void
    127 free_dyn_info (unw_dyn_info_t *di)
    128 {
    129   switch (di->format)
    130     {
    131     case UNW_INFO_FORMAT_DYNAMIC:
    132       if (di->u.pi.regions)
    133 	{
    134 	  free_regions (di->u.pi.regions);
    135 	  di->u.pi.regions = NULL;
    136 	}
    137       break;
    138 
    139     case UNW_INFO_FORMAT_TABLE:
    140       if (di->u.ti.table_data)
    141 	{
    142 	  free (di->u.ti.table_data);
    143 	  di->u.ti.table_data = NULL;
    144 	}
    145       break;
    146 
    147     case UNW_INFO_FORMAT_REMOTE_TABLE:
    148     default:
    149       break;
    150     }
    151 }
    152 
    153 static int
    154 intern_dyn_info (unw_addr_space_t as, unw_accessors_t *a,
    155 		 unw_word_t *addr, unw_dyn_info_t *di, void *arg)
    156 {
    157   unw_word_t first_region;
    158   int ret;
    159 
    160   switch (di->format)
    161     {
    162     case UNW_INFO_FORMAT_DYNAMIC:
    163       if ((ret = fetchw (as, a, addr, &di->u.pi.name_ptr, arg)) < 0
    164 	  || (ret = fetchw (as, a, addr, &di->u.pi.handler, arg)) < 0
    165 	  || (ret = fetch32 (as, a, addr,
    166 			     (int32_t *) &di->u.pi.flags, arg)) < 0)
    167 	goto out;
    168       *addr += 4;	/* skip over pad0 */
    169       if ((ret = fetchw (as, a, addr, &first_region, arg)) < 0
    170 	  || (ret = intern_regions (as, a, &first_region, &di->u.pi.regions,
    171 				    arg)) < 0)
    172 	goto out;
    173       break;
    174 
    175     case UNW_INFO_FORMAT_TABLE:
    176       if ((ret = fetchw (as, a, addr, &di->u.ti.name_ptr, arg)) < 0
    177 	  || (ret = fetchw (as, a, addr, &di->u.ti.segbase, arg)) < 0
    178 	  || (ret = fetchw (as, a, addr, &di->u.ti.table_len, arg)) < 0
    179 	  || (ret = intern_array (as, a, addr, di->u.ti.table_len,
    180 				  &di->u.ti.table_data, arg)) < 0)
    181 	goto out;
    182       break;
    183 
    184     case UNW_INFO_FORMAT_REMOTE_TABLE:
    185       if ((ret = fetchw (as, a, addr, &di->u.rti.name_ptr, arg)) < 0
    186 	  || (ret = fetchw (as, a, addr, &di->u.rti.segbase, arg)) < 0
    187 	  || (ret = fetchw (as, a, addr, &di->u.rti.table_len, arg)) < 0
    188 	  || (ret = fetchw (as, a, addr, &di->u.rti.table_data, arg)) < 0)
    189 	goto out;
    190       break;
    191 
    192     default:
    193       ret = -UNW_ENOINFO;
    194       goto out;
    195     }
    196   return 0;
    197 
    198  out:
    199   free_dyn_info (di);
    200   return ret;
    201 }
    202 
    203 HIDDEN int
    204 unwi_dyn_remote_find_proc_info (unw_addr_space_t as, unw_word_t ip,
    205 				unw_proc_info_t *pi,
    206 				int need_unwind_info, void *arg)
    207 {
    208   unw_accessors_t *a = unw_get_accessors (as);
    209   unw_word_t dyn_list_addr, addr, next_addr, gen1, gen2, start_ip, end_ip;
    210   unw_dyn_info_t *di = NULL;
    211   int ret;
    212 
    213   if (as->dyn_info_list_addr)
    214     dyn_list_addr = as->dyn_info_list_addr;
    215   else
    216     {
    217       if ((*a->get_dyn_info_list_addr) (as, &dyn_list_addr, arg) < 0)
    218 	return -UNW_ENOINFO;
    219       if (as->caching_policy != UNW_CACHE_NONE)
    220 	as->dyn_info_list_addr = dyn_list_addr;
    221     }
    222 
    223   do
    224     {
    225       addr = dyn_list_addr;
    226 
    227       ret = -UNW_ENOINFO;
    228 
    229       if (fetchw (as, a, &addr, &gen1, arg) < 0
    230 	  || fetchw (as, a, &addr, &next_addr, arg) < 0)
    231 	return ret;
    232 
    233       for (addr = next_addr; addr != 0; addr = next_addr)
    234 	{
    235 	  if (fetchw (as, a, &addr, &next_addr, arg) < 0)
    236 	    goto recheck;	/* only fail if generation # didn't change */
    237 
    238 	  addr += WSIZE;	/* skip over prev_addr */
    239 
    240 	  if (fetchw (as, a, &addr, &start_ip, arg) < 0
    241 	      || fetchw (as, a, &addr, &end_ip, arg) < 0)
    242 	    goto recheck;	/* only fail if generation # didn't change */
    243 
    244 	  if (ip >= start_ip && ip < end_ip)
    245 	    {
    246 	      if (!di)
    247 		di = calloc (1, sizeof (*di));
    248 
    249 	      di->start_ip = start_ip;
    250 	      di->end_ip = end_ip;
    251 
    252 	      if (fetchw (as, a, &addr, &di->gp, arg) < 0
    253 		  || fetch32 (as, a, &addr, &di->format, arg) < 0)
    254 		goto recheck;	/* only fail if generation # didn't change */
    255 
    256 	      addr += 4;	/* skip over padding */
    257 
    258 	      if (need_unwind_info
    259 		  && intern_dyn_info (as, a, &addr, di, arg) < 0)
    260 		goto recheck;	/* only fail if generation # didn't change */
    261 
    262 	      if (unwi_extract_dynamic_proc_info (as, ip, pi, di,
    263 						  need_unwind_info, arg) < 0)
    264 		{
    265 		  free_dyn_info (di);
    266 		  goto recheck;	/* only fail if generation # didn't change */
    267 		}
    268 	      ret = 0;	/* OK, found it */
    269 	      break;
    270 	    }
    271 	}
    272 
    273       /* Re-check generation number to ensure the data we have is
    274 	 consistent.  */
    275     recheck:
    276       addr = dyn_list_addr;
    277       if (fetchw (as, a, &addr, &gen2, arg) < 0)
    278 	return ret;
    279     }
    280   while (gen1 != gen2);
    281 
    282   if (ret < 0 && di)
    283     free (di);
    284 
    285   return ret;
    286 }
    287 
    288 HIDDEN void
    289 unwi_dyn_remote_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi,
    290 				 void *arg)
    291 {
    292   if (!pi->unwind_info)
    293     return;
    294 
    295   free_dyn_info (pi->unwind_info);
    296   free (pi->unwind_info);
    297   pi->unwind_info = NULL;
    298 }
    299 
    300 /* Returns 1 if the cache is up-to-date or -1 if the cache contained
    301    stale data and had to be flushed.  */
    302 
    303 HIDDEN int
    304 unwi_dyn_validate_cache (unw_addr_space_t as, void *arg)
    305 {
    306   unw_word_t addr, gen;
    307   unw_accessors_t *a;
    308 
    309   if (!as->dyn_info_list_addr)
    310     /* If we don't have the dyn_info_list_addr, we don't have anything
    311        in the cache.  */
    312     return 0;
    313 
    314   a = unw_get_accessors (as);
    315   addr = as->dyn_info_list_addr;
    316 
    317   if (fetchw (as, a, &addr, &gen, arg) < 0)
    318     return 1;
    319 
    320   if (gen == as->dyn_generation)
    321     return 1;
    322 
    323   unw_flush_cache (as, 0, 0);
    324   as->dyn_generation = gen;
    325   return -1;
    326 }
    327