Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp
39642 views
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 &reg_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, dst, length, bytes_read);
43
return bytes_read;
44
}
45
46
static 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
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
eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
64
65
Status error =
66
emulator_baton->m_reg_context.ReadRegister(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 &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
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 &regsiter_context) {
91
const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo(
92
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
93
return regsiter_context.ReadRegisterAsUnsigned(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(pc, 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 &register_context = thread.GetRegisterContext();
134
const ArchSpec &arch = process.GetArchitecture();
135
136
std::unique_ptr<EmulateInstruction> emulator_up(
137
EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying,
138
nullptr));
139
140
if (emulator_up == nullptr)
141
return Status("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("Read instruction failed!");
155
bool success = false;
156
auto pc = emulator_up->ReadRegisterUnsigned(eRegisterKindGeneric,
157
LLDB_REGNUM_GENERIC_PC,
158
LLDB_INVALID_ADDRESS, &success);
159
if (!success)
160
return Status("Reading pc failed!");
161
lldb::addr_t next_pc = pc + *instr_size;
162
auto result =
163
SetSoftwareBreakpointOnPC(arch, next_pc, /* next_flags */ 0x0, process);
164
m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
165
return result;
166
}
167
168
bool emulation_result =
169
emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
170
171
const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo(
172
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
173
const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo(
174
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
175
176
auto pc_it =
177
baton.m_register_values.find(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
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(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(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("Instruction emulation failed unexpectedly.");
206
}
207
auto result = SetSoftwareBreakpointOnPC(arch, next_pc, next_flags, process);
208
m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
209
return result;
210
}
211
212