1 //===-- NativeRegisterContextNetBSD_x86_64.cpp ---------------*- C++ -*-===//
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(__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_x86_64.h"
20
21 // clang-format off
22 #include <sys/types.h>
23 #include <sys/ptrace.h>
24 #include <sys/sysctl.h>
25 #include <sys/uio.h>
26 #include <x86/cpu.h>
27 #include <x86/cpu_extended_state.h>
28 #include <x86/specialreg.h>
29 #include <elf.h>
30 #include <err.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 // clang-format on
34
35 using namespace lldb_private;
36 using namespace lldb_private::process_netbsd;
37
38 // Private namespace.
39
40 namespace {
41 // x86 64-bit general purpose registers.
42 static const uint32_t g_gpr_regnums_x86_64[] = {
43 lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64,
44 lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64,
45 lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64,
46 lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64,
47 lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64,
48 lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64,
49 lldb_eax_x86_64, lldb_ebx_x86_64, lldb_ecx_x86_64, lldb_edx_x86_64,
50 lldb_edi_x86_64, lldb_esi_x86_64, lldb_ebp_x86_64, lldb_esp_x86_64,
51 lldb_r8d_x86_64, // Low 32 bits or r8
52 lldb_r9d_x86_64, // Low 32 bits or r9
53 lldb_r10d_x86_64, // Low 32 bits or r10
54 lldb_r11d_x86_64, // Low 32 bits or r11
55 lldb_r12d_x86_64, // Low 32 bits or r12
56 lldb_r13d_x86_64, // Low 32 bits or r13
57 lldb_r14d_x86_64, // Low 32 bits or r14
58 lldb_r15d_x86_64, // Low 32 bits or r15
59 lldb_ax_x86_64, lldb_bx_x86_64, lldb_cx_x86_64, lldb_dx_x86_64,
60 lldb_di_x86_64, lldb_si_x86_64, lldb_bp_x86_64, lldb_sp_x86_64,
61 lldb_r8w_x86_64, // Low 16 bits or r8
62 lldb_r9w_x86_64, // Low 16 bits or r9
63 lldb_r10w_x86_64, // Low 16 bits or r10
64 lldb_r11w_x86_64, // Low 16 bits or r11
65 lldb_r12w_x86_64, // Low 16 bits or r12
66 lldb_r13w_x86_64, // Low 16 bits or r13
67 lldb_r14w_x86_64, // Low 16 bits or r14
68 lldb_r15w_x86_64, // Low 16 bits or r15
69 lldb_ah_x86_64, lldb_bh_x86_64, lldb_ch_x86_64, lldb_dh_x86_64,
70 lldb_al_x86_64, lldb_bl_x86_64, lldb_cl_x86_64, lldb_dl_x86_64,
71 lldb_dil_x86_64, lldb_sil_x86_64, lldb_bpl_x86_64, lldb_spl_x86_64,
72 lldb_r8l_x86_64, // Low 8 bits or r8
73 lldb_r9l_x86_64, // Low 8 bits or r9
74 lldb_r10l_x86_64, // Low 8 bits or r10
75 lldb_r11l_x86_64, // Low 8 bits or r11
76 lldb_r12l_x86_64, // Low 8 bits or r12
77 lldb_r13l_x86_64, // Low 8 bits or r13
78 lldb_r14l_x86_64, // Low 8 bits or r14
79 lldb_r15l_x86_64, // Low 8 bits or r15
80 LLDB_INVALID_REGNUM // register sets need to end with this flag
81 };
82 static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) -
83 1 ==
84 k_num_gpr_registers_x86_64,
85 "g_gpr_regnums_x86_64 has wrong number of register infos");
86
87 // x86 64-bit floating point registers.
88 static const uint32_t g_fpu_regnums_x86_64[] = {
89 lldb_fctrl_x86_64, lldb_fstat_x86_64, lldb_ftag_x86_64,
90 lldb_fop_x86_64, lldb_fiseg_x86_64, lldb_fioff_x86_64,
91 lldb_foseg_x86_64, lldb_fooff_x86_64, lldb_mxcsr_x86_64,
92 lldb_mxcsrmask_x86_64, lldb_st0_x86_64, lldb_st1_x86_64,
93 lldb_st2_x86_64, lldb_st3_x86_64, lldb_st4_x86_64,
94 lldb_st5_x86_64, lldb_st6_x86_64, lldb_st7_x86_64,
95 lldb_mm0_x86_64, lldb_mm1_x86_64, lldb_mm2_x86_64,
96 lldb_mm3_x86_64, lldb_mm4_x86_64, lldb_mm5_x86_64,
97 lldb_mm6_x86_64, lldb_mm7_x86_64, lldb_xmm0_x86_64,
98 lldb_xmm1_x86_64, lldb_xmm2_x86_64, lldb_xmm3_x86_64,
99 lldb_xmm4_x86_64, lldb_xmm5_x86_64, lldb_xmm6_x86_64,
100 lldb_xmm7_x86_64, lldb_xmm8_x86_64, lldb_xmm9_x86_64,
101 lldb_xmm10_x86_64, lldb_xmm11_x86_64, lldb_xmm12_x86_64,
102 lldb_xmm13_x86_64, 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 // x86 64-bit registers available via XState.
111 static const uint32_t g_xstate_regnums_x86_64[] = {
112 lldb_ymm0_x86_64, lldb_ymm1_x86_64, lldb_ymm2_x86_64, lldb_ymm3_x86_64,
113 lldb_ymm4_x86_64, lldb_ymm5_x86_64, lldb_ymm6_x86_64, lldb_ymm7_x86_64,
114 lldb_ymm8_x86_64, lldb_ymm9_x86_64, lldb_ymm10_x86_64, lldb_ymm11_x86_64,
115 lldb_ymm12_x86_64, lldb_ymm13_x86_64, lldb_ymm14_x86_64, lldb_ymm15_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_xstate_regnums_x86_64) / sizeof(g_xstate_regnums_x86_64[0])) -
123 1 ==
124 k_num_avx_registers_x86_64 + k_num_mpx_registers_x86_64,
125 "g_xstate_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 // Number of register sets provided by this context.
139 enum { k_num_register_sets = 4 };
140
141 // Register sets for x86 64-bit.
142 static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = {
143 {"General Purpose Registers", "gpr", k_num_gpr_registers_x86_64,
144 g_gpr_regnums_x86_64},
145 {"Floating Point Registers", "fpu", k_num_fpr_registers_x86_64,
146 g_fpu_regnums_x86_64},
147 {"Extended State Registers", "xstate",
148 k_num_avx_registers_x86_64 + k_num_mpx_registers_x86_64,
149 g_xstate_regnums_x86_64},
150 {"Debug Registers", "dbr", k_num_dbr_registers_x86_64,
151 g_dbr_regnums_x86_64},
152 };
153
154 #define REG_CONTEXT_SIZE (GetRegisterInfoInterface().GetGPRSize())
155 } // namespace
156
157 NativeRegisterContextNetBSD *
CreateHostNativeRegisterContextNetBSD(const ArchSpec & target_arch,NativeThreadProtocol & native_thread)158 NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD(
159 const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
160 return new NativeRegisterContextNetBSD_x86_64(target_arch, native_thread);
161 }
162
163 // NativeRegisterContextNetBSD_x86_64 members.
164
165 static RegisterInfoInterface *
CreateRegisterInfoInterface(const ArchSpec & target_arch)166 CreateRegisterInfoInterface(const ArchSpec &target_arch) {
167 assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) &&
168 "Register setting path assumes this is a 64-bit host");
169 // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the x86_64
170 // register context.
171 return new RegisterContextNetBSD_x86_64(target_arch);
172 }
173
NativeRegisterContextNetBSD_x86_64(const ArchSpec & target_arch,NativeThreadProtocol & native_thread)174 NativeRegisterContextNetBSD_x86_64::NativeRegisterContextNetBSD_x86_64(
175 const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
176 : NativeRegisterContextNetBSD(native_thread,
177 CreateRegisterInfoInterface(target_arch)),
178 m_gpr_x86_64(), m_fpr_x86_64(), m_dbr_x86_64() {}
179
180 // CONSIDER after local and llgs debugging are merged, register set support can
181 // be moved into a base x86-64 class with IsRegisterSetAvailable made virtual.
GetRegisterSetCount() const182 uint32_t NativeRegisterContextNetBSD_x86_64::GetRegisterSetCount() const {
183 uint32_t sets = 0;
184 for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) {
185 if (GetSetForNativeRegNum(set_index) != -1)
186 ++sets;
187 }
188
189 return sets;
190 }
191
192 const RegisterSet *
GetRegisterSet(uint32_t set_index) const193 NativeRegisterContextNetBSD_x86_64::GetRegisterSet(uint32_t set_index) const {
194 switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
195 case llvm::Triple::x86_64:
196 return &g_reg_sets_x86_64[set_index];
197 default:
198 assert(false && "Unhandled target architecture.");
199 return nullptr;
200 }
201
202 return nullptr;
203 }
204
GetSetForNativeRegNum(int reg_num) const205 int NativeRegisterContextNetBSD_x86_64::GetSetForNativeRegNum(
206 int reg_num) const {
207 if (reg_num <= k_last_gpr_x86_64)
208 return GPRegSet;
209 else if (reg_num <= k_last_fpr_x86_64)
210 return FPRegSet;
211 else if (reg_num <= k_last_avx_x86_64)
212 return XStateRegSet; // AVX
213 else if (reg_num <= k_last_mpxr_x86_64)
214 return -1; // MPXR
215 else if (reg_num <= k_last_mpxc_x86_64)
216 return -1; // MPXC
217 else if (reg_num <= lldb_dr7_x86_64)
218 return DBRegSet; // DBR
219 else
220 return -1;
221 }
222
ReadRegisterSet(uint32_t set)223 Status NativeRegisterContextNetBSD_x86_64::ReadRegisterSet(uint32_t set) {
224 switch (set) {
225 case GPRegSet:
226 return DoRegisterSet(PT_GETREGS, &m_gpr_x86_64);
227 case FPRegSet:
228 return DoRegisterSet(PT_GETFPREGS, &m_fpr_x86_64);
229 case DBRegSet:
230 return DoRegisterSet(PT_GETDBREGS, &m_dbr_x86_64);
231 case XStateRegSet:
232 #ifdef HAVE_XSTATE
233 {
234 struct iovec iov = {&m_xstate_x86_64, sizeof(m_xstate_x86_64)};
235 return DoRegisterSet(PT_GETXSTATE, &iov);
236 }
237 #else
238 return Status("XState is not supported by the kernel");
239 #endif
240 }
241 llvm_unreachable("NativeRegisterContextNetBSD_x86_64::ReadRegisterSet");
242 }
243
WriteRegisterSet(uint32_t set)244 Status NativeRegisterContextNetBSD_x86_64::WriteRegisterSet(uint32_t set) {
245 switch (set) {
246 case GPRegSet:
247 return DoRegisterSet(PT_SETREGS, &m_gpr_x86_64);
248 case FPRegSet:
249 return DoRegisterSet(PT_SETFPREGS, &m_fpr_x86_64);
250 case DBRegSet:
251 return DoRegisterSet(PT_SETDBREGS, &m_dbr_x86_64);
252 case XStateRegSet:
253 #ifdef HAVE_XSTATE
254 {
255 struct iovec iov = {&m_xstate_x86_64, sizeof(m_xstate_x86_64)};
256 return DoRegisterSet(PT_SETXSTATE, &iov);
257 }
258 #else
259 return Status("XState is not supported by the kernel");
260 #endif
261 }
262 llvm_unreachable("NativeRegisterContextNetBSD_x86_64::WriteRegisterSet");
263 }
264
265 Status
ReadRegister(const RegisterInfo * reg_info,RegisterValue & reg_value)266 NativeRegisterContextNetBSD_x86_64::ReadRegister(const RegisterInfo *reg_info,
267 RegisterValue ®_value) {
268 Status error;
269
270 if (!reg_info) {
271 error.SetErrorString("reg_info NULL");
272 return error;
273 }
274
275 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
276 if (reg == LLDB_INVALID_REGNUM) {
277 // This is likely an internal register for lldb use only and should not be
278 // directly queried.
279 error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
280 "register, cannot read directly",
281 reg_info->name);
282 return error;
283 }
284
285 int set = GetSetForNativeRegNum(reg);
286 if (set == -1) {
287 // This is likely an internal register for lldb use only and should not be
288 // directly queried.
289 error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
290 reg_info->name);
291 return error;
292 }
293
294 error = ReadRegisterSet(set);
295 if (error.Fail())
296 return error;
297
298 switch (reg) {
299 case lldb_rax_x86_64:
300 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RAX];
301 break;
302 case lldb_rbx_x86_64:
303 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RBX];
304 break;
305 case lldb_rcx_x86_64:
306 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RCX];
307 break;
308 case lldb_rdx_x86_64:
309 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RDX];
310 break;
311 case lldb_rdi_x86_64:
312 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RDI];
313 break;
314 case lldb_rsi_x86_64:
315 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RSI];
316 break;
317 case lldb_rbp_x86_64:
318 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RBP];
319 break;
320 case lldb_rsp_x86_64:
321 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RSP];
322 break;
323 case lldb_r8_x86_64:
324 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R8];
325 break;
326 case lldb_r9_x86_64:
327 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R9];
328 break;
329 case lldb_r10_x86_64:
330 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R10];
331 break;
332 case lldb_r11_x86_64:
333 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R11];
334 break;
335 case lldb_r12_x86_64:
336 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R12];
337 break;
338 case lldb_r13_x86_64:
339 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R13];
340 break;
341 case lldb_r14_x86_64:
342 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R14];
343 break;
344 case lldb_r15_x86_64:
345 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R15];
346 break;
347 case lldb_rip_x86_64:
348 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RIP];
349 break;
350 case lldb_rflags_x86_64:
351 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RFLAGS];
352 break;
353 case lldb_cs_x86_64:
354 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_CS];
355 break;
356 case lldb_fs_x86_64:
357 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_FS];
358 break;
359 case lldb_gs_x86_64:
360 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_GS];
361 break;
362 case lldb_ss_x86_64:
363 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_SS];
364 break;
365 case lldb_ds_x86_64:
366 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_DS];
367 break;
368 case lldb_es_x86_64:
369 reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_ES];
370 break;
371 case lldb_fctrl_x86_64:
372 reg_value = (uint16_t)m_fpr_x86_64.fxstate.fx_cw;
373 break;
374 case lldb_fstat_x86_64:
375 reg_value = (uint16_t)m_fpr_x86_64.fxstate.fx_sw;
376 break;
377 case lldb_ftag_x86_64:
378 reg_value = (uint8_t)m_fpr_x86_64.fxstate.fx_tw;
379 break;
380 case lldb_fop_x86_64:
381 reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_opcode;
382 break;
383 case lldb_fiseg_x86_64:
384 reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_ip.fa_64;
385 break;
386 case lldb_fioff_x86_64:
387 reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_ip.fa_32.fa_off;
388 break;
389 case lldb_foseg_x86_64:
390 reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_dp.fa_64;
391 break;
392 case lldb_fooff_x86_64:
393 reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_dp.fa_32.fa_off;
394 break;
395 case lldb_mxcsr_x86_64:
396 reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_mxcsr;
397 break;
398 case lldb_mxcsrmask_x86_64:
399 reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_mxcsr_mask;
400 break;
401 case lldb_st0_x86_64:
402 case lldb_st1_x86_64:
403 case lldb_st2_x86_64:
404 case lldb_st3_x86_64:
405 case lldb_st4_x86_64:
406 case lldb_st5_x86_64:
407 case lldb_st6_x86_64:
408 case lldb_st7_x86_64:
409 reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_st0_x86_64],
410 reg_info->byte_size, endian::InlHostByteOrder());
411 break;
412 case lldb_mm0_x86_64:
413 case lldb_mm1_x86_64:
414 case lldb_mm2_x86_64:
415 case lldb_mm3_x86_64:
416 case lldb_mm4_x86_64:
417 case lldb_mm5_x86_64:
418 case lldb_mm6_x86_64:
419 case lldb_mm7_x86_64:
420 reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_mm0_x86_64],
421 reg_info->byte_size, endian::InlHostByteOrder());
422 break;
423 case lldb_xmm0_x86_64:
424 case lldb_xmm1_x86_64:
425 case lldb_xmm2_x86_64:
426 case lldb_xmm3_x86_64:
427 case lldb_xmm4_x86_64:
428 case lldb_xmm5_x86_64:
429 case lldb_xmm6_x86_64:
430 case lldb_xmm7_x86_64:
431 case lldb_xmm8_x86_64:
432 case lldb_xmm9_x86_64:
433 case lldb_xmm10_x86_64:
434 case lldb_xmm11_x86_64:
435 case lldb_xmm12_x86_64:
436 case lldb_xmm13_x86_64:
437 case lldb_xmm14_x86_64:
438 case lldb_xmm15_x86_64:
439 reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_xmm0_x86_64],
440 reg_info->byte_size, endian::InlHostByteOrder());
441 break;
442 case lldb_ymm0_x86_64:
443 case lldb_ymm1_x86_64:
444 case lldb_ymm2_x86_64:
445 case lldb_ymm3_x86_64:
446 case lldb_ymm4_x86_64:
447 case lldb_ymm5_x86_64:
448 case lldb_ymm6_x86_64:
449 case lldb_ymm7_x86_64:
450 case lldb_ymm8_x86_64:
451 case lldb_ymm9_x86_64:
452 case lldb_ymm10_x86_64:
453 case lldb_ymm11_x86_64:
454 case lldb_ymm12_x86_64:
455 case lldb_ymm13_x86_64:
456 case lldb_ymm14_x86_64:
457 case lldb_ymm15_x86_64:
458 #ifdef HAVE_XSTATE
459 if (!(m_xstate_x86_64.xs_rfbm & XCR0_SSE) ||
460 !(m_xstate_x86_64.xs_rfbm & XCR0_YMM_Hi128)) {
461 error.SetErrorStringWithFormat("register \"%s\" not supported by CPU/kernel",
462 reg_info->name);
463 } else {
464 uint32_t reg_index = reg - lldb_ymm0_x86_64;
465 YMMReg ymm = XStateToYMM(
466 m_xstate_x86_64.xs_fxsave.fx_xmm[reg_index].xmm_bytes,
467 m_xstate_x86_64.xs_ymm_hi128.xs_ymm[reg_index].ymm_bytes);
468 reg_value.SetBytes(ymm.bytes, reg_info->byte_size,
469 endian::InlHostByteOrder());
470 }
471 #else
472 error.SetErrorString("XState queries not supported by the kernel");
473 #endif
474 break;
475 case lldb_dr0_x86_64:
476 case lldb_dr1_x86_64:
477 case lldb_dr2_x86_64:
478 case lldb_dr3_x86_64:
479 case lldb_dr4_x86_64:
480 case lldb_dr5_x86_64:
481 case lldb_dr6_x86_64:
482 case lldb_dr7_x86_64:
483 reg_value = (uint64_t)m_dbr_x86_64.dr[reg - lldb_dr0_x86_64];
484 break;
485 }
486
487 return error;
488 }
489
WriteRegister(const RegisterInfo * reg_info,const RegisterValue & reg_value)490 Status NativeRegisterContextNetBSD_x86_64::WriteRegister(
491 const RegisterInfo *reg_info, const RegisterValue ®_value) {
492
493 Status error;
494
495 if (!reg_info) {
496 error.SetErrorString("reg_info NULL");
497 return error;
498 }
499
500 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
501 if (reg == LLDB_INVALID_REGNUM) {
502 // This is likely an internal register for lldb use only and should not be
503 // directly queried.
504 error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
505 "register, cannot read directly",
506 reg_info->name);
507 return error;
508 }
509
510 int set = GetSetForNativeRegNum(reg);
511 if (set == -1) {
512 // This is likely an internal register for lldb use only and should not be
513 // directly queried.
514 error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
515 reg_info->name);
516 return error;
517 }
518
519 error = ReadRegisterSet(set);
520 if (error.Fail())
521 return error;
522
523 switch (reg) {
524 case lldb_rax_x86_64:
525 m_gpr_x86_64.regs[_REG_RAX] = reg_value.GetAsUInt64();
526 break;
527 case lldb_rbx_x86_64:
528 m_gpr_x86_64.regs[_REG_RBX] = reg_value.GetAsUInt64();
529 break;
530 case lldb_rcx_x86_64:
531 m_gpr_x86_64.regs[_REG_RCX] = reg_value.GetAsUInt64();
532 break;
533 case lldb_rdx_x86_64:
534 m_gpr_x86_64.regs[_REG_RDX] = reg_value.GetAsUInt64();
535 break;
536 case lldb_rdi_x86_64:
537 m_gpr_x86_64.regs[_REG_RDI] = reg_value.GetAsUInt64();
538 break;
539 case lldb_rsi_x86_64:
540 m_gpr_x86_64.regs[_REG_RSI] = reg_value.GetAsUInt64();
541 break;
542 case lldb_rbp_x86_64:
543 m_gpr_x86_64.regs[_REG_RBP] = reg_value.GetAsUInt64();
544 break;
545 case lldb_rsp_x86_64:
546 m_gpr_x86_64.regs[_REG_RSP] = reg_value.GetAsUInt64();
547 break;
548 case lldb_r8_x86_64:
549 m_gpr_x86_64.regs[_REG_R8] = reg_value.GetAsUInt64();
550 break;
551 case lldb_r9_x86_64:
552 m_gpr_x86_64.regs[_REG_R9] = reg_value.GetAsUInt64();
553 break;
554 case lldb_r10_x86_64:
555 m_gpr_x86_64.regs[_REG_R10] = reg_value.GetAsUInt64();
556 break;
557 case lldb_r11_x86_64:
558 m_gpr_x86_64.regs[_REG_R11] = reg_value.GetAsUInt64();
559 break;
560 case lldb_r12_x86_64:
561 m_gpr_x86_64.regs[_REG_R12] = reg_value.GetAsUInt64();
562 break;
563 case lldb_r13_x86_64:
564 m_gpr_x86_64.regs[_REG_R13] = reg_value.GetAsUInt64();
565 break;
566 case lldb_r14_x86_64:
567 m_gpr_x86_64.regs[_REG_R14] = reg_value.GetAsUInt64();
568 break;
569 case lldb_r15_x86_64:
570 m_gpr_x86_64.regs[_REG_R15] = reg_value.GetAsUInt64();
571 break;
572 case lldb_rip_x86_64:
573 m_gpr_x86_64.regs[_REG_RIP] = reg_value.GetAsUInt64();
574 break;
575 case lldb_rflags_x86_64:
576 m_gpr_x86_64.regs[_REG_RFLAGS] = reg_value.GetAsUInt64();
577 break;
578 case lldb_cs_x86_64:
579 m_gpr_x86_64.regs[_REG_CS] = reg_value.GetAsUInt64();
580 break;
581 case lldb_fs_x86_64:
582 m_gpr_x86_64.regs[_REG_FS] = reg_value.GetAsUInt64();
583 break;
584 case lldb_gs_x86_64:
585 m_gpr_x86_64.regs[_REG_GS] = reg_value.GetAsUInt64();
586 break;
587 case lldb_ss_x86_64:
588 m_gpr_x86_64.regs[_REG_SS] = reg_value.GetAsUInt64();
589 break;
590 case lldb_ds_x86_64:
591 m_gpr_x86_64.regs[_REG_DS] = reg_value.GetAsUInt64();
592 break;
593 case lldb_es_x86_64:
594 m_gpr_x86_64.regs[_REG_ES] = reg_value.GetAsUInt64();
595 break;
596 case lldb_fctrl_x86_64:
597 m_fpr_x86_64.fxstate.fx_cw = reg_value.GetAsUInt16();
598 break;
599 case lldb_fstat_x86_64:
600 m_fpr_x86_64.fxstate.fx_sw = reg_value.GetAsUInt16();
601 break;
602 case lldb_ftag_x86_64:
603 m_fpr_x86_64.fxstate.fx_tw = reg_value.GetAsUInt8();
604 break;
605 case lldb_fop_x86_64:
606 m_fpr_x86_64.fxstate.fx_opcode = reg_value.GetAsUInt16();
607 break;
608 case lldb_fiseg_x86_64:
609 m_fpr_x86_64.fxstate.fx_ip.fa_64 = reg_value.GetAsUInt64();
610 break;
611 case lldb_fioff_x86_64:
612 m_fpr_x86_64.fxstate.fx_ip.fa_32.fa_off = reg_value.GetAsUInt32();
613 break;
614 case lldb_foseg_x86_64:
615 m_fpr_x86_64.fxstate.fx_dp.fa_64 = reg_value.GetAsUInt64();
616 break;
617 case lldb_fooff_x86_64:
618 m_fpr_x86_64.fxstate.fx_dp.fa_32.fa_off = reg_value.GetAsUInt32();
619 break;
620 case lldb_mxcsr_x86_64:
621 m_fpr_x86_64.fxstate.fx_mxcsr = reg_value.GetAsUInt32();
622 break;
623 case lldb_mxcsrmask_x86_64:
624 m_fpr_x86_64.fxstate.fx_mxcsr_mask = reg_value.GetAsUInt32();
625 break;
626 case lldb_st0_x86_64:
627 case lldb_st1_x86_64:
628 case lldb_st2_x86_64:
629 case lldb_st3_x86_64:
630 case lldb_st4_x86_64:
631 case lldb_st5_x86_64:
632 case lldb_st6_x86_64:
633 case lldb_st7_x86_64:
634 ::memcpy(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_st0_x86_64],
635 reg_value.GetBytes(), reg_value.GetByteSize());
636 break;
637 case lldb_mm0_x86_64:
638 case lldb_mm1_x86_64:
639 case lldb_mm2_x86_64:
640 case lldb_mm3_x86_64:
641 case lldb_mm4_x86_64:
642 case lldb_mm5_x86_64:
643 case lldb_mm6_x86_64:
644 case lldb_mm7_x86_64:
645 ::memcpy(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_mm0_x86_64],
646 reg_value.GetBytes(), reg_value.GetByteSize());
647 break;
648 case lldb_xmm0_x86_64:
649 case lldb_xmm1_x86_64:
650 case lldb_xmm2_x86_64:
651 case lldb_xmm3_x86_64:
652 case lldb_xmm4_x86_64:
653 case lldb_xmm5_x86_64:
654 case lldb_xmm6_x86_64:
655 case lldb_xmm7_x86_64:
656 case lldb_xmm8_x86_64:
657 case lldb_xmm9_x86_64:
658 case lldb_xmm10_x86_64:
659 case lldb_xmm11_x86_64:
660 case lldb_xmm12_x86_64:
661 case lldb_xmm13_x86_64:
662 case lldb_xmm14_x86_64:
663 case lldb_xmm15_x86_64:
664 ::memcpy(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_xmm0_x86_64],
665 reg_value.GetBytes(), reg_value.GetByteSize());
666 break;
667 case lldb_ymm0_x86_64:
668 case lldb_ymm1_x86_64:
669 case lldb_ymm2_x86_64:
670 case lldb_ymm3_x86_64:
671 case lldb_ymm4_x86_64:
672 case lldb_ymm5_x86_64:
673 case lldb_ymm6_x86_64:
674 case lldb_ymm7_x86_64:
675 case lldb_ymm8_x86_64:
676 case lldb_ymm9_x86_64:
677 case lldb_ymm10_x86_64:
678 case lldb_ymm11_x86_64:
679 case lldb_ymm12_x86_64:
680 case lldb_ymm13_x86_64:
681 case lldb_ymm14_x86_64:
682 case lldb_ymm15_x86_64:
683 #ifdef HAVE_XSTATE
684 if (!(m_xstate_x86_64.xs_rfbm & XCR0_SSE) ||
685 !(m_xstate_x86_64.xs_rfbm & XCR0_YMM_Hi128)) {
686 error.SetErrorStringWithFormat("register \"%s\" not supported by CPU/kernel",
687 reg_info->name);
688 } else {
689 uint32_t reg_index = reg - lldb_ymm0_x86_64;
690 YMMReg ymm;
691 ::memcpy(ymm.bytes, reg_value.GetBytes(), reg_value.GetByteSize());
692 YMMToXState(ymm,
693 m_xstate_x86_64.xs_fxsave.fx_xmm[reg_index].xmm_bytes,
694 m_xstate_x86_64.xs_ymm_hi128.xs_ymm[reg_index].ymm_bytes);
695 }
696 #else
697 error.SetErrorString("XState not supported by the kernel");
698 #endif
699 break;
700 case lldb_dr0_x86_64:
701 case lldb_dr1_x86_64:
702 case lldb_dr2_x86_64:
703 case lldb_dr3_x86_64:
704 case lldb_dr4_x86_64:
705 case lldb_dr5_x86_64:
706 case lldb_dr6_x86_64:
707 case lldb_dr7_x86_64:
708 m_dbr_x86_64.dr[reg - lldb_dr0_x86_64] = reg_value.GetAsUInt64();
709 break;
710 }
711
712 return WriteRegisterSet(set);
713 }
714
ReadAllRegisterValues(lldb::DataBufferSP & data_sp)715 Status NativeRegisterContextNetBSD_x86_64::ReadAllRegisterValues(
716 lldb::DataBufferSP &data_sp) {
717 Status error;
718
719 data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
720 error = ReadRegisterSet(GPRegSet);
721 if (error.Fail())
722 return error;
723
724 uint8_t *dst = data_sp->GetBytes();
725 ::memcpy(dst, &m_gpr_x86_64, GetRegisterInfoInterface().GetGPRSize());
726 dst += GetRegisterInfoInterface().GetGPRSize();
727
728 return error;
729 }
730
WriteAllRegisterValues(const lldb::DataBufferSP & data_sp)731 Status NativeRegisterContextNetBSD_x86_64::WriteAllRegisterValues(
732 const lldb::DataBufferSP &data_sp) {
733 Status error;
734
735 if (!data_sp) {
736 error.SetErrorStringWithFormat(
737 "NativeRegisterContextNetBSD_x86_64::%s invalid data_sp provided",
738 __FUNCTION__);
739 return error;
740 }
741
742 if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
743 error.SetErrorStringWithFormat(
744 "NativeRegisterContextNetBSD_x86_64::%s data_sp contained mismatched "
745 "data size, expected %" PRIu64 ", actual %" PRIu64,
746 __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
747 return error;
748 }
749
750 uint8_t *src = data_sp->GetBytes();
751 if (src == nullptr) {
752 error.SetErrorStringWithFormat("NativeRegisterContextNetBSD_x86_64::%s "
753 "DataBuffer::GetBytes() returned a null "
754 "pointer",
755 __FUNCTION__);
756 return error;
757 }
758 ::memcpy(&m_gpr_x86_64, src, GetRegisterInfoInterface().GetGPRSize());
759
760 error = WriteRegisterSet(GPRegSet);
761 if (error.Fail())
762 return error;
763 src += GetRegisterInfoInterface().GetGPRSize();
764
765 return error;
766 }
767
IsWatchpointHit(uint32_t wp_index,bool & is_hit)768 Status NativeRegisterContextNetBSD_x86_64::IsWatchpointHit(uint32_t wp_index,
769 bool &is_hit) {
770 if (wp_index >= NumSupportedHardwareWatchpoints())
771 return Status("Watchpoint index out of range");
772
773 RegisterValue reg_value;
774 const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(lldb_dr6_x86_64);
775 Status error = ReadRegister(reg_info, reg_value);
776 if (error.Fail()) {
777 is_hit = false;
778 return error;
779 }
780
781 uint64_t status_bits = reg_value.GetAsUInt64();
782
783 is_hit = status_bits & (1 << wp_index);
784
785 return error;
786 }
787
GetWatchpointHitIndex(uint32_t & wp_index,lldb::addr_t trap_addr)788 Status NativeRegisterContextNetBSD_x86_64::GetWatchpointHitIndex(
789 uint32_t &wp_index, lldb::addr_t trap_addr) {
790 uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
791 for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) {
792 bool is_hit;
793 Status error = IsWatchpointHit(wp_index, is_hit);
794 if (error.Fail()) {
795 wp_index = LLDB_INVALID_INDEX32;
796 return error;
797 } else if (is_hit) {
798 return error;
799 }
800 }
801 wp_index = LLDB_INVALID_INDEX32;
802 return Status();
803 }
804
IsWatchpointVacant(uint32_t wp_index,bool & is_vacant)805 Status NativeRegisterContextNetBSD_x86_64::IsWatchpointVacant(uint32_t wp_index,
806 bool &is_vacant) {
807 if (wp_index >= NumSupportedHardwareWatchpoints())
808 return Status("Watchpoint index out of range");
809
810 RegisterValue reg_value;
811 const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(lldb_dr7_x86_64);
812 Status error = ReadRegister(reg_info, reg_value);
813 if (error.Fail()) {
814 is_vacant = false;
815 return error;
816 }
817
818 uint64_t control_bits = reg_value.GetAsUInt64();
819
820 is_vacant = !(control_bits & (1 << (2 * wp_index + 1)));
821
822 return error;
823 }
824
SetHardwareWatchpointWithIndex(lldb::addr_t addr,size_t size,uint32_t watch_flags,uint32_t wp_index)825 Status NativeRegisterContextNetBSD_x86_64::SetHardwareWatchpointWithIndex(
826 lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
827
828 if (wp_index >= NumSupportedHardwareWatchpoints())
829 return Status("Watchpoint index out of range");
830
831 // Read only watchpoints aren't supported on x86_64. Fall back to read/write
832 // waitchpoints instead.
833 // TODO: Add logic to detect when a write happens and ignore that watchpoint
834 // hit.
835 if (watch_flags == 0x2)
836 watch_flags = 0x3;
837
838 if (watch_flags != 0x1 && watch_flags != 0x3)
839 return Status("Invalid read/write bits for watchpoint");
840
841 if (size != 1 && size != 2 && size != 4 && size != 8)
842 return Status("Invalid size for watchpoint");
843
844 bool is_vacant;
845 Status error = IsWatchpointVacant(wp_index, is_vacant);
846 if (error.Fail())
847 return error;
848 if (!is_vacant)
849 return Status("Watchpoint index not vacant");
850
851 const RegisterInfo *const reg_info_dr7 =
852 GetRegisterInfoAtIndex(lldb_dr7_x86_64);
853 RegisterValue dr7_value;
854 error = ReadRegister(reg_info_dr7, dr7_value);
855 if (error.Fail())
856 return error;
857
858 // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7
859 uint64_t enable_bit = 1 << (2 * wp_index + 1);
860
861 // set bits 16-17, 20-21, 24-25, or 28-29
862 // with 0b01 for write, and 0b11 for read/write
863 uint64_t rw_bits = watch_flags << (16 + 4 * wp_index);
864
865 // set bits 18-19, 22-23, 26-27, or 30-31
866 // with 0b00, 0b01, 0b10, or 0b11
867 // for 1, 2, 8 (if supported), or 4 bytes, respectively
868 uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index);
869
870 uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
871
872 uint64_t control_bits = dr7_value.GetAsUInt64() & ~bit_mask;
873
874 control_bits |= enable_bit | rw_bits | size_bits;
875
876 const RegisterInfo *const reg_info_drN =
877 GetRegisterInfoAtIndex(lldb_dr0_x86_64 + wp_index);
878 RegisterValue drN_value;
879 error = ReadRegister(reg_info_drN, drN_value);
880 if (error.Fail())
881 return error;
882
883 // clear dr6 if address or bits changed (i.e. we're not reenabling the same
884 // watchpoint)
885 if (drN_value.GetAsUInt64() != addr ||
886 (dr7_value.GetAsUInt64() & bit_mask) != (rw_bits | size_bits)) {
887 ClearWatchpointHit(wp_index);
888
889 error = WriteRegister(reg_info_drN, RegisterValue(addr));
890 if (error.Fail())
891 return error;
892 }
893
894 error = WriteRegister(reg_info_dr7, RegisterValue(control_bits));
895 if (error.Fail())
896 return error;
897
898 error.Clear();
899 return error;
900 }
901
ClearHardwareWatchpoint(uint32_t wp_index)902 bool NativeRegisterContextNetBSD_x86_64::ClearHardwareWatchpoint(
903 uint32_t wp_index) {
904 if (wp_index >= NumSupportedHardwareWatchpoints())
905 return false;
906
907 // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0-1, 2-3, 4-5
908 // or 6-7 of the debug control register (DR7)
909 const RegisterInfo *const reg_info_dr7 =
910 GetRegisterInfoAtIndex(lldb_dr7_x86_64);
911 RegisterValue reg_value;
912 Status error = ReadRegister(reg_info_dr7, reg_value);
913 if (error.Fail())
914 return false;
915 uint64_t bit_mask = 0x3 << (2 * wp_index);
916 uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
917
918 return WriteRegister(reg_info_dr7, RegisterValue(control_bits)).Success();
919 }
920
ClearWatchpointHit(uint32_t wp_index)921 Status NativeRegisterContextNetBSD_x86_64::ClearWatchpointHit(uint32_t wp_index) {
922 if (wp_index >= NumSupportedHardwareWatchpoints())
923 return Status("Watchpoint index out of range");
924
925 // for watchpoints 0, 1, 2, or 3, respectively, check bits 0, 1, 2, or 3 of
926 // the debug status register (DR6)
927 const RegisterInfo *const reg_info_dr6 =
928 GetRegisterInfoAtIndex(lldb_dr6_x86_64);
929 RegisterValue reg_value;
930 Status error = ReadRegister(reg_info_dr6, reg_value);
931 if (error.Fail())
932 return error;
933
934 uint64_t bit_mask = 1 << wp_index;
935 uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
936 return WriteRegister(reg_info_dr6, RegisterValue(status_bits));
937 }
938
ClearAllHardwareWatchpoints()939 Status NativeRegisterContextNetBSD_x86_64::ClearAllHardwareWatchpoints() {
940 RegisterValue reg_value;
941
942 // clear bits {0-4} of the debug status register (DR6)
943 const RegisterInfo *const reg_info_dr6 =
944 GetRegisterInfoAtIndex(lldb_dr6_x86_64);
945 Status error = ReadRegister(reg_info_dr6, reg_value);
946 if (error.Fail())
947 return error;
948 uint64_t bit_mask = 0xF;
949 uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
950 error = WriteRegister(reg_info_dr6, RegisterValue(status_bits));
951 if (error.Fail())
952 return error;
953
954 // clear bits {0-7,16-31} of the debug control register (DR7)
955 const RegisterInfo *const reg_info_dr7 =
956 GetRegisterInfoAtIndex(lldb_dr7_x86_64);
957 error = ReadRegister(reg_info_dr7, reg_value);
958 if (error.Fail())
959 return error;
960 bit_mask = 0xFF | (0xFFFF << 16);
961 uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
962 return WriteRegister(reg_info_dr7, RegisterValue(control_bits));
963 }
964
SetHardwareWatchpoint(lldb::addr_t addr,size_t size,uint32_t watch_flags)965 uint32_t NativeRegisterContextNetBSD_x86_64::SetHardwareWatchpoint(
966 lldb::addr_t addr, size_t size, uint32_t watch_flags) {
967 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
968 const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
969 for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) {
970 bool is_vacant;
971 Status error = IsWatchpointVacant(wp_index, is_vacant);
972 if (is_vacant) {
973 error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index);
974 if (error.Success())
975 return wp_index;
976 }
977 if (error.Fail() && log) {
978 LLDB_LOGF(log, "NativeRegisterContextNetBSD_x86_64::%s Error: %s",
979 __FUNCTION__, error.AsCString());
980 }
981 }
982 return LLDB_INVALID_INDEX32;
983 }
984
985 lldb::addr_t
GetWatchpointAddress(uint32_t wp_index)986 NativeRegisterContextNetBSD_x86_64::GetWatchpointAddress(uint32_t wp_index) {
987 if (wp_index >= NumSupportedHardwareWatchpoints())
988 return LLDB_INVALID_ADDRESS;
989 RegisterValue reg_value;
990 const RegisterInfo *const reg_info_drN =
991 GetRegisterInfoAtIndex(lldb_dr0_x86_64 + wp_index);
992 if (ReadRegister(reg_info_drN, reg_value).Fail())
993 return LLDB_INVALID_ADDRESS;
994 return reg_value.GetAsUInt64();
995 }
996
NumSupportedHardwareWatchpoints()997 uint32_t NativeRegisterContextNetBSD_x86_64::NumSupportedHardwareWatchpoints() {
998 // Available debug address registers: dr0, dr1, dr2, dr3
999 return 4;
1000 }
1001
CopyHardwareWatchpointsFrom(NativeRegisterContextNetBSD & source)1002 Status NativeRegisterContextNetBSD_x86_64::CopyHardwareWatchpointsFrom(
1003 NativeRegisterContextNetBSD &source) {
1004 auto &r_source = static_cast<NativeRegisterContextNetBSD_x86_64&>(source);
1005 Status res = r_source.ReadRegisterSet(DBRegSet);
1006 if (!res.Fail()) {
1007 // copy dbregs only if any watchpoints were set
1008 if ((r_source.m_dbr_x86_64.dr[7] & 0xFF) == 0)
1009 return res;
1010
1011 m_dbr_x86_64 = r_source.m_dbr_x86_64;
1012 res = WriteRegisterSet(DBRegSet);
1013 }
1014 return res;
1015 }
1016
1017 #endif // defined(__x86_64__)
1018