[Lldb-commits] [lldb] [lldb] [debugserver] Use "full" x86_64 GPR state when available. (PR #108663)
Brendan Shanks via lldb-commits
lldb-commits at lists.llvm.org
Fri Sep 13 16:41:47 PDT 2024
https://github.com/mrpippy created https://github.com/llvm/llvm-project/pull/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
>From 4453801c7d8abf7a6adfb7fae57ad9fa9d52a0c4 Mon Sep 17 00:00:00 2001
From: Brendan Shanks <bshanks at codeweavers.com>
Date: Thu, 12 Sep 2024 16:01:30 -0700
Subject: [PATCH] [lldb] [debugserver] Use "full" x86_64 GPR state when
available.
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, 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.
Fixes #57591
---
.../MacOSX/x86_64/DNBArchImplX86_64.cpp | 52 +++++++++++++++----
.../source/MacOSX/x86_64/DNBArchImplX86_64.h | 4 +-
.../MacOSX/x86_64/MachRegisterStatesX86_64.h | 5 ++
3 files changed, 51 insertions(+), 10 deletions(-)
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 {
More information about the lldb-commits
mailing list