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 lldb::addr_t ReadFlags(NativeRegisterContext ®siter_context) { |
91 | const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo( |
92 | reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); |
93 | return regsiter_context.ReadRegisterAsUnsigned(reg_info: flags_info, |
94 | LLDB_INVALID_ADDRESS); |
95 | } |
96 | |
97 | static int GetSoftwareBreakpointSize(const ArchSpec &arch, |
98 | lldb::addr_t next_flags) { |
99 | if (arch.GetMachine() == llvm::Triple::arm) { |
100 | if (next_flags & 0x20) |
101 | // Thumb mode |
102 | return 2; |
103 | // Arm mode |
104 | return 4; |
105 | } |
106 | if (arch.IsMIPS() || arch.GetTriple().isPPC64() || |
107 | arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch()) |
108 | return 4; |
109 | return 0; |
110 | } |
111 | |
112 | static Status SetSoftwareBreakpointOnPC(const ArchSpec &arch, lldb::addr_t pc, |
113 | lldb::addr_t next_flags, |
114 | NativeProcessProtocol &process) { |
115 | int size_hint = GetSoftwareBreakpointSize(arch, next_flags); |
116 | Status error; |
117 | error = process.SetBreakpoint(addr: pc, size: size_hint, /*hardware=*/false); |
118 | |
119 | // If setting the breakpoint fails because pc is out of the address |
120 | // space, ignore it and let the debugee segfault. |
121 | if (error.GetError() == EIO || error.GetError() == EFAULT) |
122 | return Status(); |
123 | if (error.Fail()) |
124 | return error; |
125 | |
126 | return Status(); |
127 | } |
128 | |
129 | Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping( |
130 | NativeThreadProtocol &thread) { |
131 | Status error; |
132 | NativeProcessProtocol &process = thread.GetProcess(); |
133 | NativeRegisterContext ®ister_context = thread.GetRegisterContext(); |
134 | const ArchSpec &arch = process.GetArchitecture(); |
135 | |
136 | std::unique_ptr<EmulateInstruction> emulator_up( |
137 | EmulateInstruction::FindPlugin(arch, supported_inst_type: eInstructionTypePCModifying, |
138 | plugin_name: nullptr)); |
139 | |
140 | if (emulator_up == nullptr) |
141 | return Status::FromErrorString(str: "Instruction emulator not found!" ); |
142 | |
143 | EmulatorBaton baton(process, register_context); |
144 | emulator_up->SetBaton(&baton); |
145 | emulator_up->SetReadMemCallback(&ReadMemoryCallback); |
146 | emulator_up->SetReadRegCallback(&ReadRegisterCallback); |
147 | emulator_up->SetWriteMemCallback(&WriteMemoryCallback); |
148 | emulator_up->SetWriteRegCallback(&WriteRegisterCallback); |
149 | |
150 | if (!emulator_up->ReadInstruction()) { |
151 | // try to get at least the size of next instruction to set breakpoint. |
152 | auto instr_size = emulator_up->GetLastInstrSize(); |
153 | if (!instr_size) |
154 | return Status::FromErrorString(str: "Read instruction failed!" ); |
155 | bool success = false; |
156 | auto pc = emulator_up->ReadRegisterUnsigned(reg_kind: eRegisterKindGeneric, |
157 | LLDB_REGNUM_GENERIC_PC, |
158 | LLDB_INVALID_ADDRESS, success_ptr: &success); |
159 | if (!success) |
160 | return Status::FromErrorString(str: "Reading pc failed!" ); |
161 | lldb::addr_t next_pc = pc + *instr_size; |
162 | auto result = |
163 | SetSoftwareBreakpointOnPC(arch, pc: next_pc, /* next_flags */ 0x0, process); |
164 | m_threads_stepping_with_breakpoint.insert(x: {thread.GetID(), next_pc}); |
165 | return result; |
166 | } |
167 | |
168 | bool emulation_result = |
169 | emulator_up->EvaluateInstruction(evaluate_options: eEmulateInstructionOptionAutoAdvancePC); |
170 | |
171 | const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo( |
172 | reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); |
173 | const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo( |
174 | reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); |
175 | |
176 | auto pc_it = |
177 | baton.m_register_values.find(x: reg_info_pc->kinds[eRegisterKindDWARF]); |
178 | auto flags_it = reg_info_flags == nullptr |
179 | ? baton.m_register_values.end() |
180 | : baton.m_register_values.find( |
181 | x: reg_info_flags->kinds[eRegisterKindDWARF]); |
182 | |
183 | lldb::addr_t next_pc; |
184 | lldb::addr_t next_flags; |
185 | if (emulation_result) { |
186 | assert(pc_it != baton.m_register_values.end() && |
187 | "Emulation was successfull but PC wasn't updated" ); |
188 | next_pc = pc_it->second.GetAsUInt64(); |
189 | |
190 | if (flags_it != baton.m_register_values.end()) |
191 | next_flags = flags_it->second.GetAsUInt64(); |
192 | else |
193 | next_flags = ReadFlags(regsiter_context&: register_context); |
194 | } else if (pc_it == baton.m_register_values.end()) { |
195 | // Emulate instruction failed and it haven't changed PC. Advance PC with |
196 | // the size of the current opcode because the emulation of all |
197 | // PC modifying instruction should be successful. The failure most |
198 | // likely caused by a not supported instruction which don't modify PC. |
199 | next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize(); |
200 | next_flags = ReadFlags(regsiter_context&: register_context); |
201 | } else { |
202 | // The instruction emulation failed after it modified the PC. It is an |
203 | // unknown error where we can't continue because the next instruction is |
204 | // modifying the PC but we don't know how. |
205 | return Status::FromErrorString( |
206 | str: "Instruction emulation failed unexpectedly." ); |
207 | } |
208 | auto result = SetSoftwareBreakpointOnPC(arch, pc: next_pc, next_flags, process); |
209 | m_threads_stepping_with_breakpoint.insert(x: {thread.GetID(), next_pc}); |
210 | return result; |
211 | } |
212 | |