bool Disassembler::Disassemble ( Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, uint32_t num_instructions, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm ) { AddressRange range; StackFrame *frame = exe_ctx.GetFramePtr(); if (frame) { SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); if (sc.function) { range = sc.function->GetAddressRange(); } else if (sc.symbol && sc.symbol->ValueIsAddress()) { range.GetBaseAddress() = sc.symbol->GetAddress(); range.SetByteSize (sc.symbol->GetByteSize()); } else { range.GetBaseAddress() = frame->GetFrameCodeAddress(); } if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0) range.SetByteSize (DEFAULT_DISASM_BYTE_SIZE); } return Disassemble (debugger, arch, plugin_name, flavor, exe_ctx, range, num_instructions, num_mixed_context_lines, options, strm); }
bool AddressRange::Extend(const AddressRange &rhs_range) { addr_t lhs_end_addr = GetBaseAddress().GetFileAddress() + GetByteSize(); addr_t rhs_base_addr = rhs_range.GetBaseAddress().GetFileAddress(); if (!ContainsFileAddress(rhs_range.GetBaseAddress()) && lhs_end_addr != rhs_base_addr) // The ranges don't intersect at all on the right side of this range. return false; addr_t rhs_end_addr = rhs_base_addr + rhs_range.GetByteSize(); if (lhs_end_addr >= rhs_end_addr) // The rhs range totally overlaps this one, nothing to add. return false; m_byte_size += rhs_end_addr - lhs_end_addr; return true; }
size_t Disassembler::ParseInstructions ( const ExecutionContext *exe_ctx, const AddressRange &range, DataExtractor& data ) { Target *target = exe_ctx->target; const addr_t byte_size = range.GetByteSize(); if (target == NULL || byte_size == 0 || !range.GetBaseAddress().IsValid()) return 0; DataBufferHeap *heap_buffer = new DataBufferHeap (byte_size, '\0'); DataBufferSP data_sp(heap_buffer); Error error; const size_t bytes_read = target->ReadMemory (range.GetBaseAddress(), heap_buffer->GetBytes(), heap_buffer->GetByteSize(), error); if (bytes_read > 0) { if (bytes_read != heap_buffer->GetByteSize()) heap_buffer->SetByteSize (bytes_read); data.SetData(data_sp); if (exe_ctx->process) { data.SetByteOrder(exe_ctx->process->GetByteOrder()); data.SetAddressByteSize(exe_ctx->process->GetAddressByteSize()); } else { data.SetByteOrder(target->GetArchitecture().GetDefaultEndian()); data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); } return DecodeInstructions (data, 0, UINT32_MAX); } return 0; }
lldb::DisassemblerSP Disassembler::DisassembleRange ( const ArchSpec &arch, const char *plugin_name, const ExecutionContext &exe_ctx, const AddressRange &range ) { lldb::DisassemblerSP disasm_sp; if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid()) { disasm_sp.reset (Disassembler::FindPlugin(arch, plugin_name)); if (disasm_sp) { size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, NULL); if (bytes_disassembled == 0) disasm_sp.reset(); } } return disasm_sp; }
void UnwindPlan::SetPlanValidAddressRange (const AddressRange& range) { if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0) m_plan_valid_address_range = range; }
//---------------------------------------------------------------------- // ThreadPlanStepOut: Step out of the current frame //---------------------------------------------------------------------- ThreadPlanStepOut::ThreadPlanStepOut( Thread &thread, SymbolContext *context, bool first_insn, bool stop_others, Vote stop_vote, Vote run_vote, uint32_t frame_idx, LazyBool step_out_avoids_code_without_debug_info, bool continue_to_next_branch, bool gather_return_value) : ThreadPlan(ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, run_vote), ThreadPlanShouldStopHere(this), m_step_from_insn(LLDB_INVALID_ADDRESS), m_return_bp_id(LLDB_INVALID_BREAK_ID), m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others), m_immediate_step_from_function(nullptr), m_calculate_return_value(gather_return_value) { SetFlagsToDefault(); SetupAvoidNoDebug(step_out_avoids_code_without_debug_info); m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0); StackFrameSP return_frame_sp(m_thread.GetStackFrameAtIndex(frame_idx + 1)); StackFrameSP immediate_return_from_sp( m_thread.GetStackFrameAtIndex(frame_idx)); if (!return_frame_sp || !immediate_return_from_sp) return; // we can't do anything here. ValidatePlan() will return false. m_step_out_to_id = return_frame_sp->GetStackID(); m_immediate_step_from_id = immediate_return_from_sp->GetStackID(); // If the frame directly below the one we are returning to is inlined, we have // to be // a little more careful. It is non-trivial to determine the real "return // code address" for // an inlined frame, so we have to work our way to that frame and then step // out. if (immediate_return_from_sp && immediate_return_from_sp->IsInlined()) { if (frame_idx > 0) { // First queue a plan that gets us to this inlined frame, and when we get // there we'll queue a second // plan that walks us out of this frame. m_step_out_to_inline_plan_sp.reset(new ThreadPlanStepOut( m_thread, nullptr, false, stop_others, eVoteNoOpinion, eVoteNoOpinion, frame_idx - 1, eLazyBoolNo, continue_to_next_branch)); static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get()) ->SetShouldStopHereCallbacks(nullptr, nullptr); m_step_out_to_inline_plan_sp->SetPrivate(true); } else { // If we're already at the inlined frame we're stepping through, then just // do that now. QueueInlinedStepPlan(false); } } else if (return_frame_sp) { // Find the return address and set a breakpoint there: // FIXME - can we do this more securely if we know first_insn? Address return_address(return_frame_sp->GetFrameCodeAddress()); if (continue_to_next_branch) { SymbolContext return_address_sc; AddressRange range; Address return_address_decr_pc = return_address; if (return_address_decr_pc.GetOffset() > 0) return_address_decr_pc.Slide(-1); return_address_decr_pc.CalculateSymbolContext( &return_address_sc, lldb::eSymbolContextLineEntry); if (return_address_sc.line_entry.IsValid()) { range = return_address_sc.line_entry.GetSameLineContiguousAddressRange(); if (range.GetByteSize() > 0) { return_address = m_thread.GetProcess()->AdvanceAddressToNextBranchInstruction( return_address, range); } } } m_return_addr = return_address.GetLoadAddress(&m_thread.GetProcess()->GetTarget()); if (m_return_addr == LLDB_INVALID_ADDRESS) return; Breakpoint *return_bp = m_thread.CalculateTarget() ->CreateBreakpoint(m_return_addr, true, false) .get(); if (return_bp != nullptr) { return_bp->SetThreadID(m_thread.GetID()); m_return_bp_id = return_bp->GetID(); return_bp->SetBreakpointKind("step-out"); } if (immediate_return_from_sp) { const SymbolContext &sc = immediate_return_from_sp->GetSymbolContext(eSymbolContextFunction); if (sc.function) { m_immediate_step_from_function = sc.function; } } } }
bool CommandObjectDisassemble::Execute ( CommandInterpreter &interpreter, Args& command, CommandReturnObject &result ) { Target *target = interpreter.GetDebugger().GetCurrentTarget().get(); if (target == NULL) { result.AppendError ("invalid target, set executable file using 'file' command"); result.SetStatus (eReturnStatusFailed); return false; } ArchSpec arch(target->GetArchitecture()); if (!arch.IsValid()) { result.AppendError ("target needs valid architecure in order to be able to disassemble"); result.SetStatus (eReturnStatusFailed); return false; } Disassembler *disassembler = Disassembler::FindPlugin(arch); if (disassembler == NULL) { result.AppendErrorWithFormat ("Unable to find Disassembler plug-in for %s architecture.\n", arch.AsCString()); result.SetStatus (eReturnStatusFailed); return false; } result.SetStatus (eReturnStatusSuccessFinishResult); if (command.GetArgumentCount() != 0) { result.AppendErrorWithFormat ("\"disassemble\" doesn't take any arguments.\n"); result.SetStatus (eReturnStatusFailed); return false; } ExecutionContext exe_ctx(interpreter.GetDebugger().GetExecutionContext()); if (m_options.show_mixed && m_options.num_lines_context == 0) m_options.num_lines_context = 3; if (!m_options.m_func_name.empty()) { ConstString name(m_options.m_func_name.c_str()); if (Disassembler::Disassemble (interpreter.GetDebugger(), arch, exe_ctx, name, NULL, // Module * m_options.show_mixed ? m_options.num_lines_context : 0, m_options.show_bytes, result.GetOutputStream())) { result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat ("Unable to find symbol with name '%s'.\n", name.GetCString()); result.SetStatus (eReturnStatusFailed); } } else { AddressRange range; if (m_options.m_start_addr != LLDB_INVALID_ADDRESS) { range.GetBaseAddress().SetOffset (m_options.m_start_addr); if (m_options.m_end_addr != LLDB_INVALID_ADDRESS) { if (m_options.m_end_addr < m_options.m_start_addr) { result.AppendErrorWithFormat ("End address before start address.\n"); result.SetStatus (eReturnStatusFailed); return false; } range.SetByteSize (m_options.m_end_addr - m_options.m_start_addr); } else range.SetByteSize (DEFAULT_DISASM_BYTE_SIZE); } else { if (exe_ctx.frame) { SymbolContext sc(exe_ctx.frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); if (sc.function) range = sc.function->GetAddressRange(); else if (sc.symbol && sc.symbol->GetAddressRangePtr()) range = *sc.symbol->GetAddressRangePtr(); else range.GetBaseAddress() = exe_ctx.frame->GetPC(); } else { result.AppendError ("invalid frame"); result.SetStatus (eReturnStatusFailed); return false; } } if (range.GetByteSize() == 0) range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); if (Disassembler::Disassemble (interpreter.GetDebugger(), arch, exe_ctx, range, m_options.show_mixed ? m_options.num_lines_context : 0, m_options.show_bytes, result.GetOutputStream())) { result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat ("Failed to disassemble memory at 0x%8.8llx.\n", m_options.m_start_addr); result.SetStatus (eReturnStatusFailed); } } return result.Succeeded(); }
bool CommandObjectDisassemble::DoExecute (Args& command, CommandReturnObject &result) { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == NULL) { result.AppendError ("invalid target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); return false; } if (!m_options.arch.IsValid()) m_options.arch = target->GetArchitecture(); if (!m_options.arch.IsValid()) { result.AppendError ("use the --arch option or set the target architecure to disassemble"); result.SetStatus (eReturnStatusFailed); return false; } const char *plugin_name = m_options.GetPluginName (); const char *flavor_string = m_options.GetFlavorString(); DisassemblerSP disassembler = Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name); if (!disassembler) { if (plugin_name) { result.AppendErrorWithFormat ("Unable to find Disassembler plug-in named '%s' that supports the '%s' architecture.\n", plugin_name, m_options.arch.GetArchitectureName()); } else result.AppendErrorWithFormat ("Unable to find Disassembler plug-in for the '%s' architecture.\n", m_options.arch.GetArchitectureName()); result.SetStatus (eReturnStatusFailed); return false; } else if (flavor_string != NULL && !disassembler->FlavorValidForArchSpec(m_options.arch, flavor_string)) result.AppendWarningWithFormat("invalid disassembler flavor \"%s\", using default.\n", flavor_string); result.SetStatus (eReturnStatusSuccessFinishResult); if (command.GetArgumentCount() != 0) { result.AppendErrorWithFormat ("\"disassemble\" arguments are specified as options.\n"); GetOptions()->GenerateOptionUsage (result.GetErrorStream(), this); result.SetStatus (eReturnStatusFailed); return false; } if (m_options.show_mixed && m_options.num_lines_context == 0) m_options.num_lines_context = 1; // Always show the PC in the disassembly uint32_t options = Disassembler::eOptionMarkPCAddress; // Mark the source line for the current PC only if we are doing mixed source and assembly if (m_options.show_mixed) options |= Disassembler::eOptionMarkPCSourceLine; if (m_options.show_bytes) options |= Disassembler::eOptionShowBytes; if (m_options.raw) options |= Disassembler::eOptionRawOuput; if (!m_options.func_name.empty()) { ConstString name(m_options.func_name.c_str()); if (Disassembler::Disassemble (m_interpreter.GetDebugger(), m_options.arch, plugin_name, flavor_string, m_exe_ctx, name, NULL, // Module * m_options.num_instructions, m_options.show_mixed ? m_options.num_lines_context : 0, options, result.GetOutputStream())) { result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat ("Unable to find symbol with name '%s'.\n", name.GetCString()); result.SetStatus (eReturnStatusFailed); } } else { std::vector<AddressRange> ranges; AddressRange range; StackFrame *frame = m_exe_ctx.GetFramePtr(); if (m_options.frame_line) { if (frame == NULL) { result.AppendError ("Cannot disassemble around the current line without a selected frame.\n"); result.SetStatus (eReturnStatusFailed); return false; } LineEntry pc_line_entry (frame->GetSymbolContext(eSymbolContextLineEntry).line_entry); if (pc_line_entry.IsValid()) { range = pc_line_entry.range; } else { m_options.at_pc = true; // No line entry, so just disassemble around the current pc m_options.show_mixed = false; } } else if (m_options.current_function) { if (frame == NULL) { result.AppendError ("Cannot disassemble around the current function without a selected frame.\n"); result.SetStatus (eReturnStatusFailed); return false; } Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol; if (symbol) { range.GetBaseAddress() = symbol->GetAddress(); range.SetByteSize(symbol->GetByteSize()); } } // Did the "m_options.frame_line" find a valid range already? If so // skip the rest... if (range.GetByteSize() == 0) { if (m_options.at_pc) { if (frame == NULL) { result.AppendError ("Cannot disassemble around the current PC without a selected frame.\n"); result.SetStatus (eReturnStatusFailed); return false; } range.GetBaseAddress() = frame->GetFrameCodeAddress(); if (m_options.num_instructions == 0) { // Disassembling at the PC always disassembles some number of instructions (not the whole function). m_options.num_instructions = DEFAULT_DISASM_NUM_INS; } ranges.push_back(range); } else { range.GetBaseAddress().SetOffset (m_options.start_addr); if (range.GetBaseAddress().IsValid()) { if (m_options.end_addr != LLDB_INVALID_ADDRESS) { if (m_options.end_addr <= m_options.start_addr) { result.AppendErrorWithFormat ("End address before start address.\n"); result.SetStatus (eReturnStatusFailed); return false; } range.SetByteSize (m_options.end_addr - m_options.start_addr); } ranges.push_back(range); } else { if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS && target) { if (!target->GetSectionLoadList().IsEmpty()) { bool failed = false; Address symbol_containing_address; if (target->GetSectionLoadList().ResolveLoadAddress (m_options.symbol_containing_addr, symbol_containing_address)) { ModuleSP module_sp (symbol_containing_address.GetModule()); SymbolContext sc; bool resolve_tail_call_address = true; // PC can be one past the address range of the function. module_sp->ResolveSymbolContextForAddress (symbol_containing_address, eSymbolContextEverything, sc, resolve_tail_call_address); if (sc.function || sc.symbol) { sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range); } else { failed = true; } } else { failed = true; } if (failed) { result.AppendErrorWithFormat ("Could not find function bounds for address 0x%" PRIx64 "\n", m_options.symbol_containing_addr); result.SetStatus (eReturnStatusFailed); return false; } ranges.push_back(range); } else { for (lldb::ModuleSP module_sp : target->GetImages().Modules()) { lldb::addr_t file_addr = m_options.symbol_containing_addr; Address file_address; if (module_sp->ResolveFileAddress(file_addr, file_address)) { SymbolContext sc; bool resolve_tail_call_address = true; // PC can be one past the address range of the function. module_sp->ResolveSymbolContextForAddress (file_address, eSymbolContextEverything, sc, resolve_tail_call_address); if (sc.function || sc.symbol) { sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range); ranges.push_back(range); } } } } } } } } else ranges.push_back(range); if (m_options.num_instructions != 0) { if (ranges.size() == 0) { // The default action is to disassemble the current frame function. if (frame) { SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); if (sc.function) range.GetBaseAddress() = sc.function->GetAddressRange().GetBaseAddress(); else if (sc.symbol && sc.symbol->ValueIsAddress()) range.GetBaseAddress() = sc.symbol->GetAddress(); else range.GetBaseAddress() = frame->GetFrameCodeAddress(); } if (!range.GetBaseAddress().IsValid()) { result.AppendError ("invalid frame"); result.SetStatus (eReturnStatusFailed); return false; } } bool print_sc_header = ranges.size() > 1; for (AddressRange cur_range : ranges) { if (Disassembler::Disassemble (m_interpreter.GetDebugger(), m_options.arch, plugin_name, flavor_string, m_exe_ctx, cur_range.GetBaseAddress(), m_options.num_instructions, m_options.show_mixed ? m_options.num_lines_context : 0, options, result.GetOutputStream())) { result.SetStatus (eReturnStatusSuccessFinishResult); } else { if (m_options.start_addr != LLDB_INVALID_ADDRESS) result.AppendErrorWithFormat ("Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", m_options.start_addr); else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) result.AppendErrorWithFormat ("Failed to disassemble memory in function at 0x%8.8" PRIx64 ".\n", m_options.symbol_containing_addr); result.SetStatus (eReturnStatusFailed); } } if (print_sc_header) result.AppendMessage("\n"); } else { if (ranges.size() == 0) { // The default action is to disassemble the current frame function. if (frame) { SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); if (sc.function) range = sc.function->GetAddressRange(); else if (sc.symbol && sc.symbol->ValueIsAddress()) { range.GetBaseAddress() = sc.symbol->GetAddress(); range.SetByteSize (sc.symbol->GetByteSize()); } else range.GetBaseAddress() = frame->GetFrameCodeAddress(); } else { result.AppendError ("invalid frame"); result.SetStatus (eReturnStatusFailed); return false; } ranges.push_back(range); } bool print_sc_header = ranges.size() > 1; for (AddressRange cur_range : ranges) { if (cur_range.GetByteSize() == 0) cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); if (Disassembler::Disassemble (m_interpreter.GetDebugger(), m_options.arch, plugin_name, flavor_string, m_exe_ctx, cur_range, m_options.num_instructions, m_options.show_mixed ? m_options.num_lines_context : 0, options, result.GetOutputStream())) { result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat ("Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", m_options.start_addr); result.SetStatus (eReturnStatusFailed); } if (print_sc_header) result.AppendMessage("\n"); } } } return result.Succeeded(); }
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; }
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 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 Disassembler::Disassemble ( Debugger &debugger, const ArchSpec &arch, const ExecutionContext &exe_ctx, const AddressRange &disasm_range, uint32_t num_mixed_context_lines, bool show_bytes, Stream &strm ) { if (disasm_range.GetByteSize()) { Disassembler *disassembler = Disassembler::FindPlugin(arch); if (disassembler) { AddressRange range(disasm_range); Process *process = exe_ctx.process; // If we weren't passed in a section offset address range, // try and resolve it to something if (range.GetBaseAddress().IsSectionOffset() == false) { if (process && process->IsAlive()) { process->ResolveLoadAddress (range.GetBaseAddress().GetOffset(), range.GetBaseAddress()); } else if (exe_ctx.target) { exe_ctx.target->GetImages().ResolveFileAddress (range.GetBaseAddress().GetOffset(), range.GetBaseAddress()); } } DataExtractor data; size_t bytes_disassembled = disassembler->ParseInstructions (&exe_ctx, range, data); if (bytes_disassembled == 0) { return false; } else { // We got some things disassembled... size_t num_instructions = disassembler->GetInstructionList().GetSize(); uint32_t offset = 0; SymbolContext sc; SymbolContext prev_sc; AddressRange sc_range; if (num_mixed_context_lines) strm.IndentMore (); Address addr(range.GetBaseAddress()); // We extract the section to make sure we don't transition out // of the current section when disassembling const Section *addr_section = addr.GetSection(); Module *range_module = range.GetBaseAddress().GetModule(); for (size_t i=0; i<num_instructions; ++i) { Disassembler::Instruction *inst = disassembler->GetInstructionList().GetInstructionAtIndex (i); if (inst) { addr_t file_addr = addr.GetFileAddress(); if (addr_section == NULL || addr_section->ContainsFileAddress (file_addr) == false) { if (range_module) range_module->ResolveFileAddress (file_addr, addr); else if (exe_ctx.target) exe_ctx.target->GetImages().ResolveFileAddress (file_addr, addr); addr_section = addr.GetSection(); } prev_sc = sc; if (addr_section) { Module *module = addr_section->GetModule(); uint32_t resolved_mask = module->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); if (resolved_mask) { if (prev_sc.function != sc.function || prev_sc.symbol != sc.symbol) { if (prev_sc.function || prev_sc.symbol) strm.EOL(); strm << sc.module_sp->GetFileSpec().GetFilename(); if (sc.function) strm << '`' << sc.function->GetMangled().GetName(); else if (sc.symbol) strm << '`' << sc.symbol->GetMangled().GetName(); strm << ":\n"; } if (num_mixed_context_lines && !sc_range.ContainsFileAddress (addr)) { sc.GetAddressRange (eSymbolContextEverything, sc_range); if (sc != prev_sc) { if (offset != 0) strm.EOL(); sc.DumpStopContext(&strm, process, addr); if (sc.comp_unit && sc.line_entry.IsValid()) { debugger.GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.line_entry.file, sc.line_entry.line, num_mixed_context_lines, num_mixed_context_lines, num_mixed_context_lines ? "->" : "", &strm); } } } } else { sc.Clear(); } } if (num_mixed_context_lines) strm.IndentMore (); strm.Indent(); size_t inst_byte_size = inst->GetByteSize(); inst->Dump(&strm, &addr, show_bytes ? &data : NULL, offset, exe_ctx, show_bytes); strm.EOL(); offset += inst_byte_size; addr.SetOffset (addr.GetOffset() + inst_byte_size); if (num_mixed_context_lines) strm.IndentLess (); } else { break; } } if (num_mixed_context_lines) strm.IndentLess (); } } return true; } 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, 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; }