[Lldb-commits] [lldb] [lldb] [debugserver] Use "full" x86_64 GPR state when available. (PR #108663)

via lldb-commits lldb-commits at lists.llvm.org
Fri Sep 13 16:42:39 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Brendan Shanks (mrpippy)

<details>
<summary>Changes</summary>

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 

---
Full diff: https://github.com/llvm/llvm-project/pull/108663.diff


3 Files Affected:

- (modified) lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp (+43-9) 
- (modified) lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h (+3-1) 
- (modified) lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h (+5) 


``````````diff
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..286fd72267b349 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 ? __x86_64_THREAD_FULL_STATE
+                                : __x86_64_THREAD_STATE,
+        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"
@@ -461,9 +480,11 @@ kern_return_t DNBArchImplX86_64::SetGPRState() {
 
   m_state.SetError(e_regSetGPR, Write,
                    ::thread_set_state(m_thread->MachPortNumber(),
-                                      __x86_64_THREAD_STATE,
+                                      m_state.hasFullGPRState ? __x86_64_THREAD_FULL_STATE
+                                                              : __x86_64_THREAD_STATE,
                                       (thread_state_t)&m_state.context.gpr,
-                                      e_regSetWordSizeGPR));
+                                      m_state.hasFullGPRState ? e_regSetWordSizeGPRFull
+                                                              : e_regSetWordSizeGPR));
   DNBLogThreadedIf(
       LOG_THREAD,
       "::thread_set_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
@@ -1157,6 +1178,10 @@ enum {
   gpr_cs,
   gpr_fs,
   gpr_gs,
+  gpr_ds,
+  gpr_es,
+  gpr_ss,
+  gpr_gsbase,
   gpr_eax,
   gpr_ebx,
   gpr_ecx,
@@ -1543,6 +1568,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 +1716,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 +2343,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 +2556,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 {

``````````

</details>


https://github.com/llvm/llvm-project/pull/108663


More information about the lldb-commits mailing list