• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2001-2002, 2005 Hewlett-Packard Co
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 <stdlib.h>
27 
28 #include "libunwind_i.h"
29 #include "remote.h"
30 
31 static void
free_regions(unw_dyn_region_info_t * region)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
intern_op(unw_addr_space_t as,unw_accessors_t * a,unw_word_t * addr,unw_dyn_op_t * op,void * arg)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
intern_regions(unw_addr_space_t as,unw_accessors_t * a,unw_word_t * addr,unw_dyn_region_info_t ** regionp,void * arg)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
intern_array(unw_addr_space_t as,unw_accessors_t * a,unw_word_t * addr,unw_word_t table_len,unw_word_t ** table_data,void * arg)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
free_dyn_info(unw_dyn_info_t * di)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
intern_dyn_info(unw_addr_space_t as,unw_accessors_t * a,unw_word_t * addr,unw_dyn_info_t * di,void * arg)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
unwi_dyn_remote_find_proc_info(unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)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_int (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
unwi_dyn_remote_put_unwind_info(unw_addr_space_t as,unw_proc_info_t * pi,void * arg)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
unwi_dyn_validate_cache(unw_addr_space_t as,void * arg)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_int (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