4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2012 Milan Jurik. All rights reserved.
29 * interface used by unwind support to query frame descriptor info
35 #include <sys/types.h>
36 #include "stack_unwind.h"
37 #include "unwind_context.h"
42 DW_CFA_set_loc
= 0x01,
43 DW_CFA_advance_loc1
= 0x02,
44 DW_CFA_advance_loc2
= 0x03,
45 DW_CFA_advance_loc4
= 0x04,
46 DW_CFA_offset_extended
= 0x05,
47 DW_CFA_restore_extended
= 0x06,
48 DW_CFA_undefined
= 0x07,
49 DW_CFA_same_value
= 0x08,
50 DW_CFA_register
= 0x09,
51 DW_CFA_remember_state
= 0x0a,
52 DW_CFA_restore_state
= 0x0b,
53 DW_CFA_def_cfa
= 0x0c,
54 DW_CFA_def_cfa_register
= 0x0d,
55 DW_CFA_def_cfa_offset
= 0x0e,
56 DW_CFA_def_cfa_expression
= 0x0f,
57 DW_CFA_expression
= 0x10,
58 DW_CFA_offset_extended_sf
= 0x11,
59 DW_CFA_def_cfa_sf
= 0x12,
60 DW_CFA_def_cfa_offset_sf
= 0x13,
62 DW_CFA_SUNW_advance_loc
= 0x1d,
63 DW_CFA_SUNW_offset
= 0x1e,
64 DW_CFA_SUNW_restore
= 0x1f,
65 DW_CFA_advance_loc
= 0x40,
70 struct operation_desc
{
71 enum operand_desc op1
;
72 enum operand_desc op2
;
75 struct operation_desc cfa_operations
[] = {
76 {NO_OPR
, NO_OPR
}, /* DW_CFA_nop */
77 {ADDR
, NO_OPR
}, /* DW_CFA_set_loc - address */
78 {UNUM8
, NO_OPR
}, /* DW_CFA_advance_loc1 - delta */
79 {UNUM16
, NO_OPR
}, /* DW_CFA_advance_loc2 - delta */
80 {UNUM32
, NO_OPR
}, /* DW_CFA_advance_loc4 - delta */
81 {ULEB128
, ULEB128_FAC
}, /* DW_CFA_offset_extended - reg, */
82 /* data factored offset */
83 {ULEB128
, NO_OPR
}, /* DW_CFA_restore_extended - register */
84 {ULEB128
, NO_OPR
}, /* DW_CFA_undefined - register */
85 {ULEB128
, NO_OPR
}, /* DW_CFA_same_value - register */
86 {ULEB128
, ULEB128_SREG
}, /* DW_CFA_register - register, register */
87 {NO_OPR
, NO_OPR
}, /* DW_CFA_remember_state */
88 {NO_OPR
, NO_OPR
}, /* DW_CFA_restore_state */
89 {ULEB128_SREG
, ULEB128
}, /* DW_CFA_def_cfa - register, offset */
90 {ULEB128_SREG
, NO_OPR
}, /* DW_CFA_def_cfa_register - register */
91 {ULEB128
, NO_OPR
}, /* DW_CFA_def_cfa_offset - offset */
92 {BLOCK
, NO_OPR
}, /* DW_CFA_def_cfa_expression - expression */
93 {ULEB128
, BLOCK
}, /* DW_CFA_expression - reg, expression */
94 {ULEB128
, SLEB128_FAC
}, /* DW_CFA_offset_extended_sf - reg, */
95 /* data factored offset */
96 {ULEB128_SREG
, SLEB128_FAC
}, /* DW_CFA_def_cfa_sf - reg, */
97 /* data factored offset */
98 {SLEB128_FAC
, NO_OPR
}, /* DW_CFA_def_cfa_offset_sf - */
99 /* data fctored offset */
109 {UNUM6_CFAC
, NO_OPR
}, /* DW_CFA_SUNW_advance_loc - */
110 /* code factored delta */
111 {UNUM6
, ULEB128_FAC
}, /* DW_CFA_SUNW_offset - reg */
112 /* data factored offset */
113 {UNUM6
, NO_OPR
} /* DW_CFA_SUNW_restore */
116 uint64_t interpret_ops(void *data
, void *data_end
,
117 ptrdiff_t reloc
, uint64_t current_loc
, uint64_t pc
,
118 struct register_state f_state
[],
119 struct register_state f_start_state
[],
120 int daf
, int caf
, int enc
);
123 * The entry-point state of old_ctx defines the current
124 * suspended state of the caller (in new_ctx). If the old info
125 * will not be refered to again, old_ctx == new_ctx is OK
128 _Unw_Propagate_Registers(struct _Unwind_Context
*old_ctx
,
129 struct _Unwind_Context
*new_ctx
)
131 new_ctx
->current_regs
[SP_RSP
] = old_ctx
->cfa
;
132 new_ctx
->pc
= old_ctx
->ra
;
133 new_ctx
->current_regs
[FP_RBP
] = old_ctx
->entry_regs
[FP_RBP
];
134 new_ctx
->current_regs
[GPR_RBX
] = old_ctx
->entry_regs
[GPR_RBX
];
135 new_ctx
->current_regs
[EIR_R12
] = old_ctx
->entry_regs
[EIR_R12
];
136 new_ctx
->current_regs
[EIR_R13
] = old_ctx
->entry_regs
[EIR_R13
];
137 new_ctx
->current_regs
[EIR_R14
] = old_ctx
->entry_regs
[EIR_R14
];
138 new_ctx
->current_regs
[EIR_R15
] = old_ctx
->entry_regs
[EIR_R15
];
142 fix_cfa(struct _Unwind_Context
*ctx
, struct register_state
*rs
)
144 switch (rs
[CF_ADDR
].rule
) {
148 case register_rule
: /* CFA = offset + source_reg */
149 ctx
->cfa
= (ctx
->current_regs
)[rs
[CF_ADDR
].source_reg
] +
152 case constant_rule
: /* CFA = offset */
153 ctx
->cfa
= rs
[CF_ADDR
].offset
;
155 case indirect_rule
: /* CFA = *(offset + source_reg) */
156 ctx
->cfa
= *(uint64_t *)
157 (ctx
->current_regs
[rs
[CF_ADDR
].source_reg
] +
161 ctx
->entry_regs
[SP_RSP
] = ctx
->cfa
;
165 fix_ra(struct _Unwind_Context
*ctx
, struct register_state
*rs
)
167 switch (rs
[RET_ADD
].rule
) {
172 case offset_rule
: /* RA = *(offset + CFA) */
173 ctx
->ra
= *(uint64_t *)(ctx
->cfa
+ rs
[RET_ADD
].offset
);
175 case register_rule
: /* RA = offset + source_reg */
176 ctx
->ra
= ctx
->current_regs
[rs
[RET_ADD
].source_reg
] +
179 case indirect_rule
: /* RA = *(offset + source_reg) */
180 ctx
->ra
= *(uint64_t *)
181 (ctx
->current_regs
[rs
[RET_ADD
].source_reg
] +
188 fix_reg(struct _Unwind_Context
*ctx
, struct register_state
*rs
, int index
)
190 switch (rs
[index
].rule
) {
192 ctx
->entry_regs
[index
] = ctx
->current_regs
[index
];
194 case offset_rule
: /* target_reg = *(offset + CFA) */
195 ctx
->entry_regs
[index
] = *(uint64_t *)
196 (ctx
->cfa
+ rs
[index
].offset
);
198 case is_offset_rule
: /* target_reg = offset + CFA */
199 ctx
->entry_regs
[index
] = ctx
->cfa
+ rs
[index
].offset
;
201 case register_rule
: /* target_reg = offset + source_reg */
202 ctx
->entry_regs
[index
] =
203 ctx
->current_regs
[rs
[index
].source_reg
] +
206 case constant_rule
: /* target_reg = offset */
207 ctx
->entry_regs
[index
] = rs
[index
].offset
;
209 case indirect_rule
: /* target_reg = *(offset + source_reg) */
210 ctx
->entry_regs
[index
] = *(uint64_t *)
211 (ctx
->current_regs
[rs
[index
].source_reg
] +
219 * Input: f->{cie_ops, cie_ops_end, fde_ops, fde_ops_end}
220 * + location of DWARF opcodes
221 * ctx->{current_regs, pc}
222 * + register values and pc at point of suspension
223 * Output: ctx->{entry_regs, cfa, ra}
224 * + register values when function was entered
225 * + Cannonical Frame Address
229 _Unw_Rollback_Registers(struct eh_frame_fields
*f
,
230 struct _Unwind_Context
*ctx
)
232 /* GPRs, RET_ADD, and CF_ADDR */
233 struct register_state func_state
[18];
234 struct register_state func_start_state
[18];
235 struct register_state nop
= { 0, undefined_rule
, 0 };
241 * When no FDE we assume all routines have a frame pointer
242 * and pass back existing callee saves registers
244 if (ctx
->current_regs
[FP_RBP
] < ctx
->current_regs
[SP_RSP
]) {
250 ctx
->entry_regs
[FP_RBP
] = ((uint64_t *)
251 (ctx
->current_regs
[FP_RBP
]))[0];
252 ctx
->cfa
= ctx
->current_regs
[FP_RBP
] + 16;
253 ctx
->entry_regs
[SP_RSP
] = ctx
->cfa
;
254 ctx
->entry_regs
[GPR_RBX
] = ctx
->current_regs
[GPR_RBX
];
255 ctx
->entry_regs
[EIR_R12
] = ctx
->current_regs
[EIR_R12
];
256 ctx
->entry_regs
[EIR_R13
] = ctx
->current_regs
[EIR_R13
];
257 ctx
->entry_regs
[EIR_R14
] = ctx
->current_regs
[EIR_R14
];
258 ctx
->entry_regs
[EIR_R15
] = ctx
->current_regs
[EIR_R15
];
259 ctx
->ra
= ((uint64_t *)ctx
->cfa
)[-1];
263 for (i
= 0; i
< 18; i
++)
264 func_start_state
[i
] = nop
;
265 first_pc
= interpret_ops(f
->cie_ops
, f
->cie_ops_end
,
266 f
->cie_reloc
, ctx
->func
, ctx
->pc
, func_start_state
, 0,
267 f
->data_align
, f
->code_align
, f
->code_enc
);
268 for (i
= 0; i
< 18; i
++)
269 func_state
[i
] = func_start_state
[i
];
270 (void) interpret_ops(f
->fde_ops
, f
->fde_ops_end
,
271 f
->fde_reloc
, first_pc
, ctx
->pc
, func_state
, func_start_state
,
272 f
->data_align
, f
->code_align
, f
->code_enc
);
274 fix_cfa(ctx
, func_state
);
275 if (ctx
->cfa
< ctx
->current_regs
[SP_RSP
]) {
281 fix_ra(ctx
, func_state
);
282 fix_reg(ctx
, func_state
, GPR_RBX
);
283 fix_reg(ctx
, func_state
, FP_RBP
);
284 fix_reg(ctx
, func_state
, EIR_R12
);
285 fix_reg(ctx
, func_state
, EIR_R13
);
286 fix_reg(ctx
, func_state
, EIR_R14
);
287 fix_reg(ctx
, func_state
, EIR_R15
);
293 * remap two-bit opcodes into a separate range or grab eight-bit opcode
294 * and advance pointer past it.
297 separate_op(void **pp
)
299 uint8_t c
= **((uint8_t **)pp
);
303 case DW_CFA_advance_loc
:
304 return (DW_CFA_SUNW_advance_loc
);
306 return (DW_CFA_SUNW_offset
);
308 return (DW_CFA_SUNW_restore
);
311 *pp
= (void *)((*(intptr_t *)pp
) + 1);
317 extractuleb(void **datap
)
319 uint8_t *data
= *(uint8_t **)datap
;
326 val
= (*data
) & 0x7f;
327 more
= ((*data
++) & 0x80) >> 7;
328 res
= res
| val
<< shift
;
331 *datap
= (void *)data
;
336 extractsleb(void** datap
)
338 uint8_t *data
= *datap
;
345 val
= (*data
) & 0x7f;
346 more
= ((*data
++) & 0x80) >> 7;
347 res
= res
| val
<< shift
;
350 *datap
= (void*) data
;
351 res
= (res
<< (64 - shift
)) >> (64 - shift
);
355 static uint64_t get_encoded_val(void **datap
, ptrdiff_t reloc
, int enc
);
358 * do all field extractions needed for CFA operands and encoded FDE
362 _Unw_get_val(void **datap
, ptrdiff_t reloc
,
363 enum operand_desc opr
, int daf
, int caf
, int enc
)
365 intptr_t data
= (intptr_t)*datap
;
374 return (daf
* extractuleb(datap
));
376 return (extractuleb(datap
));
378 res
= (uint64_t)(*((uint8_t *)data
));
381 /* verify that register is one which is being tracked */
396 res
= (uint64_t)(0x3f & *((uint8_t *)data
));
400 res
= (uint64_t)(*((uint8_t *)data
));
404 res
= (uint64_t)(*((uint16_t *)data
));
408 res
= (uint64_t)(*((uint32_t *)data
));
412 res
= caf
* (uint64_t)(0x3f & *((uint8_t *)data
));
416 res
= caf
* (uint64_t)(*((uint8_t *)data
));
420 res
= caf
* (uint64_t)(*((uint16_t *)data
));
424 res
= caf
* (uint64_t)(*((uint32_t *)data
));
428 res
= (uint64_t)(*((uint64_t *)data
));
432 res
= (uint64_t)(int64_t)(*((int8_t *)data
));
436 res
= (uint64_t)(int64_t)(*((int16_t *)data
));
440 res
= (uint64_t)(int64_t)(*((int32_t *)data
));
444 res
= (uint64_t)(*((int64_t *)data
));
448 return (daf
* extractsleb(datap
));
450 return (extractsleb(datap
));
452 /* max length of augmentation string is 4 */
455 while (*rp
++ = *dp
++)
460 return (get_encoded_val(datap
, reloc
, enc
));
462 return (get_encoded_val(datap
, reloc
, enc
& 0x7));
464 res
= 0; /* not implemented */
467 *datap
= (void*)data
;
472 get_encoded_val(void **datap
, ptrdiff_t reloc
, int enc
)
475 int rel
= (enc
>> 4) & 0xf;
476 intptr_t loc
= ((intptr_t)*datap
) + reloc
;
481 res
= _Unw_get_val(datap
, reloc
, ULEB128
, 1, 1, 0);
484 res
= _Unw_get_val(datap
, reloc
, UNUM16
, 1, 1, 0);
487 res
= _Unw_get_val(datap
, reloc
, UNUM32
, 1, 1, 0);
490 res
= _Unw_get_val(datap
, reloc
, UNUM64
, 1, 1, 0);
493 res
= _Unw_get_val(datap
, reloc
, SLEB128
, 1, 1, 0);
496 res
= _Unw_get_val(datap
, reloc
, SNUM16
, 1, 1, 0);
499 res
= _Unw_get_val(datap
, reloc
, SNUM32
, 1, 1, 0);
502 res
= _Unw_get_val(datap
, reloc
, SNUM64
, 1, 1, 0);
514 /* remainder not implemented */
521 int interpret_op(void **datap
, ptrdiff_t reloc
,
522 uint64_t *reached_pc_p
, uint64_t pc
,
523 struct register_state f_state
[],
524 struct register_state f_start_state
[],
525 int daf
, int caf
, int enc
);
528 interpret_ops(void *data
, void *data_end
,
530 uint64_t start_pc
, uint64_t pc
,
531 struct register_state f_state
[],
532 struct register_state f_start_state
[],
533 int daf
, int caf
, int enc
)
536 uint64_t reached_pc
= start_pc
;
538 while (d
< data_end
) {
539 if (interpret_op(&d
, reloc
, &reached_pc
, pc
,
540 f_state
, f_start_state
, daf
, caf
, enc
))
547 interpret_op(void **datap
, ptrdiff_t reloc
,
548 uint64_t *reached_pc_p
, uint64_t pc
,
549 struct register_state f_state
[],
550 struct register_state f_start_state
[],
551 int daf
, int caf
, int enc
)
553 enum CFA_ops op
= separate_op(datap
);
554 enum operand_desc opr1
= (cfa_operations
[op
]).op1
;
555 enum operand_desc opr2
= (cfa_operations
[op
]).op2
;
557 uint64_t val1
= _Unw_get_val(datap
, reloc
, opr1
, daf
, caf
, enc
);
558 uint64_t val2
= _Unw_get_val(datap
, reloc
, opr2
, daf
, caf
, enc
);
559 if ((opr1
== ULEB128_SREG
&& val1
== BAD_REG
) ||
560 (opr2
== ULEB128_SREG
&& val2
== BAD_REG
))
568 *reached_pc_p
= val1
;
570 case DW_CFA_advance_loc1
:
571 case DW_CFA_advance_loc2
:
572 case DW_CFA_advance_loc4
:
573 if (*reached_pc_p
+ val1
> pc
)
575 *reached_pc_p
+= val1
;
577 case DW_CFA_offset_extended
:
578 f_state
[val1
].rule
= offset_rule
;
579 f_state
[val1
].source_reg
= CF_ADDR
;
580 f_state
[val1
].offset
= val2
;
582 case DW_CFA_restore_extended
:
583 if (f_start_state
!= 0)
584 f_state
[val1
] = f_start_state
[val1
];
586 case DW_CFA_undefined
:
587 f_state
[val1
].rule
= undefined_rule
;
589 case DW_CFA_same_value
:
590 f_state
[val1
].rule
= same_value_rule
;
592 case DW_CFA_register
:
593 f_state
[val1
].rule
= register_rule
;
594 f_state
[val1
].source_reg
= val2
;
595 f_state
[val1
].offset
= 0;
597 case DW_CFA_remember_state
:
599 case DW_CFA_restore_state
:
602 f_state
[CF_ADDR
].rule
= register_rule
;
603 f_state
[CF_ADDR
].source_reg
= val1
;
604 f_state
[CF_ADDR
].offset
= val2
;
606 case DW_CFA_def_cfa_register
:
607 f_state
[CF_ADDR
].source_reg
= val1
;
609 case DW_CFA_def_cfa_offset
:
610 f_state
[CF_ADDR
].offset
= val1
;
612 case DW_CFA_def_cfa_expression
:
614 case DW_CFA_expression
:
616 case DW_CFA_offset_extended_sf
:
617 f_state
[val1
].rule
= offset_rule
;
618 f_state
[val1
].source_reg
= CF_ADDR
;
619 f_state
[val1
].offset
= val2
;
621 case DW_CFA_def_cfa_sf
:
622 f_state
[CF_ADDR
].rule
= register_rule
;
623 f_state
[CF_ADDR
].source_reg
= val1
;
624 f_state
[CF_ADDR
].offset
= val2
;
626 case DW_CFA_def_cfa_offset_sf
:
627 f_state
[CF_ADDR
].offset
= val1
;
629 case DW_CFA_SUNW_advance_loc
:
630 if (*reached_pc_p
+ val1
> pc
)
632 *reached_pc_p
+= val1
;
634 case DW_CFA_SUNW_offset
:
635 f_state
[val1
].rule
= offset_rule
;
636 f_state
[val1
].source_reg
= CF_ADDR
;
637 f_state
[val1
].offset
= val2
;
639 case DW_CFA_SUNW_restore
:
640 if (f_start_state
!= 0)
641 f_state
[val1
] = f_start_state
[val1
];