Ken Thomases : ntdll: For Mac 64-bit, poke NtCurrentTeb()-> ThreadLocalStoragePointer to the corresponding offset from %gs.

Alexandre Julliard julliard at winehq.org
Fri Jul 29 10:17:08 CDT 2016


Module: wine
Branch: master
Commit: 3d8efb238808a519902e047d8673237debb0f0a2
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=3d8efb238808a519902e047d8673237debb0f0a2

Author: Ken Thomases <ken at codeweavers.com>
Date:   Thu Jul 28 16:33:25 2016 -0500

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

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>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/ntdll/loader.c        |  9 ++++++
 dlls/ntdll/signal_x86_64.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index d9176d7..f1ef7ab 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 */
         }
@@ -1017,6 +1021,11 @@ static NTSTATUS alloc_thread_tls(void)
                GetCurrentThreadId(), i, size, dir->SizeOfZeroFill, pointers[i] );
     }
     NtCurrentTeb()->ThreadLocalStoragePointer = pointers;
+#if defined(__APPLE__) && defined(__x86_64__)
+    __asm__ volatile (".byte 0x65\n\tmovq %0,%c1"
+                      :
+                      : "r" (pointers), "n" (FIELD_OFFSET(TEB, ThreadLocalStoragePointer)));
+#endif
     return STATUS_SUCCESS;
 }
 
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index 10faaea..decae5c 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -2832,6 +2832,69 @@ void signal_free_thread( TEB *teb )
     NtFreeVirtualMemory( NtCurrentProcess(), (void **)&teb, &size, MEM_RELEASE );
 }
 
+#ifdef __APPLE__
+/**********************************************************************
+ *		mac_thread_gsbase
+ */
+static void *mac_thread_gsbase(void)
+{
+    static int gsbase_offset = -1;
+    void *ret;
+
+    if (gsbase_offset < 0)
+    {
+        /* Search for the array of TLS slots within the pthread data structure.
+           That's what the macOS pthread implementation uses for gsbase. */
+        const void* const sentinel1 = (const void*)0x2bffb6b4f11228ae;
+        const void* const sentinel2 = (const void*)0x0845a7ff6ab76707;
+        int rc;
+        pthread_key_t key;
+        const void** p = (const void**)pthread_self();
+        int i;
+
+        gsbase_offset = 0;
+        if ((rc = pthread_key_create(&key, NULL)))
+        {
+            ERR("failed to create sentinel key for gsbase search: %d\n", rc);
+            return NULL;
+        }
+
+        pthread_setspecific(key, sentinel1);
+
+        for (i = key + 1; i < 2000; i++) /* arbitrary limit */
+        {
+            if (p[i] == sentinel1)
+            {
+                pthread_setspecific(key, sentinel2);
+
+                if (p[i] == sentinel2)
+                {
+                    gsbase_offset = (i - key) * sizeof(*p);
+                    break;
+                }
+
+                pthread_setspecific(key, sentinel1);
+            }
+        }
+
+        pthread_key_delete(key);
+    }
+
+    if (gsbase_offset)
+    {
+        ret = (char*)pthread_self() + gsbase_offset;
+        TRACE("pthread_self() %p + offset 0x%08x -> gsbase %p\n", pthread_self(), gsbase_offset, ret);
+    }
+    else
+    {
+        ret = NULL;
+        ERR("failed to locate gsbase; won't be able to poke ThreadLocalStoragePointer into pthread TLS; expect crashes\n");
+    }
+
+    return ret;
+}
+#endif
+
 /**********************************************************************
  *		signal_init_thread
  */
@@ -2850,6 +2913,14 @@ void signal_init_thread( TEB *teb )
     __asm__ volatile (".byte 0x65\n\tmovq %0,%c1"
                       :
                       : "r" (teb->Tib.Self), "n" (FIELD_OFFSET(TEB, Tib.Self)));
+    __asm__ volatile (".byte 0x65\n\tmovq %0,%c1"
+                      :
+                      : "r" (teb->ThreadLocalStoragePointer), "n" (FIELD_OFFSET(TEB, ThreadLocalStoragePointer)));
+
+    /* alloc_tls_slot() needs to poke a value to an address relative to each
+       thread's gsbase.  Have each thread record its gsbase pointer into its
+       TEB so alloc_tls_slot() can find it. */
+    teb->Reserved5[0] = mac_thread_gsbase();
 #else
 # error Please define setting %gs for your architecture
 #endif




More information about the wine-cvs mailing list