//===-- NativeProcessSoftwareSingleStep.cpp -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "NativeProcessSoftwareSingleStep.h" #include "lldb/Core/EmulateInstruction.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Utility/RegisterValue.h" #include using namespace lldb; using namespace lldb_private; namespace { struct EmulatorBaton { NativeProcessProtocol &m_process; NativeRegisterContext &m_reg_context; // eRegisterKindDWARF -> RegsiterValue std::unordered_map m_register_values; EmulatorBaton(NativeProcessProtocol &process, NativeRegisterContext ®_context) : m_process(process), m_reg_context(reg_context) {} }; } // anonymous namespace static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst, size_t length) { EmulatorBaton *emulator_baton = static_cast(baton); size_t bytes_read; emulator_baton->m_process.ReadMemory(addr, dst, length, bytes_read); return bytes_read; } static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, const RegisterInfo *reg_info, RegisterValue ®_value) { EmulatorBaton *emulator_baton = static_cast(baton); auto it = emulator_baton->m_register_values.find( reg_info->kinds[eRegisterKindDWARF]); if (it != emulator_baton->m_register_values.end()) { reg_value = it->second; return true; } // The emulator only fill in the dwarf regsiter numbers (and in some case the // generic register numbers). Get the full register info from the register // context based on the dwarf register numbers. const RegisterInfo *full_reg_info = emulator_baton->m_reg_context.GetRegisterInfo( eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); Status error = emulator_baton->m_reg_context.ReadRegister(full_reg_info, reg_value); if (error.Success()) return true; return false; } static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, const RegisterInfo *reg_info, const RegisterValue ®_value) { EmulatorBaton *emulator_baton = static_cast(baton); emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = reg_value; return true; } static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, const void *dst, size_t length) { return length; } Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping( NativeThreadProtocol &thread) { NativeProcessProtocol &process = thread.GetProcess(); NativeRegisterContext ®ister_context = thread.GetRegisterContext(); const ArchSpec &arch = process.GetArchitecture(); std::unique_ptr emulator_up( EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying, nullptr)); if (emulator_up == nullptr) return Status::FromErrorString("Instruction emulator not found!"); EmulatorBaton baton(process, register_context); emulator_up->SetBaton(&baton); emulator_up->SetReadMemCallback(&ReadMemoryCallback); emulator_up->SetReadRegCallback(&ReadRegisterCallback); emulator_up->SetWriteMemCallback(&WriteMemoryCallback); emulator_up->SetWriteRegCallback(&WriteRegisterCallback); auto bp_locations_predictor = EmulateInstruction::CreateBreakpointLocationPredictor( std::move(emulator_up)); llvm::Expected candidates = bp_locations_predictor->GetBreakpointLocations(); if (!candidates) return Status::FromError(candidates.takeError()); for (addr_t bp_addr : *candidates) { if (process.HasSoftwareBreakpoint(bp_addr)) continue; llvm::Expected bp_size = bp_locations_predictor->GetBreakpointSize(bp_addr); if (auto err = bp_size.takeError()) return Status::FromError(std::move(err)); Status error = process.SetBreakpoint(bp_addr, *bp_size, /*hardware=*/false); // If setting the breakpoint fails because pc is out of the address // space, ignore it and let the debugee segfault. if (error.GetError() == EIO || error.GetError() == EFAULT) continue; if (error.Fail()) return error; m_step_breakpoints.emplace(bp_addr); } m_threads_stepping_with_breakpoint.emplace(thread.GetID(), std::move(*candidates)); return Status(); }