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