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
17using namespace lldb;
18using namespace lldb_private;
19
20namespace {
21
22struct 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 &reg_context)
31 : m_process(process), m_reg_context(reg_context) {}
32};
33
34} // anonymous namespace
35
36static 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
46static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
47 const RegisterInfo *reg_info,
48 RegisterValue &reg_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
73static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton,
74 const EmulateInstruction::Context &context,
75 const RegisterInfo *reg_info,
76 const RegisterValue &reg_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
83static 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
90static lldb::addr_t ReadFlags(NativeRegisterContext &regsiter_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
97Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
98 NativeThreadProtocol &thread) {
99 Status error;
100 NativeProcessProtocol &process = thread.GetProcess();
101 NativeRegisterContext &register_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

source code of lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp