Index: src/core/dll_helper.d =================================================================== --- src/core/dll_helper.d (revision 0) +++ src/core/dll_helper.d (revision 0) @@ -0,0 +1,290 @@ +/** + * This module provides OS specific helper function for DLL support + */ + +module core.dll_helper; + +version( Windows ) +{ + import std.c.windows.windows; + import core.stdc.string; + + public import core.thread_helper; + + /////////////////////////////////////////////////////////////////// + // support fixing implicite TLS for dynamically loaded DLLs on Windows XP + + extern (C) + { + extern __gshared void* _tlsstart; + extern __gshared void* _tlsend; + extern __gshared int _tls_index; + extern __gshared void* _tls_callbacks_a; + } + +private: + struct LdrpTlsListEntry + { + LdrpTlsListEntry* next; + LdrpTlsListEntry* prev; + void* tlsstart; + void* tlsend; + void* ptr_tlsindex; + void* callbacks; + void* zerofill; + int tlsindex; + } + + alias extern(Windows) + void* fnRtlAllocateHeap(void* HeapHandle, uint Flags, uint Size); + +// LdrInitializeThunk +// -> _LdrpInitialize@12 +// -> _LdrpInitializeThread@4 +// -> _LdrpAllocateTls@0 +// -> je chunk +// _LdrpNumberOfTlsEntries +// _NtdllBaseTag +// _LdrpTlsList_first + + void* findCodeReference( void* adr, int len, ref ubyte[] pattern, bool relative ) + { + if( !adr ) + return null; + + ubyte* code = cast(ubyte*) adr; + for( int p = 0; p < len; p++ ) + { + 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 null; + } + + __gshared int* pNtdllBaseTag; + + __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize + __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize + __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread + __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls + __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls + __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries + __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag + __gshared ubyte[] mov_LdrpTlsListEntry = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsListEntry + + LdrpTlsListEntry* addTlsListEntry( void** peb, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) + { + HANDLE hnd = GetModuleHandleA( "NTDLL" ); + assert( hnd, "cannot get module handle for ntdll" ); + ubyte* fn = cast(ubyte*) GetProcAddress( hnd, "LdrInitializeThunk" ); + assert( fn, "cannot find LdrInitializeThunk in ntdll" ); + + try + { + void* pLdrpInitialize = findCodeReference( fn, 20, jmp_LdrpInitialize, true ); + + void* p_LdrpInitialize = findCodeReference( pLdrpInitialize, 40, jmp__LdrpInitialize, true ); + + void* pLdrpInitializeThread = findCodeReference( p_LdrpInitialize, 200, call_LdrpInitializeThread, true ); + + void* pLdrpAllocateTls = findCodeReference( pLdrpInitializeThread, 40, call_LdrpAllocateTls, true ); + + void* pBodyAllocateTls = findCodeReference( pLdrpAllocateTls, 40, jne_LdrpAllocateTls, true ); + + int* pLdrpNumberOfTlsEntries = cast(int*) findCodeReference( pBodyAllocateTls, 20, mov_LdrpNumberOfTlsEntries, false ); + + pNtdllBaseTag = cast(int*) findCodeReference( pBodyAllocateTls, 30, mov_NtdllBaseTag, false ); + + LdrpTlsListEntry* pLdrpTlsList = cast(LdrpTlsListEntry*)findCodeReference( pBodyAllocateTls, 60, mov_LdrpTlsListEntry, false ); + + if( !pLdrpNumberOfTlsEntries || !pNtdllBaseTag || !pLdrpTlsList ) + return null; + + fnRtlAllocateHeap* fnAlloc = cast(fnRtlAllocateHeap*) GetProcAddress( hnd, "RtlAllocateHeap" ); + if( !fnAlloc ) + return null; + + void* heap = peb[6]; + LdrpTlsListEntry* entry = cast(LdrpTlsListEntry*) (*fnAlloc)( heap, *pNtdllBaseTag | 0xc0000, LdrpTlsListEntry.sizeof ); + if( !entry ) + return null; + + entry.tlsstart = tlsstart; + entry.tlsend = tlsend; + entry.ptr_tlsindex = tlsindex; + entry.callbacks = tls_callbacks_a; + entry.zerofill = null; + entry.tlsindex = *pLdrpNumberOfTlsEntries; + + *tlsindex = *pLdrpNumberOfTlsEntries; + entry.next = pLdrpTlsList; + entry.prev = pLdrpTlsList.prev; + pLdrpTlsList.prev.next = entry; + pLdrpTlsList.prev = entry; + (*pLdrpNumberOfTlsEntries)++; + + return entry; + } + catch( Exception e ) + { + // assert( false, e.msg ); + return null; + } + } + + bool addTlsData( void** teb, void* tlsstart, void* tlsend, int tlsindex ) + { + try + { + HANDLE hnd = GetModuleHandleA( "NTDLL" ); + assert( hnd, "cannot get module handle for ntdll" ); + + fnRtlAllocateHeap* fnAlloc = cast(fnRtlAllocateHeap*) GetProcAddress( hnd, "RtlAllocateHeap" ); + if( !fnAlloc ) + return false; + + void** peb = cast(void**) teb[12]; + void* heap = peb[6]; + + int sz = tlsend - tlsstart; + void* tlsdata = cast(void*) (*fnAlloc)( heap, *pNtdllBaseTag | 0xc0000, sz ); + if( !tlsdata ) + return false; + + core.stdc.string.memcpy( tlsdata, tlsstart, sz ); // no relocations! + + void** array = cast(void**) (*fnAlloc)( heap, *pNtdllBaseTag | 0xc0000, (tlsindex + 1) * (void*).sizeof ); + if( !array ) + return false; + + if( tlsindex> 0 && teb[11] ) + core.stdc.string.memcpy( array, teb[11], tlsindex * (void*).sizeof); // copy tls-pointers + array[tlsindex] = tlsdata; + teb[11] = cast(void*) array; + } + catch( Exception e ) + { + // assert( false, e.msg ); + return false; + } + return true; + } + + alias bool BOOLEAN; + + struct UNICODE_STRING + { + short Length; + short MaximumLength; + // CAUTION: May not be null-terminated. + wchar* Buffer; + } + + struct LIST_ENTRY + { + LIST_ENTRY* next; + LIST_ENTRY* prev; + } + + struct LDR_MODULE + { + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID BaseAddress; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; // 0x80000,0x40000,4: no thread-init, + SHORT LoadCount; + SHORT TlsIndex; + LIST_ENTRY HashTableEntry; + ULONG TimeDateStamp; + } + + struct PEB_LDR_DATA + { + ULONG Length; + BOOLEAN Initialized; + PVOID SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + } + + bool setDllTlsUsage( HINSTANCE hInstance, void** peb ) + { + try + { + PEB_LDR_DATA* ldrData = cast(PEB_LDR_DATA*) peb[3]; + + LDR_MODULE *thisMod; + LIST_ENTRY* root = &ldrData.InLoadOrderModuleList; + for(LIST_ENTRY* entry = root.next; entry != root; entry = entry.next) + { + LDR_MODULE *ldrMod = cast(LDR_MODULE*) entry; + if(ldrMod.BaseAddress == hInstance) + thisMod = ldrMod; + } + + if( !thisMod ) + return false; + + thisMod.TlsIndex = -1; // uses TLS (not the index itself) + thisMod.LoadCount = -1; // never unload + return true; + } + catch( Exception e ) + { + // assert( false, e.msg ); + return false; + } + } + + public bool dll_fixTLS( HINSTANCE hInstance, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) + { + if( _tls_index != 0 ) + return true; + + void** teb; + void** peb; + void** tlsarray; + asm + { + mov EAX,FS:[0x18]; // linear address of TEB + mov teb,EAX; + mov EAX,FS:[0x2c]; + mov tlsarray, EAX; + mov EAX,FS:[0x30]; + mov peb, EAX; + } + if( tlsarray && tlsarray[0] == tlsstart ) + return true; + + LdrpTlsListEntry* entry = addTlsListEntry( peb, tlsstart, tlsend, tls_callbacks_a, tlsindex ); + if( !entry ) + return false; + + if( !enumProcessThreads( + function (uint id, void* context) { + LdrpTlsListEntry* entry = cast(LdrpTlsListEntry*) context; + return addTlsData( getTEB( id ), entry.tlsstart, entry.tlsend, entry.tlsindex ); + }, entry ) ) + return false; + + if( !setDllTlsUsage( hInstance, peb ) ) + return false; + +// char* tstart = cast(char*) findSectionInMem (hInstance, ".tls"); +// _tls_index = 1; +// p[_tls_index] = cast(int)tstart; + + return true; + } +} Index: src/core/thread.d =================================================================== --- src/core/thread.d (revision 255) +++ src/core/thread.d (working copy) @@ -98,6 +98,7 @@ { import core.stdc.stdint : uintptr_t; // for _beginthreadex decl below import core.sys.windows.windows; + import core.thread_helper; const DWORD TLS_OUT_OF_INDEXES = 0xFFFFFFFF; @@ -192,7 +193,6 @@ return 0; } - // // Copy of the same-named function in phobos.std.thread--it uses the // Windows naming convention to be consistent with GetCurrentThreadId @@ -1092,7 +1092,11 @@ // on why this might occur. version( Windows ) { - return cast(Thread) TlsGetValue( sm_this ); + Thread t = cast(Thread) TlsGetValue( sm_this ); + if( t is null ) + // if the thread has been attached from another thread, the tls value was not set + setThis( t = findThread( GetCurrentThreadId() ) ); + return t; } else version( Posix ) { @@ -1124,6 +1128,24 @@ } } + /** + * Search the list of all threads for a thread with the given thread identifier + * + * Params: + * addr = The thread identifier to search for + * Returns: + * the thread object associated with the thread identifier, null if not found + */ + static Thread findThread( ThreadAddr addr ) + { + synchronized( slock ) + { + foreach( Thread t; Thread ) + if( t.m_addr == addr ) + return t; + } + return null; + } /** * Operates on all threads currently being tracked by the system. The @@ -1478,6 +1500,14 @@ // Add a context to the global context list. // static void add( Context* c ) + { + synchronized( slock ) + { + add_nolock( c ); + } + } + + static void add_nolock( Context* c ) in { assert( c ); @@ -1485,16 +1515,13 @@ } body { - synchronized( slock ) + if( sm_cbeg ) { - if( sm_cbeg ) - { - c.next = sm_cbeg; - sm_cbeg.prev = c; - } - sm_cbeg = c; - ++sm_clen; + c.next = sm_cbeg; + sm_cbeg.prev = c; } + sm_cbeg = c; + ++sm_clen; } @@ -1537,6 +1564,14 @@ // Add a thread to the global thread list. // static void add( Thread t ) + { + synchronized( slock ) + { + add_nolock( t ); + } + } + + static void add_nolock( Thread t ) in { assert( t ); @@ -1545,16 +1580,13 @@ } body { - synchronized( slock ) + if( sm_tbeg ) { - if( sm_tbeg ) - { - t.next = sm_tbeg; - sm_tbeg.prev = t; - } - sm_tbeg = t; - ++sm_tlen; + t.next = sm_tbeg; + sm_tbeg.prev = t; } + sm_tbeg = t; + ++sm_tlen; } @@ -1566,10 +1598,12 @@ { assert( t ); assert( t.next || t.prev ); - version( Windows ) + version( none ) { // NOTE: This doesn't work for Posix as m_isRunning must be set to // false after the thread is removed during normal execution. + // NOTE: This doesn't work for Windows either because remove is called + // from the thread itself assert( !t.isRunning ); } } @@ -1693,44 +1727,90 @@ */ extern (C) void thread_attachThis() { + synchronized( Thread.slock ) + { + version( Windows ) + { + Thread thisThread = thread_attach_nolock( GetCurrentThreadId(), getStackBottom() ); + } + else version( Posix ) + { + Thread thisThread = thread_attach_nolock( pthread_self(), getStackBottom() ); + } + Thread.setThis( thisThread ); + } +} + +/** + * Registers the thread with the given identifier for use with the D Runtime. If this routine + * is called for a thread which is already registered, the result is undefined. + */ +extern (C) Thread thread_attach( Thread.ThreadAddr addr ) +{ + synchronized( Thread.slock ) + { + version( Windows ) + { + void* bstack = getThreadStackBottom( addr ); + } + else version( Posix ) + { + void* bstack = null; + assert( false ); // not implemented + } + return thread_attach_nolock( addr, bstack ); + // setThis cannot be called, because it sets the value in the current thread, + // not in the thread ew just attached to + } +} + +extern (C) Thread thread_attach_nolock( Thread.ThreadAddr addr, void* bstack ) +{ + // the gc should not be touched before attaching to the thread, because + // it might interfere with threads that use the GC, but won't suspend + // this thread. So we use a Thread object created by an attached thread. + // This is not necessary for the main (first) thread, because there is no + // concurrent thread. + static Thread nextThread; + Thread thisThread = nextThread ? nextThread : new Thread(); + version( Windows ) { - Thread thisThread = new Thread(); - Thread.Context* thisContext = &thisThread.m_main; - assert( thisContext == thisThread.m_curr ); + Thread.Context* thisContext = &thisThread.m_main; + assert( thisContext == thisThread.m_curr ); - thisThread.m_addr = GetCurrentThreadId(); - thisThread.m_hndl = GetCurrentThreadHandle(); - thisContext.bstack = getStackBottom(); - thisContext.tstack = thisContext.bstack; + thisThread.m_addr = addr; + thisThread.m_hndl = addr == GetCurrentThreadId() ? GetCurrentThreadHandle() : OpenThreadHandle( addr ); + thisContext.bstack = bstack; + thisContext.tstack = thisContext.bstack; - thisThread.m_isDaemon = true; - - Thread.setThis( thisThread ); + thisThread.m_isDaemon = true; } else version( Posix ) { - Thread thisThread = new Thread(); - Thread.Context* thisContext = thisThread.m_curr; - assert( thisContext == &thisThread.m_main ); + Thread.Context* thisContext = thisThread.m_curr; + assert( thisContext == &thisThread.m_main ); - thisThread.m_addr = pthread_self(); - thisContext.bstack = getStackBottom(); - thisContext.tstack = thisContext.bstack; + thisThread.m_addr = addr; + thisContext.bstack = bstack; + thisContext.tstack = thisContext.bstack; - thisThread.m_isRunning = true; - thisThread.m_isDaemon = true; - - Thread.setThis( thisThread ); + thisThread.m_isRunning = true; + thisThread.m_isDaemon = true; } version( OSX ) { - thisThread.m_tmach = pthread_mach_thread_np( thisThread.m_addr ); - assert( thisThread.m_tmach != thisThread.m_tmach.init ); - } + thisThread.m_tmach = pthread_mach_thread_np( thisThread.m_addr ); + assert( thisThread.m_tmach != thisThread.m_tmach.init ); + } - Thread.add( thisThread ); - Thread.add( thisContext ); + Thread.add_nolock( thisThread ); + Thread.add_nolock( thisContext ); + + // Now it is safe to alloc anything from the GC + nextThread = new Thread(); + + return thisThread; } @@ -1743,7 +1823,18 @@ Thread.remove( Thread.getThis() ); } +/** + * Deregisters the thread with the given thread identifier from use with the runtime. + * If this routine is called for a thread which is not registered, it does nothing. + */ +extern (C) void thread_detach( Thread.ThreadAddr addr ) +{ + Thread t = Thread.findThread( addr ); + if( t ) + Thread.remove( t ); +} + /** * Joins all non-daemon threads that are currently running. This is done by * performing successive scans through the thread list until a scan consists @@ -1791,7 +1882,6 @@ // Used for needLock below. private __gshared bool multiThreadedFlag = false; - /** * This function is used to determine whether the the process is * multi-threaded. Optimizations may only be performed on this @@ -1806,6 +1896,12 @@ return multiThreadedFlag; } +// a DLL needs to set multiThreadedFlag, because threads might be +// created from other parts of the program that are not under our control +extern (C) void thread_setNeedLock(bool need) nothrow +{ + multiThreadedFlag = need; +} // Used for suspendAll/resumeAll below. private __gshared uint suspendDepth = 0; Index: src/core/thread_helper.d =================================================================== --- src/core/thread_helper.d (revision 0) +++ src/core/thread_helper.d (revision 0) @@ -0,0 +1,165 @@ +/** + * This module provides OS specific helper function for threads support + */ + +module core.thread_helper; + +version( Windows ) +{ + import std.c.windows.windows; + import std.c.stdlib; + + public import core.thread; + + /////////////////////////////////////////////////////////////////// + + const SystemProcessInformation = 5; + const STATUS_INFO_LENGTH_MISMATCH = 0xc0000004; + + // abbreviated versions of these structs + struct _SYSTEM_PROCESS_INFORMATION + { + int NextEntryOffset; // When this entry is 0, there are no more processes to be read. + int NumberOfThreads; + int[15] fill1; + int ProcessId; + int[28] fill2; + + // SYSTEM_THREAD_INFORMATION or SYSTEM_EXTENDED_THREAD_INFORMATION structures follow. + } + + struct _SYSTEM_THREAD_INFORMATION + { + int[8] fill1; + int ProcessId; + int ThreadId; + int[6] fill2; + } + + alias extern(Windows) + HRESULT fnNtQuerySystemInformation( uint SystemInformationClass, void* info, uint infoLength, uint* ReturnLength ); + + const ThreadBasicInformation = 0; + + struct THREAD_BASIC_INFORMATION + { + int ExitStatus; + void** TebBaseAddress; + int ProcessId; + int ThreadId; + int AffinityMask; + int Priority; + int BasePriority; + } + + alias extern(Windows) + int fnNtQueryInformationThread( HANDLE ThreadHandle, uint ThreadInformationClass, void* buf, uint size, uint* ReturnLength ); + + /////////////////////////////////////////////////////////////////// + // support attaching to thread other than just executing + void** getTEB( HANDLE hnd ) + { + HANDLE nthnd = GetModuleHandleA( "NTDLL" ); + assert( nthnd, "cannot get module handle for ntdll" ); + fnNtQueryInformationThread* fn = cast(fnNtQueryInformationThread*) GetProcAddress( nthnd, "NtQueryInformationThread" ); + assert( fn, "cannot find NtQueryInformationThread in ntdll" ); + + THREAD_BASIC_INFORMATION tbi; + int Status = (*fn)(hnd, ThreadBasicInformation, &tbi, tbi.sizeof, null); + assert(Status == 0); + + return tbi.TebBaseAddress; + } + + extern(Windows) + HANDLE OpenThread(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId); + + const SYNCHRONIZE = 0x00100000; + const THREAD_GET_CONTEXT = 8; + const THREAD_QUERY_INFORMATION = 0x40; + const THREAD_SUSPEND_RESUME = 2; + + void** getTEB( uint id ) + { + HANDLE hnd = OpenThread( THREAD_QUERY_INFORMATION, FALSE, id ); + assert( hnd, "OpenThread failed" ); + + void** teb = getTEB( hnd ); + CloseHandle( hnd ); + return teb; + } + + void* getThreadStackBottom( HANDLE hnd ) + { + void** teb = getTEB( hnd ); + return teb[1]; + } + + void* getThreadStackBottom( uint id ) + { + void** teb = getTEB( id ); + return teb[1]; + } + + HANDLE OpenThreadHandle( uint id ) + { + return OpenThread( SYNCHRONIZE|THREAD_GET_CONTEXT|THREAD_QUERY_INFORMATION|THREAD_SUSPEND_RESUME, FALSE, id ); + } + + /////////////////////////////////////////////////////////////////// + // support attaching to all running threads + // using unction instead of delegate here to avoid allocating closure + bool enumProcessThreads( uint procid, bool function( uint id, void* context ) dg, void* context ) + { + HANDLE hnd = GetModuleHandleA( "NTDLL" ); + fnNtQuerySystemInformation* fn = cast(fnNtQuerySystemInformation*) GetProcAddress( hnd, "NtQuerySystemInformation" ); + if( !fn ) + return false; + + uint sz = 16384; + uint retLength; + HRESULT rc; + char* buf; + for( ; ; ) + { + buf = cast(char*) std.c.stdlib.malloc(sz); + if(!buf) + return false; + rc = (*fn)( SystemProcessInformation, buf, sz, &retLength ); + if( rc != STATUS_INFO_LENGTH_MISMATCH ) + break; + std.c.stdlib.free( buf ); + sz *= 2; + } + scope(exit) std.c.stdlib.free( buf ); + + if(rc != 0) + return false; + + auto pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) buf; + auto pend = cast(_SYSTEM_PROCESS_INFORMATION*) (buf + retLength); + for( ; pinfo < pend; ) + { + if( pinfo.ProcessId == procid ) + { + auto tinfo = cast(_SYSTEM_THREAD_INFORMATION*)(pinfo + 1); + for( int i = 0; i < pinfo.NumberOfThreads; i++, tinfo++ ) + if( tinfo.ProcessId == procid ) + if( !dg( tinfo.ThreadId, context ) ) + return false; + } + if( pinfo.NextEntryOffset == 0 ) + break; + pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) (cast(char*) pinfo + pinfo.NextEntryOffset); + } + return true; + } + + bool enumProcessThreads( bool function( uint id, void* context ) dg, void* context ) + { + return enumProcessThreads( GetCurrentProcessId(), dg, context ); + } + + +} + Index: win32.mak =================================================================== --- win32.mak (revision 255) +++ win32.mak (working copy) @@ -96,6 +96,8 @@ src/core/sync/rwmutex.d \ src/core/sync/semaphore.d \ src/core/thread.d \ + src/core/thread_helper.d \ + src/core/dll_helper.d \ src/core/threadasm.S \ src/core/vararg.d \ src/object_.d \ @@ -192,6 +194,8 @@ src\core\memory.d \ src\core\runtime.d \ src\core\thread.d \ + src\core\thread_helper.d \ + src\core\dll_helper.d \ src\core\vararg.d \ \ src\core\sync\barrier.d \

AltStyle によって変換されたページ (->オリジナル) /