Author: Brendan Shanks Date: 2024-09-18T22:57:01-07:00 New Revision: 7281e0cb3bbcce396aab8b3ea0967d7a17cd287a
URL: https://github.com/llvm/llvm-project/commit/7281e0cb3bbcce396aab8b3ea0967d7a17cd287a DIFF: https://github.com/llvm/llvm-project/commit/7281e0cb3bbcce396aab8b3ea0967d7a17cd287a.diff LOG: [lldb] [debugserver] Use "full" x86_64 GPR state when available. (#108663) macOS 10.15 added a "full" x86_64 GPR thread state flavor, equivalent to the normal one but with DS, ES, SS, and GSbase added. This flavor can only be used with processes that install a custom LDT (functionality that was also added in 10.15 and is used by apps like Wine to execute 32-bit code). Along with allowing DS, ES, SS, and GSbase to be viewed/modified, using the full flavor is necessary when debugging a thread executing 32-bit code. If thread_set_state() is used with the regular thread state flavor, the kernel resets CS to the 64-bit code segment (see [set_thread_state64()](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/i386/pcb.c#L723), which makes debugging impossible. There's no way to detect whether the full flavor is available, try to use it and fall back to the regular one if it's not available. A downside is that this patch exposes the DS, ES, SS, and GSbase registers for all x86_64 processes, even though they are not populated unless the full thread state is available. I'm not sure if there's a way to tell LLDB that a register is unavailable. The classic GDB `g` command [allows returning `x`](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#Packets) to denote unavailable registers, but it seems like the debug server uses newer commands like `jThreadsInfo` and I'm not sure if those have the same support. Fixes #57591 (also filed as Apple FB11464104) @jasonmolenda Added: Modified: lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h Removed: ################################################################################ diff --git a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp index 5a62e2a8d12e2c..3b3f1f02a2851f 100644 --- a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp +++ b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp @@ -182,22 +182,39 @@ kern_return_t DNBArchImplX86_64::GetGPRState(bool force) { m_state.context.gpr.__gs = ('g' << 8) + 's'; m_state.SetError(e_regSetGPR, Read, 0); #else - mach_msg_type_number_t count = e_regSetWordSizeGPR; + mach_msg_type_number_t count = e_regSetWordSizeGPRFull; + int flavor = __x86_64_THREAD_FULL_STATE; m_state.SetError( e_regSetGPR, Read, - ::thread_get_state(m_thread->MachPortNumber(), __x86_64_THREAD_STATE, + ::thread_get_state(m_thread->MachPortNumber(), flavor, (thread_state_t)&m_state.context.gpr, &count)); + + if (!m_state.GetError(e_regSetGPR, Read)) { + m_state.hasFullGPRState = true; + } else { + m_state.hasFullGPRState = false; + count = e_regSetWordSizeGPR; + flavor = __x86_64_THREAD_STATE; + m_state.SetError( + e_regSetGPR, Read, + ::thread_get_state(m_thread->MachPortNumber(), flavor, + (thread_state_t)&m_state.context.gpr, &count)); + } DNBLogThreadedIf( LOG_THREAD, - "::thread_get_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x" + "::thread_get_state (0x%4.4x, %u (%s), &gpr, %u) => 0x%8.8x" "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx" "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx" "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx" "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx" "\n\trip = %16.16llx" - "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx", - m_thread->MachPortNumber(), x86_THREAD_STATE64, - x86_THREAD_STATE64_COUNT, m_state.GetError(e_regSetGPR, Read), + "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx" + "\n\t ds = %16.16llx es = %16.16llx ss = %16.16llx gsB = %16.16llx", + m_thread->MachPortNumber(), flavor, + m_state.hasFullGPRState ? "full" : "non-full", + m_state.hasFullGPRState ? e_regSetWordSizeGPRFull + : e_regSetWordSizeGPR, + m_state.GetError(e_regSetGPR, Read), m_state.context.gpr.__rax, m_state.context.gpr.__rbx, m_state.context.gpr.__rcx, m_state.context.gpr.__rdx, m_state.context.gpr.__rdi, m_state.context.gpr.__rsi, @@ -208,7 +225,9 @@ kern_return_t DNBArchImplX86_64::GetGPRState(bool force) { m_state.context.gpr.__r14, m_state.context.gpr.__r15, m_state.context.gpr.__rip, m_state.context.gpr.__rflags, m_state.context.gpr.__cs, m_state.context.gpr.__fs, - m_state.context.gpr.__gs); + m_state.context.gpr.__gs, m_state.context.gpr.__ds, + m_state.context.gpr.__es, m_state.context.gpr.__ss, + m_state.context.gpr.__gsbase ); // DNBLogThreadedIf (LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) // => 0x%8.8x" @@ -459,21 +478,26 @@ kern_return_t DNBArchImplX86_64::SetGPRState() { "(SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount()); + mach_msg_type_number_t count = + m_state.hasFullGPRState ? e_regSetWordSizeGPRFull : e_regSetWordSizeGPR; + int flavor = m_state.hasFullGPRState ? __x86_64_THREAD_FULL_STATE + : __x86_64_THREAD_STATE; m_state.SetError(e_regSetGPR, Write, - ::thread_set_state(m_thread->MachPortNumber(), - __x86_64_THREAD_STATE, + ::thread_set_state(m_thread->MachPortNumber(), flavor, (thread_state_t)&m_state.context.gpr, - e_regSetWordSizeGPR)); + count)); DNBLogThreadedIf( LOG_THREAD, - "::thread_set_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x" + "::thread_set_state (0x%4.4x, %u (%s), &gpr, %u) => 0x%8.8x" "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx" "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx" "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx" "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx" "\n\trip = %16.16llx" - "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx", - m_thread->MachPortNumber(), __x86_64_THREAD_STATE, e_regSetWordSizeGPR, + "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx" + "\n\t ds = %16.16llx es = %16.16llx ss = %16.16llx gsB = %16.16llx", + m_thread->MachPortNumber(), flavor, + m_state.hasFullGPRState ? "full" : "non-full", count, m_state.GetError(e_regSetGPR, Write), m_state.context.gpr.__rax, m_state.context.gpr.__rbx, m_state.context.gpr.__rcx, m_state.context.gpr.__rdx, m_state.context.gpr.__rdi, @@ -484,7 +508,9 @@ kern_return_t DNBArchImplX86_64::SetGPRState() { m_state.context.gpr.__r13, m_state.context.gpr.__r14, m_state.context.gpr.__r15, m_state.context.gpr.__rip, m_state.context.gpr.__rflags, m_state.context.gpr.__cs, - m_state.context.gpr.__fs, m_state.context.gpr.__gs); + m_state.context.gpr.__fs, m_state.context.gpr.__gs, + m_state.context.gpr.__ds, m_state.context.gpr.__es, + m_state.context.gpr.__ss, m_state.context.gpr.__gsbase); return m_state.GetError(e_regSetGPR, Write); } @@ -1157,6 +1183,10 @@ enum { gpr_cs, gpr_fs, gpr_gs, + gpr_ds, + gpr_es, + gpr_ss, + gpr_gsbase, gpr_eax, gpr_ebx, gpr_ecx, @@ -1543,6 +1573,7 @@ enum debugserver_regnums { debugserver_k5 = 123, debugserver_k6 = 124, debugserver_k7 = 125, + debugserver_gsbase = 126, }; #define GPR_OFFSET(reg) (offsetof(DNBArchImplX86_64::GPR, __##reg)) @@ -1690,6 +1721,10 @@ const DNBRegisterInfo DNBArchImplX86_64::g_gpr_registers[] = { DEFINE_GPR_ALT2(cs, NULL), DEFINE_GPR_ALT2(fs, NULL), DEFINE_GPR_ALT2(gs, NULL), + DEFINE_GPR_ALT2(ds, NULL), + DEFINE_GPR_ALT2(es, NULL), + DEFINE_GPR_ALT2(ss, NULL), + DEFINE_GPR_ALT2(gsbase, NULL), DEFINE_GPR_PSEUDO_32(eax, rax), DEFINE_GPR_PSEUDO_32(ebx, rbx), DEFINE_GPR_PSEUDO_32(ecx, rcx), @@ -2313,6 +2348,8 @@ bool DNBArchImplX86_64::GetRegisterValue(uint32_t set, uint32_t reg, value->info = *regInfo; switch (set) { case e_regSetGPR: + if (reg > gpr_gs && !m_state.hasFullGPRState) + return false; if (reg < k_num_gpr_registers) { value->value.uint64 = ((uint64_t *)(&m_state.context.gpr))[reg]; return true; @@ -2524,6 +2561,8 @@ bool DNBArchImplX86_64::SetRegisterValue(uint32_t set, uint32_t reg, if (regInfo) { switch (set) { case e_regSetGPR: + if (reg > gpr_gs && !m_state.hasFullGPRState) + return false; if (reg < k_num_gpr_registers) { ((uint64_t *)(&m_state.context.gpr))[reg] = value->value.uint64; success = true; diff --git a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h index 96da02a4c9ff9f..7fffd60b2064e0 100644 --- a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h +++ b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h @@ -103,7 +103,8 @@ class DNBArchImplX86_64 : public DNBArchProtocol { }; enum RegisterSetWordSize { - e_regSetWordSizeGPR = sizeof(GPR) / sizeof(int), + e_regSetWordSizeGPR = (sizeof(GPR) - 32) / sizeof(int), + e_regSetWordSizeGPRFull = sizeof(GPR) / sizeof(int), e_regSetWordSizeFPU = sizeof(FPU) / sizeof(int), e_regSetWordSizeEXC = sizeof(EXC) / sizeof(int), e_regSetWordSizeAVX = sizeof(AVX) / sizeof(int), @@ -130,6 +131,7 @@ class DNBArchImplX86_64 : public DNBArchProtocol { kern_return_t fpu_errs[2]; // Read/Write errors kern_return_t exc_errs[2]; // Read/Write errors kern_return_t dbg_errs[2]; // Read/Write errors + bool hasFullGPRState; State() { uint32_t i; diff --git a/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h b/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h index b566accd397285..743c665b691067 100644 --- a/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h +++ b/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h @@ -22,6 +22,7 @@ #define __x86_64_DEBUG_STATE 11 #define __x86_64_AVX_STATE 17 #define __x86_64_AVX512F_STATE 20 +#define __x86_64_THREAD_FULL_STATE 23 typedef struct { uint64_t __rax; @@ -45,6 +46,10 @@ typedef struct { uint64_t __cs; uint64_t __fs; uint64_t __gs; + uint64_t __ds; + uint64_t __es; + uint64_t __ss; + uint64_t __gsbase; } __x86_64_thread_state_t; typedef struct { _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits