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