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 11 #include "NativeRegisterContextFreeBSD_arm64.h" 12 13 #include "lldb/Utility/DataBufferHeap.h" 14 #include "lldb/Utility/RegisterValue.h" 15 #include "lldb/Utility/Status.h" 16 17 #include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h" 18 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" 19 #include "Plugins/Process/Utility/RegisterFlagsDetector_arm64.h" 20 #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" 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 28 using namespace lldb; 29 using namespace lldb_private; 30 using 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. 36 static std::mutex g_register_flags_detector_mutex; 37 static Arm64RegisterFlagsDetector g_register_flags_detector; 38 39 NativeRegisterContextFreeBSD * 40 NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( 41 const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) { 42 std::lock_guard<std::mutex> lock(g_register_flags_detector_mutex); 43 if (!g_register_flags_detector.HasDetected()) { 44 NativeProcessFreeBSD &process = native_thread.GetProcess(); 45 g_register_flags_detector.DetectFields( 46 process.GetAuxValue(AuxVector::AUXV_FREEBSD_AT_HWCAP).value_or(0), 47 process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2).value_or(0)); 48 } 49 50 return new NativeRegisterContextFreeBSD_arm64(target_arch, native_thread); 51 } 52 53 NativeRegisterContextFreeBSD_arm64::NativeRegisterContextFreeBSD_arm64( 54 const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) 55 : NativeRegisterContextRegisterInfo( 56 native_thread, new RegisterInfoPOSIX_arm64(target_arch, 0)) 57 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT 58 , 59 m_read_dbreg(false) 60 #endif 61 { 62 g_register_flags_detector.UpdateRegisterInfo( 63 GetRegisterInfoInterface().GetRegisterInfo(), 64 GetRegisterInfoInterface().GetRegisterCount()); 65 66 ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); 67 ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); 68 } 69 70 RegisterInfoPOSIX_arm64 & 71 NativeRegisterContextFreeBSD_arm64::GetRegisterInfo() const { 72 return static_cast<RegisterInfoPOSIX_arm64 &>(*m_register_info_interface_up); 73 } 74 75 uint32_t NativeRegisterContextFreeBSD_arm64::GetRegisterSetCount() const { 76 return GetRegisterInfo().GetRegisterSetCount(); 77 } 78 79 const RegisterSet * 80 NativeRegisterContextFreeBSD_arm64::GetRegisterSet(uint32_t set_index) const { 81 return GetRegisterInfo().GetRegisterSet(set_index); 82 } 83 84 uint32_t NativeRegisterContextFreeBSD_arm64::GetUserRegisterCount() const { 85 uint32_t count = 0; 86 for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) 87 count += GetRegisterSet(set_index)->num_registers; 88 return count; 89 } 90 91 Status NativeRegisterContextFreeBSD_arm64::ReadRegisterSet(uint32_t set) { 92 switch (set) { 93 case RegisterInfoPOSIX_arm64::GPRegSet: 94 return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), 95 m_reg_data.data()); 96 case RegisterInfoPOSIX_arm64::FPRegSet: 97 return NativeProcessFreeBSD::PtraceWrapper( 98 PT_GETFPREGS, m_thread.GetID(), 99 m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR)); 100 } 101 llvm_unreachable("NativeRegisterContextFreeBSD_arm64::ReadRegisterSet"); 102 } 103 104 Status NativeRegisterContextFreeBSD_arm64::WriteRegisterSet(uint32_t set) { 105 switch (set) { 106 case RegisterInfoPOSIX_arm64::GPRegSet: 107 return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), 108 m_reg_data.data()); 109 case RegisterInfoPOSIX_arm64::FPRegSet: 110 return NativeProcessFreeBSD::PtraceWrapper( 111 PT_SETFPREGS, m_thread.GetID(), 112 m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR)); 113 } 114 llvm_unreachable("NativeRegisterContextFreeBSD_arm64::WriteRegisterSet"); 115 } 116 117 Status 118 NativeRegisterContextFreeBSD_arm64::ReadRegister(const RegisterInfo *reg_info, 119 RegisterValue ®_value) { 120 Status error; 121 122 if (!reg_info) { 123 error.SetErrorString("reg_info NULL"); 124 return error; 125 } 126 127 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; 128 129 if (reg == LLDB_INVALID_REGNUM) 130 return Status("no lldb regnum for %s", reg_info && reg_info->name 131 ? reg_info->name 132 : "<unknown register>"); 133 134 uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg); 135 error = ReadRegisterSet(set); 136 if (error.Fail()) 137 return error; 138 139 assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); 140 reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset, 141 reg_info->byte_size, endian::InlHostByteOrder()); 142 return error; 143 } 144 145 Status NativeRegisterContextFreeBSD_arm64::WriteRegister( 146 const RegisterInfo *reg_info, const RegisterValue ®_value) { 147 Status error; 148 149 if (!reg_info) 150 return Status("reg_info NULL"); 151 152 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; 153 154 if (reg == LLDB_INVALID_REGNUM) 155 return Status("no lldb regnum for %s", reg_info && reg_info->name 156 ? reg_info->name 157 : "<unknown register>"); 158 159 uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg); 160 error = ReadRegisterSet(set); 161 if (error.Fail()) 162 return error; 163 164 assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); 165 ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(), 166 reg_info->byte_size); 167 168 return WriteRegisterSet(set); 169 } 170 171 Status NativeRegisterContextFreeBSD_arm64::ReadAllRegisterValues( 172 lldb::WritableDataBufferSP &data_sp) { 173 Status error; 174 175 error = ReadRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet); 176 if (error.Fail()) 177 return error; 178 179 error = ReadRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet); 180 if (error.Fail()) 181 return error; 182 183 data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0)); 184 uint8_t *dst = data_sp->GetBytes(); 185 ::memcpy(dst, m_reg_data.data(), m_reg_data.size()); 186 187 return error; 188 } 189 190 Status NativeRegisterContextFreeBSD_arm64::WriteAllRegisterValues( 191 const lldb::DataBufferSP &data_sp) { 192 Status error; 193 194 if (!data_sp) { 195 error.SetErrorStringWithFormat( 196 "NativeRegisterContextFreeBSD_arm64::%s invalid data_sp provided", 197 __FUNCTION__); 198 return error; 199 } 200 201 if (data_sp->GetByteSize() != m_reg_data.size()) { 202 error.SetErrorStringWithFormat( 203 "NativeRegisterContextFreeBSD_arm64::%s data_sp contained mismatched " 204 "data size, expected %" PRIu64 ", actual %" PRIu64, 205 __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize()); 206 return error; 207 } 208 209 const uint8_t *src = data_sp->GetBytes(); 210 if (src == nullptr) { 211 error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_arm64::%s " 212 "DataBuffer::GetBytes() returned a null " 213 "pointer", 214 __FUNCTION__); 215 return error; 216 } 217 ::memcpy(m_reg_data.data(), src, m_reg_data.size()); 218 219 error = WriteRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet); 220 if (error.Fail()) 221 return error; 222 223 return WriteRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet); 224 } 225 226 llvm::Error NativeRegisterContextFreeBSD_arm64::CopyHardwareWatchpointsFrom( 227 NativeRegisterContextFreeBSD &source) { 228 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT 229 auto &r_source = static_cast<NativeRegisterContextFreeBSD_arm64 &>(source); 230 llvm::Error error = r_source.ReadHardwareDebugInfo(); 231 if (error) 232 return error; 233 234 m_dbreg = r_source.m_dbreg; 235 m_hbp_regs = r_source.m_hbp_regs; 236 m_hwp_regs = r_source.m_hwp_regs; 237 m_max_hbp_supported = r_source.m_max_hbp_supported; 238 m_max_hwp_supported = r_source.m_max_hwp_supported; 239 m_read_dbreg = true; 240 241 // on FreeBSD this writes both breakpoints and watchpoints 242 return WriteHardwareDebugRegs(eDREGTypeWATCH); 243 #else 244 return llvm::Error::success(); 245 #endif 246 } 247 248 llvm::Error NativeRegisterContextFreeBSD_arm64::ReadHardwareDebugInfo() { 249 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT 250 Log *log = GetLog(POSIXLog::Registers); 251 252 // we're fully stateful, so no need to reread control registers ever 253 if (m_read_dbreg) 254 return llvm::Error::success(); 255 256 Status res = NativeProcessFreeBSD::PtraceWrapper(PT_GETDBREGS, 257 m_thread.GetID(), &m_dbreg); 258 if (res.Fail()) 259 return res.ToError(); 260 261 LLDB_LOG(log, "m_dbreg read: debug_ver={0}, nbkpts={1}, nwtpts={2}", 262 m_dbreg.db_debug_ver, m_dbreg.db_nbkpts, m_dbreg.db_nwtpts); 263 m_max_hbp_supported = m_dbreg.db_nbkpts; 264 m_max_hwp_supported = m_dbreg.db_nwtpts; 265 assert(m_max_hbp_supported <= m_hbp_regs.size()); 266 assert(m_max_hwp_supported <= m_hwp_regs.size()); 267 268 m_read_dbreg = true; 269 return llvm::Error::success(); 270 #else 271 return llvm::createStringError( 272 llvm::inconvertibleErrorCode(), 273 "Hardware breakpoints/watchpoints require FreeBSD 14.0"); 274 #endif 275 } 276 277 llvm::Error 278 NativeRegisterContextFreeBSD_arm64::WriteHardwareDebugRegs(DREGType) { 279 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT 280 assert(m_read_dbreg && "dbregs must be read before writing them back"); 281 282 // copy data from m_*_regs to m_dbreg before writing it back 283 for (uint32_t i = 0; i < m_max_hbp_supported; i++) { 284 m_dbreg.db_breakregs[i].dbr_addr = m_hbp_regs[i].address; 285 m_dbreg.db_breakregs[i].dbr_ctrl = m_hbp_regs[i].control; 286 } 287 for (uint32_t i = 0; i < m_max_hwp_supported; i++) { 288 m_dbreg.db_watchregs[i].dbw_addr = m_hwp_regs[i].address; 289 m_dbreg.db_watchregs[i].dbw_ctrl = m_hwp_regs[i].control; 290 } 291 292 return NativeProcessFreeBSD::PtraceWrapper(PT_SETDBREGS, m_thread.GetID(), 293 &m_dbreg) 294 .ToError(); 295 #else 296 return llvm::createStringError( 297 llvm::inconvertibleErrorCode(), 298 "Hardware breakpoints/watchpoints require FreeBSD 14.0"); 299 #endif 300 } 301 302 #endif // defined (__aarch64__) 303