Initial import
[external/libunwind.git] / src / mi / Gdyn-remote.c
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
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 }