diff --git a/src/core/sys/windows/_dll.d b/src/core/sys/windows/_dll.d index 1d384dada13..0a4e199f344 100644 --- a/src/core/sys/windows/_dll.d +++ b/src/core/sys/windows/_dll.d @@ -52,8 +52,8 @@ private: alias extern(Windows) void* fnRtlAllocateHeap(void* HeapHandle, uint Flags, uint Size); - // find a code sequence and return the (relative) address that follows - static void* findCodeReference( void* adr, int len, ref ubyte[] pattern, bool relative ) + // find a code sequence and return the address after the sequence + static void* findCodeSequence( void* adr, int len, ref ubyte[] pattern ) { if( !adr ) return null; @@ -64,14 +64,28 @@ private: if( code[ p .. p + pattern.length ] == pattern[ 0 .. $ ] ) { ubyte* padr = code + p + pattern.length; - if( relative ) - return padr + 4 + *cast(int*) padr; - return *cast(void**) padr; + return padr; } } return null; } + // find a code sequence and return the (relative) address that follows + static void* findCodeReference( void* adr, int len, ref ubyte[] pattern, bool relative ) + { + if( !adr ) + return null; + + ubyte* padr = cast(ubyte*) findCodeSequence( adr, len, pattern ); + if( padr ) + { + if( relative ) + return padr + 4 + *cast(int*) padr; + return *cast(void**) padr; + } + return null; + } + // crawl through ntdll to find function _LdrpAllocateTls@0 and references // to _LdrpNumberOfTlsEntries, _NtdllBaseTag and _LdrpTlsList // LdrInitializeThunk @@ -87,11 +101,14 @@ private: static __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize static __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize + static __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop; static __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread static __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls + static __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls static __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls static __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries static __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag + static __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag static __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList static LdrpTlsListEntry* addTlsListEntry( void** peb, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) @@ -105,13 +122,19 @@ private: { void* pLdrpInitialize = findCodeReference( fn, 20, jmp_LdrpInitialize, true ); void* p_LdrpInitialize = findCodeReference( pLdrpInitialize, 40, jmp__LdrpInitialize, true ); + if( !p_LdrpInitialize ) + p_LdrpInitialize = findCodeSequence( pLdrpInitialize, 40, jmp__LdrpInitialize_xp64 ); void* pLdrpInitializeThread = findCodeReference( p_LdrpInitialize, 200, call_LdrpInitializeThread, true ); void* pLdrpAllocateTls = findCodeReference( pLdrpInitializeThread, 40, call_LdrpAllocateTls, true ); + if(!pLdrpAllocateTls) + pLdrpAllocateTls = findCodeReference( pLdrpInitializeThread, 100, call_LdrpAllocateTls_svr03, true ); void* pBodyAllocateTls = findCodeReference( pLdrpAllocateTls, 40, jne_LdrpAllocateTls, true ); - int* pLdrpNumberOfTlsEntries = cast(int*) findCodeReference( pBodyAllocateTls, 20, mov_LdrpNumberOfTlsEntries, false ); + int* pLdrpNumberOfTlsEntries = cast(int*) findCodeReference( pBodyAllocateTls, 60, mov_LdrpNumberOfTlsEntries, false ); pNtdllBaseTag = cast(int*) findCodeReference( pBodyAllocateTls, 30, mov_NtdllBaseTag, false ); - LdrpTlsListEntry* pLdrpTlsList = cast(LdrpTlsListEntry*)findCodeReference( pBodyAllocateTls, 60, mov_LdrpTlsList, false ); + if(!pNtdllBaseTag) + pNtdllBaseTag = cast(int*) findCodeReference( pBodyAllocateTls, 30, mov_NtdllBaseTag_srv03, false ); + LdrpTlsListEntry* pLdrpTlsList = cast(LdrpTlsListEntry*)findCodeReference( pBodyAllocateTls, 80, mov_LdrpTlsList, false ); if( !pLdrpNumberOfTlsEntries || !pNtdllBaseTag || !pLdrpTlsList ) return null; @@ -346,8 +369,12 @@ public: function (uint id, void* context) { if( !thread_findByAddr( id ) ) { - thread_attachByAddr( id ); - thread_moduleTlsCtor( id ); + // if the OS has not prepared TLS for us, don't attach to the thread + if( GetTlsDataAddress( id ) ) + { + thread_attachByAddr( id ); + thread_moduleTlsCtor( id ); + } } return true; }, null ); @@ -382,22 +409,39 @@ public: */ static bool tlsCtorRun; static this() { tlsCtorRun = true; } + static ~this() { tlsCtorRun = false; } // to be called from DllMain with reason DLL_THREAD_ATTACH - void dll_thread_attach( bool attach_thread = true, bool initTls = true ) + bool dll_thread_attach( bool attach_thread = true, bool initTls = true ) { - if( attach_thread && !thread_findByAddr( GetCurrentThreadId() ) ) - thread_attachThis(); - if( initTls && !tlsCtorRun ) // avoid duplicate calls - _moduleTlsCtor(); + // if the OS has not prepared TLS for us, don't attach to the thread + // (happened when running under x64 OS) + if( !GetTlsDataAddress( GetCurrentThreadId() ) ) + return false; + if( !thread_findByAddr( GetCurrentThreadId() ) ) + { + // only attach to thread and initalize it if it is not in the thread list (so it's not created by "new Thread") + if( attach_thread ) + thread_attachThis(); + if( initTls && !tlsCtorRun ) // avoid duplicate calls + _moduleTlsCtor(); + } + return true; } // to be called from DllMain with reason DLL_THREAD_DETACH - void dll_thread_detach( bool detach_thread = true, bool exitTls = true ) + bool dll_thread_detach( bool detach_thread = true, bool exitTls = true ) { - if( exitTls ) - _moduleTlsDtor(); - if( detach_thread ) - thread_detachThis(); + // if the OS has not prepared TLS for us, we did not attach to the thread + if( !GetTlsDataAddress( GetCurrentThreadId() ) ) + return false; + if( thread_findByAddr( GetCurrentThreadId() ) ) + { + if( exitTls && tlsCtorRun ) // avoid dtors to be run twice + _moduleTlsDtor(); + if( detach_thread ) + thread_detachThis(); + } + return true; } } diff --git a/src/core/sys/windows/_thread.d b/src/core/sys/windows/_thread.d index 6fcac8cf9c0..8df9e601e3d 100644 --- a/src/core/sys/windows/_thread.d +++ b/src/core/sys/windows/_thread.d @@ -201,7 +201,7 @@ private: return; } - // temporarily set current TLS data pointer to the data pointer of the referenced thread + // temporarily set current TLS array pointer to the array pointer of the referenced thread void** curteb = getTEB(); void** teb = getTEB( id ); assert( teb && curteb ); @@ -211,14 +211,9 @@ private: if( !curtlsarray || !tlsarray ) return; - void** curtlsdata = cast(void**) curtlsarray[_tls_index]; - void** tlsdata = cast(void**) tlsarray[_tls_index]; - if( !curtlsdata || !tlsdata ) - return; - - curtlsarray[_tls_index] = tlsdata; + curteb[11] = tlsarray; fn(); - curtlsarray[_tls_index] = curtlsdata; + curteb[11] = curtlsarray; } } @@ -229,6 +224,7 @@ public: alias thread_aux.OpenThreadHandle OpenThreadHandle; alias thread_aux.enumProcessThreads enumProcessThreads; + // get the start of the TLS memory of the thread with the given handle void* GetTlsDataAddress( HANDLE hnd ) { if( void** teb = getTEB( hnd ) ) @@ -237,6 +233,17 @@ public: return null; } + // get the start of the TLS memory of the thread with the given identifier + void* GetTlsDataAddress( uint id ) + { + HANDLE hnd = OpenThread( thread_aux.THREAD_QUERY_INFORMATION, FALSE, id ); + assert( hnd, "OpenThread failed" ); + + void* tls = GetTlsDataAddress( hnd ); + CloseHandle( hnd ); + return tls; + } + /////////////////////////////////////////////////////////////////// // run _moduleTlsCtor in the context of the given thread void thread_moduleTlsCtor( uint id ) diff --git a/src/core/thread.d b/src/core/thread.d index 5d160099dad..a3a6adad032 100644 --- a/src/core/thread.d +++ b/src/core/thread.d @@ -794,6 +794,16 @@ class Thread // having thread being treated like a daemon thread. synchronized( slock ) { + // when creating threads from inside a DLL, DllMain(THREAD_ATTACH) + // might be called before _beginthreadex returns, but the dll + // helper functions need to know whether the thread is created + // from the runtime itself or from another DLL or the application + // to just attach to it + // as the consequence, the new Thread object is added before actual + // creation of the thread. There should be no problem with the GC + // calling thread_suspendAll, because of the slock synchronization + add( this ); + version( Windows ) { m_hndl = cast(HANDLE) _beginthreadex( null, m_sz, &thread_entryPoint, cast(void*) this, 0, &m_addr ); @@ -817,7 +827,6 @@ class Thread if( m_tmach == m_tmach.init ) throw new ThreadException( "Error creating thread" ); } - add( this ); } } @@ -2039,7 +2048,10 @@ version( Windows ) auto pstart = cast(void*) &_tlsstart; auto pend = cast(void*) &_tlsend; auto pos = GetTlsDataAddress( thisThread.m_hndl ); - thisThread.m_tls = pos[0 .. pend - pstart]; + if( pos ) // on x64, threads without TLS happen to exist + thisThread.m_tls = pos[0 .. pend - pstart]; + else + thisThread.m_tls = []; } } else