blob: 3969cb3a71cb9762a22ce6578e29399d29696e2a [file] [log] [blame]
homeip.net!davidm88160e02004-08-17 15:34:28 +00001/* libunwind - a platform-independent unwind library
hp.com!davidmdf3d6af2005-05-20 09:48:08 +00002 Copyright (c) 2003, 2005 Hewlett-Packard Development Company, L.P.
homeip.net!davidm88160e02004-08-17 15:34:28 +00003 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
Arun Sharma00db7f72006-07-26 21:18:49 -060026#include <stddef.h>
homeip.net!davidm88160e02004-08-17 15:34:28 +000027#include "dwarf_i.h"
hp.com!davidmdf3d6af2005-05-20 09:48:08 +000028#include "libunwind_i.h"
homeip.net!davidm88160e02004-08-17 15:34:28 +000029
30#define alloc_reg_state() (mempool_alloc (&dwarf_reg_state_pool))
31#define free_reg_state(rs) (mempool_free (&dwarf_reg_state_pool, rs))
32
33static inline int
34read_regnum (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr,
35 unw_word_t *valp, void *arg)
36{
37 int ret;
38
39 if ((ret = dwarf_read_uleb128 (as, a, addr, valp, arg)) < 0)
40 return ret;
41
42 if (*valp >= DWARF_NUM_PRESERVED_REGS)
43 {
homeip.net!davidm642607d2004-08-20 11:23:15 +000044 Debug (1, "Invalid register number %u\n", (unsigned int) *valp);
homeip.net!davidm88160e02004-08-17 15:34:28 +000045 return -UNW_EBADREG;
46 }
47 return 0;
48}
49
50static inline void
51set_reg (dwarf_state_record_t *sr, unw_word_t regnum, dwarf_where_t where,
52 unw_word_t val)
53{
54 sr->rs_current.reg[regnum].where = where;
55 sr->rs_current.reg[regnum].val = val;
56}
57
58/* Run a CFI program to update the register state. */
59static int
60run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr,
61 unw_word_t ip, unw_word_t *addr, unw_word_t end_addr,
mostang.com!davidmc1437142005-05-03 09:13:17 +000062 struct dwarf_cie_info *dci)
homeip.net!davidm88160e02004-08-17 15:34:28 +000063{
64 unw_word_t curr_ip, operand = 0, regnum, val, len, fde_encoding;
65 dwarf_reg_state_t *rs_stack = NULL, *new_rs, *old_rs;
66 unw_addr_space_t as;
67 unw_accessors_t *a;
68 uint8_t u8, op;
69 uint16_t u16;
70 uint32_t u32;
71 void *arg;
72 int ret;
73
74 as = c->as;
75 arg = c->as_arg;
76 a = unw_get_accessors (as);
77 curr_ip = c->pi.start_ip;
78
79 while (curr_ip < ip && *addr < end_addr)
80 {
81 if ((ret = dwarf_readu8 (as, a, addr, &op, arg)) < 0)
82 return ret;
83
84 if (op & DWARF_CFA_OPCODE_MASK)
85 {
86 operand = op & DWARF_CFA_OPERAND_MASK;
87 op &= ~DWARF_CFA_OPERAND_MASK;
88 }
89 switch ((dwarf_cfa_t) op)
90 {
91 case DW_CFA_advance_loc:
mostang.com!davidmc1437142005-05-03 09:13:17 +000092 curr_ip += operand * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +000093 Debug (15, "CFA_advance_loc to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +000094 break;
95
96 case DW_CFA_advance_loc1:
97 if ((ret = dwarf_readu8 (as, a, addr, &u8, arg)) < 0)
98 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +000099 curr_ip += u8 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000100 Debug (15, "CFA_advance_loc1 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000101 break;
102
103 case DW_CFA_advance_loc2:
104 if ((ret = dwarf_readu16 (as, a, addr, &u16, arg)) < 0)
105 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000106 curr_ip += u16 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000107 Debug (15, "CFA_advance_loc2 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000108 break;
109
110 case DW_CFA_advance_loc4:
111 if ((ret = dwarf_readu32 (as, a, addr, &u32, arg)) < 0)
112 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000113 curr_ip += u32 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000114 Debug (15, "CFA_advance_loc4 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000115 break;
116
117 case DW_CFA_MIPS_advance_loc8:
118#ifdef UNW_TARGET_MIPS
119 {
120 uint64_t u64;
121
122 if ((ret = dwarf_readu64 (as, a, addr, &u64, arg)) < 0)
123 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000124 curr_ip += u64 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000125 Debug (15, "CFA_MIPS_advance_loc8\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000126 break;
127 }
128#else
129 Debug (1, "DW_CFA_MIPS_advance_loc8 on non-MIPS target\n");
130 ret = -UNW_EINVAL;
131 goto fail;
132#endif
133
134 case DW_CFA_offset:
135 regnum = operand;
136 if (regnum >= DWARF_NUM_PRESERVED_REGS)
137 {
138 Debug (1, "Invalid register number %u in DW_cfa_OFFSET\n",
homeip.net!davidm642607d2004-08-20 11:23:15 +0000139 (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000140 ret = -UNW_EBADREG;
141 goto fail;
142 }
143 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
144 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000145 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000146 Debug (15, "CFA_offset r%lu at cfa+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000147 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000148 break;
149
150 case DW_CFA_offset_extended:
151 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
152 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
153 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000154 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000155 Debug (15, "CFA_offset_extended r%lu at cf+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000156 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000157 break;
158
159 case DW_CFA_offset_extended_sf:
160 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
161 || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
162 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000163 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000164 Debug (15, "CFA_offset_extended_sf r%lu at cf+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000165 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000166 break;
167
168 case DW_CFA_restore:
169 regnum = operand;
170 if (regnum >= DWARF_NUM_PRESERVED_REGS)
171 {
172 Debug (1, "Invalid register number %u in DW_CFA_restore\n",
homeip.net!davidm642607d2004-08-20 11:23:15 +0000173 (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000174 ret = -UNW_EINVAL;
175 goto fail;
176 }
177 sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000178 Debug (15, "CFA_restore r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000179 break;
180
181 case DW_CFA_restore_extended:
182 if ((ret = dwarf_read_uleb128 (as, a, addr, &regnum, arg)) < 0)
183 goto fail;
184 if (regnum >= DWARF_NUM_PRESERVED_REGS)
185 {
186 Debug (1, "Invalid register number %u in "
homeip.net!davidm642607d2004-08-20 11:23:15 +0000187 "DW_CFA_restore_extended\n", (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000188 ret = -UNW_EINVAL;
189 goto fail;
190 }
191 sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000192 Debug (15, "CFA_restore_extended r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000193 break;
194
195 case DW_CFA_nop:
196 break;
197
198 case DW_CFA_set_loc:
mostang.com!davidmc1437142005-05-03 09:13:17 +0000199 fde_encoding = dci->fde_encoding;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000200 if ((ret = dwarf_read_encoded_pointer (as, a, addr, fde_encoding,
201 &c->pi, &curr_ip,
202 arg)) < 0)
203 goto fail;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000204 Debug (15, "CFA_set_loc to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000205 break;
206
207 case DW_CFA_undefined:
208 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
209 goto fail;
210 set_reg (sr, regnum, DWARF_WHERE_UNDEF, 0);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000211 Debug (15, "CFA_undefined r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000212 break;
213
214 case DW_CFA_same_value:
215 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
216 goto fail;
217 set_reg (sr, regnum, DWARF_WHERE_SAME, 0);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000218 Debug (15, "CFA_same_value r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000219 break;
220
221 case DW_CFA_register:
222 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
223 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
224 goto fail;
225 set_reg (sr, regnum, DWARF_WHERE_REG, val);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000226 Debug (15, "CFA_register r%lu to r%lu\n", (long) regnum, (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000227 break;
228
229 case DW_CFA_remember_state:
230 new_rs = alloc_reg_state ();
231 if (!new_rs)
232 {
233 Debug (1, "Out of memory in DW_CFA_remember_state\n");
234 ret = -UNW_ENOMEM;
235 goto fail;
236 }
237
238 memcpy (new_rs->reg, sr->rs_current.reg, sizeof (new_rs->reg));
239 new_rs->next = rs_stack;
240 rs_stack = new_rs;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000241 Debug (15, "CFA_remember_state\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000242 break;
243
244 case DW_CFA_restore_state:
245 if (!rs_stack)
246 {
247 Debug (1, "register-state stack underflow\n");
248 ret = -UNW_EINVAL;
249 goto fail;
250 }
251 memcpy (&sr->rs_current.reg, &rs_stack->reg, sizeof (rs_stack->reg));
252 old_rs = rs_stack;
253 rs_stack = rs_stack->next;
254 free_reg_state (old_rs);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000255 Debug (15, "CFA_restore_state\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000256 break;
257
258 case DW_CFA_def_cfa:
259 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
260 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
261 goto fail;
262 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
263 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000264 Debug (15, "CFA_def_cfa r%lu+0x%lx\n", (long) regnum, (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000265 break;
266
267 case DW_CFA_def_cfa_sf:
268 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
269 || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
270 goto fail;
271 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
272 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000273 val * dci->data_align); /* factored! */
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000274 Debug (15, "CFA_def_cfa_sf r%lu+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000275 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000276 break;
277
278 case DW_CFA_def_cfa_register:
279 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
280 goto fail;
281 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000282 Debug (15, "CFA_def_cfa_register r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000283 break;
284
285 case DW_CFA_def_cfa_offset:
286 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
287 goto fail;
288 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */
mostang.com!davidmc1437142005-05-03 09:13:17 +0000289 Debug (15, "CFA_def_cfa_offset 0x%lx\n", (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000290 break;
291
292 case DW_CFA_def_cfa_offset_sf:
293 if ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0)
294 goto fail;
295 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000296 val * dci->data_align); /* factored! */
297 Debug (15, "CFA_def_cfa_offset_sf 0x%lx\n",
298 (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000299 break;
300
301 case DW_CFA_def_cfa_expression:
302 /* Save the address of the DW_FORM_block for later evaluation. */
303 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPR, *addr);
304
305 if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
306 goto fail;
307
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000308 Debug (15, "CFA_def_cfa_expr @ 0x%lx [%lu bytes]\n",
homeip.net!davidm88160e02004-08-17 15:34:28 +0000309 (long) *addr, (long) len);
310 *addr += len;
311 break;
312
mostang.com!davidmc1437142005-05-03 09:13:17 +0000313 case DW_CFA_expression:
homeip.net!davidm88160e02004-08-17 15:34:28 +0000314 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
315 goto fail;
316
317 /* Save the address of the DW_FORM_block for later evaluation. */
318 set_reg (sr, regnum, DWARF_WHERE_EXPR, *addr);
319
320 if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
321 goto fail;
322
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000323 Debug (15, "CFA_expression r%lu @ 0x%lx [%lu bytes]\n",
homeip.net!davidm88160e02004-08-17 15:34:28 +0000324 (long) regnum, (long) addr, (long) len);
325 *addr += len;
326 break;
327
328 case DW_CFA_GNU_args_size:
329 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
330 goto fail;
331 sr->args_size = val;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000332 Debug (15, "CFA_GNU_args_size %lu\n", (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000333 break;
334
335 case DW_CFA_GNU_negative_offset_extended:
336 /* A comment in GCC says that this is obsoleted by
337 DW_CFA_offset_extended_sf, but that it's used by older
338 PowerPC code. */
339 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
340 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
341 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000342 set_reg (sr, regnum, DWARF_WHERE_CFAREL, -(val * dci->data_align));
343 Debug (15, "CFA_GNU_negative_offset_extended cfa+0x%lx\n",
344 (long) -(val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000345 break;
346
347 case DW_CFA_GNU_window_save:
348#ifdef UNW_TARGET_SPARC
349 /* This is a special CFA to handle all 16 windowed registers
350 on SPARC. */
351 for (regnum = 16; regnum < 32; ++regnum)
352 set_reg (sr, regnum, DWARF_WHERE_CFAREL,
353 (regnum - 16) * sizeof (unw_word_t));
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000354 Debug (15, "CFA_GNU_window_save\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000355 break;
356#else
357 /* FALL THROUGH */
358#endif
359 case DW_CFA_lo_user:
360 case DW_CFA_hi_user:
mostang.com!davidmc1437142005-05-03 09:13:17 +0000361 Debug (1, "Unexpected CFA opcode 0x%x\n", op);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000362 ret = -UNW_EINVAL;
363 goto fail;
364 }
365 }
366 ret = 0;
367
368 fail:
369 /* Free the register-state stack, if not empty already. */
370 while (rs_stack)
371 {
372 old_rs = rs_stack;
373 rs_stack = rs_stack->next;
374 free_reg_state (old_rs);
375 }
376 return ret;
377}
378
379static int
380fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info)
381{
382 int ret, dynamic = 1;
383
mostang.com!davidmc1437142005-05-03 09:13:17 +0000384 --ip;
385
homeip.net!davidm88160e02004-08-17 15:34:28 +0000386 if (c->pi_valid && !need_unwind_info)
387 return 0;
388
mostang.com!davidmc1437142005-05-03 09:13:17 +0000389 memset (&c->pi, 0, sizeof (c->pi));
390
homeip.net!davidm88160e02004-08-17 15:34:28 +0000391 /* check dynamic info first --- it overrides everything else */
392 ret = unwi_find_dynamic_proc_info (c->as, ip, &c->pi, need_unwind_info,
393 c->as_arg);
394 if (ret == -UNW_ENOINFO)
395 {
396 dynamic = 0;
397 if ((ret = tdep_find_proc_info (c, ip, need_unwind_info)) < 0)
398 return ret;
399 }
400
401 c->pi_valid = 1;
402 c->pi_is_dynamic = dynamic;
403 return ret;
404}
405
406static int
407parse_dynamic (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr)
408{
409 Debug (1, "Not yet implemented\n");
410#if 0
411 /* Don't forget to set the ret_addr_column! */
412 c->ret_addr_column = XXX;
413#endif
414 return -UNW_ENOINFO;
415}
416
417static inline void
418put_unwind_info (struct dwarf_cursor *c, unw_proc_info_t *pi)
419{
420 if (!c->pi_valid)
421 return;
422
423 if (c->pi_is_dynamic)
424 unwi_put_dynamic_unwind_info (c->as, pi, c->as_arg);
mostang.com!davidmc1437142005-05-03 09:13:17 +0000425 else if (pi->unwind_info);
426 {
427 mempool_free (&dwarf_cie_info_pool, pi->unwind_info);
428 pi->unwind_info = NULL;
429 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000430}
431
432static inline int
433parse_fde (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr)
434{
mostang.com!davidmc1437142005-05-03 09:13:17 +0000435 struct dwarf_cie_info *dci;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000436 unw_word_t addr;
437 int ret;
438
mostang.com!davidmc1437142005-05-03 09:13:17 +0000439 dci = c->pi.unwind_info;
440 c->ret_addr_column = dci->ret_addr_column;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000441
mostang.com!davidmc1437142005-05-03 09:13:17 +0000442 addr = dci->cie_instr_start;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000443 if ((ret = run_cfi_program (c, sr, ~(unw_word_t) 0, &addr,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000444 dci->cie_instr_end, dci)) < 0)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000445 return ret;
446
447 memcpy (&sr->rs_initial, &sr->rs_current, sizeof (sr->rs_initial));
448
mostang.com!davidmc1437142005-05-03 09:13:17 +0000449 addr = dci->fde_instr_start;
450 if ((ret = run_cfi_program (c, sr, ip, &addr, dci->fde_instr_end, dci)) < 0)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000451 return ret;
452
453 return 0;
454}
455
Arun Sharma00db7f72006-07-26 21:18:49 -0600456static inline void
457flush_rs_cache (struct dwarf_rs_cache *cache)
458{
459 int i;
460
461 cache->lru_head = DWARF_UNW_CACHE_SIZE - 1;
462 cache->lru_tail = 0;
463
464 for (i = 0; i < DWARF_UNW_CACHE_SIZE; ++i)
465 {
466 if (i > 0)
467 cache->buckets[i].lru_chain = (i - 1);
468 cache->buckets[i].coll_chain = -1;
469 cache->buckets[i].ip = 0;
470 }
471 for (i = 0; i<DWARF_UNW_HASH_SIZE; ++i)
472 cache->hash[i] = -1;
473}
474
Arun Sharma43127192006-07-26 21:44:38 -0600475static inline struct dwarf_rs_cache *
476get_rs_cache (unw_addr_space_t as, intrmask_t *saved_maskp)
Arun Sharma00db7f72006-07-26 21:18:49 -0600477{
478 struct dwarf_rs_cache *cache = &as->global_cache;
Arun Sharma43127192006-07-26 21:44:38 -0600479 unw_caching_policy_t caching = as->caching_policy;
480
481 if (caching == UNW_CACHE_NONE)
482 return NULL;
483
484#ifdef HAVE_ATOMIC_H
485 if (!spin_trylock_irqsave (&cache->busy, *saved_maskp))
486 return NULL;
487#else
Arun Sharma43127192006-07-26 21:44:38 -0600488# ifdef HAVE_ATOMIC_OPS_H
489 if (AO_test_and_set (&cache->busy) == AO_TS_SET)
490 return NULL;
491# else
Arun Sharma43127192006-07-26 21:44:38 -0600492 if (likely (caching == UNW_CACHE_GLOBAL))
493 {
494 Debug (16, "%s: acquiring lock\n", __FUNCTION__);
Paul Pluzhnikov84d41502009-09-21 12:02:07 -0700495 lock_acquire (&cache->lock, *saved_maskp);
Arun Sharma43127192006-07-26 21:44:38 -0600496 }
497# endif
498#endif
499
Arun Sharma00db7f72006-07-26 21:18:49 -0600500 if (atomic_read (&as->cache_generation) != atomic_read (&cache->generation))
501 {
502 flush_rs_cache (cache);
503 cache->generation = as->cache_generation;
504 }
505
506 return cache;
507}
508
Arun Sharma43127192006-07-26 21:44:38 -0600509static inline void
510put_rs_cache (unw_addr_space_t as, struct dwarf_rs_cache *cache,
511 intrmask_t *saved_maskp)
512{
513 assert (as->caching_policy != UNW_CACHE_NONE);
514
515 Debug (16, "unmasking signals/interrupts and releasing lock\n");
516#ifdef HAVE_ATOMIC_H
517 spin_unlock_irqrestore (&cache->busy, *saved_maskp);
518#else
519# ifdef HAVE_ATOMIC_OPS_H
520 AO_CLEAR (&cache->busy);
521# else
522 if (likely (as->caching_policy == UNW_CACHE_GLOBAL))
Paul Pluzhnikov84d41502009-09-21 12:02:07 -0700523 lock_release (&cache->lock, *saved_maskp);
Arun Sharma43127192006-07-26 21:44:38 -0600524# endif
525#endif
526}
527
Arun Sharma00db7f72006-07-26 21:18:49 -0600528static inline unw_hash_index_t
529hash (unw_word_t ip)
530{
531 /* based on (sqrt(5)/2-1)*2^64 */
532# define magic ((unw_word_t) 0x9e3779b97f4a7c16ULL)
533
Arun Sharma55fe5242006-08-16 15:43:49 -0600534 return ip * magic >> ((sizeof(unw_word_t) * 8) - DWARF_LOG_UNW_HASH_SIZE);
Arun Sharma00db7f72006-07-26 21:18:49 -0600535}
536
537static inline long
538cache_match (dwarf_reg_state_t *rs, unw_word_t ip)
539{
540 if (ip == rs->ip)
541 return 1;
542 return 0;
543}
544
545static dwarf_reg_state_t *
546rs_lookup (struct dwarf_rs_cache *cache, struct dwarf_cursor *c)
547{
548 dwarf_reg_state_t *rs = cache->buckets + c->hint;
549 unsigned short index;
550 unw_word_t ip;
551
552 ip = c->ip;
553
554 if (cache_match (rs, ip))
555 return rs;
556
557 index = cache->hash[hash (ip)];
558 if (index >= DWARF_UNW_CACHE_SIZE)
559 return 0;
560
561 rs = cache->buckets + index;
562 while (1)
563 {
564 if (cache_match (rs, ip))
565 {
566 /* update hint; no locking needed: single-word writes are atomic */
567 c->hint = cache->buckets[c->prev_rs].hint =
568 (rs - cache->buckets);
569 return rs;
570 }
571 if (rs->coll_chain >= DWARF_UNW_HASH_SIZE)
572 return 0;
573 rs = cache->buckets + rs->coll_chain;
574 }
575}
576
577static inline dwarf_reg_state_t *
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600578rs_new (struct dwarf_rs_cache *cache, struct dwarf_cursor * c)
Arun Sharma00db7f72006-07-26 21:18:49 -0600579{
580 dwarf_reg_state_t *rs, *prev, *tmp;
581 unw_hash_index_t index;
582 unsigned short head;
583
584 head = cache->lru_head;
585 rs = cache->buckets + head;
586 cache->lru_head = rs->lru_chain;
587
588 /* re-insert rs at the tail of the LRU chain: */
589 cache->buckets[cache->lru_tail].lru_chain = head;
590 cache->lru_tail = head;
591
592 /* remove the old rs from the hash table (if it's there): */
593 if (rs->ip)
594 {
595 index = hash (rs->ip);
596 tmp = cache->buckets + cache->hash[index];
597 prev = 0;
598 while (1)
599 {
600 if (tmp == rs)
601 {
602 if (prev)
603 prev->coll_chain = tmp->coll_chain;
604 else
605 cache->hash[index] = tmp->coll_chain;
606 break;
607 }
608 else
609 prev = tmp;
610 if (tmp->coll_chain >= DWARF_UNW_CACHE_SIZE)
611 /* old rs wasn't in the hash-table */
612 break;
613 tmp = cache->buckets + tmp->coll_chain;
614 }
615 }
616
617 /* enter new rs in the hash table */
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600618 index = hash (c->ip);
Arun Sharma00db7f72006-07-26 21:18:49 -0600619 rs->coll_chain = cache->hash[index];
620 cache->hash[index] = rs - cache->buckets;
621
622 rs->hint = 0;
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600623 rs->ip = c->ip;
624 rs->ret_addr_column = c->ret_addr_column;
Arun Sharma00db7f72006-07-26 21:18:49 -0600625
626 return rs;
627}
628
homeip.net!davidm88160e02004-08-17 15:34:28 +0000629static int
630create_state_record_for (struct dwarf_cursor *c, dwarf_state_record_t *sr,
631 unw_word_t ip)
632{
633 int i, ret;
634
635 assert (c->pi_valid);
636
mostang.com!davidmc1437142005-05-03 09:13:17 +0000637 memset (sr, 0, sizeof (*sr));
638 for (i = 0; i < DWARF_NUM_PRESERVED_REGS + 2; ++i)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000639 set_reg (sr, i, DWARF_WHERE_SAME, 0);
640
641 switch (c->pi.format)
642 {
mostang.com!davidmc1437142005-05-03 09:13:17 +0000643 case UNW_INFO_FORMAT_TABLE:
644 case UNW_INFO_FORMAT_REMOTE_TABLE:
homeip.net!davidm88160e02004-08-17 15:34:28 +0000645 ret = parse_fde (c, ip, sr);
646 break;
647
648 case UNW_INFO_FORMAT_DYNAMIC:
649 ret = parse_dynamic (c, ip, sr);
650 break;
651
homeip.net!davidm88160e02004-08-17 15:34:28 +0000652 default:
653 Debug (1, "Unexpected unwind-info format %d\n", c->pi.format);
654 ret = -UNW_EINVAL;
655 }
656 return ret;
657}
658
659static inline int
660eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as,
661 unw_accessors_t *a, unw_word_t addr,
662 dwarf_loc_t *locp, void *arg)
663{
664 int ret, is_register;
665 unw_word_t len, val;
666
667 /* read the length of the expression: */
668 if ((ret = dwarf_read_uleb128 (as, a, &addr, &len, arg)) < 0)
669 return ret;
670
671 /* evaluate the expression: */
672 if ((ret = dwarf_eval_expr (c, &addr, len, &val, &is_register)) < 0)
673 return ret;
674
675 if (is_register)
676 *locp = DWARF_REG_LOC (c, dwarf_to_unw_regnum (val));
677 else
678 *locp = DWARF_MEM_LOC (c, val);
679
680 return 0;
681}
682
683static int
684apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
685{
Arun Sharma00db7f72006-07-26 21:18:49 -0600686 unw_word_t regnum, addr, cfa, ip;
687 unw_word_t prev_ip, prev_cfa;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000688 unw_addr_space_t as;
689 dwarf_loc_t cfa_loc;
690 unw_accessors_t *a;
691 int i, ret;
692 void *arg;
693
Arun Sharma00db7f72006-07-26 21:18:49 -0600694 prev_ip = c->ip;
695 prev_cfa = c->cfa;
696
homeip.net!davidm88160e02004-08-17 15:34:28 +0000697 as = c->as;
698 arg = c->as_arg;
699 a = unw_get_accessors (as);
700
mostang.com!davidmc1437142005-05-03 09:13:17 +0000701 /* Evaluate the CFA first, because it may be referred to by other
homeip.net!davidm88160e02004-08-17 15:34:28 +0000702 expressions. */
703
704 if (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG)
705 {
706 /* CFA is equal to [reg] + offset: */
707
homeip.net!davidm642607d2004-08-20 11:23:15 +0000708 /* As a special-case, if the stack-pointer is the CFA and the
709 stack-pointer wasn't saved, popping the CFA implicitly pops
710 the stack-pointer as well. */
711 if ((rs->reg[DWARF_CFA_REG_COLUMN].val == UNW_TDEP_SP)
712 && (rs->reg[UNW_TDEP_SP].where == DWARF_WHERE_SAME))
homeip.net!davidm642607d2004-08-20 11:23:15 +0000713 cfa = c->cfa;
homeip.net!davidm642607d2004-08-20 11:23:15 +0000714 else
715 {
716 regnum = dwarf_to_unw_regnum (rs->reg[DWARF_CFA_REG_COLUMN].val);
717 if ((ret = unw_get_reg ((unw_cursor_t *) c, regnum, &cfa)) < 0)
718 return ret;
719 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000720 cfa += rs->reg[DWARF_CFA_OFF_COLUMN].val;
721 }
722 else
723 {
724 /* CFA is equal to EXPR: */
725
726 assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPR);
727
728 addr = rs->reg[DWARF_CFA_REG_COLUMN].val;
729 if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0)
730 return ret;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000731 /* the returned location better be a memory location... */
732 if (DWARF_IS_REG_LOC (cfa_loc))
733 return -UNW_EBADFRAME;
734 cfa = DWARF_GET_LOC (cfa_loc);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000735 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000736
737 for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
738 {
739 switch ((dwarf_where_t) rs->reg[i].where)
740 {
741 case DWARF_WHERE_UNDEF:
742 c->loc[i] = DWARF_NULL_LOC;
743 break;
744
745 case DWARF_WHERE_SAME:
746 break;
747
748 case DWARF_WHERE_CFAREL:
749 c->loc[i] = DWARF_MEM_LOC (c, cfa + rs->reg[i].val);
750 break;
751
752 case DWARF_WHERE_REG:
753 c->loc[i] = DWARF_REG_LOC (c, dwarf_to_unw_regnum (rs->reg[i].val));
754 break;
755
756 case DWARF_WHERE_EXPR:
757 addr = rs->reg[i].val;
758 if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) , 0)
759 return ret;
760 break;
761 }
762 }
mostang.com!davidmc1437142005-05-03 09:13:17 +0000763 c->cfa = cfa;
Arun Sharma00db7f72006-07-26 21:18:49 -0600764 ret = dwarf_get (c, c->loc[c->ret_addr_column], &ip);
765 if (ret < 0)
766 return ret;
767 c->ip = ip;
768 /* XXX: check for ip to be code_aligned */
769
770 if (c->ip == prev_ip && c->cfa == prev_cfa)
771 {
Arun Sharmaec53de82009-03-16 18:42:27 +0000772 Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
Arun Sharma00db7f72006-07-26 21:18:49 -0600773 __FUNCTION__, (long) c->ip);
774 return -UNW_EBADFRAME;
775 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000776 return 0;
777}
778
Arun Sharma00db7f72006-07-26 21:18:49 -0600779static int
780uncached_dwarf_find_save_locs (struct dwarf_cursor *c)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000781{
782 dwarf_state_record_t sr;
783 int ret;
784
785 if ((ret = fetch_proc_info (c, c->ip, 1)) < 0)
786 return ret;
787
788 if ((ret = create_state_record_for (c, &sr, c->ip)) < 0)
789 return ret;
790
791 if ((ret = apply_reg_state (c, &sr.rs_current)) < 0)
792 return ret;
793
794 put_unwind_info (c, &c->pi);
795 return 0;
796}
797
Arun Sharma60b7af72006-07-26 21:25:37 -0600798/* The function finds the saved locations and applies the register
799 state as well. */
Arun Sharma00db7f72006-07-26 21:18:49 -0600800HIDDEN int
801dwarf_find_save_locs (struct dwarf_cursor *c)
802{
803 dwarf_state_record_t sr;
804 dwarf_reg_state_t *rs, *rs1;
805 struct dwarf_rs_cache *cache;
Arun Sharma43127192006-07-26 21:44:38 -0600806 int ret = 0;
807 intrmask_t saved_mask;
Arun Sharma00db7f72006-07-26 21:18:49 -0600808
809 if (c->as->caching_policy == UNW_CACHE_NONE)
810 return uncached_dwarf_find_save_locs (c);
811
Arun Sharma43127192006-07-26 21:44:38 -0600812 cache = get_rs_cache(c->as, &saved_mask);
813 if (!cache)
814 return -UNW_ENOINFO; /* cache is busy */
Arun Sharma00db7f72006-07-26 21:18:49 -0600815 rs = rs_lookup(cache, c);
816
817 if (rs)
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600818 {
819 c->ret_addr_column = rs->ret_addr_column;
820 goto apply;
821 }
Arun Sharma00db7f72006-07-26 21:18:49 -0600822
823 if ((ret = fetch_proc_info (c, c->ip, 1)) < 0)
Arun Sharma43127192006-07-26 21:44:38 -0600824 goto out;
Arun Sharma00db7f72006-07-26 21:18:49 -0600825
826 if ((ret = create_state_record_for (c, &sr, c->ip)) < 0)
Arun Sharma43127192006-07-26 21:44:38 -0600827 goto out;
Arun Sharma00db7f72006-07-26 21:18:49 -0600828
829 rs1 = &sr.rs_current;
830 if (rs1)
831 {
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600832 rs = rs_new (cache, c);
Arun Sharma00db7f72006-07-26 21:18:49 -0600833 memcpy(rs, rs1, offsetof(struct dwarf_reg_state, ip));
834 if (!rs)
835 {
Arun Sharmaec53de82009-03-16 18:42:27 +0000836 Dprintf ("%s: failed to create unwind rs\n", __FUNCTION__);
Arun Sharma00db7f72006-07-26 21:18:49 -0600837 ret = -UNW_EUNSPEC;
Arun Sharma43127192006-07-26 21:44:38 -0600838 goto out;
Arun Sharma00db7f72006-07-26 21:18:49 -0600839 }
840 }
841 cache->buckets[c->prev_rs].hint = rs - cache->buckets;
842
843 c->hint = rs->hint;
844 c->prev_rs = rs - cache->buckets;
845
Arun Sharma3dfde7a2006-07-26 21:23:30 -0600846 put_unwind_info (c, &c->pi);
Arun Sharma58888a52006-07-26 21:55:09 -0600847 ret = apply_reg_state (c, rs);
Arun Sharma3dfde7a2006-07-26 21:23:30 -0600848
Arun Sharma43127192006-07-26 21:44:38 -0600849out:
850 put_rs_cache (c->as, cache, &saved_mask);
851 return ret;
852
Arun Sharma00db7f72006-07-26 21:18:49 -0600853apply:
Arun Sharma43127192006-07-26 21:44:38 -0600854 put_rs_cache (c->as, cache, &saved_mask);
Arun Sharma00db7f72006-07-26 21:18:49 -0600855 if ((ret = apply_reg_state (c, rs)) < 0)
856 return ret;
857
Arun Sharma00db7f72006-07-26 21:18:49 -0600858 return 0;
859}
860
homeip.net!davidm88160e02004-08-17 15:34:28 +0000861/* The proc-info must be valid for IP before this routine can be
862 called. */
863HIDDEN int
864dwarf_create_state_record (struct dwarf_cursor *c, dwarf_state_record_t *sr)
865{
866 return create_state_record_for (c, sr, c->ip);
867}
868
869HIDDEN int
870dwarf_make_proc_info (struct dwarf_cursor *c)
871{
872#if 0
873 if (c->as->caching_policy == UNW_CACHE_NONE
874 || get_cached_proc_info (c) < 0)
875#endif
876 /* Lookup it up the slow way... */
877 return fetch_proc_info (c, c->ip, 0);
878 return 0;
879}