LLDB mainline
NativeRegisterContextFreeBSD_arm64.cpp
Go to the documentation of this file.
1//===-- NativeRegisterContextFreeBSD_arm64.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(__aarch64__)
10
12
15#include "lldb/Utility/Status.h"
16
21
22// clang-format off
23#include <sys/param.h>
24#include <sys/ptrace.h>
25#include <sys/types.h>
26// clang-format on
27
28using namespace lldb;
29using namespace lldb_private;
30using namespace lldb_private::process_freebsd;
31
32// A NativeRegisterContext is constructed per thread, but all threads' registers
33// will contain the same fields. Therefore this mutex prevents each instance
34// competing with the other, and subsequent instances from having to detect the
35// fields all over again.
36static std::mutex g_register_type_detector_mutex;
37static Arm64RegisterTypeDetector g_register_type_detector;
38
41 const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) {
42 std::lock_guard<std::mutex> lock(g_register_type_detector_mutex);
43 if (!g_register_type_detector.HasDetected()) {
44 NativeProcessFreeBSD &process = native_thread.GetProcess();
45 g_register_type_detector.DetectTypes(
47 process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2).value_or(0),
48 /*hwcap3=*/0);
49 }
50
51 return new NativeRegisterContextFreeBSD_arm64(target_arch, native_thread);
52}
53
54NativeRegisterContextFreeBSD_arm64::NativeRegisterContextFreeBSD_arm64(
55 const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread)
57 native_thread, new RegisterInfoPOSIX_arm64(target_arch, 0)),
58 m_read_dbreg(false) {
59 g_register_type_detector.UpdateRegisterInfo(
60 GetRegisterInfoInterface().GetRegisterInfo(),
61 GetRegisterInfoInterface().GetRegisterCount());
62}
63
65NativeRegisterContextFreeBSD_arm64::GetRegisterInfo() const {
66 return static_cast<RegisterInfoPOSIX_arm64 &>(*m_register_info_interface_up);
67}
68
69uint32_t NativeRegisterContextFreeBSD_arm64::GetRegisterSetCount() const {
70 return GetRegisterInfo().GetRegisterSetCount();
71}
72
73const RegisterSet *
74NativeRegisterContextFreeBSD_arm64::GetRegisterSet(uint32_t set_index) const {
75 return GetRegisterInfo().GetRegisterSet(set_index);
76}
77
78uint32_t NativeRegisterContextFreeBSD_arm64::GetUserRegisterCount() const {
79 uint32_t count = 0;
80 for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
81 count += GetRegisterSet(set_index)->num_registers;
82 return count;
83}
84
85Status NativeRegisterContextFreeBSD_arm64::ReadRegisterSet(uint32_t set) {
86 switch (set) {
88 return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(),
89 m_reg_data.data());
92 PT_GETFPREGS, m_thread.GetID(),
93 m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR));
94 }
95 llvm_unreachable("NativeRegisterContextFreeBSD_arm64::ReadRegisterSet");
96}
97
98Status NativeRegisterContextFreeBSD_arm64::WriteRegisterSet(uint32_t set) {
99 switch (set) {
101 return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(),
102 m_reg_data.data());
105 PT_SETFPREGS, m_thread.GetID(),
106 m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR));
107 }
108 llvm_unreachable("NativeRegisterContextFreeBSD_arm64::WriteRegisterSet");
109}
110
111Status
112NativeRegisterContextFreeBSD_arm64::ReadRegister(const RegisterInfo *reg_info,
113 RegisterValue &reg_value) {
115
116 if (!reg_info)
117 return Status::FromErrorString("reg_info NULL");
118
119 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
120
121 if (reg == LLDB_INVALID_REGNUM)
123 "no lldb regnum for %s",
124 reg_info && reg_info->name ? reg_info->name : "<unknown register>");
125
126 uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg);
127 error = ReadRegisterSet(set);
128 if (error.Fail())
129 return error;
130
131 assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size());
132 reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset,
134 return error;
135}
136
137Status NativeRegisterContextFreeBSD_arm64::WriteRegister(
138 const RegisterInfo *reg_info, const RegisterValue &reg_value) {
140
141 if (!reg_info)
142 return Status::FromErrorString("reg_info NULL");
143
144 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
145
146 if (reg == LLDB_INVALID_REGNUM)
148 "no lldb regnum for %s",
149 reg_info && reg_info->name ? reg_info->name : "<unknown register>");
150
151 uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg);
152 error = ReadRegisterSet(set);
153 if (error.Fail())
154 return error;
155
156 assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size());
157 ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(),
158 reg_info->byte_size);
159
160 return WriteRegisterSet(set);
161}
162
163Status NativeRegisterContextFreeBSD_arm64::ReadAllRegisterValues(
166
167 error = ReadRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet);
168 if (error.Fail())
169 return error;
170
171 error = ReadRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet);
172 if (error.Fail())
173 return error;
174
175 data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0));
176 uint8_t *dst = data_sp->GetBytes();
177 ::memcpy(dst, m_reg_data.data(), m_reg_data.size());
178
179 return error;
180}
181
182Status NativeRegisterContextFreeBSD_arm64::WriteAllRegisterValues(
183 const lldb::DataBufferSP &data_sp) {
185
186 if (!data_sp) {
188 "NativeRegisterContextFreeBSD_arm64::%s invalid data_sp provided",
189 __FUNCTION__);
190 return error;
191 }
192
193 if (data_sp->GetByteSize() != m_reg_data.size()) {
195 "NativeRegisterContextFreeBSD_arm64::%s data_sp contained mismatched "
196 "data size, expected %" PRIu64 ", actual %" PRIu64,
197 __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize());
198 return error;
199 }
200
201 const uint8_t *src = data_sp->GetBytes();
202 if (src == nullptr) {
204 "NativeRegisterContextFreeBSD_arm64::%s "
205 "DataBuffer::GetBytes() returned a null "
206 "pointer",
207 __FUNCTION__);
208 return error;
209 }
210 ::memcpy(m_reg_data.data(), src, m_reg_data.size());
211
212 error = WriteRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet);
213 if (error.Fail())
214 return error;
215
216 return WriteRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet);
217}
218
219llvm::Error NativeRegisterContextFreeBSD_arm64::CopyHardwareWatchpointsFrom(
221 auto &r_source = static_cast<NativeRegisterContextFreeBSD_arm64 &>(source);
222 llvm::Error error = r_source.ReadHardwareDebugInfo();
223 if (error)
224 return error;
225
226 m_dbreg = r_source.m_dbreg;
227 m_hbp_regs = r_source.m_hbp_regs;
228 m_hwp_regs = r_source.m_hwp_regs;
229 m_max_hbp_supported = r_source.m_max_hbp_supported;
230 m_max_hwp_supported = r_source.m_max_hwp_supported;
231 m_read_dbreg = true;
232
233 // on FreeBSD this writes both breakpoints and watchpoints
234 return WriteHardwareDebugRegs(eDREGTypeWATCH);
235}
236
237llvm::Error NativeRegisterContextFreeBSD_arm64::ReadHardwareDebugInfo() {
239
240 // we're fully stateful, so no need to reread control registers ever
241 if (m_read_dbreg)
242 return llvm::Error::success();
243
245 m_thread.GetID(), &m_dbreg);
246 if (res.Fail())
247 return res.ToError();
248
249 LLDB_LOG(log, "m_dbreg read: debug_ver={0}, nbkpts={1}, nwtpts={2}",
250 m_dbreg.db_debug_ver, m_dbreg.db_nbkpts, m_dbreg.db_nwtpts);
251 m_max_hbp_supported = m_dbreg.db_nbkpts;
252 m_max_hwp_supported = m_dbreg.db_nwtpts;
253 assert(m_max_hbp_supported <= m_hbp_regs.size());
254 assert(m_max_hwp_supported <= m_hwp_regs.size());
255
256 m_read_dbreg = true;
257 return llvm::Error::success();
258}
259
260llvm::Error
261NativeRegisterContextFreeBSD_arm64::WriteHardwareDebugRegs(DREGType) {
262 assert(m_read_dbreg && "dbregs must be read before writing them back");
263
264 // copy data from m_*_regs to m_dbreg before writing it back
265 for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
266 m_dbreg.db_breakregs[i].dbr_addr = m_hbp_regs[i].address;
267 m_dbreg.db_breakregs[i].dbr_ctrl = m_hbp_regs[i].control;
268 }
269 for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
270 m_dbreg.db_watchregs[i].dbw_addr = m_hwp_regs[i].address;
271 m_dbreg.db_watchregs[i].dbw_ctrl = m_hwp_regs[i].control;
272 }
273
274 return NativeProcessFreeBSD::PtraceWrapper(PT_SETDBREGS, m_thread.GetID(),
275 &m_dbreg)
276 .ToError();
277}
278
279#endif // defined (__aarch64__)
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:375
@ AUXV_FREEBSD_AT_HWCAP
FreeBSD specific AT_HWCAP value.
Definition AuxVector.h:72
@ AUXV_AT_HWCAP2
Extension of AT_HWCAP.
Definition AuxVector.h:59
size_t GetRegisterSetCount() const override
This class manages the storage and detection of register type information.
bool HasDetected() const
Returns true if field detection has been run at least once.
void UpdateRegisterInfo(const RegisterInfo *reg_info, uint32_t num_regs)
Add the type information of any registers named in this class, to the relevant RegisterInfo instances...
void DetectTypes(uint64_t hwcap, uint64_t hwcap2, uint64_t hwcap3)
For the registers listed in this class, detect which fields are present and build types for those.
A subclass of DataBuffer that stores a data buffer on the heap.
std::optional< uint64_t > GetAuxValue(enum AuxVector::EntryType type)
void SetBytes(const void *bytes, size_t length, lldb::ByteOrder byte_order)
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
Manages communication with the inferior (debugee) process.
static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr=nullptr, int data=0, int *result=nullptr)
static NativeRegisterContextFreeBSD * CreateHostNativeRegisterContextFreeBSD(const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread)
#define LLDB_INVALID_REGNUM
lldb::ByteOrder InlHostByteOrder()
Definition Endian.h:25
Status WriteHardwareDebugRegs(int hwbType, ::pid_t tid, uint32_t max_supported, const std::array< NativeRegisterContextDBReg::DREG, 16 > &regs)
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
@ 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.