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
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH 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
31static void
32free_regions (unw_dyn_region_info_t *region)
33{
34  if (region->next)
35    free_regions (region->next);
36  free (region);
37}
38
39static int
40intern_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
54static int
55intern_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
99static int
100intern_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
126static void
127free_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
153static int
154intern_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
203HIDDEN int
204unwi_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
288HIDDEN void
289unwi_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
303HIDDEN int
304unwi_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