bool ABISysV_ppc::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); uint32_t lr_reg_num = gcc_dwarf_lr; uint32_t sp_reg_num = gcc_dwarf_r1; uint32_t pc_reg_num = gcc_dwarf_pc; UnwindPlan::RowSP row(new UnwindPlan::Row); // Our Call Frame Address is the stack pointer value row->GetCFAValue().SetIsRegisterPlusOffset (sp_reg_num, 0); // The previous PC is in the LR row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("ppc at-func-entry default"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); return true; }
bool ABIMacOSX_arm::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) { uint32_t reg_kind = unwind_plan.GetRegisterKind(); uint32_t lr_reg_num = LLDB_INVALID_REGNUM; uint32_t sp_reg_num = LLDB_INVALID_REGNUM; uint32_t pc_reg_num = LLDB_INVALID_REGNUM; switch (reg_kind) { case eRegisterKindDWARF: case eRegisterKindGCC: lr_reg_num = dwarf_lr; sp_reg_num = dwarf_sp; pc_reg_num = dwarf_pc; break; case eRegisterKindGeneric: lr_reg_num = LLDB_REGNUM_GENERIC_RA; sp_reg_num = LLDB_REGNUM_GENERIC_SP; pc_reg_num = LLDB_REGNUM_GENERIC_PC; break; } if (lr_reg_num == LLDB_INVALID_REGNUM || sp_reg_num == LLDB_INVALID_REGNUM || pc_reg_num == LLDB_INVALID_REGNUM) return false; UnwindPlan::RowSP row(new UnwindPlan::Row); // Our Call Frame Address is the stack pointer value row->SetCFARegister (sp_reg_num); // The previous PC is in the LR row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("arm at-func-entry default"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); return true; }
bool ABISysV_ppc64::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); uint32_t sp_reg_num = dwarf_r1; uint32_t pc_reg_num = dwarf_lr; UnwindPlan::RowSP row(new UnwindPlan::Row); const int32_t ptr_size = 8; row->GetCFAValue().SetIsRegisterDereferenced(sp_reg_num); row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * 2, true); row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); row->SetRegisterLocationToAtCFAPlusOffset(dwarf_cr, ptr_size, true); unwind_plan.AppendRow (row); unwind_plan.SetSourceName ("ppc64 default unwind plan"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); unwind_plan.SetReturnAddressRegister(dwarf_lr); return true; }
bool ABISysV_i386::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); uint32_t sp_reg_num = gcc_dwarf_esp; uint32_t pc_reg_num = gcc_dwarf_eip; UnwindPlan::RowSP row(new UnwindPlan::Row); row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 4); row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false); row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); unwind_plan.AppendRow (row); unwind_plan.SetSourceName ("i386 at-func-entry default"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); return true; }
bool ABISysV_s390x::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); // Our Call Frame Address is the stack pointer value + 160 row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r15_s390x, 160); // The previous PC is in r14 row->SetRegisterLocationToRegister(dwarf_pswa_s390x, dwarf_r14_s390x, true); // All other registers are the same. unwind_plan.AppendRow(row); unwind_plan.SetSourceName("s390x at-func-entry default"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); return true; }
bool ABISysV_i386::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); uint32_t fp_reg_num = gcc_dwarf_ebp; uint32_t sp_reg_num = gcc_dwarf_esp; uint32_t pc_reg_num = gcc_dwarf_eip; UnwindPlan::RowSP row(new UnwindPlan::Row); const int32_t ptr_size = 4; row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size); row->SetOffset (0); row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); unwind_plan.AppendRow (row); unwind_plan.SetSourceName ("i386 default unwind plan"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); return true; }
bool ABIMacOSX_arm::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) { uint32_t fp_reg_num = dwarf_r7; // apple uses r7 for all frames. Normal arm uses r11; uint32_t pc_reg_num = dwarf_pc; UnwindPlan::RowSP row(new UnwindPlan::Row); const int32_t ptr_size = 4; unwind_plan.Clear (); unwind_plan.SetRegisterKind (eRegisterKindDWARF); row->SetCFARegister (fp_reg_num); row->SetCFAOffset (2 * ptr_size); row->SetOffset (0); row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); unwind_plan.AppendRow (row); unwind_plan.SetSourceName ("arm-apple-ios default unwind plan"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); return true; }
bool ABISysV_arm::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear (); unwind_plan.SetRegisterKind (eRegisterKindDWARF); //TODO: Handle thumb uint32_t fp_reg_num = dwarf_r11; uint32_t pc_reg_num = dwarf_pc; UnwindPlan::RowSP row(new UnwindPlan::Row); const int32_t ptr_size = 4; row->GetCFAValue().SetIsRegisterPlusOffset (fp_reg_num, 2 * ptr_size); row->SetOffset (0); row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); unwind_plan.AppendRow (row); unwind_plan.SetSourceName ("arm default unwind plan"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); return true; }
bool CompactUnwindInfo::GetUnwindPlan (Target &target, Address addr, UnwindPlan& unwind_plan) { if (!IsValid (target.GetProcessSP())) { return false; } FunctionInfo function_info; if (GetCompactUnwindInfoForFunction (target, addr, function_info)) { // shortcut return for functions that have no compact unwind if (function_info.encoding == 0) return false; ArchSpec arch; if (m_objfile.GetArchitecture (arch)) { Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { StreamString strm; addr.Dump (&strm, NULL, Address::DumpStyle::DumpStyleResolvedDescriptionNoFunctionArguments, Address::DumpStyle::DumpStyleFileAddress, arch.GetAddressByteSize()); log->Printf ("Got compact unwind encoding 0x%x for function %s", function_info.encoding, strm.GetData()); } if (function_info.valid_range_offset_start != 0 && function_info.valid_range_offset_end != 0) { SectionList *sl = m_objfile.GetSectionList (); if (sl) { addr_t func_range_start_file_addr = function_info.valid_range_offset_start + m_objfile.GetHeaderAddress().GetFileAddress(); AddressRange func_range (func_range_start_file_addr, function_info.valid_range_offset_end - function_info.valid_range_offset_start, sl); unwind_plan.SetPlanValidAddressRange (func_range); } } if (arch.GetTriple().getArch() == llvm::Triple::x86_64) { return CreateUnwindPlan_x86_64 (target, function_info, unwind_plan, addr); } if (arch.GetTriple().getArch() == llvm::Triple::x86) { return CreateUnwindPlan_i386 (target, function_info, unwind_plan, addr); } } } return false; }
bool ABINyuzi::CreateDefaultUnwindPlan ( UnwindPlan &unwind_plan ) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindDWARF); unwind_plan.SetReturnAddressRegister(30); UnwindPlan::RowSP row(new UnwindPlan::Row); row->GetCFAValue().SetIsRegisterPlusOffset(29, 0); unwind_plan.AppendRow(row); unwind_plan.SetSourceName("nyuzi default unwind plan"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); return true; }
bool EmulateInstructionARM64::CreateFunctionEntryUnwind( UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindLLDB); UnwindPlan::RowSP row(new UnwindPlan::Row); // Our previous Call Frame Address is the stack pointer row->GetCFAValue().SetIsRegisterPlusOffset(gpr_sp_arm64, 0); unwind_plan.AppendRow(row); unwind_plan.SetSourceName("EmulateInstructionARM64"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); unwind_plan.SetReturnAddressRegister(gpr_lr_arm64); return true; }
bool ABISysV_mips::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); row->GetCFAValue().SetIsRegisterPlusOffset(gcc_dwarf_r29, 0); row->SetRegisterLocationToRegister(gcc_dwarf_pc, gcc_dwarf_r31, true); unwind_plan.AppendRow (row); unwind_plan.SetSourceName ("mips default unwind plan"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); return true; }
// See lib/Target/Nyuzi/NyuziFrameLowering.cpp, emitPrologue bool ABINyuzi::CreateFunctionEntryUnwindPlan ( UnwindPlan &unwind_plan ) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindDWARF); unwind_plan.SetReturnAddressRegister(30); UnwindPlan::RowSP row(new UnwindPlan::Row); // Our Call Frame Address is the stack pointer value row->GetCFAValue().SetIsRegisterPlusOffset(29, 0); unwind_plan.AppendRow(row); unwind_plan.SetSourceName("nyuzi at-func-entry default"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); return true; }
// called when we are on the first instruction of a new function for hexagon // the return address is in RA (R31) bool ABISysV_hexagon::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindGeneric); unwind_plan.SetReturnAddressRegister(LLDB_REGNUM_GENERIC_RA); UnwindPlan::RowSP row(new UnwindPlan::Row); // Our Call Frame Address is the stack pointer value row->GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_SP, 4); row->SetOffset(0); // The previous PC is in the LR row->SetRegisterLocationToRegister(LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_RA, true); unwind_plan.AppendRow(row); unwind_plan.SetSourceName("hexagon at-func-entry default"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); return true; }
bool ABISysV_mips::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); // Our Call Frame Address is the stack pointer value row->GetCFAValue().SetIsRegisterPlusOffset(gcc_dwarf_r29, 0); // The previous PC is in the RA row->SetRegisterLocationToRegister(gcc_dwarf_pc, gcc_dwarf_r31, true); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("mips at-func-entry default"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetReturnAddressRegister(gcc_dwarf_r31); return true; }
bool ABISysV_hexagon::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindGeneric); uint32_t fp_reg_num = LLDB_REGNUM_GENERIC_FP; uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP; uint32_t pc_reg_num = LLDB_REGNUM_GENERIC_PC; UnwindPlan::RowSP row(new UnwindPlan::Row); row->GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_FP, 8); row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, -8, true); row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, true); row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); unwind_plan.AppendRow(row); unwind_plan.SetSourceName("hexagon default unwind plan"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); return true; }
bool EmulateInstructionMIPS::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); const bool can_replace = false; // Our previous Call Frame Address is the stack pointer row->GetCFAValue().SetIsRegisterPlusOffset(gcc_dwarf_sp_mips64, 0); // Our previous PC is in the RA row->SetRegisterLocationToRegister(gcc_dwarf_pc_mips64, gcc_dwarf_ra_mips64, can_replace); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("EmulateInstructionMIPS"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); return true; }
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& range, Thread& thread, UnwindPlan& unwind_plan) { if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() && m_inst_emulator_ap.get()) { // The instruction emulation subclass setup the unwind plan for the // first instruction. m_inst_emulator_ap->CreateFunctionEntryUnwind (unwind_plan); // CreateFunctionEntryUnwind should have created the first row. If it // doesn't, then we are done. if (unwind_plan.GetRowCount() == 0) return false; ExecutionContext exe_ctx; thread.CalculateExecutionContext(exe_ctx); const bool prefer_file_cache = true; DisassemblerSP disasm_sp (Disassembler::DisassembleRange (m_arch, NULL, NULL, exe_ctx, range, prefer_file_cache)); Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); if (disasm_sp) { m_range_ptr = ⦥ m_thread_ptr = &thread; m_unwind_plan_ptr = &unwind_plan; const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); const bool show_address = true; const bool show_bytes = true; m_inst_emulator_ap->GetRegisterInfo (unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister(), m_cfa_reg_info); m_fp_is_cfa = false; m_register_values.clear(); m_pushed_regs.clear(); // Initialize the CFA with a known value. In the 32 bit case // it will be 0x80000000, and in the 64 bit case 0x8000000000000000. // We use the address byte size to be safe for any future address sizes m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); RegisterValue cfa_reg_value; cfa_reg_value.SetUInt (m_initial_sp, m_cfa_reg_info.byte_size); SetRegisterValue (m_cfa_reg_info, cfa_reg_value); const InstructionList &inst_list = disasm_sp->GetInstructionList (); const size_t num_instructions = inst_list.GetSize(); if (num_instructions > 0) { Instruction *inst = inst_list.GetInstructionAtIndex (0).get(); const addr_t base_addr = inst->GetAddress().GetFileAddress(); // Make a copy of the current instruction Row and save it in m_curr_row // so we can add updates as we process the instructions. UnwindPlan::RowSP last_row = unwind_plan.GetLastRow(); UnwindPlan::Row *newrow = new UnwindPlan::Row; if (last_row.get()) *newrow = *last_row.get(); m_curr_row.reset(newrow); // Once we've seen the initial prologue instructions complete, save a // copy of the CFI at that point into prologue_completed_row for possible // use later. int instructions_since_last_prologue_insn = 0; // # of insns since last CFI was update bool reinstate_prologue_next_instruction = false; // Next iteration, re-install the prologue row of CFI bool last_instruction_restored_return_addr_reg = false; // re-install the prologue row of CFI if the next instruction is a branch immediate bool return_address_register_has_been_saved = false; // if we've seen the ra register get saved yet UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI // cache the pc register number (in whatever register numbering this UnwindPlan uses) for // quick reference during instruction parsing. uint32_t pc_reg_num = LLDB_INVALID_REGNUM; RegisterInfo pc_reg_info; if (m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info)) pc_reg_num = pc_reg_info.kinds[unwind_plan.GetRegisterKind()]; else pc_reg_num = LLDB_INVALID_REGNUM; // cache the return address register number (in whatever register numbering this UnwindPlan uses) for // quick reference during instruction parsing. uint32_t ra_reg_num = LLDB_INVALID_REGNUM; RegisterInfo ra_reg_info; if (m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info)) ra_reg_num = ra_reg_info.kinds[unwind_plan.GetRegisterKind()]; else ra_reg_num = LLDB_INVALID_REGNUM; for (size_t idx=0; idx<num_instructions; ++idx) { m_curr_row_modified = false; m_curr_insn_restored_a_register = false; inst = inst_list.GetInstructionAtIndex (idx).get(); if (inst) { if (log && log->GetVerbose ()) { StreamString strm; inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL); log->PutCString (strm.GetData()); } m_inst_emulator_ap->SetInstruction (inst->GetOpcode(), inst->GetAddress(), exe_ctx.GetTargetPtr()); m_inst_emulator_ap->EvaluateInstruction (eEmulateInstructionOptionIgnoreConditions); // Were there any changes to the CFI while evaluating this instruction? if (m_curr_row_modified) { reinstate_prologue_next_instruction = false; m_curr_row->SetOffset (inst->GetAddress().GetFileAddress() + inst->GetOpcode().GetByteSize() - base_addr); // Append the new row unwind_plan.AppendRow (m_curr_row); // Allocate a new Row for m_curr_row, copy the current state into it UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *m_curr_row.get(); m_curr_row.reset(newrow); // If m_curr_insn_restored_a_register == true, we're looking at an epilogue instruction. // Set instructions_since_last_prologue_insn to a very high number so we don't append // any of these epilogue instructions to our prologue_complete row. if (m_curr_insn_restored_a_register == false && instructions_since_last_prologue_insn < 8) instructions_since_last_prologue_insn = 0; else instructions_since_last_prologue_insn = 99; UnwindPlan::Row::RegisterLocation pc_regloc; UnwindPlan::Row::RegisterLocation ra_regloc; // While parsing the instructions of this function, if we've ever // seen the return address register (aka lr on arm) in a non-IsSame() state, // it has been saved on the stack. If it's ever back to IsSame(), we've // executed an epilogue. if (ra_reg_num != LLDB_INVALID_REGNUM && m_curr_row->GetRegisterInfo (ra_reg_num, ra_regloc) && !ra_regloc.IsSame()) { return_address_register_has_been_saved = true; } // If the caller's pc is "same", we've just executed an epilogue and we return to the caller // after this instruction completes executing. // If there are any instructions past this, there must have been flow control over this // epilogue so we'll reinstate the original prologue setup instructions. if (prologue_completed_row.get() && pc_reg_num != LLDB_INVALID_REGNUM && m_curr_row->GetRegisterInfo (pc_reg_num, pc_regloc) && pc_regloc.IsSame()) { if (log && log->GetVerbose()) log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- pc is <same>, restore prologue instructions."); reinstate_prologue_next_instruction = true; } else if (prologue_completed_row.get() && return_address_register_has_been_saved && ra_reg_num != LLDB_INVALID_REGNUM && m_curr_row->GetRegisterInfo (ra_reg_num, ra_regloc) && ra_regloc.IsSame()) { if (log && log->GetVerbose()) log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- lr is <same>, restore prologue instruction if the next instruction is a branch immediate."); last_instruction_restored_return_addr_reg = true; } } else { // If the previous instruction was a return-to-caller (epilogue), and we're still executing // instructions in this function, there must be a code path that jumps over that epilogue. // Also detect the case where we epilogue & branch imm to another function (tail-call opt) // instead of a normal pop lr-into-pc exit. // Reinstate the frame setup from the prologue. if (reinstate_prologue_next_instruction || (m_curr_insn_is_branch_immediate && last_instruction_restored_return_addr_reg)) { if (log && log->GetVerbose()) log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- Reinstating prologue instruction set"); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *prologue_completed_row.get(); m_curr_row.reset(newrow); m_curr_row->SetOffset (inst->GetAddress().GetFileAddress() + inst->GetOpcode().GetByteSize() - base_addr); unwind_plan.AppendRow(m_curr_row); newrow = new UnwindPlan::Row; *newrow = *m_curr_row.get(); m_curr_row.reset(newrow); reinstate_prologue_next_instruction = false; last_instruction_restored_return_addr_reg = false; m_curr_insn_is_branch_immediate = false; } // clear both of these if either one wasn't set if (last_instruction_restored_return_addr_reg) { last_instruction_restored_return_addr_reg = false; } if (m_curr_insn_is_branch_immediate) { m_curr_insn_is_branch_immediate = false; } // Stop updating the prologue instructions if we've seen 8 non-prologue instructions // in a row. if (instructions_since_last_prologue_insn++ < 8) { UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *m_curr_row.get(); prologue_completed_row.reset(newrow); if (log && log->GetVerbose()) log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- saving a copy of the current row as the prologue row."); } } } } } // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. // I'll fix that but for now, just clear the list and it will go away nicely. disasm_sp->GetInstructionList().Clear(); } if (log && log->GetVerbose ()) { StreamString strm; lldb::addr_t base_addr = range.GetBaseAddress().GetLoadAddress(thread.CalculateTarget().get()); strm.Printf ("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", base_addr, base_addr + range.GetByteSize()); unwind_plan.Dump(strm, &thread, base_addr); log->PutCString (strm.GetData()); } return unwind_plan.GetRowCount() > 0; } return false; }
bool EmulateInstruction::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan) { unwind_plan.Clear(); return false; }
bool CompactUnwindInfo::CreateUnwindPlan_x86_64 (Target &target, FunctionInfo &function_info, UnwindPlan &unwind_plan, Address pc_or_function_start) { unwind_plan.SetSourceName ("compact unwind info"); unwind_plan.SetSourcedFromCompiler (eLazyBoolYes); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); unwind_plan.SetRegisterKind (eRegisterKindEHFrame); unwind_plan.SetLSDAAddress (function_info.lsda_address); unwind_plan.SetPersonalityFunctionPtr (function_info.personality_ptr_address); UnwindPlan::RowSP row (new UnwindPlan::Row); const int wordsize = 8; int mode = function_info.encoding & UNWIND_X86_64_MODE_MASK; switch (mode) { case UNWIND_X86_64_MODE_RBP_FRAME: { row->GetCFAValue().SetIsRegisterPlusOffset ( translate_to_eh_frame_regnum_x86_64 (UNWIND_X86_64_REG_RBP), 2 * wordsize); row->SetOffset (0); row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rbp, wordsize * -2, true); row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rip, wordsize * -1, true); row->SetRegisterLocationToIsCFAPlusOffset (x86_64_eh_regnum::rsp, 0, true); uint32_t saved_registers_offset = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); uint32_t saved_registers_locations = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); saved_registers_offset += 2; for (int i = 0; i < 5; i++) { uint32_t regnum = saved_registers_locations & 0x7; switch (regnum) { case UNWIND_X86_64_REG_NONE: break; case UNWIND_X86_64_REG_RBX: case UNWIND_X86_64_REG_R12: case UNWIND_X86_64_REG_R13: case UNWIND_X86_64_REG_R14: case UNWIND_X86_64_REG_R15: row->SetRegisterLocationToAtCFAPlusOffset (translate_to_eh_frame_regnum_x86_64 (regnum), wordsize * -saved_registers_offset, true); break; } saved_registers_offset--; saved_registers_locations >>= 3; } unwind_plan.AppendRow (row); return true; } break; case UNWIND_X86_64_MODE_STACK_IND: { // The clang in Xcode 6 is emitting incorrect compact unwind encodings for this // style of unwind. It was fixed in llvm r217020. // The clang in Xcode 7 has this fixed. return false; } break; case UNWIND_X86_64_MODE_STACK_IMMD: { uint32_t stack_size = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); uint32_t register_count = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); uint32_t permutation = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); if (mode == UNWIND_X86_64_MODE_STACK_IND && function_info.valid_range_offset_start != 0) { uint32_t stack_adjust = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); // offset into the function instructions; 0 == beginning of first instruction uint32_t offset_to_subl_insn = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); SectionList *sl = m_objfile.GetSectionList (); if (sl) { ProcessSP process_sp = target.GetProcessSP(); if (process_sp) { Address subl_payload_addr (function_info.valid_range_offset_start, sl); subl_payload_addr.Slide (offset_to_subl_insn); Error error; uint64_t large_stack_size = process_sp->ReadUnsignedIntegerFromMemory (subl_payload_addr.GetLoadAddress (&target), 4, 0, error); if (large_stack_size != 0 && error.Success ()) { // Got the large stack frame size correctly - use it stack_size = large_stack_size + (stack_adjust * wordsize); } else { return false; } } else { return false; } } else { return false; } } int32_t offset = mode == UNWIND_X86_64_MODE_STACK_IND ? stack_size : stack_size * wordsize; row->GetCFAValue().SetIsRegisterPlusOffset (x86_64_eh_regnum::rsp, offset); row->SetOffset (0); row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rip, wordsize * -1, true); row->SetRegisterLocationToIsCFAPlusOffset (x86_64_eh_regnum::rsp, 0, true); if (register_count > 0) { // We need to include (up to) 6 registers in 10 bits. // That would be 18 bits if we just used 3 bits per reg to indicate // the order they're saved on the stack. // // This is done with Lehmer code permutation, e.g. see // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms int permunreg[6] = {0, 0, 0, 0, 0, 0}; // This decodes the variable-base number in the 10 bits // and gives us the Lehmer code sequence which can then // be decoded. switch (register_count) { case 6: permunreg[0] = permutation/120; // 120 == 5! permutation -= (permunreg[0]*120); permunreg[1] = permutation/24; // 24 == 4! permutation -= (permunreg[1]*24); permunreg[2] = permutation/6; // 6 == 3! permutation -= (permunreg[2]*6); permunreg[3] = permutation/2; // 2 == 2! permutation -= (permunreg[3]*2); permunreg[4] = permutation; // 1 == 1! permunreg[5] = 0; break; case 5: permunreg[0] = permutation/120; permutation -= (permunreg[0]*120); permunreg[1] = permutation/24; permutation -= (permunreg[1]*24); permunreg[2] = permutation/6; permutation -= (permunreg[2]*6); permunreg[3] = permutation/2; permutation -= (permunreg[3]*2); permunreg[4] = permutation; break; case 4: permunreg[0] = permutation/60; permutation -= (permunreg[0]*60); permunreg[1] = permutation/12; permutation -= (permunreg[1]*12); permunreg[2] = permutation/3; permutation -= (permunreg[2]*3); permunreg[3] = permutation; break; case 3: permunreg[0] = permutation/20; permutation -= (permunreg[0]*20); permunreg[1] = permutation/4; permutation -= (permunreg[1]*4); permunreg[2] = permutation; break; case 2: permunreg[0] = permutation/5; permutation -= (permunreg[0]*5); permunreg[1] = permutation; break; case 1: permunreg[0] = permutation; break; } // Decode the Lehmer code for this permutation of // the registers v. http://en.wikipedia.org/wiki/Lehmer_code int registers[6] = { UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE }; bool used[7] = { false, false, false, false, false, false, false }; for (uint32_t i = 0; i < register_count; i++) { int renum = 0; for (int j = 1; j < 7; j++) { if (used[j] == false) { if (renum == permunreg[i]) { registers[i] = j; used[j] = true; break; } renum++; } } } uint32_t saved_registers_offset = 1; saved_registers_offset++; for (int i = (sizeof (registers) / sizeof (int)) - 1; i >= 0; i--) { switch (registers[i]) { case UNWIND_X86_64_REG_NONE: break; case UNWIND_X86_64_REG_RBX: case UNWIND_X86_64_REG_R12: case UNWIND_X86_64_REG_R13: case UNWIND_X86_64_REG_R14: case UNWIND_X86_64_REG_R15: case UNWIND_X86_64_REG_RBP: row->SetRegisterLocationToAtCFAPlusOffset (translate_to_eh_frame_regnum_x86_64 (registers[i]), wordsize * -saved_registers_offset, true); saved_registers_offset++; break; } } } unwind_plan.AppendRow (row); return true; } break; case UNWIND_X86_64_MODE_DWARF: { return false; } break; case 0: { return false; } break; } return false; }
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& range, Thread& thread, UnwindPlan& unwind_plan) { if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() && m_inst_emulator_ap.get()) { // The the instruction emulation subclass setup the unwind plan for the // first instruction. m_inst_emulator_ap->CreateFunctionEntryUnwind (unwind_plan); // CreateFunctionEntryUnwind should have created the first row. If it // doesn't, then we are done. if (unwind_plan.GetRowCount() == 0) return false; ExecutionContext exe_ctx; thread.CalculateExecutionContext(exe_ctx); DisassemblerSP disasm_sp (Disassembler::DisassembleRange (m_arch, NULL, exe_ctx, range)); LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); if (disasm_sp) { m_range_ptr = ⦥ m_thread_ptr = &thread; m_unwind_plan_ptr = &unwind_plan; const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); const bool show_address = true; const bool show_bytes = true; const bool raw = false; // Initialize the CFA with a known value. In the 32 bit case // it will be 0x80000000, and in the 64 bit case 0x8000000000000000. // We use the address byte size to be safe for any future addresss sizes m_inst_emulator_ap->GetRegisterInfo (unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister(), m_cfa_reg_info); m_fp_is_cfa = false; m_register_values.clear(); m_pushed_regs.clear(); m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); RegisterValue cfa_reg_value; cfa_reg_value.SetUInt (m_initial_sp, m_cfa_reg_info.byte_size); SetRegisterValue (m_cfa_reg_info, cfa_reg_value); const InstructionList &inst_list = disasm_sp->GetInstructionList (); const size_t num_instructions = inst_list.GetSize(); if (num_instructions > 0) { Instruction *inst = inst_list.GetInstructionAtIndex (0).get(); const addr_t base_addr = inst->GetAddress().GetFileAddress(); // Initialize the current row with the one row that was created // from the CreateFunctionEntryUnwind call above... m_curr_row = unwind_plan.GetLastRow(); for (size_t idx=0; idx<num_instructions; ++idx) { inst = inst_list.GetInstructionAtIndex (idx).get(); if (inst) { if (log && log->GetVerbose ()) { StreamString strm; inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, &exe_ctx, raw); log->PutCString (strm.GetData()); } m_inst_emulator_ap->SetInstruction (inst->GetOpcode(), inst->GetAddress(), exe_ctx.GetTargetPtr()); m_inst_emulator_ap->EvaluateInstruction (eEmulateInstructionOptionIgnoreConditions); if (unwind_plan.GetLastRow() != m_curr_row) { // Be sure to not edit the offset unless our row has changed // so that the "!=" call above doesn't trigger every time m_curr_row.SetOffset (inst->GetAddress().GetFileAddress() + inst->GetOpcode().GetByteSize() - base_addr); // Append the new row unwind_plan.AppendRow (m_curr_row); } } } } } if (log && log->GetVerbose ()) { StreamString strm; lldb::addr_t base_addr = range.GetBaseAddress().GetLoadAddress(&thread.GetProcess().GetTarget()); strm.Printf ("Resulting unwind rows for [0x%llx - 0x%llx):", base_addr, base_addr + range.GetByteSize()); unwind_plan.Dump(strm, &thread, base_addr); log->PutCString (strm.GetData()); } return unwind_plan.GetRowCount() > 0; } return false; }
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( AddressRange &range, uint8_t *opcode_data, size_t opcode_size, UnwindPlan &unwind_plan) { if (opcode_data == nullptr || opcode_size == 0) return false; if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() && m_inst_emulator_ap.get()) { // The instruction emulation subclass setup the unwind plan for the // first instruction. m_inst_emulator_ap->CreateFunctionEntryUnwind(unwind_plan); // CreateFunctionEntryUnwind should have created the first row. If it // doesn't, then we are done. if (unwind_plan.GetRowCount() == 0) return false; const bool prefer_file_cache = true; DisassemblerSP disasm_sp(Disassembler::DisassembleBytes( m_arch, NULL, NULL, range.GetBaseAddress(), opcode_data, opcode_size, 99999, prefer_file_cache)); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (disasm_sp) { m_range_ptr = ⦥ m_unwind_plan_ptr = &unwind_plan; const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); const bool show_address = true; const bool show_bytes = true; m_inst_emulator_ap->GetRegisterInfo(unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister(), m_cfa_reg_info); m_fp_is_cfa = false; m_register_values.clear(); m_pushed_regs.clear(); // Initialize the CFA with a known value. In the 32 bit case // it will be 0x80000000, and in the 64 bit case 0x8000000000000000. // We use the address byte size to be safe for any future address sizes m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); RegisterValue cfa_reg_value; cfa_reg_value.SetUInt(m_initial_sp, m_cfa_reg_info.byte_size); SetRegisterValue(m_cfa_reg_info, cfa_reg_value); const InstructionList &inst_list = disasm_sp->GetInstructionList(); const size_t num_instructions = inst_list.GetSize(); if (num_instructions > 0) { Instruction *inst = inst_list.GetInstructionAtIndex(0).get(); const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress(); // Map for storing the unwind plan row and the value of the registers at // a given offset. // When we see a forward branch we add a new entry to this map with the // actual unwind plan // row and register context for the target address of the branch as the // current data have // to be valid for the target address of the branch too if we are in the // same function. std::map<lldb::addr_t, std::pair<UnwindPlan::RowSP, RegisterValueMap>> saved_unwind_states; // Make a copy of the current instruction Row and save it in m_curr_row // so we can add updates as we process the instructions. UnwindPlan::RowSP last_row = unwind_plan.GetLastRow(); UnwindPlan::Row *newrow = new UnwindPlan::Row; if (last_row.get()) *newrow = *last_row.get(); m_curr_row.reset(newrow); // Add the initial state to the save list with offset 0. saved_unwind_states.insert({0, {last_row, m_register_values}}); // cache the pc register number (in whatever register numbering this // UnwindPlan uses) for // quick reference during instruction parsing. RegisterInfo pc_reg_info; m_inst_emulator_ap->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info); // cache the return address register number (in whatever register // numbering this UnwindPlan uses) for // quick reference during instruction parsing. RegisterInfo ra_reg_info; m_inst_emulator_ap->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info); // The architecture dependent condition code of the last processed // instruction. EmulateInstruction::InstructionCondition last_condition = EmulateInstruction::UnconditionalCondition; lldb::addr_t condition_block_start_offset = 0; for (size_t idx = 0; idx < num_instructions; ++idx) { m_curr_row_modified = false; m_forward_branch_offset = 0; inst = inst_list.GetInstructionAtIndex(idx).get(); if (inst) { lldb::addr_t current_offset = inst->GetAddress().GetFileAddress() - base_addr; auto it = saved_unwind_states.upper_bound(current_offset); assert(it != saved_unwind_states.begin() && "Unwind row for the function entry missing"); --it; // Move it to the row corresponding to the current offset // If the offset of m_curr_row don't match with the offset we see in // saved_unwind_states // then we have to update m_curr_row and m_register_values based on // the saved values. It // is happenning after we processed an epilogue and a return to // caller instruction. if (it->second.first->GetOffset() != m_curr_row->GetOffset()) { UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *it->second.first; m_curr_row.reset(newrow); m_register_values = it->second.second; } m_inst_emulator_ap->SetInstruction(inst->GetOpcode(), inst->GetAddress(), nullptr); if (last_condition != m_inst_emulator_ap->GetInstructionCondition()) { if (m_inst_emulator_ap->GetInstructionCondition() != EmulateInstruction::UnconditionalCondition && saved_unwind_states.count(current_offset) == 0) { // If we don't have a saved row for the current offset then save // our // current state because we will have to restore it after the // conditional block. auto new_row = std::make_shared<UnwindPlan::Row>(*m_curr_row.get()); saved_unwind_states.insert( {current_offset, {new_row, m_register_values}}); } // If the last instruction was conditional with a different // condition // then the then current condition then restore the condition. if (last_condition != EmulateInstruction::UnconditionalCondition) { const auto &saved_state = saved_unwind_states.at(condition_block_start_offset); m_curr_row = std::make_shared<UnwindPlan::Row>(*saved_state.first); m_curr_row->SetOffset(current_offset); m_register_values = saved_state.second; bool replace_existing = true; // The last instruction might already // created a row for this offset and // we want to overwrite it. unwind_plan.InsertRow( std::make_shared<UnwindPlan::Row>(*m_curr_row), replace_existing); } // We are starting a new conditional block at the catual offset condition_block_start_offset = current_offset; } if (log && log->GetVerbose()) { StreamString strm; lldb_private::FormatEntity::Entry format; FormatEntity::Parse("${frame.pc}: ", format); inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address, show_bytes, NULL, NULL, NULL, &format, 0); log->PutString(strm.GetString()); } last_condition = m_inst_emulator_ap->GetInstructionCondition(); m_inst_emulator_ap->EvaluateInstruction( eEmulateInstructionOptionIgnoreConditions); // If the current instruction is a branch forward then save the // current CFI information // for the offset where we are branching. if (m_forward_branch_offset != 0 && range.ContainsFileAddress(inst->GetAddress().GetFileAddress() + m_forward_branch_offset)) { auto newrow = std::make_shared<UnwindPlan::Row>(*m_curr_row.get()); newrow->SetOffset(current_offset + m_forward_branch_offset); saved_unwind_states.insert( {current_offset + m_forward_branch_offset, {newrow, m_register_values}}); unwind_plan.InsertRow(newrow); } // Were there any changes to the CFI while evaluating this // instruction? if (m_curr_row_modified) { // Save the modified row if we don't already have a CFI row in the // currennt address if (saved_unwind_states.count( current_offset + inst->GetOpcode().GetByteSize()) == 0) { m_curr_row->SetOffset(current_offset + inst->GetOpcode().GetByteSize()); unwind_plan.InsertRow(m_curr_row); saved_unwind_states.insert( {current_offset + inst->GetOpcode().GetByteSize(), {m_curr_row, m_register_values}}); // Allocate a new Row for m_curr_row, copy the current state // into it UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *m_curr_row.get(); m_curr_row.reset(newrow); } } } } } } if (log && log->GetVerbose()) { StreamString strm; lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress(); strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", base_addr, base_addr + range.GetByteSize()); unwind_plan.Dump(strm, nullptr, base_addr); log->PutString(strm.GetString()); } return unwind_plan.GetRowCount() > 0; } return false; }
bool DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t dwarf_offset, Address startaddr, UnwindPlan& unwind_plan) { lldb::offset_t offset = dwarf_offset; lldb::offset_t current_entry = offset; if (m_section_sp.get() == nullptr || m_section_sp->IsEncrypted()) return false; if (m_cfi_data_initialized == false) GetCFIData(); uint32_t length = m_cfi_data.GetU32 (&offset); dw_offset_t cie_offset; bool is_64bit = (length == UINT32_MAX); if (is_64bit) { length = m_cfi_data.GetU64 (&offset); cie_offset = m_cfi_data.GetU64 (&offset); } else { cie_offset = m_cfi_data.GetU32 (&offset); } assert (cie_offset != 0 && cie_offset != UINT32_MAX); // Translate the CIE_id from the eh_frame format, which // is relative to the FDE offset, into a __eh_frame section // offset if (m_is_eh_frame) { unwind_plan.SetSourceName ("eh_frame CFI"); cie_offset = current_entry + (is_64bit ? 12 : 4) - cie_offset; unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); } else { unwind_plan.SetSourceName ("DWARF CFI"); // In theory the debug_frame info should be valid at all call sites // ("asynchronous unwind info" as it is sometimes called) but in practice // gcc et al all emit call frame info for the prologue and call sites, but // not for the epilogue or all the other locations during the function reliably. unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); } unwind_plan.SetSourcedFromCompiler (eLazyBoolYes); const CIE *cie = GetCIE (cie_offset); assert (cie != nullptr); const dw_offset_t end_offset = current_entry + length + (is_64bit ? 12 : 4); const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress(); const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS; const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS; lldb::addr_t range_base = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr); lldb::addr_t range_len = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr); AddressRange range (range_base, m_objfile.GetAddressByteSize(), m_objfile.GetSectionList()); range.SetByteSize (range_len); addr_t lsda_data_file_address = LLDB_INVALID_ADDRESS; if (cie->augmentation[0] == 'z') { uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset); if (aug_data_len != 0 && cie->lsda_addr_encoding != DW_EH_PE_omit) { offset_t saved_offset = offset; lsda_data_file_address = m_cfi_data.GetGNUEHPointer(&offset, cie->lsda_addr_encoding, pc_rel_addr, text_addr, data_addr); if (offset - saved_offset != aug_data_len) { // There is more in the augmentation region than we know how to process; // don't read anything. lsda_data_file_address = LLDB_INVALID_ADDRESS; } offset = saved_offset; } offset += aug_data_len; } Address lsda_data; Address personality_function_ptr; if (lsda_data_file_address != LLDB_INVALID_ADDRESS && cie->personality_loc != LLDB_INVALID_ADDRESS) { m_objfile.GetModule()->ResolveFileAddress (lsda_data_file_address, lsda_data); m_objfile.GetModule()->ResolveFileAddress (cie->personality_loc, personality_function_ptr); } if (lsda_data.IsValid() && personality_function_ptr.IsValid()) { unwind_plan.SetLSDAAddress (lsda_data); unwind_plan.SetPersonalityFunctionPtr (personality_function_ptr); } uint32_t code_align = cie->code_align; int32_t data_align = cie->data_align; unwind_plan.SetPlanValidAddressRange (range); UnwindPlan::Row *cie_initial_row = new UnwindPlan::Row; *cie_initial_row = cie->initial_row; UnwindPlan::RowSP row(cie_initial_row); unwind_plan.SetRegisterKind (m_reg_kind); unwind_plan.SetReturnAddressRegister (cie->return_addr_reg_num); std::vector<UnwindPlan::RowSP> stack; UnwindPlan::Row::RegisterLocation reg_location; while (m_cfi_data.ValidOffset(offset) && offset < end_offset) { uint8_t inst = m_cfi_data.GetU8(&offset); uint8_t primary_opcode = inst & 0xC0; uint8_t extended_opcode = inst & 0x3F; if (!HandleCommonDwarfOpcode(primary_opcode, extended_opcode, data_align, offset, *row)) { if (primary_opcode) { switch (primary_opcode) { case DW_CFA_advance_loc : // (Row Creation Instruction) { // 0x40 - high 2 bits are 0x1, lower 6 bits are delta // takes a single argument that represents a constant delta. The // required action is to create a new table row with a location // value that is computed by taking the current entry's location // value and adding (delta * code_align). All other // values in the new row are initially identical to the current row. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset(extended_opcode * code_align); break; } case DW_CFA_restore : { // 0xC0 - high 2 bits are 0x3, lower 6 bits are register // takes a single argument that represents a register number. The // required action is to change the rule for the indicated register // to the rule assigned it by the initial_instructions in the CIE. uint32_t reg_num = extended_opcode; // We only keep enough register locations around to // unwind what is in our thread, and these are organized // by the register index in that state, so we need to convert our // GCC register number from the EH frame info, to a register index if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location)) row->SetRegisterInfo (reg_num, reg_location); break; } } } else { switch (extended_opcode) { case DW_CFA_set_loc : // 0x1 (Row Creation Instruction) { // DW_CFA_set_loc takes a single argument that represents an address. // The required action is to create a new table row using the // specified address as the location. All other values in the new row // are initially identical to the current row. The new location value // should always be greater than the current one. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SetOffset(m_cfi_data.GetPointer(&offset) - startaddr.GetFileAddress()); break; } case DW_CFA_advance_loc1 : // 0x2 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU8(&offset) * code_align); break; } case DW_CFA_advance_loc2 : // 0x3 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU16(&offset) * code_align); break; } case DW_CFA_advance_loc4 : // 0x4 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU32(&offset) * code_align); break; } case DW_CFA_restore_extended : // 0x6 { // takes a single unsigned LEB128 argument that represents a register // number. This instruction is identical to DW_CFA_restore except for // the encoding and size of the register argument. uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location)) row->SetRegisterInfo (reg_num, reg_location); break; } case DW_CFA_remember_state : // 0xA { // These instructions define a stack of information. Encountering the // DW_CFA_remember_state instruction means to save the rules for every // register on the current row on the stack. Encountering the // DW_CFA_restore_state instruction means to pop the set of rules off // the stack and place them in the current row. (This operation is // useful for compilers that move epilogue code into the body of a // function.) stack.push_back (row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); break; } case DW_CFA_restore_state : // 0xB { // These instructions define a stack of information. Encountering the // DW_CFA_remember_state instruction means to save the rules for every // register on the current row on the stack. Encountering the // DW_CFA_restore_state instruction means to pop the set of rules off // the stack and place them in the current row. (This operation is // useful for compilers that move epilogue code into the body of a // function.) lldb::addr_t offset = row->GetOffset (); row = stack.back (); stack.pop_back (); row->SetOffset (offset); break; } case DW_CFA_val_offset : // 0x14 case DW_CFA_val_offset_sf : // 0x15 default: break; } } } } unwind_plan.AppendRow(row); return true; }
bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite( AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { bool do_augment_unwindplan = true; UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset(0); UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset(-1); int wordsize = 8; ProcessSP process_sp(thread.GetProcess()); if (process_sp.get() == nullptr) return false; wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); RegisterNumber sp_regnum(thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); RegisterNumber pc_regnum(thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); // Does this UnwindPlan describe the prologue? I want to see that the CFA is // set in terms of the stack pointer plus an offset, and I want to see that // rip is retrieved at the CFA-wordsize. If there is no description of the // prologue, don't try to augment this eh_frame unwinder code, fall back to // assembly parsing instead. if (first_row->GetCFAValue().GetValueType() != UnwindPlan::Row::FAValue::isRegisterPlusOffset || RegisterNumber(thread, unwind_plan.GetRegisterKind(), first_row->GetCFAValue().GetRegisterNumber()) != sp_regnum || first_row->GetCFAValue().GetOffset() != wordsize) { return false; } UnwindPlan::Row::RegisterLocation first_row_pc_loc; if (!first_row->GetRegisterInfo( pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()), first_row_pc_loc) || !first_row_pc_loc.IsAtCFAPlusOffset() || first_row_pc_loc.GetOffset() != -wordsize) { return false; } // It looks like the prologue is described. Is the epilogue described? If it // is, no need to do any augmentation. if (first_row != last_row && first_row->GetOffset() != last_row->GetOffset()) { // The first & last row have the same CFA register and the same CFA offset // value and the CFA register is esp/rsp (the stack pointer). // We're checking that both of them have an unwind rule like "CFA=esp+4" or // CFA+rsp+8". if (first_row->GetCFAValue().GetValueType() == last_row->GetCFAValue().GetValueType() && first_row->GetCFAValue().GetRegisterNumber() == last_row->GetCFAValue().GetRegisterNumber() && first_row->GetCFAValue().GetOffset() == last_row->GetCFAValue().GetOffset()) { // Get the register locations for eip/rip from the first & last rows. Are // they both CFA plus an offset? Is it the same offset? UnwindPlan::Row::RegisterLocation last_row_pc_loc; if (last_row->GetRegisterInfo( pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()), last_row_pc_loc)) { if (last_row_pc_loc.IsAtCFAPlusOffset() && first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) { // One last sanity check: Is the unwind rule for getting the caller // pc value "deref the CFA-4" or "deref the CFA-8"? // If so, we have an UnwindPlan that already describes the epilogue // and we don't need to modify it at all. if (first_row_pc_loc.GetOffset() == -wordsize) { do_augment_unwindplan = false; } } } } } if (do_augment_unwindplan) { if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0) return false; if (m_assembly_inspection_engine == nullptr) return false; const bool prefer_file_cache = true; std::vector<uint8_t> function_text(func.GetByteSize()); Status error; if (process_sp->GetTarget().ReadMemory( func.GetBaseAddress(), prefer_file_cache, function_text.data(), func.GetByteSize(), error) == func.GetByteSize()) { RegisterContextSP reg_ctx(thread.GetRegisterContext()); m_assembly_inspection_engine->Initialize(reg_ctx); return m_assembly_inspection_engine->AugmentUnwindPlanFromCallSite( function_text.data(), func.GetByteSize(), func, unwind_plan, reg_ctx); } } return false; }
bool DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t offset, Address startaddr, UnwindPlan& unwind_plan) { dw_offset_t current_entry = offset; if (m_section_sp.get() == NULL || m_section_sp->IsEncrypted()) return false; if (m_cfi_data_initialized == false) GetCFIData(); uint32_t length = m_cfi_data.GetU32 (&offset); dw_offset_t cie_offset = m_cfi_data.GetU32 (&offset); assert (cie_offset != 0 && cie_offset != UINT32_MAX); // Translate the CIE_id from the eh_frame format, which // is relative to the FDE offset, into a __eh_frame section // offset if (m_is_eh_frame) { unwind_plan.SetSourceName ("eh_frame CFI"); cie_offset = current_entry + 4 - cie_offset; unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); } else { unwind_plan.SetSourceName ("DWARF CFI"); // In theory the debug_frame info should be valid at all call sites // ("asynchronous unwind info" as it is sometimes called) but in practice // gcc et al all emit call frame info for the prologue and call sites, but // not for the epilogue or all the other locations during the function reliably. unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); } unwind_plan.SetSourcedFromCompiler (eLazyBoolYes); const CIE *cie = GetCIE (cie_offset); assert (cie != NULL); const dw_offset_t end_offset = current_entry + length + 4; const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress(); const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS; const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS; lldb::addr_t range_base = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr); lldb::addr_t range_len = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr); AddressRange range (range_base, m_objfile.GetAddressByteSize(), m_objfile.GetSectionList()); range.SetByteSize (range_len); if (cie->augmentation[0] == 'z') { uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset); offset += aug_data_len; } uint32_t reg_num = 0; int32_t op_offset = 0; uint32_t code_align = cie->code_align; int32_t data_align = cie->data_align; unwind_plan.SetPlanValidAddressRange (range); UnwindPlan::Row *cie_initial_row = new UnwindPlan::Row; *cie_initial_row = cie->initial_row; UnwindPlan::RowSP row(cie_initial_row); unwind_plan.SetRegisterKind (m_reg_kind); unwind_plan.SetReturnAddressRegister (cie->return_addr_reg_num); UnwindPlan::Row::RegisterLocation reg_location; while (m_cfi_data.ValidOffset(offset) && offset < end_offset) { uint8_t inst = m_cfi_data.GetU8(&offset); uint8_t primary_opcode = inst & 0xC0; uint8_t extended_opcode = inst & 0x3F; if (primary_opcode) { switch (primary_opcode) { case DW_CFA_advance_loc : // (Row Creation Instruction) { // 0x40 - high 2 bits are 0x1, lower 6 bits are delta // takes a single argument that represents a constant delta. The // required action is to create a new table row with a location // value that is computed by taking the current entry's location // value and adding (delta * code_align). All other // values in the new row are initially identical to the current row. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset(extended_opcode * code_align); } break; case DW_CFA_offset : { // 0x80 - high 2 bits are 0x2, lower 6 bits are register // takes two arguments: an unsigned LEB128 constant representing a // factored offset and a register number. The required action is to // change the rule for the register indicated by the register number // to be an offset(N) rule with a value of // (N = factored offset * data_align). reg_num = extended_opcode; op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align; reg_location.SetAtCFAPlusOffset(op_offset); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_restore : { // 0xC0 - high 2 bits are 0x3, lower 6 bits are register // takes a single argument that represents a register number. The // required action is to change the rule for the indicated register // to the rule assigned it by the initial_instructions in the CIE. reg_num = extended_opcode; // We only keep enough register locations around to // unwind what is in our thread, and these are organized // by the register index in that state, so we need to convert our // GCC register number from the EH frame info, to a register index if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location)) row->SetRegisterInfo (reg_num, reg_location); } break; } } else { switch (extended_opcode) { case DW_CFA_nop : // 0x0 break; case DW_CFA_set_loc : // 0x1 (Row Creation Instruction) { // DW_CFA_set_loc takes a single argument that represents an address. // The required action is to create a new table row using the // specified address as the location. All other values in the new row // are initially identical to the current row. The new location value // should always be greater than the current one. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SetOffset(m_cfi_data.GetPointer(&offset) - startaddr.GetFileAddress()); } break; case DW_CFA_advance_loc1 : // 0x2 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU8(&offset) * code_align); } break; case DW_CFA_advance_loc2 : // 0x3 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU16(&offset) * code_align); } break; case DW_CFA_advance_loc4 : // 0x4 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU32(&offset) * code_align); } break; case DW_CFA_offset_extended : // 0x5 { // takes two unsigned LEB128 arguments representing a register number // and a factored offset. This instruction is identical to DW_CFA_offset // except for the encoding and size of the register argument. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align; reg_location.SetAtCFAPlusOffset(op_offset); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_restore_extended : // 0x6 { // takes a single unsigned LEB128 argument that represents a register // number. This instruction is identical to DW_CFA_restore except for // the encoding and size of the register argument. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location)) row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_undefined : // 0x7 { // takes a single unsigned LEB128 argument that represents a register // number. The required action is to set the rule for the specified // register to undefined. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); reg_location.SetUndefined(); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_same_value : // 0x8 { // takes a single unsigned LEB128 argument that represents a register // number. The required action is to set the rule for the specified // register to same value. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); reg_location.SetSame(); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_register : // 0x9 { // takes two unsigned LEB128 arguments representing register numbers. // The required action is to set the rule for the first register to be // the second register. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); uint32_t other_reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); reg_location.SetInRegister(other_reg_num); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_remember_state : // 0xA { // These instructions define a stack of information. Encountering the // DW_CFA_remember_state instruction means to save the rules for every // register on the current row on the stack. Encountering the // DW_CFA_restore_state instruction means to pop the set of rules off // the stack and place them in the current row. (This operation is // useful for compilers that move epilogue code into the body of a // function.) unwind_plan.AppendRow (row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); } break; case DW_CFA_restore_state : // 0xB // These instructions define a stack of information. Encountering the // DW_CFA_remember_state instruction means to save the rules for every // register on the current row on the stack. Encountering the // DW_CFA_restore_state instruction means to pop the set of rules off // the stack and place them in the current row. (This operation is // useful for compilers that move epilogue code into the body of a // function.) { row = unwind_plan.GetRowAtIndex(unwind_plan.GetRowCount() - 1); } break; case DW_CFA_def_cfa : // 0xC (CFA Definition Instruction) { // Takes two unsigned LEB128 operands representing a register // number and a (non-factored) offset. The required action // is to define the current CFA rule to use the provided // register and offset. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); op_offset = (int32_t)m_cfi_data.GetULEB128(&offset); row->SetCFARegister (reg_num); row->SetCFAOffset (op_offset); } break; case DW_CFA_def_cfa_register : // 0xD (CFA Definition Instruction) { // takes a single unsigned LEB128 argument representing a register // number. The required action is to define the current CFA rule to // use the provided register (but to keep the old offset). reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); row->SetCFARegister (reg_num); } break; case DW_CFA_def_cfa_offset : // 0xE (CFA Definition Instruction) { // Takes a single unsigned LEB128 operand representing a // (non-factored) offset. The required action is to define // the current CFA rule to use the provided offset (but // to keep the old register). op_offset = (int32_t)m_cfi_data.GetULEB128(&offset); row->SetCFAOffset (op_offset); } break; case DW_CFA_def_cfa_expression : // 0xF (CFA Definition Instruction) { size_t block_len = (size_t)m_cfi_data.GetULEB128(&offset); offset += (uint32_t)block_len; } break; case DW_CFA_expression : // 0x10 { // Takes two operands: an unsigned LEB128 value representing // a register number, and a DW_FORM_block value representing a DWARF // expression. The required action is to change the rule for the // register indicated by the register number to be an expression(E) // rule where E is the DWARF expression. That is, the DWARF // expression computes the address. The value of the CFA is // pushed on the DWARF evaluation stack prior to execution of // the DWARF expression. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset); const uint8_t *block_data = (uint8_t *)m_cfi_data.GetData(&offset, block_len); reg_location.SetAtDWARFExpression(block_data, block_len); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_offset_extended_sf : // 0x11 { // takes two operands: an unsigned LEB128 value representing a // register number and a signed LEB128 factored offset. This // instruction is identical to DW_CFA_offset_extended except //that the second operand is signed and factored. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; reg_location.SetAtCFAPlusOffset(op_offset); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_def_cfa_sf : // 0x12 (CFA Definition Instruction) { // Takes two operands: an unsigned LEB128 value representing // a register number and a signed LEB128 factored offset. // This instruction is identical to DW_CFA_def_cfa except // that the second operand is signed and factored. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; row->SetCFARegister (reg_num); row->SetCFAOffset (op_offset); } break; case DW_CFA_def_cfa_offset_sf : // 0x13 (CFA Definition Instruction) { // takes a signed LEB128 operand representing a factored // offset. This instruction is identical to DW_CFA_def_cfa_offset // except that the operand is signed and factored. op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; row->SetCFAOffset (op_offset); } break; case DW_CFA_val_expression : // 0x16 { // takes two operands: an unsigned LEB128 value representing a register // number, and a DW_FORM_block value representing a DWARF expression. // The required action is to change the rule for the register indicated // by the register number to be a val_expression(E) rule where E is the // DWARF expression. That is, the DWARF expression computes the value of // the given register. The value of the CFA is pushed on the DWARF // evaluation stack prior to execution of the DWARF expression. reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset); const uint8_t* block_data = (uint8_t*)m_cfi_data.GetData(&offset, block_len); //#if defined(__i386__) || defined(__x86_64__) // // The EH frame info for EIP and RIP contains code that looks for traps to // // be a specific type and increments the PC. // // For i386: // // DW_CFA_val_expression where: // // eip = DW_OP_breg6(+28), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x34), // // DW_OP_deref, DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref, // // DW_OP_dup, DW_OP_lit3, DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, // // DW_OP_and, DW_OP_plus // // This basically does a: // // eip = ucontenxt.mcontext32->gpr.eip; // // if (ucontenxt.mcontext32->exc.trapno != 3 && ucontenxt.mcontext32->exc.trapno != 4) // // eip++; // // // // For x86_64: // // DW_CFA_val_expression where: // // rip = DW_OP_breg3(+48), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x90), DW_OP_deref, // // DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref_size(4), DW_OP_dup, DW_OP_lit3, // // DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, DW_OP_and, DW_OP_plus // // This basically does a: // // rip = ucontenxt.mcontext64->gpr.rip; // // if (ucontenxt.mcontext64->exc.trapno != 3 && ucontenxt.mcontext64->exc.trapno != 4) // // rip++; // // The trap comparisons and increments are not needed as it hoses up the unwound PC which // // is expected to point at least past the instruction that causes the fault/trap. So we // // take it out by trimming the expression right at the first "DW_OP_swap" opcodes // if (block_data != NULL && thread->GetPCRegNum(Thread::GCC) == reg_num) // { // if (thread->Is64Bit()) // { // if (block_len > 9 && block_data[8] == DW_OP_swap && block_data[9] == DW_OP_plus_uconst) // block_len = 8; // } // else // { // if (block_len > 8 && block_data[7] == DW_OP_swap && block_data[8] == DW_OP_plus_uconst) // block_len = 7; // } // } //#endif reg_location.SetIsDWARFExpression(block_data, block_len); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_val_offset : // 0x14 case DW_CFA_val_offset_sf : // 0x15 default: break; } } } unwind_plan.AppendRow(row); return true; }
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& range, Thread& thread, UnwindPlan& unwind_plan) { if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() && m_inst_emulator_ap.get()) { // The instruction emulation subclass setup the unwind plan for the // first instruction. m_inst_emulator_ap->CreateFunctionEntryUnwind (unwind_plan); // CreateFunctionEntryUnwind should have created the first row. If it // doesn't, then we are done. if (unwind_plan.GetRowCount() == 0) return false; ExecutionContext exe_ctx; thread.CalculateExecutionContext(exe_ctx); const bool prefer_file_cache = true; DisassemblerSP disasm_sp (Disassembler::DisassembleRange (m_arch, NULL, NULL, exe_ctx, range, prefer_file_cache)); Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); if (disasm_sp) { m_range_ptr = ⦥ m_thread_ptr = &thread; m_unwind_plan_ptr = &unwind_plan; const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); const bool show_address = true; const bool show_bytes = true; m_inst_emulator_ap->GetRegisterInfo (unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister(), m_cfa_reg_info); m_fp_is_cfa = false; m_register_values.clear(); m_pushed_regs.clear(); // Initialize the CFA with a known value. In the 32 bit case // it will be 0x80000000, and in the 64 bit case 0x8000000000000000. // We use the address byte size to be safe for any future address sizes m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); RegisterValue cfa_reg_value; cfa_reg_value.SetUInt (m_initial_sp, m_cfa_reg_info.byte_size); SetRegisterValue (m_cfa_reg_info, cfa_reg_value); const InstructionList &inst_list = disasm_sp->GetInstructionList (); const size_t num_instructions = inst_list.GetSize(); if (num_instructions > 0) { Instruction *inst = inst_list.GetInstructionAtIndex (0).get(); const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress(); // Map for storing the unwind plan row and the value of the registers at a given offset. // When we see a forward branch we add a new entry to this map with the actual unwind plan // row and register context for the target address of the branch as the current data have // to be valid for the target address of the branch too if we are in the same function. std::map<lldb::addr_t, std::pair<UnwindPlan::RowSP, RegisterValueMap>> saved_unwind_states; // Make a copy of the current instruction Row and save it in m_curr_row // so we can add updates as we process the instructions. UnwindPlan::RowSP last_row = unwind_plan.GetLastRow(); UnwindPlan::Row *newrow = new UnwindPlan::Row; if (last_row.get()) *newrow = *last_row.get(); m_curr_row.reset(newrow); // Add the initial state to the save list with offset 0. saved_unwind_states.insert({0, {last_row, m_register_values}}); // cache the pc register number (in whatever register numbering this UnwindPlan uses) for // quick reference during instruction parsing. uint32_t pc_reg_num = LLDB_INVALID_REGNUM; RegisterInfo pc_reg_info; if (m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info)) pc_reg_num = pc_reg_info.kinds[unwind_plan.GetRegisterKind()]; else pc_reg_num = LLDB_INVALID_REGNUM; // cache the return address register number (in whatever register numbering this UnwindPlan uses) for // quick reference during instruction parsing. uint32_t ra_reg_num = LLDB_INVALID_REGNUM; RegisterInfo ra_reg_info; if (m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info)) ra_reg_num = ra_reg_info.kinds[unwind_plan.GetRegisterKind()]; else ra_reg_num = LLDB_INVALID_REGNUM; for (size_t idx=0; idx<num_instructions; ++idx) { m_curr_row_modified = false; m_forward_branch_offset = 0; inst = inst_list.GetInstructionAtIndex (idx).get(); if (inst) { lldb::addr_t current_offset = inst->GetAddress().GetFileAddress() - base_addr; auto it = saved_unwind_states.upper_bound(current_offset); assert(it != saved_unwind_states.begin() && "Unwind row for the function entry missing"); --it; // Move it to the row corresponding to the current offset // If the offset of m_curr_row don't match with the offset we see in saved_unwind_states // then we have to update m_curr_row and m_register_values based on the saved values. It // is happenning after we processed an epilogue and a return to caller instruction. if (it->second.first->GetOffset() != m_curr_row->GetOffset()) { UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *it->second.first; m_curr_row.reset(newrow); m_register_values = it->second.second;; } if (log && log->GetVerbose ()) { StreamString strm; lldb_private::FormatEntity::Entry format; FormatEntity::Parse("${frame.pc}: ", format); inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL, NULL, NULL, &format, 0); log->PutCString (strm.GetData()); } m_inst_emulator_ap->SetInstruction (inst->GetOpcode(), inst->GetAddress(), exe_ctx.GetTargetPtr()); m_inst_emulator_ap->EvaluateInstruction (eEmulateInstructionOptionIgnoreConditions); // If the current instruction is a branch forward then save the current CFI information // for the offset where we are branching. if (m_forward_branch_offset != 0 && range.ContainsFileAddress(inst->GetAddress().GetFileAddress() + m_forward_branch_offset)) { auto newrow = std::make_shared<UnwindPlan::Row>(*m_curr_row.get()); newrow->SetOffset(current_offset + m_forward_branch_offset); saved_unwind_states.insert({current_offset + m_forward_branch_offset, {newrow, m_register_values}}); unwind_plan.InsertRow(newrow); } // Were there any changes to the CFI while evaluating this instruction? if (m_curr_row_modified) { // Save the modified row if we don't already have a CFI row in the currennt address if (saved_unwind_states.count(current_offset + inst->GetOpcode().GetByteSize()) == 0) { m_curr_row->SetOffset (current_offset + inst->GetOpcode().GetByteSize()); unwind_plan.InsertRow (m_curr_row); saved_unwind_states.insert({current_offset + inst->GetOpcode().GetByteSize(), {m_curr_row, m_register_values}}); // Allocate a new Row for m_curr_row, copy the current state into it UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *m_curr_row.get(); m_curr_row.reset(newrow); } } } } } // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. // I'll fix that but for now, just clear the list and it will go away nicely. disasm_sp->GetInstructionList().Clear(); } if (log && log->GetVerbose ()) { StreamString strm; lldb::addr_t base_addr = range.GetBaseAddress().GetLoadAddress(thread.CalculateTarget().get()); strm.Printf ("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", base_addr, base_addr + range.GetByteSize()); unwind_plan.Dump(strm, &thread, base_addr); log->PutCString (strm.GetData()); } return unwind_plan.GetRowCount() > 0; } return false; }