/* * Given the state of the current frame as stored in REGS, execute the unwind * operations in unwind_info until the location counter reaches POS. The result is * stored back into REGS. OUT_CFA will receive the value of the CFA. * If SAVE_LOCATIONS is non-NULL, it should point to an array of size SAVE_LOCATIONS_LEN. * On return, the nth entry will point to the address of the stack slot where register * N was saved, or NULL, if it was not saved by this frame. * This function is signal safe. */ void mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, int nregs, mgreg_t **save_locations, int save_locations_len, guint8 **out_cfa) { Loc locations [NUM_REGS]; int i, pos, reg, cfa_reg, cfa_offset; guint8 *p; guint8 *cfa_val; for (i = 0; i < NUM_REGS; ++i) locations [i].loc_type = LOC_SAME; p = unwind_info; pos = 0; cfa_reg = -1; cfa_offset = -1; while (pos <= ip - start_ip && p < unwind_info + unwind_info_len) { int op = *p & 0xc0; switch (op) { case DW_CFA_advance_loc: UNW_DEBUG (print_dwarf_state (cfa_reg, cfa_offset, pos, nregs, locations)); pos += *p & 0x3f; p ++; break; case DW_CFA_offset: reg = *p & 0x3f; p ++; locations [reg].loc_type = LOC_OFFSET; locations [reg].offset = decode_uleb128 (p, &p) * DWARF_DATA_ALIGN; break; case 0: { int ext_op = *p; p ++; switch (ext_op) { case DW_CFA_def_cfa: cfa_reg = decode_uleb128 (p, &p); cfa_offset = decode_uleb128 (p, &p); break; case DW_CFA_def_cfa_offset: cfa_offset = decode_uleb128 (p, &p); break; case DW_CFA_def_cfa_register: cfa_reg = decode_uleb128 (p, &p); break; case DW_CFA_offset_extended_sf: reg = decode_uleb128 (p, &p); locations [reg].loc_type = LOC_OFFSET; locations [reg].offset = decode_sleb128 (p, &p) * DWARF_DATA_ALIGN; break; case DW_CFA_advance_loc4: pos += read32 (p); p += 4; break; default: g_assert_not_reached (); } break; } default: g_assert_not_reached (); } } if (save_locations) memset (save_locations, 0, save_locations_len * sizeof (mgreg_t*)); cfa_val = (guint8*)regs [mono_dwarf_reg_to_hw_reg (cfa_reg)] + cfa_offset; for (i = 0; i < NUM_REGS; ++i) { if (locations [i].loc_type == LOC_OFFSET) { int hreg = mono_dwarf_reg_to_hw_reg (i); g_assert (hreg < nregs); regs [hreg] = *(mgreg_t*)(cfa_val + locations [i].offset); if (save_locations && hreg < save_locations_len) save_locations [hreg] = (mgreg_t*)(cfa_val + locations [i].offset); } } *out_cfa = cfa_val; }
/* * Given the state of the current frame as stored in REGS, execute the unwind * operations in unwind_info until the location counter reaches POS. The result is * stored back into REGS. OUT_CFA will receive the value of the CFA. * If SAVE_LOCATIONS is non-NULL, it should point to an array of size SAVE_LOCATIONS_LEN. * On return, the nth entry will point to the address of the stack slot where register * N was saved, or NULL, if it was not saved by this frame. * MARK_LOCATIONS should contain the locations marked by mono_emit_unwind_op_mark_loc (), if any. * This function is signal safe. */ void mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, guint8 *start_ip, guint8 *end_ip, guint8 *ip, guint8 **mark_locations, mono_unwind_reg_t *regs, int nregs, mgreg_t **save_locations, int save_locations_len, guint8 **out_cfa) { Loc locations [NUM_REGS]; guint8 reg_saved [NUM_REGS]; int i, pos, reg, cfa_reg = -1, cfa_offset = 0, offset; guint8 *p; guint8 *cfa_val; UnwindState state_stack [1]; int state_stack_pos; memset (reg_saved, 0, sizeof (reg_saved)); state_stack [0].cfa_reg = -1; state_stack [0].cfa_offset = 0; p = unwind_info; pos = 0; cfa_reg = -1; cfa_offset = -1; state_stack_pos = 0; while (pos <= ip - start_ip && p < unwind_info + unwind_info_len) { int op = *p & 0xc0; switch (op) { case DW_CFA_advance_loc: UNW_DEBUG (print_dwarf_state (cfa_reg, cfa_offset, pos, nregs, locations)); pos += *p & 0x3f; p ++; break; case DW_CFA_offset: reg = *p & 0x3f; p ++; reg_saved [reg] = TRUE; locations [reg].loc_type = LOC_OFFSET; locations [reg].offset = decode_uleb128 (p, &p) * DWARF_DATA_ALIGN; break; case 0: { int ext_op = *p; p ++; switch (ext_op) { case DW_CFA_def_cfa: cfa_reg = decode_uleb128 (p, &p); cfa_offset = decode_uleb128 (p, &p); break; case DW_CFA_def_cfa_offset: cfa_offset = decode_uleb128 (p, &p); break; case DW_CFA_def_cfa_register: cfa_reg = decode_uleb128 (p, &p); break; case DW_CFA_offset_extended_sf: reg = decode_uleb128 (p, &p); offset = decode_sleb128 (p, &p); g_assert (reg < NUM_REGS); reg_saved [reg] = TRUE; locations [reg].loc_type = LOC_OFFSET; locations [reg].offset = offset * DWARF_DATA_ALIGN; break; case DW_CFA_offset_extended: reg = decode_uleb128 (p, &p); offset = decode_uleb128 (p, &p); g_assert (reg < NUM_REGS); reg_saved [reg] = TRUE; locations [reg].loc_type = LOC_OFFSET; locations [reg].offset = offset * DWARF_DATA_ALIGN; break; case DW_CFA_same_value: reg = decode_uleb128 (p, &p); locations [reg].loc_type = LOC_SAME; break; case DW_CFA_advance_loc1: pos += *p; p += 1; break; case DW_CFA_advance_loc2: pos += read16 (p); p += 2; break; case DW_CFA_advance_loc4: pos += read32 (p); p += 4; break; case DW_CFA_remember_state: g_assert (state_stack_pos == 0); memcpy (&state_stack [0].locations, &locations, sizeof (locations)); memcpy (&state_stack [0].reg_saved, ®_saved, sizeof (reg_saved)); state_stack [0].cfa_reg = cfa_reg; state_stack [0].cfa_offset = cfa_offset; state_stack_pos ++; break; case DW_CFA_restore_state: g_assert (state_stack_pos == 1); state_stack_pos --; memcpy (&locations, &state_stack [0].locations, sizeof (locations)); memcpy (®_saved, &state_stack [0].reg_saved, sizeof (reg_saved)); cfa_reg = state_stack [0].cfa_reg; cfa_offset = state_stack [0].cfa_offset; break; case DW_CFA_mono_advance_loc: g_assert (mark_locations [0]); pos = mark_locations [0] - start_ip; break; default: g_assert_not_reached (); } break; } default: g_assert_not_reached (); } } if (save_locations) memset (save_locations, 0, save_locations_len * sizeof (mgreg_t*)); g_assert (cfa_reg != -1); cfa_val = (guint8*)regs [mono_dwarf_reg_to_hw_reg (cfa_reg)] + cfa_offset; for (i = 0; i < NUM_REGS; ++i) { if (reg_saved [i] && locations [i].loc_type == LOC_OFFSET) { int hreg = mono_dwarf_reg_to_hw_reg (i); g_assert (hreg < nregs); if (IS_DOUBLE_REG (i)) regs [hreg] = *(guint64*)(cfa_val + locations [i].offset); else regs [hreg] = *(mgreg_t*)(cfa_val + locations [i].offset); if (save_locations && hreg < save_locations_len) save_locations [hreg] = (mgreg_t*)(cfa_val + locations [i].offset); } } *out_cfa = cfa_val; }