LLDB mainline
NativeRegisterContextLinux_riscv64.cpp
Go to the documentation of this file.
1//===-- NativeRegisterContextLinux_riscv64.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#if defined(__riscv) && __riscv_xlen == 64
10
12
17#include "lldb/Host/HostInfo.h"
19#include "lldb/Utility/Log.h"
21#include "lldb/Utility/Status.h"
22
23// System includes - They have to be included after framework includes because
24// they define some macros which collide with variable names in other modules
25#include <elf.h>
26#include <sys/ptrace.h>
27#include <sys/uio.h>
28
29#ifndef PTRACE_GETREGSET
30#define PTRACE_GETREGSET 0x4204
31#endif
32
33using namespace lldb;
34using namespace lldb_private;
35using namespace lldb_private::process_linux;
36
37std::unique_ptr<NativeRegisterContextLinux>
39 const ArchSpec &target_arch, NativeThreadLinux &native_thread) {
40 switch (target_arch.GetMachine()) {
41 case llvm::Triple::riscv64: {
43
45 struct iovec ioVec;
46 ioVec.iov_base = &fpr;
47 ioVec.iov_len = sizeof(fpr);
48 unsigned int regset = NT_FPREGSET;
49
51 native_thread.GetID(), &regset,
52 &ioVec, sizeof(fpr))
53 .Success()) {
55 }
56
57 auto register_info_up =
58 std::make_unique<RegisterInfoPOSIX_riscv64>(target_arch, opt_regsets);
59 return std::make_unique<NativeRegisterContextLinux_riscv64>(
60 target_arch, native_thread, std::move(register_info_up));
61 }
62 default:
63 llvm_unreachable("have no register context for architecture");
64 }
65}
66
67llvm::Expected<ArchSpec>
69 return HostInfo::GetArchitecture();
70}
71
72NativeRegisterContextLinux_riscv64::NativeRegisterContextLinux_riscv64(
73 const ArchSpec &target_arch, NativeThreadProtocol &native_thread,
74 std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info_up)
76 register_info_up.release()),
77 NativeRegisterContextLinux(native_thread) {
78 ::memset(&m_fpr, 0, sizeof(m_fpr));
79 ::memset(&m_gpr, 0, sizeof(m_gpr));
80
81 m_gpr_is_valid = false;
82 m_fpu_is_valid = false;
83}
84
86NativeRegisterContextLinux_riscv64::GetRegisterInfo() const {
87 return static_cast<const RegisterInfoPOSIX_riscv64 &>(
89}
90
91uint32_t NativeRegisterContextLinux_riscv64::GetRegisterSetCount() const {
92 return GetRegisterInfo().GetRegisterSetCount();
93}
94
95const RegisterSet *
96NativeRegisterContextLinux_riscv64::GetRegisterSet(uint32_t set_index) const {
97 return GetRegisterInfo().GetRegisterSet(set_index);
98}
99
100uint32_t NativeRegisterContextLinux_riscv64::GetUserRegisterCount() const {
101 uint32_t count = 0;
102 for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
103 count += GetRegisterSet(set_index)->num_registers;
104 return count;
105}
106
107Status
108NativeRegisterContextLinux_riscv64::ReadRegister(const RegisterInfo *reg_info,
109 RegisterValue &reg_value) {
111
112 if (!reg_info) {
113 error = Status::FromErrorString("reg_info NULL");
114 return error;
115 }
116
117 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
118
119 if (reg == LLDB_INVALID_REGNUM)
121 "no lldb regnum for %s",
122 reg_info && reg_info->name ? reg_info->name : "<unknown register>");
123
124 if (reg == gpr_x0_riscv) {
125 reg_value.SetUInt(0, reg_info->byte_size);
126 return error;
127 }
128
129 uint8_t *src = nullptr;
130 uint32_t offset = LLDB_INVALID_INDEX32;
131
132 if (IsGPR(reg)) {
133 error = ReadGPR();
134 if (error.Fail())
135 return error;
136
137 offset = reg_info->byte_offset;
138 assert(offset < GetGPRSize());
139 src = (uint8_t *)GetGPRBuffer() + offset;
140
141 } else if (IsFPR(reg)) {
142 error = ReadFPR();
143 if (error.Fail())
144 return error;
145
146 offset = CalculateFprOffset(reg_info);
147 assert(offset < GetFPRSize());
148 src = (uint8_t *)GetFPRBuffer() + offset;
149 } else
151 "failed - register wasn't recognized to be a GPR or an FPR, "
152 "write strategy unknown");
153
154 reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size,
156
157 return error;
158}
159
160Status NativeRegisterContextLinux_riscv64::WriteRegister(
161 const RegisterInfo *reg_info, const RegisterValue &reg_value) {
163
164 if (!reg_info)
165 return Status::FromErrorString("reg_info NULL");
166
167 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
168
169 if (reg == LLDB_INVALID_REGNUM)
171 "no lldb regnum for %s",
172 reg_info->name != nullptr ? reg_info->name : "<unknown register>");
173
174 if (reg == gpr_x0_riscv) {
175 // do nothing.
176 return error;
177 }
178
179 uint8_t *dst = nullptr;
180 uint32_t offset = LLDB_INVALID_INDEX32;
181
182 if (IsGPR(reg)) {
183 error = ReadGPR();
184 if (error.Fail())
185 return error;
186
187 assert(reg_info->byte_offset < GetGPRSize());
188 dst = (uint8_t *)GetGPRBuffer() + reg_info->byte_offset;
189 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
190
191 return WriteGPR();
192 } else if (IsFPR(reg)) {
193 error = ReadFPR();
194 if (error.Fail())
195 return error;
196
197 offset = CalculateFprOffset(reg_info);
198 assert(offset < GetFPRSize());
199 dst = (uint8_t *)GetFPRBuffer() + offset;
200 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
201
202 return WriteFPR();
203 }
204
205 return Status::FromErrorString("Failed to write register value");
206}
207
208Status NativeRegisterContextLinux_riscv64::ReadAllRegisterValues(
211
212 data_sp.reset(new DataBufferHeap(GetRegContextSize(), 0));
213
214 error = ReadGPR();
215 if (error.Fail())
216 return error;
217
218 if (GetRegisterInfo().IsFPPresent()) {
219 error = ReadFPR();
220 if (error.Fail())
221 return error;
222 }
223
224 uint8_t *dst = const_cast<uint8_t *>(data_sp->GetBytes());
225 ::memcpy(dst, GetGPRBuffer(), GetGPRSize());
226 dst += GetGPRSize();
227 if (GetRegisterInfo().IsFPPresent())
228 ::memcpy(dst, GetFPRBuffer(), GetFPRSize());
229
230 return error;
231}
232
233Status NativeRegisterContextLinux_riscv64::WriteAllRegisterValues(
234 const lldb::DataBufferSP &data_sp) {
236
237 if (!data_sp) {
239 "NativeRegisterContextLinux_riscv64::%s invalid data_sp provided",
240 __FUNCTION__);
241 return error;
242 }
243
244 if (data_sp->GetByteSize() != GetRegContextSize()) {
246 "NativeRegisterContextLinux_riscv64::%s data_sp contained mismatched "
247 "data size, expected %" PRIu64 ", actual %" PRIu64,
248 __FUNCTION__, GetRegContextSize(), data_sp->GetByteSize());
249 return error;
250 }
251
252 uint8_t *src = const_cast<uint8_t *>(data_sp->GetBytes());
253 if (src == nullptr) {
255 "NativeRegisterContextLinux_riscv64::%s "
256 "DataBuffer::GetBytes() returned a null "
257 "pointer",
258 __FUNCTION__);
259 return error;
260 }
261 ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize());
262
263 error = WriteGPR();
264 if (error.Fail())
265 return error;
266
267 src += GetRegisterInfoInterface().GetGPRSize();
268
269 if (GetRegisterInfo().IsFPPresent()) {
270 ::memcpy(GetFPRBuffer(), src, GetFPRSize());
271
272 error = WriteFPR();
273 if (error.Fail())
274 return error;
275 }
276
277 return error;
278}
279
280size_t NativeRegisterContextLinux_riscv64::GetRegContextSize() {
281 size_t size = GetGPRSize();
282 if (GetRegisterInfo().IsFPPresent())
283 size += GetFPRSize();
284 return size;
285}
286
287bool NativeRegisterContextLinux_riscv64::IsGPR(unsigned reg) const {
288 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
290}
291
292bool NativeRegisterContextLinux_riscv64::IsFPR(unsigned reg) const {
293 return GetRegisterInfo().IsFPReg(reg);
294}
295
296Status NativeRegisterContextLinux_riscv64::ReadGPR() {
298
299 if (m_gpr_is_valid)
300 return error;
301
302 struct iovec ioVec;
303 ioVec.iov_base = GetGPRBuffer();
304 ioVec.iov_len = GetGPRSize();
305
306 error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
307
308 if (error.Success())
309 m_gpr_is_valid = true;
310
311 return error;
312}
313
314Status NativeRegisterContextLinux_riscv64::WriteGPR() {
315 Status error = ReadGPR();
316 if (error.Fail())
317 return error;
318
319 struct iovec ioVec;
320 ioVec.iov_base = GetGPRBuffer();
321 ioVec.iov_len = GetGPRSize();
322
323 m_gpr_is_valid = false;
324
325 return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
326}
327
328Status NativeRegisterContextLinux_riscv64::ReadFPR() {
330
331 if (m_fpu_is_valid)
332 return error;
333
334 struct iovec ioVec;
335 ioVec.iov_base = GetFPRBuffer();
336 ioVec.iov_len = GetFPRSize();
337
338 error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
339
340 if (error.Success())
341 m_fpu_is_valid = true;
342
343 return error;
344}
345
346Status NativeRegisterContextLinux_riscv64::WriteFPR() {
347 Status error = ReadFPR();
348 if (error.Fail())
349 return error;
350
351 struct iovec ioVec;
352 ioVec.iov_base = GetFPRBuffer();
353 ioVec.iov_len = GetFPRSize();
354
355 m_fpu_is_valid = false;
356
357 return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
358}
359
360void NativeRegisterContextLinux_riscv64::InvalidateAllRegisters() {
361 m_gpr_is_valid = false;
362 m_fpu_is_valid = false;
363}
364
365uint32_t NativeRegisterContextLinux_riscv64::CalculateFprOffset(
366 const RegisterInfo *reg_info) const {
367 return reg_info->byte_offset - GetGPRSize();
368}
369
370std::vector<uint32_t> NativeRegisterContextLinux_riscv64::GetExpeditedRegisters(
371 ExpeditedRegs expType) const {
372 std::vector<uint32_t> expedited_reg_nums =
374
375 return expedited_reg_nums;
376}
377
378#endif // defined (__riscv) && __riscv_xlen == 64
static llvm::raw_ostream & error(Stream &strm)
#define PTRACE_GETREGSET
size_t GetRegisterSetCount() const override
A subclass of DataBuffer that stores a data buffer on the heap.
virtual std::vector< uint32_t > GetExpeditedRegisters(ExpeditedRegs expType) const
uint32_t SetFromMemoryData(const RegisterInfo &reg_info, const void *src, uint32_t src_len, lldb::ByteOrder src_byte_order, Status &error)
bool SetUInt(uint64_t uint, uint32_t byte_size)
const void * GetBytes() const
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition Status.cpp:106
static Status FromErrorString(const char *str)
Definition Status.h:141
static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr=nullptr, void *data=nullptr, size_t data_size=0, long *result=nullptr)
}
static std::unique_ptr< NativeRegisterContextLinux > CreateHostNativeRegisterContextLinux(const ArchSpec &target_arch, NativeThreadLinux &native_thread)
static llvm::Expected< ArchSpec > DetermineArchitecture(lldb::tid_t tid)
#define LLDB_INVALID_INDEX32
#define LLDB_INVALID_REGNUM
A class that represents a running process on the host machine.
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
uint64_t tid_t
Definition lldb-types.h:85
@ eRegisterKindLLDB
lldb's internal register numbers
Every register is described in detail including its name, alternate name (optional),...
uint32_t byte_offset
The byte offset in the register context data where this register's value is found.
uint32_t byte_size
Size in bytes of the register.
uint32_t kinds[lldb::kNumRegisterKinds]
Holds all of the various register numbers for all register kinds.
const char * name
Name of this register, can't be NULL.
Registers are grouped into register sets.
size_t num_registers
The number of registers in REGISTERS array below.