| 1 | //===-- NativeProcessSoftwareSingleStep.cpp -------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "NativeProcessSoftwareSingleStep.h" |
| 10 | |
| 11 | #include "lldb/Core/EmulateInstruction.h" |
| 12 | #include "lldb/Host/common/NativeRegisterContext.h" |
| 13 | #include "lldb/Utility/RegisterValue.h" |
| 14 | |
| 15 | #include <unordered_map> |
| 16 | |
| 17 | using namespace lldb; |
| 18 | using namespace lldb_private; |
| 19 | |
| 20 | namespace { |
| 21 | |
| 22 | struct EmulatorBaton { |
| 23 | NativeProcessProtocol &m_process; |
| 24 | NativeRegisterContext &m_reg_context; |
| 25 | |
| 26 | // eRegisterKindDWARF -> RegsiterValue |
| 27 | std::unordered_map<uint32_t, RegisterValue> m_register_values; |
| 28 | |
| 29 | EmulatorBaton(NativeProcessProtocol &process, |
| 30 | NativeRegisterContext ®_context) |
| 31 | : m_process(process), m_reg_context(reg_context) {} |
| 32 | }; |
| 33 | |
| 34 | } // anonymous namespace |
| 35 | |
| 36 | static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, |
| 37 | const EmulateInstruction::Context &context, |
| 38 | lldb::addr_t addr, void *dst, size_t length) { |
| 39 | EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); |
| 40 | |
| 41 | size_t bytes_read; |
| 42 | emulator_baton->m_process.ReadMemory(addr, buf: dst, size: length, bytes_read); |
| 43 | return bytes_read; |
| 44 | } |
| 45 | |
| 46 | static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, |
| 47 | const RegisterInfo *reg_info, |
| 48 | RegisterValue ®_value) { |
| 49 | EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); |
| 50 | |
| 51 | auto it = emulator_baton->m_register_values.find( |
| 52 | x: reg_info->kinds[eRegisterKindDWARF]); |
| 53 | if (it != emulator_baton->m_register_values.end()) { |
| 54 | reg_value = it->second; |
| 55 | return true; |
| 56 | } |
| 57 | |
| 58 | // The emulator only fill in the dwarf regsiter numbers (and in some case the |
| 59 | // generic register numbers). Get the full register info from the register |
| 60 | // context based on the dwarf register numbers. |
| 61 | const RegisterInfo *full_reg_info = |
| 62 | emulator_baton->m_reg_context.GetRegisterInfo( |
| 63 | reg_kind: eRegisterKindDWARF, reg_num: reg_info->kinds[eRegisterKindDWARF]); |
| 64 | |
| 65 | Status error = |
| 66 | emulator_baton->m_reg_context.ReadRegister(reg_info: full_reg_info, reg_value); |
| 67 | if (error.Success()) |
| 68 | return true; |
| 69 | |
| 70 | return false; |
| 71 | } |
| 72 | |
| 73 | static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton, |
| 74 | const EmulateInstruction::Context &context, |
| 75 | const RegisterInfo *reg_info, |
| 76 | const RegisterValue ®_value) { |
| 77 | EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); |
| 78 | emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = |
| 79 | reg_value; |
| 80 | return true; |
| 81 | } |
| 82 | |
| 83 | static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton, |
| 84 | const EmulateInstruction::Context &context, |
| 85 | lldb::addr_t addr, const void *dst, |
| 86 | size_t length) { |
| 87 | return length; |
| 88 | } |
| 89 | |
| 90 | static Status SetSoftwareBreakpoint(lldb::addr_t bp_addr, unsigned bp_size, |
| 91 | NativeProcessProtocol &process) { |
| 92 | Status error; |
| 93 | error = process.SetBreakpoint(addr: bp_addr, size: bp_size, /*hardware=*/false); |
| 94 | |
| 95 | // If setting the breakpoint fails because pc is out of the address |
| 96 | // space, ignore it and let the debugee segfault. |
| 97 | if (error.GetError() == EIO || error.GetError() == EFAULT) |
| 98 | return Status(); |
| 99 | if (error.Fail()) |
| 100 | return error; |
| 101 | |
| 102 | return Status(); |
| 103 | } |
| 104 | |
| 105 | Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping( |
| 106 | NativeThreadProtocol &thread) { |
| 107 | Status error; |
| 108 | NativeProcessProtocol &process = thread.GetProcess(); |
| 109 | NativeRegisterContext ®ister_context = thread.GetRegisterContext(); |
| 110 | const ArchSpec &arch = process.GetArchitecture(); |
| 111 | |
| 112 | std::unique_ptr<EmulateInstruction> emulator_up( |
| 113 | EmulateInstruction::FindPlugin(arch, supported_inst_type: eInstructionTypePCModifying, |
| 114 | plugin_name: nullptr)); |
| 115 | if (emulator_up == nullptr) |
| 116 | return Status::FromErrorString(str: "Instruction emulator not found!" ); |
| 117 | |
| 118 | EmulatorBaton baton(process, register_context); |
| 119 | emulator_up->SetBaton(&baton); |
| 120 | emulator_up->SetReadMemCallback(&ReadMemoryCallback); |
| 121 | emulator_up->SetReadRegCallback(&ReadRegisterCallback); |
| 122 | emulator_up->SetWriteMemCallback(&WriteMemoryCallback); |
| 123 | emulator_up->SetWriteRegCallback(&WriteRegisterCallback); |
| 124 | |
| 125 | auto bp_locaions_predictor = |
| 126 | EmulateInstruction::CreateBreakpointLocationPredictor( |
| 127 | emulator_up: std::move(emulator_up)); |
| 128 | |
| 129 | auto bp_locations = bp_locaions_predictor->GetBreakpointLocations(status&: error); |
| 130 | if (error.Fail()) |
| 131 | return error; |
| 132 | |
| 133 | for (auto &&bp_addr : bp_locations) { |
| 134 | auto bp_size = bp_locaions_predictor->GetBreakpointSize(bp_addr); |
| 135 | if (auto err = bp_size.takeError()) |
| 136 | return Status(toString(E: std::move(err))); |
| 137 | |
| 138 | error = SetSoftwareBreakpoint(bp_addr, bp_size: *bp_size, process); |
| 139 | if (error.Fail()) |
| 140 | return error; |
| 141 | } |
| 142 | |
| 143 | m_threads_stepping_with_breakpoint.insert(x: {thread.GetID(), bp_locations}); |
| 144 | return error; |
| 145 | } |
| 146 | |