blob: acdd79f8a289b9f6b0dcd0d70949b32be7b09dcf [file] [log] [blame]
homeip.net!davidm88160e02004-08-17 15:34:28 +00001/* libunwind - a platform-independent unwind library
2 Copyright (c) 2003 Hewlett-Packard Development Company, L.P.
3 Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
25
26#include <string.h>
27
28#include "dwarf_i.h"
29#include "tdep.h"
30
31#define alloc_reg_state() (mempool_alloc (&dwarf_reg_state_pool))
32#define free_reg_state(rs) (mempool_free (&dwarf_reg_state_pool, rs))
33
34static inline int
35read_regnum (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr,
36 unw_word_t *valp, void *arg)
37{
38 int ret;
39
40 if ((ret = dwarf_read_uleb128 (as, a, addr, valp, arg)) < 0)
41 return ret;
42
43 if (*valp >= DWARF_NUM_PRESERVED_REGS)
44 {
homeip.net!davidm642607d2004-08-20 11:23:15 +000045 Debug (1, "Invalid register number %u\n", (unsigned int) *valp);
homeip.net!davidm88160e02004-08-17 15:34:28 +000046 return -UNW_EBADREG;
47 }
48 return 0;
49}
50
51static inline void
52set_reg (dwarf_state_record_t *sr, unw_word_t regnum, dwarf_where_t where,
53 unw_word_t val)
54{
55 sr->rs_current.reg[regnum].where = where;
56 sr->rs_current.reg[regnum].val = val;
57}
58
59/* Run a CFI program to update the register state. */
60static int
61run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr,
62 unw_word_t ip, unw_word_t *addr, unw_word_t end_addr,
mostang.com!davidmc1437142005-05-03 09:13:17 +000063 struct dwarf_cie_info *dci)
homeip.net!davidm88160e02004-08-17 15:34:28 +000064{
65 unw_word_t curr_ip, operand = 0, regnum, val, len, fde_encoding;
66 dwarf_reg_state_t *rs_stack = NULL, *new_rs, *old_rs;
67 unw_addr_space_t as;
68 unw_accessors_t *a;
69 uint8_t u8, op;
70 uint16_t u16;
71 uint32_t u32;
72 void *arg;
73 int ret;
74
75 as = c->as;
76 arg = c->as_arg;
77 a = unw_get_accessors (as);
78 curr_ip = c->pi.start_ip;
79
80 while (curr_ip < ip && *addr < end_addr)
81 {
82 if ((ret = dwarf_readu8 (as, a, addr, &op, arg)) < 0)
83 return ret;
84
85 if (op & DWARF_CFA_OPCODE_MASK)
86 {
87 operand = op & DWARF_CFA_OPERAND_MASK;
88 op &= ~DWARF_CFA_OPERAND_MASK;
89 }
90 switch ((dwarf_cfa_t) op)
91 {
92 case DW_CFA_advance_loc:
mostang.com!davidmc1437142005-05-03 09:13:17 +000093 curr_ip += operand * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +000094 Debug (15, "CFA_advance_loc to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +000095 break;
96
97 case DW_CFA_advance_loc1:
98 if ((ret = dwarf_readu8 (as, a, addr, &u8, arg)) < 0)
99 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000100 curr_ip += u8 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000101 Debug (15, "CFA_advance_loc1 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000102 break;
103
104 case DW_CFA_advance_loc2:
105 if ((ret = dwarf_readu16 (as, a, addr, &u16, arg)) < 0)
106 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000107 curr_ip += u16 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000108 Debug (15, "CFA_advance_loc2 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000109 break;
110
111 case DW_CFA_advance_loc4:
112 if ((ret = dwarf_readu32 (as, a, addr, &u32, arg)) < 0)
113 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000114 curr_ip += u32 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000115 Debug (15, "CFA_advance_loc4 to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000116 break;
117
118 case DW_CFA_MIPS_advance_loc8:
119#ifdef UNW_TARGET_MIPS
120 {
121 uint64_t u64;
122
123 if ((ret = dwarf_readu64 (as, a, addr, &u64, arg)) < 0)
124 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000125 curr_ip += u64 * dci->code_align;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000126 Debug (15, "CFA_MIPS_advance_loc8\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000127 break;
128 }
129#else
130 Debug (1, "DW_CFA_MIPS_advance_loc8 on non-MIPS target\n");
131 ret = -UNW_EINVAL;
132 goto fail;
133#endif
134
135 case DW_CFA_offset:
136 regnum = operand;
137 if (regnum >= DWARF_NUM_PRESERVED_REGS)
138 {
139 Debug (1, "Invalid register number %u in DW_cfa_OFFSET\n",
homeip.net!davidm642607d2004-08-20 11:23:15 +0000140 (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000141 ret = -UNW_EBADREG;
142 goto fail;
143 }
144 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
145 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000146 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000147 Debug (15, "CFA_offset r%lu at cfa+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000148 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000149 break;
150
151 case DW_CFA_offset_extended:
152 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
153 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
154 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000155 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000156 Debug (15, "CFA_offset_extended r%lu at cf+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000157 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000158 break;
159
160 case DW_CFA_offset_extended_sf:
161 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
162 || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
163 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000164 set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000165 Debug (15, "CFA_offset_extended_sf r%lu at cf+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000166 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000167 break;
168
169 case DW_CFA_restore:
170 regnum = operand;
171 if (regnum >= DWARF_NUM_PRESERVED_REGS)
172 {
173 Debug (1, "Invalid register number %u in DW_CFA_restore\n",
homeip.net!davidm642607d2004-08-20 11:23:15 +0000174 (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000175 ret = -UNW_EINVAL;
176 goto fail;
177 }
178 sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000179 Debug (15, "CFA_restore r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000180 break;
181
182 case DW_CFA_restore_extended:
183 if ((ret = dwarf_read_uleb128 (as, a, addr, &regnum, arg)) < 0)
184 goto fail;
185 if (regnum >= DWARF_NUM_PRESERVED_REGS)
186 {
187 Debug (1, "Invalid register number %u in "
homeip.net!davidm642607d2004-08-20 11:23:15 +0000188 "DW_CFA_restore_extended\n", (unsigned int) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000189 ret = -UNW_EINVAL;
190 goto fail;
191 }
192 sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000193 Debug (15, "CFA_restore_extended r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000194 break;
195
196 case DW_CFA_nop:
197 break;
198
199 case DW_CFA_set_loc:
mostang.com!davidmc1437142005-05-03 09:13:17 +0000200 fde_encoding = dci->fde_encoding;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000201 if ((ret = dwarf_read_encoded_pointer (as, a, addr, fde_encoding,
202 &c->pi, &curr_ip,
203 arg)) < 0)
204 goto fail;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000205 Debug (15, "CFA_set_loc to 0x%lx\n", (long) curr_ip);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000206 break;
207
208 case DW_CFA_undefined:
209 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
210 goto fail;
211 set_reg (sr, regnum, DWARF_WHERE_UNDEF, 0);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000212 Debug (15, "CFA_undefined r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000213 break;
214
215 case DW_CFA_same_value:
216 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
217 goto fail;
218 set_reg (sr, regnum, DWARF_WHERE_SAME, 0);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000219 Debug (15, "CFA_same_value r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000220 break;
221
222 case DW_CFA_register:
223 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
224 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
225 goto fail;
226 set_reg (sr, regnum, DWARF_WHERE_REG, val);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000227 Debug (15, "CFA_register r%lu to r%lu\n", (long) regnum, (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000228 break;
229
230 case DW_CFA_remember_state:
231 new_rs = alloc_reg_state ();
232 if (!new_rs)
233 {
234 Debug (1, "Out of memory in DW_CFA_remember_state\n");
235 ret = -UNW_ENOMEM;
236 goto fail;
237 }
238
239 memcpy (new_rs->reg, sr->rs_current.reg, sizeof (new_rs->reg));
240 new_rs->next = rs_stack;
241 rs_stack = new_rs;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000242 Debug (15, "CFA_remember_state\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000243 break;
244
245 case DW_CFA_restore_state:
246 if (!rs_stack)
247 {
248 Debug (1, "register-state stack underflow\n");
249 ret = -UNW_EINVAL;
250 goto fail;
251 }
252 memcpy (&sr->rs_current.reg, &rs_stack->reg, sizeof (rs_stack->reg));
253 old_rs = rs_stack;
254 rs_stack = rs_stack->next;
255 free_reg_state (old_rs);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000256 Debug (15, "CFA_restore_state\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000257 break;
258
259 case DW_CFA_def_cfa:
260 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
261 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
262 goto fail;
263 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
264 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000265 Debug (15, "CFA_def_cfa r%lu+0x%lx\n", (long) regnum, (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000266 break;
267
268 case DW_CFA_def_cfa_sf:
269 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
270 || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
271 goto fail;
272 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
273 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000274 val * dci->data_align); /* factored! */
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000275 Debug (15, "CFA_def_cfa_sf r%lu+0x%lx\n",
mostang.com!davidmc1437142005-05-03 09:13:17 +0000276 (long) regnum, (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000277 break;
278
279 case DW_CFA_def_cfa_register:
280 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
281 goto fail;
282 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000283 Debug (15, "CFA_def_cfa_register r%lu\n", (long) regnum);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000284 break;
285
286 case DW_CFA_def_cfa_offset:
287 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
288 goto fail;
289 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */
mostang.com!davidmc1437142005-05-03 09:13:17 +0000290 Debug (15, "CFA_def_cfa_offset 0x%lx\n", (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000291 break;
292
293 case DW_CFA_def_cfa_offset_sf:
294 if ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0)
295 goto fail;
296 set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000297 val * dci->data_align); /* factored! */
298 Debug (15, "CFA_def_cfa_offset_sf 0x%lx\n",
299 (long) (val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000300 break;
301
302 case DW_CFA_def_cfa_expression:
303 /* Save the address of the DW_FORM_block for later evaluation. */
304 set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPR, *addr);
305
306 if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
307 goto fail;
308
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000309 Debug (15, "CFA_def_cfa_expr @ 0x%lx [%lu bytes]\n",
homeip.net!davidm88160e02004-08-17 15:34:28 +0000310 (long) *addr, (long) len);
311 *addr += len;
312 break;
313
mostang.com!davidmc1437142005-05-03 09:13:17 +0000314 case DW_CFA_expression:
homeip.net!davidm88160e02004-08-17 15:34:28 +0000315 if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
316 goto fail;
317
318 /* Save the address of the DW_FORM_block for later evaluation. */
319 set_reg (sr, regnum, DWARF_WHERE_EXPR, *addr);
320
321 if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
322 goto fail;
323
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000324 Debug (15, "CFA_expression r%lu @ 0x%lx [%lu bytes]\n",
homeip.net!davidm88160e02004-08-17 15:34:28 +0000325 (long) regnum, (long) addr, (long) len);
326 *addr += len;
327 break;
328
329 case DW_CFA_GNU_args_size:
330 if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
331 goto fail;
332 sr->args_size = val;
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000333 Debug (15, "CFA_GNU_args_size %lu\n", (long) val);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000334 break;
335
336 case DW_CFA_GNU_negative_offset_extended:
337 /* A comment in GCC says that this is obsoleted by
338 DW_CFA_offset_extended_sf, but that it's used by older
339 PowerPC code. */
340 if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
341 || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
342 goto fail;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000343 set_reg (sr, regnum, DWARF_WHERE_CFAREL, -(val * dci->data_align));
344 Debug (15, "CFA_GNU_negative_offset_extended cfa+0x%lx\n",
345 (long) -(val * dci->data_align));
homeip.net!davidm88160e02004-08-17 15:34:28 +0000346 break;
347
348 case DW_CFA_GNU_window_save:
349#ifdef UNW_TARGET_SPARC
350 /* This is a special CFA to handle all 16 windowed registers
351 on SPARC. */
352 for (regnum = 16; regnum < 32; ++regnum)
353 set_reg (sr, regnum, DWARF_WHERE_CFAREL,
354 (regnum - 16) * sizeof (unw_word_t));
homeip.net!davidmaae368a2004-08-19 12:26:11 +0000355 Debug (15, "CFA_GNU_window_save\n");
homeip.net!davidm88160e02004-08-17 15:34:28 +0000356 break;
357#else
358 /* FALL THROUGH */
359#endif
360 case DW_CFA_lo_user:
361 case DW_CFA_hi_user:
mostang.com!davidmc1437142005-05-03 09:13:17 +0000362 Debug (1, "Unexpected CFA opcode 0x%x\n", op);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000363 ret = -UNW_EINVAL;
364 goto fail;
365 }
366 }
367 ret = 0;
368
369 fail:
370 /* Free the register-state stack, if not empty already. */
371 while (rs_stack)
372 {
373 old_rs = rs_stack;
374 rs_stack = rs_stack->next;
375 free_reg_state (old_rs);
376 }
377 return ret;
378}
379
380static int
381fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info)
382{
383 int ret, dynamic = 1;
384
mostang.com!davidmc1437142005-05-03 09:13:17 +0000385 --ip;
386
homeip.net!davidm88160e02004-08-17 15:34:28 +0000387 if (c->pi_valid && !need_unwind_info)
388 return 0;
389
mostang.com!davidmc1437142005-05-03 09:13:17 +0000390 memset (&c->pi, 0, sizeof (c->pi));
391
homeip.net!davidm88160e02004-08-17 15:34:28 +0000392 /* check dynamic info first --- it overrides everything else */
393 ret = unwi_find_dynamic_proc_info (c->as, ip, &c->pi, need_unwind_info,
394 c->as_arg);
395 if (ret == -UNW_ENOINFO)
396 {
397 dynamic = 0;
398 if ((ret = tdep_find_proc_info (c, ip, need_unwind_info)) < 0)
399 return ret;
400 }
401
402 c->pi_valid = 1;
403 c->pi_is_dynamic = dynamic;
404 return ret;
405}
406
407static int
408parse_dynamic (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr)
409{
410 Debug (1, "Not yet implemented\n");
411#if 0
412 /* Don't forget to set the ret_addr_column! */
413 c->ret_addr_column = XXX;
414#endif
415 return -UNW_ENOINFO;
416}
417
418static inline void
419put_unwind_info (struct dwarf_cursor *c, unw_proc_info_t *pi)
420{
421 if (!c->pi_valid)
422 return;
423
424 if (c->pi_is_dynamic)
425 unwi_put_dynamic_unwind_info (c->as, pi, c->as_arg);
mostang.com!davidmc1437142005-05-03 09:13:17 +0000426 else if (pi->unwind_info);
427 {
428 mempool_free (&dwarf_cie_info_pool, pi->unwind_info);
429 pi->unwind_info = NULL;
430 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000431}
432
433static inline int
434parse_fde (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr)
435{
mostang.com!davidmc1437142005-05-03 09:13:17 +0000436 struct dwarf_cie_info *dci;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000437 unw_word_t addr;
438 int ret;
439
mostang.com!davidmc1437142005-05-03 09:13:17 +0000440 dci = c->pi.unwind_info;
441 c->ret_addr_column = dci->ret_addr_column;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000442
mostang.com!davidmc1437142005-05-03 09:13:17 +0000443 addr = dci->cie_instr_start;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000444 if ((ret = run_cfi_program (c, sr, ~(unw_word_t) 0, &addr,
mostang.com!davidmc1437142005-05-03 09:13:17 +0000445 dci->cie_instr_end, dci)) < 0)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000446 return ret;
447
448 memcpy (&sr->rs_initial, &sr->rs_current, sizeof (sr->rs_initial));
449
mostang.com!davidmc1437142005-05-03 09:13:17 +0000450 addr = dci->fde_instr_start;
451 if ((ret = run_cfi_program (c, sr, ip, &addr, dci->fde_instr_end, dci)) < 0)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000452 return ret;
453
454 return 0;
455}
456
457static int
458create_state_record_for (struct dwarf_cursor *c, dwarf_state_record_t *sr,
459 unw_word_t ip)
460{
461 int i, ret;
462
463 assert (c->pi_valid);
464
mostang.com!davidmc1437142005-05-03 09:13:17 +0000465 memset (sr, 0, sizeof (*sr));
466 for (i = 0; i < DWARF_NUM_PRESERVED_REGS + 2; ++i)
homeip.net!davidm88160e02004-08-17 15:34:28 +0000467 set_reg (sr, i, DWARF_WHERE_SAME, 0);
468
469 switch (c->pi.format)
470 {
mostang.com!davidmc1437142005-05-03 09:13:17 +0000471 case UNW_INFO_FORMAT_TABLE:
472 case UNW_INFO_FORMAT_REMOTE_TABLE:
homeip.net!davidm88160e02004-08-17 15:34:28 +0000473 ret = parse_fde (c, ip, sr);
474 break;
475
476 case UNW_INFO_FORMAT_DYNAMIC:
477 ret = parse_dynamic (c, ip, sr);
478 break;
479
homeip.net!davidm88160e02004-08-17 15:34:28 +0000480 default:
481 Debug (1, "Unexpected unwind-info format %d\n", c->pi.format);
482 ret = -UNW_EINVAL;
483 }
484 return ret;
485}
486
487static inline int
488eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as,
489 unw_accessors_t *a, unw_word_t addr,
490 dwarf_loc_t *locp, void *arg)
491{
492 int ret, is_register;
493 unw_word_t len, val;
494
495 /* read the length of the expression: */
496 if ((ret = dwarf_read_uleb128 (as, a, &addr, &len, arg)) < 0)
497 return ret;
498
499 /* evaluate the expression: */
500 if ((ret = dwarf_eval_expr (c, &addr, len, &val, &is_register)) < 0)
501 return ret;
502
503 if (is_register)
504 *locp = DWARF_REG_LOC (c, dwarf_to_unw_regnum (val));
505 else
506 *locp = DWARF_MEM_LOC (c, val);
507
508 return 0;
509}
510
511static int
512apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
513{
514 unw_word_t regnum, addr, cfa;
515 unw_addr_space_t as;
516 dwarf_loc_t cfa_loc;
517 unw_accessors_t *a;
518 int i, ret;
519 void *arg;
520
521 as = c->as;
522 arg = c->as_arg;
523 a = unw_get_accessors (as);
524
mostang.com!davidmc1437142005-05-03 09:13:17 +0000525 /* Evaluate the CFA first, because it may be referred to by other
homeip.net!davidm88160e02004-08-17 15:34:28 +0000526 expressions. */
527
528 if (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG)
529 {
530 /* CFA is equal to [reg] + offset: */
531
homeip.net!davidm642607d2004-08-20 11:23:15 +0000532 /* As a special-case, if the stack-pointer is the CFA and the
533 stack-pointer wasn't saved, popping the CFA implicitly pops
534 the stack-pointer as well. */
535 if ((rs->reg[DWARF_CFA_REG_COLUMN].val == UNW_TDEP_SP)
536 && (rs->reg[UNW_TDEP_SP].where == DWARF_WHERE_SAME))
homeip.net!davidm642607d2004-08-20 11:23:15 +0000537 cfa = c->cfa;
homeip.net!davidm642607d2004-08-20 11:23:15 +0000538 else
539 {
540 regnum = dwarf_to_unw_regnum (rs->reg[DWARF_CFA_REG_COLUMN].val);
541 if ((ret = unw_get_reg ((unw_cursor_t *) c, regnum, &cfa)) < 0)
542 return ret;
543 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000544 cfa += rs->reg[DWARF_CFA_OFF_COLUMN].val;
545 }
546 else
547 {
548 /* CFA is equal to EXPR: */
549
550 assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPR);
551
552 addr = rs->reg[DWARF_CFA_REG_COLUMN].val;
553 if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0)
554 return ret;
mostang.com!davidmc1437142005-05-03 09:13:17 +0000555 /* the returned location better be a memory location... */
556 if (DWARF_IS_REG_LOC (cfa_loc))
557 return -UNW_EBADFRAME;
558 cfa = DWARF_GET_LOC (cfa_loc);
homeip.net!davidm88160e02004-08-17 15:34:28 +0000559 }
homeip.net!davidm88160e02004-08-17 15:34:28 +0000560
561 for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
562 {
563 switch ((dwarf_where_t) rs->reg[i].where)
564 {
565 case DWARF_WHERE_UNDEF:
566 c->loc[i] = DWARF_NULL_LOC;
567 break;
568
569 case DWARF_WHERE_SAME:
570 break;
571
572 case DWARF_WHERE_CFAREL:
573 c->loc[i] = DWARF_MEM_LOC (c, cfa + rs->reg[i].val);
574 break;
575
576 case DWARF_WHERE_REG:
577 c->loc[i] = DWARF_REG_LOC (c, dwarf_to_unw_regnum (rs->reg[i].val));
578 break;
579
580 case DWARF_WHERE_EXPR:
581 addr = rs->reg[i].val;
582 if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) , 0)
583 return ret;
584 break;
585 }
586 }
mostang.com!davidmc1437142005-05-03 09:13:17 +0000587 c->cfa = cfa;
homeip.net!davidm88160e02004-08-17 15:34:28 +0000588 return 0;
589}
590
591HIDDEN int
592dwarf_find_save_locs (struct dwarf_cursor *c)
593{
594 dwarf_state_record_t sr;
595 int ret;
596
597 if ((ret = fetch_proc_info (c, c->ip, 1)) < 0)
598 return ret;
599
600 if ((ret = create_state_record_for (c, &sr, c->ip)) < 0)
601 return ret;
602
603 if ((ret = apply_reg_state (c, &sr.rs_current)) < 0)
604 return ret;
605
606 put_unwind_info (c, &c->pi);
607 return 0;
608}
609
610/* The proc-info must be valid for IP before this routine can be
611 called. */
612HIDDEN int
613dwarf_create_state_record (struct dwarf_cursor *c, dwarf_state_record_t *sr)
614{
615 return create_state_record_for (c, sr, c->ip);
616}
617
618HIDDEN int
619dwarf_make_proc_info (struct dwarf_cursor *c)
620{
621#if 0
622 if (c->as->caching_policy == UNW_CACHE_NONE
623 || get_cached_proc_info (c) < 0)
624#endif
625 /* Lookup it up the slow way... */
626 return fetch_proc_info (c, c->ip, 0);
627 return 0;
628}