bool Disassembler::Disassemble ( Debugger &debugger, const ArchSpec &arch, const ExecutionContext &exe_ctx, uint32_t num_mixed_context_lines, bool show_bytes, Stream &strm ) { AddressRange range; 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(); } if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0) range.SetByteSize (DEFAULT_DISASM_BYTE_SIZE); } return Disassemble(debugger, arch, exe_ctx, range, num_mixed_context_lines, show_bytes, strm); }
bool Block::GetRangeContainingAddress (const Address& addr, AddressRange &range) { Function *function = CalculateSymbolContextFunction(); if (function) { const AddressRange &func_range = function->GetAddressRange(); if (addr.GetSection() == func_range.GetBaseAddress().GetSection()) { const addr_t addr_offset = addr.GetOffset(); const addr_t func_offset = func_range.GetBaseAddress().GetOffset(); if (addr_offset >= func_offset && addr_offset < func_offset + func_range.GetByteSize()) { addr_t offset = addr_offset - func_offset; const Range *range_ptr = m_ranges.FindEntryThatContains (offset); if (range_ptr) { range.GetBaseAddress() = func_range.GetBaseAddress(); range.GetBaseAddress().SetOffset(func_offset + range_ptr->GetRangeBase()); range.SetByteSize(range_ptr->GetByteSize()); return true; } } } } range.Clear(); return false; }
bool UnwindAssembly_x86::FirstNonPrologueInsn( AddressRange &func, const ExecutionContext &exe_ctx, Address &first_non_prologue_insn) { if (!func.GetBaseAddress().IsValid()) return false; Target *target = exe_ctx.GetTargetPtr(); if (target == nullptr) 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 (target->ReadMemory(func.GetBaseAddress(), prefer_file_cache, function_text.data(), func.GetByteSize(), error) == func.GetByteSize()) { size_t offset; if (m_assembly_inspection_engine->FindFirstNonPrologueInstruction( function_text.data(), func.GetByteSize(), offset)) { first_non_prologue_insn = func.GetBaseAddress(); first_non_prologue_insn.Slide(offset); } return true; } return false; }
size_t Disassembler::ParseInstructions (const ExecutionContext *exe_ctx, const AddressRange &range, Stream *error_strm_ptr, bool prefer_file_cache) { if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); 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; lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; const size_t bytes_read = target->ReadMemory (range.GetBaseAddress(), prefer_file_cache, heap_buffer->GetBytes(), heap_buffer->GetByteSize(), error, &load_addr); if (bytes_read > 0) { if (bytes_read != heap_buffer->GetByteSize()) heap_buffer->SetByteSize (bytes_read); DataExtractor data (data_sp, m_arch.GetByteOrder(), m_arch.GetAddressByteSize()); const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; return DecodeInstructions (range.GetBaseAddress(), data, 0, UINT32_MAX, false, data_from_file); } else if (error_strm_ptr) { const char *error_cstr = error.AsCString(); if (error_cstr) { error_strm_ptr->Printf("error: %s\n", error_cstr); } } } else if (error_strm_ptr) { error_strm_ptr->PutCString("error: invalid execution context\n"); } return 0; }
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); }
lldb::DisassemblerSP Disassembler::DisassembleRange ( const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const AddressRange &range, bool prefer_file_cache ) { lldb::DisassemblerSP disasm_sp; if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid()) { disasm_sp = Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch, flavor, plugin_name); if (disasm_sp) { size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, NULL, prefer_file_cache); if (bytes_disassembled == 0) disasm_sp.reset(); } } return disasm_sp; }
bool Disassembler::Disassemble ( Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const AddressRange &disasm_range, uint32_t num_instructions, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm ) { if (disasm_range.GetByteSize()) { lldb::DisassemblerSP disasm_sp (Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch, flavor, plugin_name)); if (disasm_sp.get()) { AddressRange range; ResolveAddress (exe_ctx, disasm_range.GetBaseAddress(), range.GetBaseAddress()); range.SetByteSize (disasm_range.GetByteSize()); const bool prefer_file_cache = false; size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, &strm, prefer_file_cache); if (bytes_disassembled == 0) return false; bool result = PrintInstructions (disasm_sp.get(), debugger, arch, exe_ctx, num_instructions, num_mixed_context_lines, options, strm); // 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(); return result; } } return false; }
bool Block::GetRangeAtIndex (uint32_t range_idx, AddressRange &range) { if (range_idx < m_ranges.GetSize()) { Function *function = CalculateSymbolContextFunction(); if (function) { const Range &vm_range = m_ranges.GetEntryRef(range_idx); range.GetBaseAddress() = function->GetAddressRange().GetBaseAddress(); range.GetBaseAddress().Slide(vm_range.GetRangeBase ()); range.SetByteSize (vm_range.GetByteSize()); return true; } } return false; }
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; }
ThreadPlanSP DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, bool stop) { LogSP log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); ThreadPlanSP thread_plan_sp; StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); Symbol *sym = context.symbol; if (sym == NULL || !sym->IsTrampoline()) return thread_plan_sp; const ConstString &sym_name = sym->GetMangled().GetName(Mangled::ePreferMangled); if (!sym_name) return thread_plan_sp; SymbolContextList target_symbols; Target &target = thread.GetProcess().GetTarget(); ModuleList &images = target.GetImages(); images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols); size_t num_targets = target_symbols.GetSize(); if (!num_targets) return thread_plan_sp; typedef std::vector<lldb::addr_t> AddressVector; AddressVector addrs; for (size_t i = 0; i < num_targets; ++i) { SymbolContext context; AddressRange range; if (target_symbols.GetContextAtIndex(i, context)) { context.GetAddressRange(eSymbolContextEverything, 0, false, range); lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target); if (addr != LLDB_INVALID_ADDRESS) addrs.push_back(addr); } } if (addrs.size() > 0) { AddressVector::iterator start = addrs.begin(); AddressVector::iterator end = addrs.end(); std::sort(start, end); addrs.erase(std::unique(start, end), end); thread_plan_sp.reset(new ThreadPlanRunToAddress(thread, addrs, stop)); } return thread_plan_sp; }
bool Disassembler::Disassemble ( Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const ExecutionContext &exe_ctx, const AddressRange &disasm_range, uint32_t num_instructions, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm ) { if (disasm_range.GetByteSize()) { std::auto_ptr<Disassembler> disasm_ap (Disassembler::FindPlugin(arch, plugin_name)); if (disasm_ap.get()) { AddressRange range; ResolveAddress (exe_ctx, disasm_range.GetBaseAddress(), range.GetBaseAddress()); range.SetByteSize (disasm_range.GetByteSize()); size_t bytes_disassembled = disasm_ap->ParseInstructions (&exe_ctx, range, &strm); if (bytes_disassembled == 0) return false; return PrintInstructions (disasm_ap.get(), debugger, arch, exe_ctx, num_instructions, num_mixed_context_lines, options, strm); } } return false; }
bool UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly( AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0) return false; if (m_assembly_inspection_engine == nullptr) return false; ProcessSP process_sp(thread.GetProcess()); if (process_sp.get() == 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->GetNonCallSiteUnwindPlanFromAssembly( function_text.data(), func.GetByteSize(), func, unwind_plan); } return false; }
bool SymbolContext::GetAddressRange (uint32_t scope, uint32_t range_idx, bool use_inline_block_range, AddressRange &range) const { if ((scope & eSymbolContextLineEntry) && line_entry.IsValid()) { range = line_entry.range; return true; } if ((scope & eSymbolContextBlock) && (block != NULL)) { if (use_inline_block_range) { Block *inline_block = block->GetContainingInlinedBlock(); if (inline_block) return inline_block->GetRangeAtIndex (range_idx, range); } else { return block->GetRangeAtIndex (range_idx, range); } } if ((scope & eSymbolContextFunction) && (function != NULL)) { if (range_idx == 0) { range = function->GetAddressRange(); return true; } } if ((scope & eSymbolContextSymbol) && (symbol != NULL)) { if (range_idx == 0) { if (symbol->ValueIsAddress()) { range.GetBaseAddress() = symbol->GetAddress(); range.SetByteSize (symbol->GetByteSize()); return true; } } } range.Clear(); return false; }
lldb::SBAddress SBBlock::GetRangeStartAddress (uint32_t idx) { lldb::SBAddress sb_addr; if (m_opaque_ptr) { AddressRange range; if (m_opaque_ptr->GetRangeAtIndex(idx, range)) { sb_addr.ref() = range.GetBaseAddress(); } } return sb_addr; }
lldb::SBAddress SBBlock::GetRangeEndAddress (uint32_t idx) { lldb::SBAddress sb_addr; if (m_opaque_ptr) { AddressRange range; if (m_opaque_ptr->GetRangeAtIndex(idx, range)) { sb_addr.ref() = range.GetBaseAddress(); sb_addr.ref().Slide(range.GetByteSize()); } } return sb_addr; }
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) { std::vector<uint8_t> function_text(range.GetByteSize()); ProcessSP process_sp(thread.GetProcess()); if (process_sp) { Error error; const bool prefer_file_cache = true; if (process_sp->GetTarget().ReadMemory( range.GetBaseAddress(), prefer_file_cache, function_text.data(), range.GetByteSize(), error) != range.GetByteSize()) { return false; } } return GetNonCallSiteUnwindPlanFromAssembly( range, function_text.data(), function_text.size(), unwind_plan); }
llvm::Optional<AddressRange> UnwindTable::GetAddressRange(const Address &addr, SymbolContext &sc) { AddressRange range; // First check the symbol context if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, false, range) && range.GetBaseAddress().IsValid()) return range; // Does the eh_frame unwind info has a function bounds for this addr? if (m_eh_frame_up && m_eh_frame_up->GetAddressRange(addr, range)) return range; // Try debug_frame as well if (m_debug_frame_up && m_debug_frame_up->GetAddressRange(addr, range)) return range; return llvm::None; }
FuncUnwindersSP UnwindTable::GetFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc) { FuncUnwindersSP no_unwind_found; Initialize(); // There is an UnwindTable per object file, so we can safely use file handles addr_t file_addr = addr.GetFileAddress(); iterator end = m_unwinds.end (); iterator insert_pos = end; if (!m_unwinds.empty()) { insert_pos = m_unwinds.lower_bound (file_addr); iterator pos = insert_pos; if ((pos == m_unwinds.end ()) || (pos != m_unwinds.begin() && pos->second->GetFunctionStartAddress() != addr)) --pos; if (pos->second->ContainsAddress (addr)) return pos->second; } AddressRange range; if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, false, range) || !range.GetBaseAddress().IsValid()) { // Does the eh_frame unwind info has a function bounds for this addr? if (m_eh_frame == NULL || !m_eh_frame->GetAddressRange (addr, range)) { return no_unwind_found; } } FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, m_assembly_profiler, range)); m_unwinds.insert (insert_pos, std::make_pair(range.GetBaseAddress().GetFileAddress(), func_unwinder_sp)); // StreamFile s(stdout); // Dump (s); return func_unwinder_sp; }
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; }
bool UnwindAssembly_x86::GetFastUnwindPlan(AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { // if prologue is // 55 pushl %ebp // 89 e5 movl %esp, %ebp // or // 55 pushq %rbp // 48 89 e5 movq %rsp, %rbp // We should pull in the ABI architecture default unwind plan and return that llvm::SmallVector<uint8_t, 4> opcode_data; ProcessSP process_sp = thread.GetProcess(); if (process_sp) { Target &target(process_sp->GetTarget()); const bool prefer_file_cache = true; Status error; if (target.ReadMemory(func.GetBaseAddress(), prefer_file_cache, opcode_data.data(), 4, error) == 4) { uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5}; uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5}; if (memcmp(opcode_data.data(), i386_push_mov, sizeof(i386_push_mov)) == 0 || memcmp(opcode_data.data(), x86_64_push_mov, sizeof(x86_64_push_mov)) == 0) { ABISP abi_sp = process_sp->GetABI(); if (abi_sp) { return abi_sp->CreateDefaultUnwindPlan(unwind_plan); } } } } return false; }
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 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; }
size_t UnwindMacOSXFrameBackchain::GetStackFrameData_i386 (const ExecutionContext &exe_ctx) { m_cursors.clear(); Frame *first_frame = exe_ctx.GetFramePtr(); Process *process = exe_ctx.GetProcessPtr(); if (process == NULL) return 0; std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair; struct Frame_i386 { uint32_t fp; uint32_t pc; }; RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); assert (reg_ctx); Cursor cursor; cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS); cursor.fp = reg_ctx->GetFP (0); Frame_i386 frame = { static_cast<uint32_t>(cursor.fp), static_cast<uint32_t>(cursor.pc) }; m_cursors.push_back(cursor); const size_t k_frame_size = sizeof(frame); Error error; while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) { // Read both the FP and PC (8 bytes) if (process->ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) break; if (frame.pc >= 0x1000) { cursor.pc = frame.pc; cursor.fp = frame.fp; m_cursors.push_back (cursor); } } if (!m_cursors.empty()) { lldb::addr_t first_frame_pc = m_cursors.front().pc; if (first_frame_pc != LLDB_INVALID_ADDRESS) { const uint32_t resolve_scope = eSymbolContextModule | eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextSymbol; SymbolContext first_frame_sc (first_frame->GetSymbolContext(resolve_scope)); const AddressRange *addr_range_ptr = NULL; AddressRange range; if (first_frame_sc.function) addr_range_ptr = &first_frame_sc.function->GetAddressRange(); else if (first_frame_sc.symbol) { range.GetBaseAddress() = first_frame_sc.symbol->GetAddress(); range.SetByteSize (first_frame_sc.symbol->GetByteSize()); addr_range_ptr = ⦥ } if (addr_range_ptr) { if (first_frame->GetFrameCodeAddress() == addr_range_ptr->GetBaseAddress()) { // We are at the first instruction, so we can recover the // previous PC by dereferencing the SP lldb::addr_t first_frame_sp = reg_ctx->GetSP (0); // Read the real second frame return address into frame.pc if (first_frame_sp && process->ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) { cursor.fp = m_cursors.front().fp; cursor.pc = frame.pc; // Set the new second frame PC // Insert the second frame m_cursors.insert(m_cursors.begin()+1, cursor); m_cursors.front().fp = first_frame_sp; } } } } } // uint32_t i=0; // printf(" PC FP\n"); // printf(" ------------------ ------------------ \n"); // for (i=0; i<m_cursors.size(); ++i) // { // printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 "\n", i, m_cursors[i].pc, m_cursors[i].fp); // } return m_cursors.size(); }
void UnwindPlan::SetPlanValidAddressRange (const AddressRange& range) { if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0) m_plan_valid_address_range = range; }
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 SymbolContext::GetParentOfInlinedScope (const Address &curr_frame_pc, SymbolContext &next_frame_sc, Address &next_frame_pc) const { next_frame_sc.Clear(false); next_frame_pc.Clear(); if (block) { //const addr_t curr_frame_file_addr = curr_frame_pc.GetFileAddress(); // In order to get the parent of an inlined function we first need to // see if we are in an inlined block as "this->block" could be an // inlined block, or a parent of "block" could be. So lets check if // this block or one of this blocks parents is an inlined function. Block *curr_inlined_block = block->GetContainingInlinedBlock(); if (curr_inlined_block) { // "this->block" is contained in an inline function block, so to // get the scope above the inlined block, we get the parent of the // inlined block itself Block *next_frame_block = curr_inlined_block->GetParent(); // Now calculate the symbol context of the containing block next_frame_block->CalculateSymbolContext (&next_frame_sc); // If we get here we weren't able to find the return line entry using the nesting of the blocks and // the line table. So just use the call site info from our inlined block. AddressRange range; if (curr_inlined_block->GetRangeContainingAddress (curr_frame_pc, range)) { // To see there this new frame block it, we need to look at the // call site information from const InlineFunctionInfo* curr_inlined_block_inlined_info = curr_inlined_block->GetInlinedFunctionInfo(); next_frame_pc = range.GetBaseAddress(); next_frame_sc.line_entry.range.GetBaseAddress() = next_frame_pc; next_frame_sc.line_entry.file = curr_inlined_block_inlined_info->GetCallSite().GetFile(); next_frame_sc.line_entry.line = curr_inlined_block_inlined_info->GetCallSite().GetLine(); next_frame_sc.line_entry.column = curr_inlined_block_inlined_info->GetCallSite().GetColumn(); return true; } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS)); if (log) { log->Printf ("warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64, curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress()); } #ifdef LLDB_CONFIGURATION_DEBUG else { ObjectFile *objfile = NULL; if (module_sp) { SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); if (symbol_vendor) { SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); if (symbol_file) objfile = symbol_file->GetObjectFile(); } } if (objfile) { Host::SystemLog (Host::eSystemLogWarning, "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 " in %s\n", curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress(), objfile->GetFileSpec().GetPath().c_str()); } else { Host::SystemLog (Host::eSystemLogWarning, "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 "\n", curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress()); } } #endif } } } return false; }
FuncUnwindersSP UnwindTable::GetUncachedFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc) { FuncUnwindersSP no_unwind_found; Initialize(); AddressRange range; if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, false, range) || !range.GetBaseAddress().IsValid()) { // Does the eh_frame unwind info has a function bounds for this addr? if (m_eh_frame == NULL || !m_eh_frame->GetAddressRange (addr, range)) { return no_unwind_found; } } FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, m_assembly_profiler, range)); return func_unwinder_sp; }