blob: b6f00b831360305c0419d94f65439453310e4e50 [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
homeip.net!davidm88160e02004-08-17 15:34:28 +000026#include "dwarf_i.h"
hp.com!davidmdf3d6af2005-05-20 09:48:08 +000027#include "libunwind_i.h"
homeip.net!davidm88160e02004-08-17 15:34:28 +000028
29#define alloc_reg_state() (mempool_alloc (&dwarf_reg_state_pool))
30#define free_reg_state(rs) (mempool_free (&dwarf_reg_state_pool, rs))
31
32static inline int
33read_regnum (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr,
34 unw_word_t *valp, void *arg)
35{
36 int ret;
37
38 if ((ret = dwarf_read_uleb128 (as, a, addr, valp, arg)) < 0)
39 return ret;
40
41 if (*valp >= DWARF_NUM_PRESERVED_REGS)
42 {
homeip.net!davidm642607d2004-08-20 11:23:15 +000043 Debug (1, "Invalid register number %u\n", (unsigned int) *valp);
homeip.net!davidm88160e02004-08-17 15:34:28 +000044 return -UNW_EBADREG;
45 }
46 return 0;
47}
48
49static inline void
50set_reg (dwarf_state_record_t *sr, unw_word_t regnum, dwarf_where_t where,
51 unw_word_t val)
52{
53 sr->rs_current.reg[regnum].where = where;
54 sr->rs_current.reg[regnum].val = val;
55}
56
57/* Run a CFI program to update the register state. */
58static int
59run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr,
60 unw_word_t ip, unw_word_t *addr, unw_word_t end_addr,
mostang.com!davidmc1437142005-05-03 09:13:17 +000061 struct dwarf_cie_info *dci)
homeip.net!davidm88160e02004-08-17 15:34:28 +000062{
63 unw_word_t curr_ip, operand = 0, regnum, val, len, fde_encoding;
64 dwarf_reg_state_t *rs_stack = NULL, *new_rs, *old_rs;
65 unw_addr_space_t as;
66 unw_accessors_t *a;
67 uint8_t u8, op;
68 uint16_t u16;
69 uint32_t u32;
70 void *arg;
71 int ret;
72
73 as = c->as;
74 arg = c->as_arg;
75 a = unw_get_accessors (as);
76 curr_ip = c->pi.start_ip;
77
78 while (curr_ip < ip && *addr < end_addr)
79 {
80 if ((ret = dwarf_readu8 (as, a, addr, &op, arg)) < 0)
81 return ret;
82
83 if (op & DWARF_CFA_OPCODE_MASK)
84 {
85 operand = op & DWARF_CFA_OPERAND_MASK;
86 op &= ~DWARF_CFA_OPERAND_MASK;
87 }
88 switch ((dwarf_cfa_t) op)
89 {
90 case DW_CFA_advance_loc:
mostang.com!davidmc1437142005-05-03 09:13:17 +000091 curr_ip += operand * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +000092 Debug (15, "CFA_advance_loc to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +000093 break;
94
95 case DW_CFA_advance_loc1:
96 if ((ret = dwarf_readu8 (as, a, addr, &u8, arg)) < 0)
97 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +000098 curr_ip += u8 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +000099 Debug (15, "CFA_advance_loc1 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000100 break;
101
102 case DW_CFA_advance_loc2:
103 if ((ret = dwarf_readu16 (as, a, addr, &u16, arg)) < 0)
104 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000105 curr_ip += u16 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000106 Debug (15, "CFA_advance_loc2 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000107 break;
108
109 case DW_CFA_advance_loc4:
110 if ((ret = dwarf_readu32 (as, a, addr, &u32, arg)) < 0)
111 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000112 curr_ip += u32 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000113 Debug (15, "CFA_advance_loc4 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000114 break;
115
116 case DW_CFA_MIPS_advance_loc8:
117#ifdef UNW_TARGET_MIPS
118 {
119 uint64_t u64;
120
121 if ((ret = dwarf_readu64 (as, a, addr, &u64, arg)) < 0)
122 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000123 curr_ip += u64 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000124 Debug (15, "CFA_MIPS_advance_loc8\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000125 break;
126 }
127#else
128 Debug (1, "DW_CFA_MIPS_advance_loc8 on non-MIPS target\n");
129 ret = -UNW_EINVAL;
130 goto fail;
131#endif
132
133 case DW_CFA_offset:
134 regnum = operand;
135 if (regnum >= DWARF_NUM_PRESERVED_REGS)
136 {
137 Debug (1, "Invalid register number %u in DW_cfa_OFFSET\n",
homeip.net!davidm642607d2004-08-20 11:23:15 +0000138 (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000139 ret = -UNW_EBADREG;
140 goto fail;
141 }
142 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
143 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000144 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000145 Debug (15, "CFA_offset r%lu at cfa+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000146 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000147 break;
148
149 case DW_CFA_offset_extended:
150 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
151 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
152 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000153 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000154 Debug (15, "CFA_offset_extended r%lu at cf+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000155 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000156 break;
157
158 case DW_CFA_offset_extended_sf:
159 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
160 || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
161 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000162 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000163 Debug (15, "CFA_offset_extended_sf r%lu at cf+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000164 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000165 break;
166
167 case DW_CFA_restore:
168 regnum = operand;
169 if (regnum >= DWARF_NUM_PRESERVED_REGS)
170 {
171 Debug (1, "Invalid register number %u in DW_CFA_restore\n",
homeip.net!davidm642607d2004-08-20 11:23:15 +0000172 (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000173 ret = -UNW_EINVAL;
174 goto fail;
175 }
176 sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000177 Debug (15, "CFA_restore r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000178 break;
179
180 case DW_CFA_restore_extended:
181 if ((ret = dwarf_read_uleb128 (as, a, addr, &regnum, arg)) < 0)
182 goto fail;
183 if (regnum >= DWARF_NUM_PRESERVED_REGS)
184 {
185 Debug (1, "Invalid register number %u in "
homeip.net!davidm642607d2004-08-20 11:23:15 +0000186 "DW_CFA_restore_extended\n", (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000187 ret = -UNW_EINVAL;
188 goto fail;
189 }
190 sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000191 Debug (15, "CFA_restore_extended r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000192 break;
193
194 case DW_CFA_nop:
195 break;
196
197 case DW_CFA_set_loc:
mostang.com!davidmc1437142005-05-03 09:13:17 +0000198 fde_encoding = dci->fde_encoding;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000199 if ((ret = dwarf_read_encoded_pointer (as, a, addr, fde_encoding,
200 &c->pi, &curr_ip,
201 arg)) < 0)
202 goto fail;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000203 Debug (15, "CFA_set_loc to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000204 break;
205
206 case DW_CFA_undefined:
207 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
208 goto fail;
209 set_reg (sr, regnum, DWARF_WHERE_UNDEF, 0);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000210 Debug (15, "CFA_undefined r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000211 break;
212
213 case DW_CFA_same_value:
214 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
215 goto fail;
216 set_reg (sr, regnum, DWARF_WHERE_SAME, 0);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000217 Debug (15, "CFA_same_value r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000218 break;
219
220 case DW_CFA_register:
221 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
222 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
223 goto fail;
224 set_reg (sr, regnum, DWARF_WHERE_REG, val);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000225 Debug (15, "CFA_register r%lu to r%lu\n", (long) regnum, (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000226 break;
227
228 case DW_CFA_remember_state:
229 new_rs = alloc_reg_state ();
230 if (!new_rs)
231 {
232 Debug (1, "Out of memory in DW_CFA_remember_state\n");
233 ret = -UNW_ENOMEM;
234 goto fail;
235 }
236
237 memcpy (new_rs->reg, sr->rs_current.reg, sizeof (new_rs->reg));
238 new_rs->next = rs_stack;
239 rs_stack = new_rs;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000240 Debug (15, "CFA_remember_state\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000241 break;
242
243 case DW_CFA_restore_state:
244 if (!rs_stack)
245 {
246 Debug (1, "register-state stack underflow\n");
247 ret = -UNW_EINVAL;
248 goto fail;
249 }
250 memcpy (&sr->rs_current.reg, &rs_stack->reg, sizeof (rs_stack->reg));
251 old_rs = rs_stack;
252 rs_stack = rs_stack->next;
253 free_reg_state (old_rs);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000254 Debug (15, "CFA_restore_state\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000255 break;
256
257 case DW_CFA_def_cfa:
258 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
259 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
260 goto fail;
261 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
262 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000263 Debug (15, "CFA_def_cfa r%lu+0x%lx\n", (long) regnum, (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000264 break;
265
266 case DW_CFA_def_cfa_sf:
267 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
268 || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
269 goto fail;
270 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
271 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000272 val * dci->data_align); /* factored! */
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000273 Debug (15, "CFA_def_cfa_sf r%lu+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000274 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000275 break;
276
277 case DW_CFA_def_cfa_register:
278 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
279 goto fail;
280 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000281 Debug (15, "CFA_def_cfa_register r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000282 break;
283
284 case DW_CFA_def_cfa_offset:
285 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
286 goto fail;
287 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */
mostang.com!davidmc1437142005-05-03 09:13:17 +0000288 Debug (15, "CFA_def_cfa_offset 0x%lx\n", (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000289 break;
290
291 case DW_CFA_def_cfa_offset_sf:
292 if ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0)
293 goto fail;
294 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000295 val * dci->data_align); /* factored! */
296 Debug (15, "CFA_def_cfa_offset_sf 0x%lx\n",
297 (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000298 break;
299
300 case DW_CFA_def_cfa_expression:
301 /* Save the address of the DW_FORM_block for later evaluation. */
302 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPR, *addr);
303
304 if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
305 goto fail;
306
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000307 Debug (15, "CFA_def_cfa_expr @ 0x%lx [%lu bytes]\n",
homeip.net!davidm88160e02004-08-17 15:34:28 +0000308 (long) *addr, (long) len);
309 *addr += len;
310 break;
311
mostang.com!davidmc1437142005-05-03 09:13:17 +0000312 case DW_CFA_expression:
homeip.net!davidm88160e02004-08-17 15:34:28 +0000313 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
314 goto fail;
315
316 /* Save the address of the DW_FORM_block for later evaluation. */
317 set_reg (sr, regnum, DWARF_WHERE_EXPR, *addr);
318
319 if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
320 goto fail;
321
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000322 Debug (15, "CFA_expression r%lu @ 0x%lx [%lu bytes]\n",
homeip.net!davidm88160e02004-08-17 15:34:28 +0000323 (long) regnum, (long) addr, (long) len);
324 *addr += len;
325 break;
326
327 case DW_CFA_GNU_args_size:
328 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
329 goto fail;
330 sr->args_size = val;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000331 Debug (15, "CFA_GNU_args_size %lu\n", (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000332 break;
333
334 case DW_CFA_GNU_negative_offset_extended:
335 /* A comment in GCC says that this is obsoleted by
336 DW_CFA_offset_extended_sf, but that it's used by older
337 PowerPC code. */
338 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
339 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
340 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000341 set_reg (sr, regnum, DWARF_WHERE_CFAREL, -(val * dci->data_align));
342 Debug (15, "CFA_GNU_negative_offset_extended cfa+0x%lx\n",
343 (long) -(val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000344 break;
345
346 case DW_CFA_GNU_window_save:
347#ifdef UNW_TARGET_SPARC
348 /* This is a special CFA to handle all 16 windowed registers
349 on SPARC. */
350 for (regnum = 16; regnum < 32; ++regnum)
351 set_reg (sr, regnum, DWARF_WHERE_CFAREL,
352 (regnum - 16) * sizeof (unw_word_t));
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000353 Debug (15, "CFA_GNU_window_save\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000354 break;
355#else
356 /* FALL THROUGH */
357#endif
358 case DW_CFA_lo_user:
359 case DW_CFA_hi_user:
mostang.com!davidmc1437142005-05-03 09:13:17 +0000360 Debug (1, "Unexpected CFA opcode 0x%x\n", op);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000361 ret = -UNW_EINVAL;
362 goto fail;
363 }
364 }
365 ret = 0;
366
367 fail:
368 /* Free the register-state stack, if not empty already. */
369 while (rs_stack)
370 {
371 old_rs = rs_stack;
372 rs_stack = rs_stack->next;
373 free_reg_state (old_rs);
374 }
375 return ret;
376}
377
378static int
379fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info)
380{
381 int ret, dynamic = 1;
382
mostang.com!davidmc1437142005-05-03 09:13:17 +0000383 --ip;
384
homeip.net!davidm88160e02004-08-17 15:34:28 +0000385 if (c->pi_valid && !need_unwind_info)
386 return 0;
387
mostang.com!davidmc1437142005-05-03 09:13:17 +0000388 memset (&c->pi, 0, sizeof (c->pi));
389
homeip.net!davidm88160e02004-08-17 15:34:28 +0000390 /* check dynamic info first --- it overrides everything else */
391 ret = unwi_find_dynamic_proc_info (c->as, ip, &c->pi, need_unwind_info,
392 c->as_arg);
393 if (ret == -UNW_ENOINFO)
394 {
395 dynamic = 0;
396 if ((ret = tdep_find_proc_info (c, ip, need_unwind_info)) < 0)
397 return ret;
398 }
399
400 c->pi_valid = 1;
401 c->pi_is_dynamic = dynamic;
402 return ret;
403}
404
405static int
406parse_dynamic (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr)
407{
408 Debug (1, "Not yet implemented\n");
409#if 0
410 /* Don't forget to set the ret_addr_column! */
411 c->ret_addr_column = XXX;
412#endif
413 return -UNW_ENOINFO;
414}
415
416static inline void
417put_unwind_info (struct dwarf_cursor *c, unw_proc_info_t *pi)
418{
419 if (!c->pi_valid)
420 return;
421
422 if (c->pi_is_dynamic)
423 unwi_put_dynamic_unwind_info (c->as, pi, c->as_arg);
mostang.com!davidmc1437142005-05-03 09:13:17 +0000424 else if (pi->unwind_info);
425 {
426 mempool_free (&dwarf_cie_info_pool, pi->unwind_info);
427 pi->unwind_info = NULL;
428 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000429}
430
431static inline int
432parse_fde (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr)
433{
mostang.com!davidmc1437142005-05-03 09:13:17 +0000434 struct dwarf_cie_info *dci;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000435 unw_word_t addr;
436 int ret;
437
mostang.com!davidmc1437142005-05-03 09:13:17 +0000438 dci = c->pi.unwind_info;
439 c->ret_addr_column = dci->ret_addr_column;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000440
mostang.com!davidmc1437142005-05-03 09:13:17 +0000441 addr = dci->cie_instr_start;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000442 if ((ret = run_cfi_program (c, sr, ~(unw_word_t) 0, &addr,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000443 dci->cie_instr_end, dci)) < 0)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000444 return ret;
445
446 memcpy (&sr->rs_initial, &sr->rs_current, sizeof (sr->rs_initial));
447
mostang.com!davidmc1437142005-05-03 09:13:17 +0000448 addr = dci->fde_instr_start;
449 if ((ret = run_cfi_program (c, sr, ip, &addr, dci->fde_instr_end, dci)) < 0)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000450 return ret;
451
452 return 0;
453}
454
455static int
456create_state_record_for (struct dwarf_cursor *c, dwarf_state_record_t *sr,
457 unw_word_t ip)
458{
459 int i, ret;
460
461 assert (c->pi_valid);
462
mostang.com!davidmc1437142005-05-03 09:13:17 +0000463 memset (sr, 0, sizeof (*sr));
464 for (i = 0; i < DWARF_NUM_PRESERVED_REGS + 2; ++i)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000465 set_reg (sr, i, DWARF_WHERE_SAME, 0);
466
467 switch (c->pi.format)
468 {
mostang.com!davidmc1437142005-05-03 09:13:17 +0000469 case UNW_INFO_FORMAT_TABLE:
470 case UNW_INFO_FORMAT_REMOTE_TABLE:
homeip.net!davidm88160e02004-08-17 15:34:28 +0000471 ret = parse_fde (c, ip, sr);
472 break;
473
474 case UNW_INFO_FORMAT_DYNAMIC:
475 ret = parse_dynamic (c, ip, sr);
476 break;
477
homeip.net!davidm88160e02004-08-17 15:34:28 +0000478 default:
479 Debug (1, "Unexpected unwind-info format %d\n", c->pi.format);
480 ret = -UNW_EINVAL;
481 }
482 return ret;
483}
484
485static inline int
486eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as,
487 unw_accessors_t *a, unw_word_t addr,
488 dwarf_loc_t *locp, void *arg)
489{
490 int ret, is_register;
491 unw_word_t len, val;
492
493 /* read the length of the expression: */
494 if ((ret = dwarf_read_uleb128 (as, a, &addr, &len, arg)) < 0)
495 return ret;
496
497 /* evaluate the expression: */
498 if ((ret = dwarf_eval_expr (c, &addr, len, &val, &is_register)) < 0)
499 return ret;
500
501 if (is_register)
502 *locp = DWARF_REG_LOC (c, dwarf_to_unw_regnum (val));
503 else
504 *locp = DWARF_MEM_LOC (c, val);
505
506 return 0;
507}
508
509static int
510apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
511{
512 unw_word_t regnum, addr, cfa;
513 unw_addr_space_t as;
514 dwarf_loc_t cfa_loc;
515 unw_accessors_t *a;
516 int i, ret;
517 void *arg;
518
519 as = c->as;
520 arg = c->as_arg;
521 a = unw_get_accessors (as);
522
mostang.com!davidmc1437142005-05-03 09:13:17 +0000523 /* Evaluate the CFA first, because it may be referred to by other
homeip.net!davidm88160e02004-08-17 15:34:28 +0000524 expressions. */
525
526 if (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG)
527 {
528 /* CFA is equal to [reg] + offset: */
529
homeip.net!davidm642607d2004-08-20 11:23:15 +0000530 /* As a special-case, if the stack-pointer is the CFA and the
531 stack-pointer wasn't saved, popping the CFA implicitly pops
532 the stack-pointer as well. */
533 if ((rs->reg[DWARF_CFA_REG_COLUMN].val == UNW_TDEP_SP)
534 && (rs->reg[UNW_TDEP_SP].where == DWARF_WHERE_SAME))
homeip.net!davidm642607d2004-08-20 11:23:15 +0000535 cfa = c->cfa;
homeip.net!davidm642607d2004-08-20 11:23:15 +0000536 else
537 {
538 regnum = dwarf_to_unw_regnum (rs->reg[DWARF_CFA_REG_COLUMN].val);
539 if ((ret = unw_get_reg ((unw_cursor_t *) c, regnum, &cfa)) < 0)
540 return ret;
541 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000542 cfa += rs->reg[DWARF_CFA_OFF_COLUMN].val;
543 }
544 else
545 {
546 /* CFA is equal to EXPR: */
547
548 assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPR);
549
550 addr = rs->reg[DWARF_CFA_REG_COLUMN].val;
551 if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0)
552 return ret;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000553 /* the returned location better be a memory location... */
554 if (DWARF_IS_REG_LOC (cfa_loc))
555 return -UNW_EBADFRAME;
556 cfa = DWARF_GET_LOC (cfa_loc);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000557 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000558
559 for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
560 {
561 switch ((dwarf_where_t) rs->reg[i].where)
562 {
563 case DWARF_WHERE_UNDEF:
564 c->loc[i] = DWARF_NULL_LOC;
565 break;
566
567 case DWARF_WHERE_SAME:
568 break;
569
570 case DWARF_WHERE_CFAREL:
571 c->loc[i] = DWARF_MEM_LOC (c, cfa + rs->reg[i].val);
572 break;
573
574 case DWARF_WHERE_REG:
575 c->loc[i] = DWARF_REG_LOC (c, dwarf_to_unw_regnum (rs->reg[i].val));
576 break;
577
578 case DWARF_WHERE_EXPR:
579 addr = rs->reg[i].val;
580 if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) , 0)
581 return ret;
582 break;
583 }
584 }
mostang.com!davidmc1437142005-05-03 09:13:17 +0000585 c->cfa = cfa;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000586 return 0;
587}
588
589HIDDEN int
590dwarf_find_save_locs (struct dwarf_cursor *c)
591{
592 dwarf_state_record_t sr;
593 int ret;
594
595 if ((ret = fetch_proc_info (c, c->ip, 1)) < 0)
596 return ret;
597
598 if ((ret = create_state_record_for (c, &sr, c->ip)) < 0)
599 return ret;
600
601 if ((ret = apply_reg_state (c, &sr.rs_current)) < 0)
602 return ret;
603
604 put_unwind_info (c, &c->pi);
605 return 0;
606}
607
608/* The proc-info must be valid for IP before this routine can be
609 called. */
610HIDDEN int
611dwarf_create_state_record (struct dwarf_cursor *c, dwarf_state_record_t *sr)
612{
613 return create_state_record_for (c, sr, c->ip);
614}
615
616HIDDEN int
617dwarf_make_proc_info (struct dwarf_cursor *c)
618{
619#if 0
620 if (c->as->caching_policy == UNW_CACHE_NONE
621 || get_cached_proc_info (c) < 0)
622#endif
623 /* Lookup it up the slow way... */
624 return fetch_proc_info (c, c->ip, 0);
625 return 0;
626}