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 (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 (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