blob: 3b1a2b40548ce43cd2bbb1e270a2b9d7ca76da1f [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
Lassi Tuuraa9dce3c2010-04-24 19:16:09 -070079 /* Process everything up to and including the current 'ip',
80 including all the DW_CFA_advance_loc instructions. See
81 'c->use_prev_instr' use in 'fetch_proc_info' for details. */
82 while (curr_ip <= ip && *addr < end_addr)
homeip.net!davidm88160e02004-08-17 15:34:28 +000083 {
84 if ((ret = dwarf_readu8 (as, a, addr, &op, arg)) < 0)
85 return ret;
86
87 if (op & DWARF_CFA_OPCODE_MASK)
88 {
89 operand = op & DWARF_CFA_OPERAND_MASK;
90 op &= ~DWARF_CFA_OPERAND_MASK;
91 }
92 switch ((dwarf_cfa_t) op)
93 {
94 case DW_CFA_advance_loc:
mostang.com!davidmc1437142005-05-03 09:13:17 +000095 curr_ip += operand * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +000096 Debug (15, "CFA_advance_loc to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +000097 break;
98
99 case DW_CFA_advance_loc1:
100 if ((ret = dwarf_readu8 (as, a, addr, &u8, arg)) < 0)
101 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000102 curr_ip += u8 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000103 Debug (15, "CFA_advance_loc1 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000104 break;
105
106 case DW_CFA_advance_loc2:
107 if ((ret = dwarf_readu16 (as, a, addr, &u16, arg)) < 0)
108 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000109 curr_ip += u16 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000110 Debug (15, "CFA_advance_loc2 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000111 break;
112
113 case DW_CFA_advance_loc4:
114 if ((ret = dwarf_readu32 (as, a, addr, &u32, arg)) < 0)
115 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000116 curr_ip += u32 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000117 Debug (15, "CFA_advance_loc4 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000118 break;
119
120 case DW_CFA_MIPS_advance_loc8:
121#ifdef UNW_TARGET_MIPS
122 {
123 uint64_t u64;
124
125 if ((ret = dwarf_readu64 (as, a, addr, &u64, arg)) < 0)
126 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000127 curr_ip += u64 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000128 Debug (15, "CFA_MIPS_advance_loc8\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000129 break;
130 }
131#else
132 Debug (1, "DW_CFA_MIPS_advance_loc8 on non-MIPS target\n");
133 ret = -UNW_EINVAL;
134 goto fail;
135#endif
136
137 case DW_CFA_offset:
138 regnum = operand;
139 if (regnum >= DWARF_NUM_PRESERVED_REGS)
140 {
141 Debug (1, "Invalid register number %u in DW_cfa_OFFSET\n",
homeip.net!davidm642607d2004-08-20 11:23:15 +0000142 (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000143 ret = -UNW_EBADREG;
144 goto fail;
145 }
146 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
147 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000148 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000149 Debug (15, "CFA_offset r%lu at cfa+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000150 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000151 break;
152
153 case DW_CFA_offset_extended:
154 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
155 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
156 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000157 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000158 Debug (15, "CFA_offset_extended r%lu at cf+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000159 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000160 break;
161
162 case DW_CFA_offset_extended_sf:
163 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
164 || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
165 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000166 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000167 Debug (15, "CFA_offset_extended_sf r%lu at cf+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000168 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000169 break;
170
171 case DW_CFA_restore:
172 regnum = operand;
173 if (regnum >= DWARF_NUM_PRESERVED_REGS)
174 {
175 Debug (1, "Invalid register number %u in DW_CFA_restore\n",
homeip.net!davidm642607d2004-08-20 11:23:15 +0000176 (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000177 ret = -UNW_EINVAL;
178 goto fail;
179 }
180 sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000181 Debug (15, "CFA_restore r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000182 break;
183
184 case DW_CFA_restore_extended:
185 if ((ret = dwarf_read_uleb128 (as, a, addr, &regnum, arg)) < 0)
186 goto fail;
187 if (regnum >= DWARF_NUM_PRESERVED_REGS)
188 {
189 Debug (1, "Invalid register number %u in "
homeip.net!davidm642607d2004-08-20 11:23:15 +0000190 "DW_CFA_restore_extended\n", (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000191 ret = -UNW_EINVAL;
192 goto fail;
193 }
194 sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000195 Debug (15, "CFA_restore_extended r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000196 break;
197
198 case DW_CFA_nop:
199 break;
200
201 case DW_CFA_set_loc:
mostang.com!davidmc1437142005-05-03 09:13:17 +0000202 fde_encoding = dci->fde_encoding;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000203 if ((ret = dwarf_read_encoded_pointer (as, a, addr, fde_encoding,
204 &c->pi, &curr_ip,
205 arg)) < 0)
206 goto fail;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000207 Debug (15, "CFA_set_loc to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000208 break;
209
210 case DW_CFA_undefined:
211 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
212 goto fail;
213 set_reg (sr, regnum, DWARF_WHERE_UNDEF, 0);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000214 Debug (15, "CFA_undefined r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000215 break;
216
217 case DW_CFA_same_value:
218 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
219 goto fail;
220 set_reg (sr, regnum, DWARF_WHERE_SAME, 0);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000221 Debug (15, "CFA_same_value r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000222 break;
223
224 case DW_CFA_register:
225 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
226 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
227 goto fail;
228 set_reg (sr, regnum, DWARF_WHERE_REG, val);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000229 Debug (15, "CFA_register r%lu to r%lu\n", (long) regnum, (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000230 break;
231
232 case DW_CFA_remember_state:
233 new_rs = alloc_reg_state ();
234 if (!new_rs)
235 {
236 Debug (1, "Out of memory in DW_CFA_remember_state\n");
237 ret = -UNW_ENOMEM;
238 goto fail;
239 }
240
241 memcpy (new_rs->reg, sr->rs_current.reg, sizeof (new_rs->reg));
242 new_rs->next = rs_stack;
243 rs_stack = new_rs;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000244 Debug (15, "CFA_remember_state\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000245 break;
246
247 case DW_CFA_restore_state:
248 if (!rs_stack)
249 {
250 Debug (1, "register-state stack underflow\n");
251 ret = -UNW_EINVAL;
252 goto fail;
253 }
254 memcpy (&sr->rs_current.reg, &rs_stack->reg, sizeof (rs_stack->reg));
255 old_rs = rs_stack;
256 rs_stack = rs_stack->next;
257 free_reg_state (old_rs);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000258 Debug (15, "CFA_restore_state\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000259 break;
260
261 case DW_CFA_def_cfa:
262 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
263 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
264 goto fail;
265 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
266 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000267 Debug (15, "CFA_def_cfa r%lu+0x%lx\n", (long) regnum, (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000268 break;
269
270 case DW_CFA_def_cfa_sf:
271 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
272 || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
273 goto fail;
274 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
275 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000276 val * dci->data_align); /* factored! */
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000277 Debug (15, "CFA_def_cfa_sf r%lu+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000278 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000279 break;
280
281 case DW_CFA_def_cfa_register:
282 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
283 goto fail;
284 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000285 Debug (15, "CFA_def_cfa_register r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000286 break;
287
288 case DW_CFA_def_cfa_offset:
289 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
290 goto fail;
291 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */
mostang.com!davidmc1437142005-05-03 09:13:17 +0000292 Debug (15, "CFA_def_cfa_offset 0x%lx\n", (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000293 break;
294
295 case DW_CFA_def_cfa_offset_sf:
296 if ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0)
297 goto fail;
298 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000299 val * dci->data_align); /* factored! */
300 Debug (15, "CFA_def_cfa_offset_sf 0x%lx\n",
301 (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000302 break;
303
304 case DW_CFA_def_cfa_expression:
305 /* Save the address of the DW_FORM_block for later evaluation. */
306 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPR, *addr);
307
308 if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
309 goto fail;
310
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000311 Debug (15, "CFA_def_cfa_expr @ 0x%lx [%lu bytes]\n",
homeip.net!davidm88160e02004-08-17 15:34:28 +0000312 (long) *addr, (long) len);
313 *addr += len;
314 break;
315
mostang.com!davidmc1437142005-05-03 09:13:17 +0000316 case DW_CFA_expression:
homeip.net!davidm88160e02004-08-17 15:34:28 +0000317 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
318 goto fail;
319
320 /* Save the address of the DW_FORM_block for later evaluation. */
321 set_reg (sr, regnum, DWARF_WHERE_EXPR, *addr);
322
323 if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
324 goto fail;
325
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000326 Debug (15, "CFA_expression r%lu @ 0x%lx [%lu bytes]\n",
homeip.net!davidm88160e02004-08-17 15:34:28 +0000327 (long) regnum, (long) addr, (long) len);
328 *addr += len;
329 break;
330
331 case DW_CFA_GNU_args_size:
332 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
333 goto fail;
334 sr->args_size = val;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000335 Debug (15, "CFA_GNU_args_size %lu\n", (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000336 break;
337
338 case DW_CFA_GNU_negative_offset_extended:
339 /* A comment in GCC says that this is obsoleted by
340 DW_CFA_offset_extended_sf, but that it's used by older
341 PowerPC code. */
342 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
343 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
344 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000345 set_reg (sr, regnum, DWARF_WHERE_CFAREL, -(val * dci->data_align));
346 Debug (15, "CFA_GNU_negative_offset_extended cfa+0x%lx\n",
347 (long) -(val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000348 break;
349
350 case DW_CFA_GNU_window_save:
351#ifdef UNW_TARGET_SPARC
352 /* This is a special CFA to handle all 16 windowed registers
353 on SPARC. */
354 for (regnum = 16; regnum < 32; ++regnum)
355 set_reg (sr, regnum, DWARF_WHERE_CFAREL,
356 (regnum - 16) * sizeof (unw_word_t));
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000357 Debug (15, "CFA_GNU_window_save\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000358 break;
359#else
360 /* FALL THROUGH */
361#endif
362 case DW_CFA_lo_user:
363 case DW_CFA_hi_user:
mostang.com!davidmc1437142005-05-03 09:13:17 +0000364 Debug (1, "Unexpected CFA opcode 0x%x\n", op);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000365 ret = -UNW_EINVAL;
366 goto fail;
367 }
368 }
369 ret = 0;
370
371 fail:
372 /* Free the register-state stack, if not empty already. */
373 while (rs_stack)
374 {
375 old_rs = rs_stack;
376 rs_stack = rs_stack->next;
377 free_reg_state (old_rs);
378 }
379 return ret;
380}
381
382static int
383fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info)
384{
385 int ret, dynamic = 1;
386
Lassi Tuuraa9dce3c2010-04-24 19:16:09 -0700387 /* The 'ip' can point either to the previous or next instruction
388 depending on what type of frame we have: normal call or a place
389 to resume execution (e.g. after signal frame).
390
391 For a normal call frame we need to back up so we point within the
392 call itself; this is important because a) the call might be the
393 very last instruction of the function and the edge of the FDE,
394 and b) so that run_cfi_program() runs locations up to the call
395 but not more.
396
397 For execution resume, we need to do the exact opposite and look
398 up using the current 'ip' value. That is where execution will
399 continue, and it's important we get this right, as 'ip' could be
400 right at the function entry and hence FDE edge, or at instruction
401 that manipulates CFA (push/pop). */
402 if (c->use_prev_instr)
403 --ip;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000404
homeip.net!davidm88160e02004-08-17 15:34:28 +0000405 if (c->pi_valid && !need_unwind_info)
406 return 0;
407
mostang.com!davidmc1437142005-05-03 09:13:17 +0000408 memset (&c->pi, 0, sizeof (c->pi));
409
homeip.net!davidm88160e02004-08-17 15:34:28 +0000410 /* check dynamic info first --- it overrides everything else */
411 ret = unwi_find_dynamic_proc_info (c->as, ip, &c->pi, need_unwind_info,
412 c->as_arg);
413 if (ret == -UNW_ENOINFO)
414 {
415 dynamic = 0;
416 if ((ret = tdep_find_proc_info (c, ip, need_unwind_info)) < 0)
417 return ret;
418 }
419
420 c->pi_valid = 1;
421 c->pi_is_dynamic = dynamic;
Lassi Tuuradac2d002010-04-20 23:13:09 +0200422
423 /* Let system/machine-dependent code determine frame-specific attributes. */
424 if (ret >= 0)
425 tdep_fetch_frame (c, ip, need_unwind_info);
426
Lassi Tuuraa9dce3c2010-04-24 19:16:09 -0700427 /* Update use_prev_instr for the next frame. */
428 if (need_unwind_info)
429 {
430 assert(c->pi.unwind_info);
431 struct dwarf_cie_info *dci = c->pi.unwind_info;
432 c->use_prev_instr = ! dci->signal_frame;
433 }
434
homeip.net!davidm88160e02004-08-17 15:34:28 +0000435 return ret;
436}
437
438static int
439parse_dynamic (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr)
440{
441 Debug (1, "Not yet implemented\n");
442#if 0
443 /* Don't forget to set the ret_addr_column! */
444 c->ret_addr_column = XXX;
445#endif
446 return -UNW_ENOINFO;
447}
448
449static inline void
450put_unwind_info (struct dwarf_cursor *c, unw_proc_info_t *pi)
451{
452 if (!c->pi_valid)
453 return;
454
455 if (c->pi_is_dynamic)
456 unwi_put_dynamic_unwind_info (c->as, pi, c->as_arg);
Arun Sharma7bd264e2010-03-23 10:37:21 -0700457 else if (pi->unwind_info)
mostang.com!davidmc1437142005-05-03 09:13:17 +0000458 {
459 mempool_free (&dwarf_cie_info_pool, pi->unwind_info);
460 pi->unwind_info = NULL;
461 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000462}
463
464static inline int
465parse_fde (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr)
466{
mostang.com!davidmc1437142005-05-03 09:13:17 +0000467 struct dwarf_cie_info *dci;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000468 unw_word_t addr;
469 int ret;
470
mostang.com!davidmc1437142005-05-03 09:13:17 +0000471 dci = c->pi.unwind_info;
472 c->ret_addr_column = dci->ret_addr_column;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000473
mostang.com!davidmc1437142005-05-03 09:13:17 +0000474 addr = dci->cie_instr_start;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000475 if ((ret = run_cfi_program (c, sr, ~(unw_word_t) 0, &addr,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000476 dci->cie_instr_end, dci)) < 0)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000477 return ret;
478
479 memcpy (&sr->rs_initial, &sr->rs_current, sizeof (sr->rs_initial));
480
mostang.com!davidmc1437142005-05-03 09:13:17 +0000481 addr = dci->fde_instr_start;
482 if ((ret = run_cfi_program (c, sr, ip, &addr, dci->fde_instr_end, dci)) < 0)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000483 return ret;
484
485 return 0;
486}
487
Arun Sharma00db7f72006-07-26 21:18:49 -0600488static inline void
489flush_rs_cache (struct dwarf_rs_cache *cache)
490{
491 int i;
492
493 cache->lru_head = DWARF_UNW_CACHE_SIZE - 1;
494 cache->lru_tail = 0;
495
496 for (i = 0; i < DWARF_UNW_CACHE_SIZE; ++i)
497 {
498 if (i > 0)
499 cache->buckets[i].lru_chain = (i - 1);
500 cache->buckets[i].coll_chain = -1;
501 cache->buckets[i].ip = 0;
502 }
503 for (i = 0; i<DWARF_UNW_HASH_SIZE; ++i)
504 cache->hash[i] = -1;
505}
506
Arun Sharma43127192006-07-26 21:44:38 -0600507static inline struct dwarf_rs_cache *
508get_rs_cache (unw_addr_space_t as, intrmask_t *saved_maskp)
Arun Sharma00db7f72006-07-26 21:18:49 -0600509{
510 struct dwarf_rs_cache *cache = &as->global_cache;
Arun Sharma43127192006-07-26 21:44:38 -0600511 unw_caching_policy_t caching = as->caching_policy;
512
513 if (caching == UNW_CACHE_NONE)
514 return NULL;
515
Arun Sharma43127192006-07-26 21:44:38 -0600516 if (likely (caching == UNW_CACHE_GLOBAL))
517 {
518 Debug (16, "%s: acquiring lock\n", __FUNCTION__);
Paul Pluzhnikov84d41502009-09-21 12:02:07 -0700519 lock_acquire (&cache->lock, *saved_maskp);
Arun Sharma43127192006-07-26 21:44:38 -0600520 }
Arun Sharma43127192006-07-26 21:44:38 -0600521
Arun Sharma00db7f72006-07-26 21:18:49 -0600522 if (atomic_read (&as->cache_generation) != atomic_read (&cache->generation))
523 {
524 flush_rs_cache (cache);
525 cache->generation = as->cache_generation;
526 }
527
528 return cache;
529}
530
Arun Sharma43127192006-07-26 21:44:38 -0600531static inline void
532put_rs_cache (unw_addr_space_t as, struct dwarf_rs_cache *cache,
533 intrmask_t *saved_maskp)
534{
535 assert (as->caching_policy != UNW_CACHE_NONE);
536
537 Debug (16, "unmasking signals/interrupts and releasing lock\n");
Arun Sharma43127192006-07-26 21:44:38 -0600538 if (likely (as->caching_policy == UNW_CACHE_GLOBAL))
Paul Pluzhnikov84d41502009-09-21 12:02:07 -0700539 lock_release (&cache->lock, *saved_maskp);
Arun Sharma43127192006-07-26 21:44:38 -0600540}
541
Arun Sharma00db7f72006-07-26 21:18:49 -0600542static inline unw_hash_index_t
543hash (unw_word_t ip)
544{
545 /* based on (sqrt(5)/2-1)*2^64 */
546# define magic ((unw_word_t) 0x9e3779b97f4a7c16ULL)
547
Arun Sharma55fe5242006-08-16 15:43:49 -0600548 return ip * magic >> ((sizeof(unw_word_t) * 8) - DWARF_LOG_UNW_HASH_SIZE);
Arun Sharma00db7f72006-07-26 21:18:49 -0600549}
550
551static inline long
552cache_match (dwarf_reg_state_t *rs, unw_word_t ip)
553{
554 if (ip == rs->ip)
555 return 1;
556 return 0;
557}
558
559static dwarf_reg_state_t *
560rs_lookup (struct dwarf_rs_cache *cache, struct dwarf_cursor *c)
561{
562 dwarf_reg_state_t *rs = cache->buckets + c->hint;
563 unsigned short index;
564 unw_word_t ip;
565
566 ip = c->ip;
567
568 if (cache_match (rs, ip))
569 return rs;
570
571 index = cache->hash[hash (ip)];
572 if (index >= DWARF_UNW_CACHE_SIZE)
573 return 0;
574
575 rs = cache->buckets + index;
576 while (1)
577 {
578 if (cache_match (rs, ip))
579 {
580 /* update hint; no locking needed: single-word writes are atomic */
581 c->hint = cache->buckets[c->prev_rs].hint =
582 (rs - cache->buckets);
583 return rs;
584 }
585 if (rs->coll_chain >= DWARF_UNW_HASH_SIZE)
586 return 0;
587 rs = cache->buckets + rs->coll_chain;
588 }
589}
590
591static inline dwarf_reg_state_t *
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600592rs_new (struct dwarf_rs_cache *cache, struct dwarf_cursor * c)
Arun Sharma00db7f72006-07-26 21:18:49 -0600593{
594 dwarf_reg_state_t *rs, *prev, *tmp;
595 unw_hash_index_t index;
596 unsigned short head;
597
598 head = cache->lru_head;
599 rs = cache->buckets + head;
600 cache->lru_head = rs->lru_chain;
601
602 /* re-insert rs at the tail of the LRU chain: */
603 cache->buckets[cache->lru_tail].lru_chain = head;
604 cache->lru_tail = head;
605
606 /* remove the old rs from the hash table (if it's there): */
607 if (rs->ip)
608 {
609 index = hash (rs->ip);
610 tmp = cache->buckets + cache->hash[index];
611 prev = 0;
612 while (1)
613 {
614 if (tmp == rs)
615 {
616 if (prev)
617 prev->coll_chain = tmp->coll_chain;
618 else
619 cache->hash[index] = tmp->coll_chain;
620 break;
621 }
622 else
623 prev = tmp;
624 if (tmp->coll_chain >= DWARF_UNW_CACHE_SIZE)
625 /* old rs wasn't in the hash-table */
626 break;
627 tmp = cache->buckets + tmp->coll_chain;
628 }
629 }
630
631 /* enter new rs in the hash table */
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600632 index = hash (c->ip);
Arun Sharma00db7f72006-07-26 21:18:49 -0600633 rs->coll_chain = cache->hash[index];
634 cache->hash[index] = rs - cache->buckets;
635
636 rs->hint = 0;
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600637 rs->ip = c->ip;
638 rs->ret_addr_column = c->ret_addr_column;
Lassi Tuuradac2d002010-04-20 23:13:09 +0200639 rs->signal_frame = 0;
640 tdep_cache_frame (c, rs);
Arun Sharma00db7f72006-07-26 21:18:49 -0600641
642 return rs;
643}
644
homeip.net!davidm88160e02004-08-17 15:34:28 +0000645static int
646create_state_record_for (struct dwarf_cursor *c, dwarf_state_record_t *sr,
647 unw_word_t ip)
648{
649 int i, ret;
650
651 assert (c->pi_valid);
652
mostang.com!davidmc1437142005-05-03 09:13:17 +0000653 memset (sr, 0, sizeof (*sr));
654 for (i = 0; i < DWARF_NUM_PRESERVED_REGS + 2; ++i)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000655 set_reg (sr, i, DWARF_WHERE_SAME, 0);
656
657 switch (c->pi.format)
658 {
mostang.com!davidmc1437142005-05-03 09:13:17 +0000659 case UNW_INFO_FORMAT_TABLE:
660 case UNW_INFO_FORMAT_REMOTE_TABLE:
homeip.net!davidm88160e02004-08-17 15:34:28 +0000661 ret = parse_fde (c, ip, sr);
662 break;
663
664 case UNW_INFO_FORMAT_DYNAMIC:
665 ret = parse_dynamic (c, ip, sr);
666 break;
667
homeip.net!davidm88160e02004-08-17 15:34:28 +0000668 default:
669 Debug (1, "Unexpected unwind-info format %d\n", c->pi.format);
670 ret = -UNW_EINVAL;
671 }
672 return ret;
673}
674
675static inline int
676eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as,
677 unw_accessors_t *a, unw_word_t addr,
678 dwarf_loc_t *locp, void *arg)
679{
680 int ret, is_register;
681 unw_word_t len, val;
682
683 /* read the length of the expression: */
684 if ((ret = dwarf_read_uleb128 (as, a, &addr, &len, arg)) < 0)
685 return ret;
686
687 /* evaluate the expression: */
688 if ((ret = dwarf_eval_expr (c, &addr, len, &val, &is_register)) < 0)
689 return ret;
690
691 if (is_register)
692 *locp = DWARF_REG_LOC (c, dwarf_to_unw_regnum (val));
693 else
694 *locp = DWARF_MEM_LOC (c, val);
695
696 return 0;
697}
698
699static int
700apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
701{
Arun Sharma00db7f72006-07-26 21:18:49 -0600702 unw_word_t regnum, addr, cfa, ip;
703 unw_word_t prev_ip, prev_cfa;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000704 unw_addr_space_t as;
705 dwarf_loc_t cfa_loc;
706 unw_accessors_t *a;
707 int i, ret;
708 void *arg;
709
Arun Sharma00db7f72006-07-26 21:18:49 -0600710 prev_ip = c->ip;
711 prev_cfa = c->cfa;
712
homeip.net!davidm88160e02004-08-17 15:34:28 +0000713 as = c->as;
714 arg = c->as_arg;
715 a = unw_get_accessors (as);
716
mostang.com!davidmc1437142005-05-03 09:13:17 +0000717 /* Evaluate the CFA first, because it may be referred to by other
homeip.net!davidm88160e02004-08-17 15:34:28 +0000718 expressions. */
719
720 if (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG)
721 {
722 /* CFA is equal to [reg] + offset: */
723
homeip.net!davidm642607d2004-08-20 11:23:15 +0000724 /* As a special-case, if the stack-pointer is the CFA and the
725 stack-pointer wasn't saved, popping the CFA implicitly pops
726 the stack-pointer as well. */
727 if ((rs->reg[DWARF_CFA_REG_COLUMN].val == UNW_TDEP_SP)
Arun Sharmad7089542009-10-23 22:38:11 -0700728 && (UNW_TDEP_SP < ARRAY_SIZE(rs->reg))
homeip.net!davidm642607d2004-08-20 11:23:15 +0000729 && (rs->reg[UNW_TDEP_SP].where == DWARF_WHERE_SAME))
homeip.net!davidm642607d2004-08-20 11:23:15 +0000730 cfa = c->cfa;
homeip.net!davidm642607d2004-08-20 11:23:15 +0000731 else
732 {
733 regnum = dwarf_to_unw_regnum (rs->reg[DWARF_CFA_REG_COLUMN].val);
734 if ((ret = unw_get_reg ((unw_cursor_t *) c, regnum, &cfa)) < 0)
735 return ret;
736 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000737 cfa += rs->reg[DWARF_CFA_OFF_COLUMN].val;
738 }
739 else
740 {
741 /* CFA is equal to EXPR: */
742
743 assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPR);
744
745 addr = rs->reg[DWARF_CFA_REG_COLUMN].val;
746 if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0)
747 return ret;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000748 /* the returned location better be a memory location... */
749 if (DWARF_IS_REG_LOC (cfa_loc))
750 return -UNW_EBADFRAME;
751 cfa = DWARF_GET_LOC (cfa_loc);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000752 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000753
754 for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
755 {
756 switch ((dwarf_where_t) rs->reg[i].where)
757 {
758 case DWARF_WHERE_UNDEF:
759 c->loc[i] = DWARF_NULL_LOC;
760 break;
761
762 case DWARF_WHERE_SAME:
763 break;
764
765 case DWARF_WHERE_CFAREL:
766 c->loc[i] = DWARF_MEM_LOC (c, cfa + rs->reg[i].val);
767 break;
768
769 case DWARF_WHERE_REG:
770 c->loc[i] = DWARF_REG_LOC (c, dwarf_to_unw_regnum (rs->reg[i].val));
771 break;
772
773 case DWARF_WHERE_EXPR:
774 addr = rs->reg[i].val;
775 if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) , 0)
776 return ret;
777 break;
778 }
779 }
Arun Sharma00db7f72006-07-26 21:18:49 -0600780
Lassi Tuura4c553ce2010-04-20 23:13:11 +0200781 c->cfa = cfa;
782 /* DWARF spec says undefined return address location means end of stack. */
783 if (DWARF_IS_NULL_LOC (c->loc[c->ret_addr_column]))
784 c->ip = 0;
785 else
786 {
787 ret = dwarf_get (c, c->loc[c->ret_addr_column], &ip);
788 if (ret < 0)
789 return ret;
790 c->ip = ip;
791 }
792
793 /* XXX: check for ip to be code_aligned */
Arun Sharma00db7f72006-07-26 21:18:49 -0600794 if (c->ip == prev_ip && c->cfa == prev_cfa)
795 {
Arun Sharmaec53de82009-03-16 18:42:27 +0000796 Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
Arun Sharma00db7f72006-07-26 21:18:49 -0600797 __FUNCTION__, (long) c->ip);
798 return -UNW_EBADFRAME;
799 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000800 return 0;
801}
802
Arun Sharma00db7f72006-07-26 21:18:49 -0600803static int
804uncached_dwarf_find_save_locs (struct dwarf_cursor *c)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000805{
806 dwarf_state_record_t sr;
807 int ret;
808
809 if ((ret = fetch_proc_info (c, c->ip, 1)) < 0)
810 return ret;
811
812 if ((ret = create_state_record_for (c, &sr, c->ip)) < 0)
813 return ret;
814
815 if ((ret = apply_reg_state (c, &sr.rs_current)) < 0)
816 return ret;
817
818 put_unwind_info (c, &c->pi);
819 return 0;
820}
821
Arun Sharma60b7af72006-07-26 21:25:37 -0600822/* The function finds the saved locations and applies the register
823 state as well. */
Arun Sharma00db7f72006-07-26 21:18:49 -0600824HIDDEN int
825dwarf_find_save_locs (struct dwarf_cursor *c)
826{
827 dwarf_state_record_t sr;
Paul Pluzhnikov9626d662009-11-24 15:18:48 -0800828 dwarf_reg_state_t *rs, rs_copy;
Arun Sharma00db7f72006-07-26 21:18:49 -0600829 struct dwarf_rs_cache *cache;
Arun Sharma43127192006-07-26 21:44:38 -0600830 int ret = 0;
831 intrmask_t saved_mask;
Arun Sharma00db7f72006-07-26 21:18:49 -0600832
833 if (c->as->caching_policy == UNW_CACHE_NONE)
834 return uncached_dwarf_find_save_locs (c);
835
Arun Sharma43127192006-07-26 21:44:38 -0600836 cache = get_rs_cache(c->as, &saved_mask);
Arun Sharma00db7f72006-07-26 21:18:49 -0600837 rs = rs_lookup(cache, c);
838
839 if (rs)
Lassi Tuuraa9dce3c2010-04-24 19:16:09 -0700840 {
841 c->ret_addr_column = rs->ret_addr_column;
842 c->use_prev_instr = ! rs->signal_frame;
843 }
Paul Pluzhnikov2648a772009-11-18 17:55:01 -0800844 else
845 {
846 if ((ret = fetch_proc_info (c, c->ip, 1)) < 0 ||
847 (ret = create_state_record_for (c, &sr, c->ip)) < 0)
848 {
849 put_rs_cache (c->as, cache, &saved_mask);
850 return ret;
851 }
852
853 rs = rs_new (cache, c);
854 memcpy(rs, &sr.rs_current, offsetof(struct dwarf_reg_state, ip));
855 cache->buckets[c->prev_rs].hint = rs - cache->buckets;
856
857 c->hint = rs->hint;
858 c->prev_rs = rs - cache->buckets;
859
860 put_unwind_info (c, &c->pi);
Arun Sharmac5dc3c12006-07-26 21:29:50 -0600861 }
Arun Sharma00db7f72006-07-26 21:18:49 -0600862
Paul Pluzhnikov9626d662009-11-24 15:18:48 -0800863 memcpy (&rs_copy, rs, sizeof (rs_copy));
Arun Sharma43127192006-07-26 21:44:38 -0600864 put_rs_cache (c->as, cache, &saved_mask);
Lassi Tuuradac2d002010-04-20 23:13:09 +0200865
866 tdep_reuse_frame (c, &rs_copy);
Paul Pluzhnikov9626d662009-11-24 15:18:48 -0800867 if ((ret = apply_reg_state (c, &rs_copy)) < 0)
Arun Sharma00db7f72006-07-26 21:18:49 -0600868 return ret;
869
Arun Sharma00db7f72006-07-26 21:18:49 -0600870 return 0;
871}
872
homeip.net!davidm88160e02004-08-17 15:34:28 +0000873/* The proc-info must be valid for IP before this routine can be
874 called. */
875HIDDEN int
876dwarf_create_state_record (struct dwarf_cursor *c, dwarf_state_record_t *sr)
877{
878 return create_state_record_for (c, sr, c->ip);
879}
880
881HIDDEN int
882dwarf_make_proc_info (struct dwarf_cursor *c)
883{
884#if 0
885 if (c->as->caching_policy == UNW_CACHE_NONE
886 || get_cached_proc_info (c) < 0)
887#endif
888 /* Lookup it up the slow way... */
889 return fetch_proc_info (c, c->ip, 0);
890 return 0;
891}