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