static void * extract_cie_info (fde *f, struct cie_info *c) { void *p; int i; c->augmentation = get_cie (f)->augmentation; if (strcmp (c->augmentation, "") != 0 && strcmp (c->augmentation, "eh") != 0 && c->augmentation[0] != 'z') return 0; p = c->augmentation + strlen (c->augmentation) + 1; if (strcmp (c->augmentation, "eh") == 0) { c->eh_ptr = read_pointer (p); p += sizeof (void *); } else c->eh_ptr = 0; p = decode_uleb128 (p, &c->code_align); p = decode_sleb128 (p, &c->data_align); c->ra_regno = *(unsigned char *)p++; /* If the augmentation starts with 'z', we now see the length of the augmentation fields. */ if (c->augmentation[0] == 'z') { p = decode_uleb128 (p, &i); p += i; } return p; }
struct frame_state * __frame_state_for (void *pc_target, struct frame_state *state_in) { fde *f; void *insn, *end, *pc; struct cie_info info; struct frame_state_internal state; f = find_fde (pc_target); if (f == 0) return 0; insn = extract_cie_info (f, &info); if (insn == 0) return 0; memset (&state, 0, sizeof (state)); state.s.retaddr_column = info.ra_regno; state.s.eh_ptr = info.eh_ptr; /* First decode all the insns in the CIE. */ end = next_fde ((fde*) get_cie (f)); while (insn < end) insn = execute_cfa_insn (insn, &state, &info, 0); insn = ((fde *)f) + 1; if (info.augmentation[0] == 'z') { int i; insn = decode_uleb128 (insn, &i); insn += i; } /* Then the insns in the FDE up to our target PC. */ end = next_fde (f); pc = f->pc_begin; while (insn < end && pc <= pc_target) insn = execute_cfa_insn (insn, &state, &info, &pc); memcpy (state_in, &state.s, sizeof (state.s)); return state_in; }
/* * 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; }
static void * execute_cfa_insn (void *p, struct frame_state_internal *state, struct cie_info *info, void **pc) { unsigned insn = *(unsigned char *)p++; unsigned reg; int offset; if (insn & DW_CFA_advance_loc) *pc += ((insn & 0x3f) * info->code_align); else if (insn & DW_CFA_offset) { reg = (insn & 0x3f); p = decode_uleb128 (p, &offset); offset *= info->data_align; state->s.saved[reg] = REG_SAVED_OFFSET; state->s.reg_or_offset[reg] = offset; } else if (insn & DW_CFA_restore) { reg = (insn & 0x3f); state->s.saved[reg] = REG_UNSAVED; } else switch (insn) { case DW_CFA_set_loc: *pc = read_pointer (p); p += sizeof (void *); break; case DW_CFA_advance_loc1: *pc += read_1byte (p); p += 1; break; case DW_CFA_advance_loc2: *pc += read_2byte (p); p += 2; break; case DW_CFA_advance_loc4: *pc += read_4byte (p); p += 4; break; case DW_CFA_offset_extended: p = decode_uleb128 (p, ®); p = decode_uleb128 (p, &offset); offset *= info->data_align; state->s.saved[reg] = REG_SAVED_OFFSET; state->s.reg_or_offset[reg] = offset; break; case DW_CFA_restore_extended: p = decode_uleb128 (p, ®); state->s.saved[reg] = REG_UNSAVED; break; case DW_CFA_undefined: case DW_CFA_same_value: case DW_CFA_nop: break; case DW_CFA_register: { unsigned reg2; p = decode_uleb128 (p, ®); p = decode_uleb128 (p, ®2); state->s.saved[reg] = REG_SAVED_REG; state->s.reg_or_offset[reg] = reg2; } break; case DW_CFA_def_cfa: p = decode_uleb128 (p, ®); p = decode_uleb128 (p, &offset); state->s.cfa_reg = reg; state->s.cfa_offset = offset; break; case DW_CFA_def_cfa_register: p = decode_uleb128 (p, ®); state->s.cfa_reg = reg; break; case DW_CFA_def_cfa_offset: p = decode_uleb128 (p, &offset); state->s.cfa_offset = offset; break; case DW_CFA_remember_state: { struct frame_state_internal *save = (struct frame_state_internal *) malloc (sizeof (struct frame_state_internal)); memcpy (save, state, sizeof (struct frame_state_internal)); state->saved_state = save; } break; case DW_CFA_restore_state: { struct frame_state_internal *save = state->saved_state; memcpy (state, save, sizeof (struct frame_state_internal)); free (save); } break; /* FIXME: Hardcoded for SPARC register window configuration. */ case DW_CFA_GNU_window_save: for (reg = 16; reg < 32; ++reg) { state->s.saved[reg] = REG_SAVED_OFFSET; state->s.reg_or_offset[reg] = (reg - 16) * sizeof (void *); } break; case DW_CFA_GNU_args_size: p = decode_uleb128 (p, &offset); state->s.args_size = offset; break; default: abort (); } return p; }
/* * 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; }
void mono_print_unwind_info (guint8 *unwind_info, int unwind_info_len) { guint8 *p; int pos, reg, offset, cfa_reg, cfa_offset; p = unwind_info; pos = 0; while (p < unwind_info + unwind_info_len) { int op = *p & 0xc0; switch (op) { case DW_CFA_advance_loc: pos += *p & 0x3f; p ++; break; case DW_CFA_offset: reg = *p & 0x3f; p ++; offset = decode_uleb128 (p, &p) * DWARF_DATA_ALIGN; if (reg == DWARF_PC_REG) printf ("CFA: [%x] offset: %s at cfa-0x%x\n", pos, "pc", -offset); else printf ("CFA: [%x] offset: %s at cfa-0x%x\n", pos, mono_arch_regname (mono_dwarf_reg_to_hw_reg (reg)), -offset); 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); printf ("CFA: [%x] def_cfa: %s+0x%x\n", pos, mono_arch_regname (mono_dwarf_reg_to_hw_reg (cfa_reg)), cfa_offset); break; case DW_CFA_def_cfa_offset: cfa_offset = decode_uleb128 (p, &p); printf ("CFA: [%x] def_cfa_offset: 0x%x\n", pos, cfa_offset); break; case DW_CFA_def_cfa_register: cfa_reg = decode_uleb128 (p, &p); printf ("CFA: [%x] def_cfa_reg: %s\n", pos, mono_arch_regname (mono_dwarf_reg_to_hw_reg (cfa_reg))); break; case DW_CFA_offset_extended_sf: reg = decode_uleb128 (p, &p); offset = decode_sleb128 (p, &p) * DWARF_DATA_ALIGN; printf ("CFA: [%x] offset_extended_sf: %s at cfa-0x%x\n", pos, mono_arch_regname (mono_dwarf_reg_to_hw_reg (reg)), -offset); break; case DW_CFA_same_value: reg = decode_uleb128 (p, &p); printf ("CFA: [%x] same_value: %s\n", pos, mono_arch_regname (mono_dwarf_reg_to_hw_reg (reg))); break; case DW_CFA_advance_loc4: pos += read32 (p); p += 4; break; case DW_CFA_remember_state: printf ("CFA: [%x] remember_state\n", pos); break; case DW_CFA_restore_state: printf ("CFA: [%x] restore_state\n", pos); break; case DW_CFA_mono_advance_loc: printf ("CFA: [%x] mono_advance_loc\n", pos); break; default: g_assert_not_reached (); } break; } default: g_assert_not_reached (); } } }