[PATCH 3/3] ntdll: For Mac 64-bit, poke NtCurrentTeb()->ThreadLocalStoragePointer to the corresponding offset from %gs.

Ken Thomases ken at codeweavers.com
Wed Jul 27 10:42:31 CDT 2016


64-bit Windows apps have hard-coded accesses to %gs:0x58 baked into them.  They
need to find the ThreadLocalStoragePointer there.

Technically, the gsbase register and the memory it points to belong to the
pthread implementation on macOS.  It's used for the pthread TLS implementation.
Slot 11 (offset 0x58) is currently used for the implementation of the ttyname()
system library function.  We do not anticipate that Wine or any of the system
libraries or frameworks it uses will call ttyname().  Furthermore, Apple has
made it so that future releases of macOS will no longer use that slot.  So, we
hijack it for our purposes.

Signed-off-by: Ken Thomases <ken at codeweavers.com>
---
 dlls/ntdll/loader.c        | 11 +++++++-
 dlls/ntdll/signal_x86_64.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index d9176d7..caf8979 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -818,6 +818,10 @@ static SHORT alloc_tls_slot( LDR_MODULE *mod )
             if (!new) return -1;
             if (old) memcpy( new, old, tls_module_count * sizeof(*new) );
             teb->ThreadLocalStoragePointer = new;
+#if defined(__APPLE__) && defined(__x86_64__)
+            if (teb->Reserved5[0])
+                ((TEB*)teb->Reserved5[0])->ThreadLocalStoragePointer = new;
+#endif
             TRACE( "thread %04lx tls block %p -> %p\n", (ULONG_PTR)teb->ClientId.UniqueThread, old, new );
             /* FIXME: can't free old block here, should be freed at thread exit */
         }
@@ -989,6 +993,7 @@ static NTSTATUS alloc_thread_tls(void)
 {
     void **pointers;
     UINT i, size;
+    TEB *teb = NtCurrentTeb();
 
     if (!tls_module_count) return STATUS_SUCCESS;
 
@@ -1016,7 +1021,11 @@ static NTSTATUS alloc_thread_tls(void)
         TRACE( "thread %04x slot %u: %u/%u bytes at %p\n",
                GetCurrentThreadId(), i, size, dir->SizeOfZeroFill, pointers[i] );
     }
-    NtCurrentTeb()->ThreadLocalStoragePointer = pointers;
+    teb->ThreadLocalStoragePointer = pointers;
+#if defined(__APPLE__) && defined(__x86_64__)
+    if (teb->Reserved5[0])
+        ((TEB*)teb->Reserved5[0])->ThreadLocalStoragePointer = pointers;
+#endif
     return STATUS_SUCCESS;
 }
 
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index 10faaea..85b8281 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -2847,9 +2847,71 @@ void signal_init_thread( TEB *teb )
 #elif defined(__NetBSD__)
     sysarch( X86_64_SET_GSBASE, &teb );
 #elif defined (__APPLE__)
+    static int gsbase_offset = -1;
+
     __asm__ volatile (".byte 0x65\n\tmovq %0,%c1"
                       :
                       : "r" (teb->Tib.Self), "n" (FIELD_OFFSET(TEB, Tib.Self)));
+
+    if (gsbase_offset < 0)
+    {
+        static const void* const sentinel1 = (const void*)0x2bffb6b4f11228ae;
+        static const void* const sentinel2 = (const void*)0x0845a7ff6ab76707;
+        int rc;
+        pthread_key_t key;
+
+        gsbase_offset = 0;
+        if ((rc = pthread_key_create(&key, NULL)))
+            ERR("failed to create sentinel key for gsbase search: %d\n", rc);
+        else
+        {
+            if ((rc = pthread_setspecific(key, sentinel1)))
+                ERR("failed to set sentinel1 value for gsbase search: %d\n", rc);
+            else
+            {
+                const void** p = (const void**)pthread_self();
+                int i;
+
+                for (i = key; i < 2000; i++) /* arbitrary limit */
+                {
+                    if (p[i] == sentinel1)
+                    {
+                        if ((rc = pthread_setspecific(key, sentinel2)))
+                        {
+                            ERR("failed to set sentinel2 value for gsbase search: %d\n", rc);
+                            break;
+                        }
+
+                        if (p[i] == sentinel2)
+                        {
+                            gsbase_offset = (i - key) * sizeof(*p);
+                            break;
+                        }
+
+                        if ((rc = pthread_setspecific(key, sentinel1)))
+                        {
+                            ERR("failed to re-set sentinel1 value for gsbase search: %d\n", rc);
+                            break;
+                        }
+                    }
+                }
+            }
+
+            pthread_key_delete(key);
+        }
+    }
+
+    if (gsbase_offset)
+    {
+        teb->Reserved5[0] = (char*)pthread_self() + gsbase_offset;
+        TRACE("pthread_self() %p + offset 0x%08x -> gsbase %p\n", pthread_self(), gsbase_offset, teb->Reserved5[0]);
+        ((TEB*)teb->Reserved5[0])->ThreadLocalStoragePointer = teb->ThreadLocalStoragePointer;
+    }
+    else
+    {
+        teb->Reserved5[0] = NULL;
+        ERR("failed to locate gsbase; won't be able to poke ThreadLocalStoragePointer into pthread TLS; expect crashes\n");
+    }
 #else
 # error Please define setting %gs for your architecture
 #endif
-- 
2.8.2




More information about the wine-patches mailing list