1 | //===-- NativeRegisterContextNetBSD_x86_64.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(__i386__) || defined(__x86_64__) |
10 | |
11 | #include "NativeRegisterContextNetBSD_x86_64.h" |
12 | |
13 | #include "lldb/Host/HostInfo.h" |
14 | #include "lldb/Utility/DataBufferHeap.h" |
15 | #include "lldb/Utility/Log.h" |
16 | #include "lldb/Utility/RegisterValue.h" |
17 | #include "lldb/Utility/Status.h" |
18 | |
19 | #include "Plugins/Process/Utility/RegisterContextNetBSD_i386.h" |
20 | #include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h" |
21 | |
22 | // clang-format off |
23 | #include <sys/types.h> |
24 | #include <sys/ptrace.h> |
25 | #include <sys/sysctl.h> |
26 | #include <sys/uio.h> |
27 | #include <x86/cpu.h> |
28 | #include <x86/cpu_extended_state.h> |
29 | #include <x86/specialreg.h> |
30 | #include <elf.h> |
31 | #include <err.h> |
32 | #include <cstdint> |
33 | #include <cstdlib> |
34 | #include <optional> |
35 | // clang-format on |
36 | |
37 | using namespace lldb_private; |
38 | using namespace lldb_private::process_netbsd; |
39 | |
40 | // x86 64-bit general purpose registers. |
41 | static const uint32_t g_gpr_regnums_x86_64[] = { |
42 | lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, |
43 | lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64, |
44 | lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64, |
45 | lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64, |
46 | lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64, |
47 | lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64, |
48 | lldb_eax_x86_64, lldb_ebx_x86_64, lldb_ecx_x86_64, lldb_edx_x86_64, |
49 | lldb_edi_x86_64, lldb_esi_x86_64, lldb_ebp_x86_64, lldb_esp_x86_64, |
50 | lldb_r8d_x86_64, // Low 32 bits or r8 |
51 | lldb_r9d_x86_64, // Low 32 bits or r9 |
52 | lldb_r10d_x86_64, // Low 32 bits or r10 |
53 | lldb_r11d_x86_64, // Low 32 bits or r11 |
54 | lldb_r12d_x86_64, // Low 32 bits or r12 |
55 | lldb_r13d_x86_64, // Low 32 bits or r13 |
56 | lldb_r14d_x86_64, // Low 32 bits or r14 |
57 | lldb_r15d_x86_64, // Low 32 bits or r15 |
58 | lldb_ax_x86_64, lldb_bx_x86_64, lldb_cx_x86_64, lldb_dx_x86_64, |
59 | lldb_di_x86_64, lldb_si_x86_64, lldb_bp_x86_64, lldb_sp_x86_64, |
60 | lldb_r8w_x86_64, // Low 16 bits or r8 |
61 | lldb_r9w_x86_64, // Low 16 bits or r9 |
62 | lldb_r10w_x86_64, // Low 16 bits or r10 |
63 | lldb_r11w_x86_64, // Low 16 bits or r11 |
64 | lldb_r12w_x86_64, // Low 16 bits or r12 |
65 | lldb_r13w_x86_64, // Low 16 bits or r13 |
66 | lldb_r14w_x86_64, // Low 16 bits or r14 |
67 | lldb_r15w_x86_64, // Low 16 bits or r15 |
68 | lldb_ah_x86_64, lldb_bh_x86_64, lldb_ch_x86_64, lldb_dh_x86_64, |
69 | lldb_al_x86_64, lldb_bl_x86_64, lldb_cl_x86_64, lldb_dl_x86_64, |
70 | lldb_dil_x86_64, lldb_sil_x86_64, lldb_bpl_x86_64, lldb_spl_x86_64, |
71 | lldb_r8l_x86_64, // Low 8 bits or r8 |
72 | lldb_r9l_x86_64, // Low 8 bits or r9 |
73 | lldb_r10l_x86_64, // Low 8 bits or r10 |
74 | lldb_r11l_x86_64, // Low 8 bits or r11 |
75 | lldb_r12l_x86_64, // Low 8 bits or r12 |
76 | lldb_r13l_x86_64, // Low 8 bits or r13 |
77 | lldb_r14l_x86_64, // Low 8 bits or r14 |
78 | lldb_r15l_x86_64, // Low 8 bits or r15 |
79 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
80 | }; |
81 | static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) - |
82 | 1 == |
83 | k_num_gpr_registers_x86_64, |
84 | "g_gpr_regnums_x86_64 has wrong number of register infos" ); |
85 | |
86 | // x86 64-bit floating point registers. |
87 | static const uint32_t g_fpu_regnums_x86_64[] = { |
88 | lldb_fctrl_x86_64, lldb_fstat_x86_64, lldb_ftag_x86_64, |
89 | lldb_fop_x86_64, lldb_fiseg_x86_64, lldb_fioff_x86_64, |
90 | lldb_fip_x86_64, lldb_foseg_x86_64, lldb_fooff_x86_64, |
91 | lldb_fdp_x86_64, lldb_mxcsr_x86_64, lldb_mxcsrmask_x86_64, |
92 | lldb_st0_x86_64, lldb_st1_x86_64, lldb_st2_x86_64, |
93 | lldb_st3_x86_64, lldb_st4_x86_64, lldb_st5_x86_64, |
94 | lldb_st6_x86_64, lldb_st7_x86_64, lldb_mm0_x86_64, |
95 | lldb_mm1_x86_64, lldb_mm2_x86_64, lldb_mm3_x86_64, |
96 | lldb_mm4_x86_64, lldb_mm5_x86_64, lldb_mm6_x86_64, |
97 | lldb_mm7_x86_64, lldb_xmm0_x86_64, lldb_xmm1_x86_64, |
98 | lldb_xmm2_x86_64, lldb_xmm3_x86_64, lldb_xmm4_x86_64, |
99 | lldb_xmm5_x86_64, lldb_xmm6_x86_64, lldb_xmm7_x86_64, |
100 | lldb_xmm8_x86_64, lldb_xmm9_x86_64, lldb_xmm10_x86_64, |
101 | lldb_xmm11_x86_64, lldb_xmm12_x86_64, lldb_xmm13_x86_64, |
102 | lldb_xmm14_x86_64, lldb_xmm15_x86_64, |
103 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
104 | }; |
105 | static_assert((sizeof(g_fpu_regnums_x86_64) / sizeof(g_fpu_regnums_x86_64[0])) - |
106 | 1 == |
107 | k_num_fpr_registers_x86_64, |
108 | "g_fpu_regnums_x86_64 has wrong number of register infos" ); |
109 | |
110 | static const uint32_t g_avx_regnums_x86_64[] = { |
111 | lldb_ymm0_x86_64, lldb_ymm1_x86_64, lldb_ymm2_x86_64, lldb_ymm3_x86_64, |
112 | lldb_ymm4_x86_64, lldb_ymm5_x86_64, lldb_ymm6_x86_64, lldb_ymm7_x86_64, |
113 | lldb_ymm8_x86_64, lldb_ymm9_x86_64, lldb_ymm10_x86_64, lldb_ymm11_x86_64, |
114 | lldb_ymm12_x86_64, lldb_ymm13_x86_64, lldb_ymm14_x86_64, lldb_ymm15_x86_64, |
115 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
116 | }; |
117 | static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) - |
118 | 1 == |
119 | k_num_avx_registers_x86_64, |
120 | "g_avx_regnums_x86_64 has wrong number of register infos" ); |
121 | |
122 | static const uint32_t g_mpx_regnums_x86_64[] = { |
123 | // Note: we currently do not provide them but this is needed to avoid |
124 | // unnamed groups in SBFrame::GetRegisterContext(). |
125 | lldb_bnd0_x86_64, lldb_bnd1_x86_64, lldb_bnd2_x86_64, |
126 | lldb_bnd3_x86_64, lldb_bndcfgu_x86_64, lldb_bndstatus_x86_64, |
127 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
128 | }; |
129 | static_assert((sizeof(g_mpx_regnums_x86_64) / sizeof(g_mpx_regnums_x86_64[0])) - |
130 | 1 == |
131 | k_num_mpx_registers_x86_64, |
132 | "g_mpx_regnums_x86_64 has wrong number of register infos" ); |
133 | |
134 | // x86 debug registers. |
135 | static const uint32_t g_dbr_regnums_x86_64[] = { |
136 | lldb_dr0_x86_64, lldb_dr1_x86_64, lldb_dr2_x86_64, lldb_dr3_x86_64, |
137 | lldb_dr4_x86_64, lldb_dr5_x86_64, lldb_dr6_x86_64, lldb_dr7_x86_64, |
138 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
139 | }; |
140 | static_assert((sizeof(g_dbr_regnums_x86_64) / sizeof(g_dbr_regnums_x86_64[0])) - |
141 | 1 == |
142 | k_num_dbr_registers_x86_64, |
143 | "g_dbr_regnums_x86_64 has wrong number of register infos" ); |
144 | |
145 | // x86 32-bit general purpose registers. |
146 | static const uint32_t g_gpr_regnums_i386[] = { |
147 | lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, |
148 | lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, |
149 | lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, |
150 | lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, |
151 | lldb_ax_i386, lldb_bx_i386, lldb_cx_i386, lldb_dx_i386, |
152 | lldb_di_i386, lldb_si_i386, lldb_bp_i386, lldb_sp_i386, |
153 | lldb_ah_i386, lldb_bh_i386, lldb_ch_i386, lldb_dh_i386, |
154 | lldb_al_i386, lldb_bl_i386, lldb_cl_i386, lldb_dl_i386, |
155 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
156 | }; |
157 | static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) - |
158 | 1 == |
159 | k_num_gpr_registers_i386, |
160 | "g_gpr_regnums_i386 has wrong number of register infos" ); |
161 | |
162 | // x86 32-bit floating point registers. |
163 | static const uint32_t g_fpu_regnums_i386[] = { |
164 | lldb_fctrl_i386, lldb_fstat_i386, lldb_ftag_i386, lldb_fop_i386, |
165 | lldb_fiseg_i386, lldb_fioff_i386, lldb_foseg_i386, lldb_fooff_i386, |
166 | lldb_mxcsr_i386, lldb_mxcsrmask_i386, lldb_st0_i386, lldb_st1_i386, |
167 | lldb_st2_i386, lldb_st3_i386, lldb_st4_i386, lldb_st5_i386, |
168 | lldb_st6_i386, lldb_st7_i386, lldb_mm0_i386, lldb_mm1_i386, |
169 | lldb_mm2_i386, lldb_mm3_i386, lldb_mm4_i386, lldb_mm5_i386, |
170 | lldb_mm6_i386, lldb_mm7_i386, lldb_xmm0_i386, lldb_xmm1_i386, |
171 | lldb_xmm2_i386, lldb_xmm3_i386, lldb_xmm4_i386, lldb_xmm5_i386, |
172 | lldb_xmm6_i386, lldb_xmm7_i386, |
173 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
174 | }; |
175 | static_assert((sizeof(g_fpu_regnums_i386) / sizeof(g_fpu_regnums_i386[0])) - |
176 | 1 == |
177 | k_num_fpr_registers_i386, |
178 | "g_fpu_regnums_i386 has wrong number of register infos" ); |
179 | |
180 | static const uint32_t g_avx_regnums_i386[] = { |
181 | lldb_ymm0_i386, lldb_ymm1_i386, lldb_ymm2_i386, lldb_ymm3_i386, |
182 | lldb_ymm4_i386, lldb_ymm5_i386, lldb_ymm6_i386, lldb_ymm7_i386, |
183 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
184 | }; |
185 | static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) - |
186 | 1 == |
187 | k_num_avx_registers_i386, |
188 | "g_avx_regnums_i386 has wrong number of register infos" ); |
189 | |
190 | static const uint32_t g_mpx_regnums_i386[] = { |
191 | // Note: we currently do not provide them but this is needed to avoid |
192 | // unnamed groups in SBFrame::GetRegisterContext(). |
193 | lldb_bnd0_i386, lldb_bnd1_i386, lldb_bnd2_i386, |
194 | lldb_bnd3_i386, lldb_bndcfgu_i386, lldb_bndstatus_i386, |
195 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
196 | }; |
197 | static_assert((sizeof(g_mpx_regnums_i386) / sizeof(g_mpx_regnums_i386[0])) - |
198 | 1 == |
199 | k_num_mpx_registers_i386, |
200 | "g_mpx_regnums_i386 has wrong number of register infos" ); |
201 | |
202 | // x86 debug registers. |
203 | static const uint32_t g_dbr_regnums_i386[] = { |
204 | lldb_dr0_i386, lldb_dr1_i386, lldb_dr2_i386, lldb_dr3_i386, |
205 | lldb_dr4_i386, lldb_dr5_i386, lldb_dr6_i386, lldb_dr7_i386, |
206 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
207 | }; |
208 | static_assert((sizeof(g_dbr_regnums_i386) / sizeof(g_dbr_regnums_i386[0])) - |
209 | 1 == |
210 | k_num_dbr_registers_i386, |
211 | "g_dbr_regnums_i386 has wrong number of register infos" ); |
212 | |
213 | // Number of register sets provided by this context. |
214 | enum { k_num_register_sets = 5 }; |
215 | |
216 | // Register sets for x86 32-bit. |
217 | static const RegisterSet g_reg_sets_i386[k_num_register_sets] = { |
218 | {.name: "General Purpose Registers" , .short_name: "gpr" , .num_registers: k_num_gpr_registers_i386, |
219 | .registers: g_gpr_regnums_i386}, |
220 | {.name: "Floating Point Registers" , .short_name: "fpu" , .num_registers: k_num_fpr_registers_i386, |
221 | .registers: g_fpu_regnums_i386}, |
222 | {.name: "Debug Registers" , .short_name: "dbr" , .num_registers: k_num_dbr_registers_i386, .registers: g_dbr_regnums_i386}, |
223 | {.name: "Advanced Vector Extensions" , .short_name: "avx" , .num_registers: k_num_avx_registers_i386, |
224 | .registers: g_avx_regnums_i386}, |
225 | {.name: "Memory Protection Extensions" , .short_name: "mpx" , .num_registers: k_num_mpx_registers_i386, |
226 | .registers: g_mpx_regnums_i386}, |
227 | }; |
228 | |
229 | // Register sets for x86 64-bit. |
230 | static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = { |
231 | {.name: "General Purpose Registers" , .short_name: "gpr" , .num_registers: k_num_gpr_registers_x86_64, |
232 | .registers: g_gpr_regnums_x86_64}, |
233 | {.name: "Floating Point Registers" , .short_name: "fpu" , .num_registers: k_num_fpr_registers_x86_64, |
234 | .registers: g_fpu_regnums_x86_64}, |
235 | {.name: "Debug Registers" , .short_name: "dbr" , .num_registers: k_num_dbr_registers_x86_64, |
236 | .registers: g_dbr_regnums_x86_64}, |
237 | {.name: "Advanced Vector Extensions" , .short_name: "avx" , .num_registers: k_num_avx_registers_x86_64, |
238 | .registers: g_avx_regnums_x86_64}, |
239 | {.name: "Memory Protection Extensions" , .short_name: "mpx" , .num_registers: k_num_mpx_registers_x86_64, |
240 | .registers: g_mpx_regnums_x86_64}, |
241 | }; |
242 | |
243 | #define REG_CONTEXT_SIZE (GetRegisterInfoInterface().GetGPRSize()) |
244 | |
245 | NativeRegisterContextNetBSD * |
246 | NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD( |
247 | const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { |
248 | return new NativeRegisterContextNetBSD_x86_64(target_arch, native_thread); |
249 | } |
250 | |
251 | // NativeRegisterContextNetBSD_x86_64 members. |
252 | |
253 | static RegisterInfoInterface * |
254 | CreateRegisterInfoInterface(const ArchSpec &target_arch) { |
255 | if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) { |
256 | // 32-bit hosts run with a RegisterContextNetBSD_i386 context. |
257 | return new RegisterContextNetBSD_i386(target_arch); |
258 | } else { |
259 | assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && |
260 | "Register setting path assumes this is a 64-bit host" ); |
261 | // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the |
262 | // x86_64 register context. |
263 | return new RegisterContextNetBSD_x86_64(target_arch); |
264 | } |
265 | } |
266 | |
267 | NativeRegisterContextNetBSD_x86_64::NativeRegisterContextNetBSD_x86_64( |
268 | const ArchSpec &target_arch, NativeThreadProtocol &native_thread) |
269 | : NativeRegisterContextRegisterInfo( |
270 | native_thread, CreateRegisterInfoInterface(target_arch)), |
271 | NativeRegisterContextDBReg_x86(native_thread), m_regset_offsets({0}) { |
272 | assert(m_gpr.size() == GetRegisterInfoInterface().GetGPRSize()); |
273 | std::array<uint32_t, MaxRegularRegSet + 1> first_regnos; |
274 | |
275 | switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { |
276 | case llvm::Triple::x86: |
277 | first_regnos[FPRegSet] = lldb_fctrl_i386; |
278 | first_regnos[DBRegSet] = lldb_dr0_i386; |
279 | break; |
280 | case llvm::Triple::x86_64: |
281 | first_regnos[FPRegSet] = lldb_fctrl_x86_64; |
282 | first_regnos[DBRegSet] = lldb_dr0_x86_64; |
283 | break; |
284 | default: |
285 | llvm_unreachable("Unhandled target architecture." ); |
286 | } |
287 | |
288 | for (int i : {FPRegSet, DBRegSet}) |
289 | m_regset_offsets[i] = GetRegisterInfoInterface() |
290 | .GetRegisterInfo()[first_regnos[i]] |
291 | .byte_offset; |
292 | } |
293 | |
294 | uint32_t NativeRegisterContextNetBSD_x86_64::GetRegisterSetCount() const { |
295 | return k_num_register_sets; |
296 | } |
297 | |
298 | const RegisterSet * |
299 | NativeRegisterContextNetBSD_x86_64::GetRegisterSet(uint32_t set_index) const { |
300 | switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { |
301 | case llvm::Triple::x86: |
302 | return &g_reg_sets_i386[set_index]; |
303 | case llvm::Triple::x86_64: |
304 | return &g_reg_sets_x86_64[set_index]; |
305 | default: |
306 | llvm_unreachable("Unhandled target architecture." ); |
307 | } |
308 | } |
309 | |
310 | std::optional<NativeRegisterContextNetBSD_x86_64::RegSetKind> |
311 | NativeRegisterContextNetBSD_x86_64::GetSetForNativeRegNum( |
312 | uint32_t reg_num) const { |
313 | switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { |
314 | case llvm::Triple::x86: |
315 | if (reg_num >= k_first_gpr_i386 && reg_num <= k_last_gpr_i386) |
316 | return GPRegSet; |
317 | if (reg_num >= k_first_fpr_i386 && reg_num <= k_last_fpr_i386) |
318 | return FPRegSet; |
319 | if (reg_num >= k_first_avx_i386 && reg_num <= k_last_avx_i386) |
320 | return YMMRegSet; |
321 | if (reg_num >= k_first_mpxr_i386 && reg_num <= k_last_mpxr_i386) |
322 | return std::nullopt; // MPXR |
323 | if (reg_num >= k_first_mpxc_i386 && reg_num <= k_last_mpxc_i386) |
324 | return std::nullopt; // MPXC |
325 | if (reg_num >= k_first_dbr_i386 && reg_num <= k_last_dbr_i386) |
326 | return DBRegSet; // DBR |
327 | break; |
328 | case llvm::Triple::x86_64: |
329 | if (reg_num >= k_first_gpr_x86_64 && reg_num <= k_last_gpr_x86_64) |
330 | return GPRegSet; |
331 | if (reg_num >= k_first_fpr_x86_64 && reg_num <= k_last_fpr_x86_64) |
332 | return FPRegSet; |
333 | if (reg_num >= k_first_avx_x86_64 && reg_num <= k_last_avx_x86_64) |
334 | return YMMRegSet; |
335 | if (reg_num >= k_first_mpxr_x86_64 && reg_num <= k_last_mpxr_x86_64) |
336 | return std::nullopt; // MPXR |
337 | if (reg_num >= k_first_mpxc_x86_64 && reg_num <= k_last_mpxc_x86_64) |
338 | return std::nullopt; // MPXC |
339 | if (reg_num >= k_first_dbr_x86_64 && reg_num <= k_last_dbr_x86_64) |
340 | return DBRegSet; // DBR |
341 | break; |
342 | default: |
343 | llvm_unreachable("Unhandled target architecture." ); |
344 | } |
345 | |
346 | llvm_unreachable("Register does not belong to any register set" ); |
347 | } |
348 | |
349 | Status NativeRegisterContextNetBSD_x86_64::ReadRegisterSet(RegSetKind set) { |
350 | switch (set) { |
351 | case GPRegSet: |
352 | return DoRegisterSet(PT_GETREGS, m_gpr.data()); |
353 | case DBRegSet: |
354 | return DoRegisterSet(PT_GETDBREGS, m_dbr.data()); |
355 | case FPRegSet: |
356 | case YMMRegSet: |
357 | case MPXRegSet: { |
358 | struct iovec iov = {m_xstate.data(), m_xstate.size()}; |
359 | Status ret = DoRegisterSet(req: PT_GETXSTATE, buf: &iov); |
360 | assert(reinterpret_cast<xstate *>(m_xstate.data())->xs_rfbm & XCR0_X87); |
361 | return ret; |
362 | } |
363 | } |
364 | llvm_unreachable("NativeRegisterContextNetBSD_x86_64::ReadRegisterSet" ); |
365 | } |
366 | |
367 | Status NativeRegisterContextNetBSD_x86_64::WriteRegisterSet(RegSetKind set) { |
368 | switch (set) { |
369 | case GPRegSet: |
370 | return DoRegisterSet(PT_SETREGS, m_gpr.data()); |
371 | case DBRegSet: |
372 | return DoRegisterSet(PT_SETDBREGS, m_dbr.data()); |
373 | case FPRegSet: |
374 | case YMMRegSet: |
375 | case MPXRegSet: { |
376 | struct iovec iov = {&m_xstate, sizeof(m_xstate)}; |
377 | return DoRegisterSet(req: PT_SETXSTATE, buf: &iov); |
378 | } |
379 | } |
380 | llvm_unreachable("NativeRegisterContextNetBSD_x86_64::WriteRegisterSet" ); |
381 | } |
382 | |
383 | Status |
384 | NativeRegisterContextNetBSD_x86_64::ReadRegister(const RegisterInfo *reg_info, |
385 | RegisterValue ®_value) { |
386 | Status error; |
387 | |
388 | if (!reg_info) { |
389 | error.SetErrorString("reg_info NULL" ); |
390 | return error; |
391 | } |
392 | |
393 | uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
394 | if (reg == LLDB_INVALID_REGNUM) { |
395 | // This is likely an internal register for lldb use only and should not be |
396 | // directly queried. |
397 | error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " |
398 | "register, cannot read directly" , |
399 | reg_info->name); |
400 | return error; |
401 | } |
402 | |
403 | std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg_num: reg); |
404 | if (!opt_set) { |
405 | // This is likely an internal register for lldb use only and should not be |
406 | // directly queried. |
407 | error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set" , |
408 | reg_info->name); |
409 | return error; |
410 | } |
411 | |
412 | RegSetKind set = *opt_set; |
413 | error = ReadRegisterSet(set); |
414 | if (error.Fail()) |
415 | return error; |
416 | |
417 | switch (set) { |
418 | case GPRegSet: |
419 | case FPRegSet: |
420 | case DBRegSet: { |
421 | void *data = GetOffsetRegSetData(set, reg_offset: reg_info->byte_offset); |
422 | FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_xstate.data() + |
423 | offsetof(xstate, xs_fxsave)); |
424 | if (data == &fpr->ftag) // ftag |
425 | reg_value.SetUInt16( |
426 | AbridgedToFullTagWord(abridged_tw: fpr->ftag, sw: fpr->fstat, st_regs: fpr->stmm)); |
427 | else |
428 | reg_value.SetBytes(bytes: data, length: reg_info->byte_size, byte_order: endian::InlHostByteOrder()); |
429 | break; |
430 | } |
431 | case YMMRegSet: { |
432 | std::optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg); |
433 | if (!ymm_reg) { |
434 | error.SetErrorStringWithFormat( |
435 | "register \"%s\" not supported by CPU/kernel" , reg_info->name); |
436 | } else { |
437 | YMMReg ymm = XStateToYMM(xmm_bytes: ymm_reg->xmm, ymmh_bytes: ymm_reg->ymm_hi); |
438 | reg_value.SetBytes(bytes: ymm.bytes, length: reg_info->byte_size, |
439 | byte_order: endian::InlHostByteOrder()); |
440 | } |
441 | break; |
442 | } |
443 | case MPXRegSet: |
444 | llvm_unreachable("MPX regset should have returned error" ); |
445 | } |
446 | |
447 | return error; |
448 | } |
449 | |
450 | Status NativeRegisterContextNetBSD_x86_64::WriteRegister( |
451 | const RegisterInfo *reg_info, const RegisterValue ®_value) { |
452 | |
453 | Status error; |
454 | |
455 | if (!reg_info) { |
456 | error.SetErrorString("reg_info NULL" ); |
457 | return error; |
458 | } |
459 | |
460 | uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
461 | if (reg == LLDB_INVALID_REGNUM) { |
462 | // This is likely an internal register for lldb use only and should not be |
463 | // directly queried. |
464 | error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " |
465 | "register, cannot read directly" , |
466 | reg_info->name); |
467 | return error; |
468 | } |
469 | |
470 | std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg_num: reg); |
471 | if (!opt_set) { |
472 | // This is likely an internal register for lldb use only and should not be |
473 | // directly queried. |
474 | error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set" , |
475 | reg_info->name); |
476 | return error; |
477 | } |
478 | |
479 | RegSetKind set = *opt_set; |
480 | uint64_t new_xstate_bv = 0; |
481 | |
482 | error = ReadRegisterSet(set); |
483 | if (error.Fail()) |
484 | return error; |
485 | |
486 | switch (set) { |
487 | case GPRegSet: |
488 | case DBRegSet: |
489 | ::memcpy(dest: GetOffsetRegSetData(set, reg_offset: reg_info->byte_offset), |
490 | src: reg_value.GetBytes(), n: reg_value.GetByteSize()); |
491 | break; |
492 | case FPRegSet: { |
493 | void *data = GetOffsetRegSetData(set, reg_offset: reg_info->byte_offset); |
494 | FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_xstate.data() + |
495 | offsetof(xstate, xs_fxsave)); |
496 | if (data == &fpr->ftag) // ftag |
497 | fpr->ftag = FullToAbridgedTagWord(tw: reg_value.GetAsUInt16()); |
498 | else |
499 | ::memcpy(dest: data, src: reg_value.GetBytes(), n: reg_value.GetByteSize()); |
500 | if (data >= &fpr->xmm) |
501 | new_xstate_bv |= XCR0_SSE; |
502 | else if (data >= &fpr->mxcsr && data < &fpr->stmm) |
503 | new_xstate_bv |= XCR0_SSE; |
504 | else |
505 | new_xstate_bv |= XCR0_X87; |
506 | break; |
507 | } |
508 | case YMMRegSet: { |
509 | std::optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg); |
510 | if (!ymm_reg) { |
511 | error.SetErrorStringWithFormat( |
512 | "register \"%s\" not supported by CPU/kernel" , reg_info->name); |
513 | } else { |
514 | YMMReg ymm; |
515 | ::memcpy(dest: ymm.bytes, src: reg_value.GetBytes(), n: reg_value.GetByteSize()); |
516 | YMMToXState(input: ymm, xmm_bytes: ymm_reg->xmm, ymmh_bytes: ymm_reg->ymm_hi); |
517 | new_xstate_bv |= XCR0_SSE | XCR0_YMM_Hi128; |
518 | } |
519 | break; |
520 | } |
521 | case MPXRegSet: |
522 | llvm_unreachable("MPX regset should have returned error" ); |
523 | } |
524 | |
525 | if (new_xstate_bv != 0) |
526 | reinterpret_cast<xstate *>(m_xstate.data())->xs_xstate_bv |= new_xstate_bv; |
527 | return WriteRegisterSet(set); |
528 | } |
529 | |
530 | Status NativeRegisterContextNetBSD_x86_64::ReadAllRegisterValues( |
531 | lldb::WritableDataBufferSP &data_sp) { |
532 | Status error; |
533 | |
534 | data_sp.reset(p: new DataBufferHeap(REG_CONTEXT_SIZE, 0)); |
535 | error = ReadRegisterSet(set: GPRegSet); |
536 | if (error.Fail()) |
537 | return error; |
538 | |
539 | uint8_t *dst = data_sp->GetBytes(); |
540 | ::memcpy(dst, m_gpr.data(), GetRegisterInfoInterface().GetGPRSize()); |
541 | dst += GetRegisterInfoInterface().GetGPRSize(); |
542 | |
543 | return error; |
544 | } |
545 | |
546 | Status NativeRegisterContextNetBSD_x86_64::WriteAllRegisterValues( |
547 | const lldb::DataBufferSP &data_sp) { |
548 | Status error; |
549 | |
550 | if (!data_sp) { |
551 | error.SetErrorStringWithFormat( |
552 | "NativeRegisterContextNetBSD_x86_64::%s invalid data_sp provided" , |
553 | __FUNCTION__); |
554 | return error; |
555 | } |
556 | |
557 | if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { |
558 | error.SetErrorStringWithFormat( |
559 | "NativeRegisterContextNetBSD_x86_64::%s data_sp contained mismatched " |
560 | "data size, expected %zu, actual %" PRIu64, |
561 | __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); |
562 | return error; |
563 | } |
564 | |
565 | const uint8_t *src = data_sp->GetBytes(); |
566 | if (src == nullptr) { |
567 | error.SetErrorStringWithFormat("NativeRegisterContextNetBSD_x86_64::%s " |
568 | "DataBuffer::GetBytes() returned a null " |
569 | "pointer" , |
570 | __FUNCTION__); |
571 | return error; |
572 | } |
573 | ::memcpy(m_gpr.data(), src, GetRegisterInfoInterface().GetGPRSize()); |
574 | |
575 | error = WriteRegisterSet(set: GPRegSet); |
576 | if (error.Fail()) |
577 | return error; |
578 | src += GetRegisterInfoInterface().GetGPRSize(); |
579 | |
580 | return error; |
581 | } |
582 | |
583 | llvm::Error NativeRegisterContextNetBSD_x86_64::CopyHardwareWatchpointsFrom( |
584 | NativeRegisterContextNetBSD &source) { |
585 | auto &r_source = static_cast<NativeRegisterContextNetBSD_x86_64 &>(source); |
586 | // NB: This implicitly reads the whole dbreg set. |
587 | RegisterValue dr7; |
588 | Status res = r_source.ReadRegister(reg_info: GetDR(7), reg_value&: dr7); |
589 | if (!res.Fail()) { |
590 | // copy dbregs only if any watchpoints were set |
591 | if ((dr7.GetAsUInt64() & 0xFF) == 0) |
592 | return llvm::Error::success(); |
593 | |
594 | m_dbr = r_source.m_dbr; |
595 | res = WriteRegisterSet(set: DBRegSet); |
596 | } |
597 | return res.ToError(); |
598 | } |
599 | |
600 | uint8_t * |
601 | NativeRegisterContextNetBSD_x86_64::GetOffsetRegSetData(RegSetKind set, |
602 | size_t reg_offset) { |
603 | uint8_t *base; |
604 | switch (set) { |
605 | case GPRegSet: |
606 | base = m_gpr.data(); |
607 | break; |
608 | case FPRegSet: |
609 | base = m_xstate.data() + offsetof(xstate, xs_fxsave); |
610 | break; |
611 | case DBRegSet: |
612 | base = m_dbr.data(); |
613 | break; |
614 | case YMMRegSet: |
615 | llvm_unreachable("GetRegSetData() is unsuitable for this regset." ); |
616 | case MPXRegSet: |
617 | llvm_unreachable("MPX regset should have returned error" ); |
618 | } |
619 | assert(reg_offset >= m_regset_offsets[set]); |
620 | return base + (reg_offset - m_regset_offsets[set]); |
621 | } |
622 | |
623 | std::optional<NativeRegisterContextNetBSD_x86_64::YMMSplitPtr> |
624 | NativeRegisterContextNetBSD_x86_64::GetYMMSplitReg(uint32_t reg) { |
625 | auto xst = reinterpret_cast<xstate *>(m_xstate.data()); |
626 | if (!(xst->xs_rfbm & XCR0_SSE) || !(xst->xs_rfbm & XCR0_YMM_Hi128)) |
627 | return std::nullopt; |
628 | |
629 | uint32_t reg_index; |
630 | switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { |
631 | case llvm::Triple::x86: |
632 | reg_index = reg - lldb_ymm0_i386; |
633 | break; |
634 | case llvm::Triple::x86_64: |
635 | reg_index = reg - lldb_ymm0_x86_64; |
636 | break; |
637 | default: |
638 | llvm_unreachable("Unhandled target architecture." ); |
639 | } |
640 | |
641 | return YMMSplitPtr{&xst->xs_fxsave.fx_xmm[reg_index], |
642 | &xst->xs_ymm_hi128.xs_ymm[reg_index]}; |
643 | } |
644 | |
645 | #endif // defined(__x86_64__) |
646 | |