1 /**
2  * This module provides OS specific helper function for threads support
3  *
4  * Copyright: Copyright Digital Mars 2010 - 2010.
5  * License: Distributed under the
6  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7  *    (See accompanying file LICENSE)
8  * Source:    $(DRUNTIMESRC core/sys/windows/_threadaux.d)
9  * Authors:   Rainer Schuetze
10  */
11 
12 /* NOTE: This file has been patched from the original DMD distribution to
13  * work with the GDC compiler.
14  */
15 module core.sys.windows.threadaux;
16 version (Windows):
17 @system:
18 
19 import core.sys.windows.basetsd/+ : HANDLE+/;
20 import core.sys.windows.winbase/+ : CloseHandle, GetCurrentThreadId, GetCurrentProcessId,
21     GetModuleHandleA, GetProcAddress+/;
22 import core.sys.windows.windef/+ : BOOL, DWORD, FALSE, HRESULT+/;
23 import core.stdc.stdlib;
24 
25 public import core.thread;
26 
27 extern(Windows)
28 HANDLE OpenThread(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId) nothrow @nogc;
29 
30 extern (C) extern __gshared int _tls_index;
31 
32 extern (C) // rt.minfo
33 {
34     void rt_moduleTlsCtor();
35     void rt_moduleTlsDtor();
36 }
37 
38 private:
39 ///////////////////////////////////////////////////////////////////
40 struct thread_aux
41 {
42     // don't let symbols leak into other modules
43 
44     enum SystemProcessInformation = 5;
45     enum STATUS_INFO_LENGTH_MISMATCH = 0xc0000004;
46 
47     // structs subject to change according to MSDN, more info at http://undocumented.ntinternals.net
48     // declarations according to http://processhacker.sourceforge.net/doc/ntexapi_8h_source.html
49     // NOTE: the declarations assume default alignment for Win64 and contain some padding data
50     struct UNICODE_STRING
51     {
52         short Length;
53         short MaximumLength;
54         wchar* Buffer;
55     }
56     // process or thread ID, documentation says it is a HANDLE, but it's actually the ID (a DWORD)
57     alias size_t PTID;
58 
59     struct _SYSTEM_PROCESS_INFORMATION
60     {
61         int     NextEntryOffset; // When this entry is 0, there are no more processes to be read.
62         int     NumberOfThreads;
63         long    WorkingSetPrivateSize;
64         uint    HardFaultCount;
65         uint    NumberOfThreadsHighWatermark;
66         ulong   CycleTime;
67         long    CreateTime;
68         long    UserTime;
69         long    KernelTime;
70         UNICODE_STRING      ImageName;
71         int     BasePriority;
72         PTID    /*Unique*/ProcessId;
73         PTID    InheritedFromUniqueProcessId;
74         uint    HandleCount;
75         uint    SessionId;
76         size_t  UniqueProcessKey;
77         size_t  PeakVirtualSize;
78         size_t  VirtualSize;
79         uint    PageFaultCount;
80         size_t  PeakWorkingSetSize;
81         size_t  WorkingSetSize;
82         size_t  QuotaPeakPagedPoolUsage;
83         size_t  QuotaPagedPoolUsage;
84         size_t  QuotaPeakNonPagedPoolUsage;
85         size_t  QuotaNonPagedPoolUsage;
86         size_t  PagefileUsage;
87         size_t  PeakPagefileUsage;
88         size_t  PrivatePageCount;
89         long    ReadOperationCount;
90         long    WriteOperationCount;
91         long    OtherOperationCount;
92         long    ReadTransferCount;
93         long    WriteTransferCount;
94         long    OtherTransferCount;
95 
96         // SYSTEM_THREAD_INFORMATION or SYSTEM_EXTENDED_THREAD_INFORMATION structures follow.
97     }
98 
99     struct _SYSTEM_THREAD_INFORMATION
100     {
101         long    KernelTime;
102         long    UserTime;
103         long    CreateTime;
104         uint    WaitTime;
105         void*   StartAddress;
106         PTID    ProcessId;
107         PTID    ThreadId;
108         int     Priority;
109         int     BasePriority;
110         uint    ContextSwitches;
111         uint    ThreadState;
112         int     WaitReason;
113         int     reserved;
114     }
115 
116     alias fnNtQuerySystemInformation = extern(Windows)
117     HRESULT function( uint SystemInformationClass, void* info, uint infoLength, uint* ReturnLength ) nothrow @nogc;
118 
119     enum ThreadBasicInformation = 0;
120 
121     struct THREAD_BASIC_INFORMATION
122     {
123         int    ExitStatus;
124         void** TebBaseAddress;
125         PTID   ProcessId;
126         PTID   ThreadId;
127         size_t AffinityMask;
128         int    Priority;
129         int    BasePriority;
130     }
131 
132     alias fnNtQueryInformationThread = extern(Windows)
133     int function( HANDLE ThreadHandle, uint ThreadInformationClass, void* buf, uint size, uint* ReturnLength ) nothrow @nogc;
134 
135     enum SYNCHRONIZE = 0x00100000;
136     enum THREAD_GET_CONTEXT = 8;
137     enum THREAD_QUERY_INFORMATION = 0x40;
138     enum THREAD_SUSPEND_RESUME = 2;
139 
140     ///////////////////////////////////////////////////////////////////
141     // get the thread environment block (TEB) of the thread with the given handle
getTEBthread_aux142     static void** getTEB( HANDLE hnd ) nothrow @nogc
143     {
144         HANDLE nthnd = GetModuleHandleA( "NTDLL" );
145         assert( nthnd, "cannot get module handle for ntdll" );
146         fnNtQueryInformationThread fn = cast(fnNtQueryInformationThread) GetProcAddress( nthnd, "NtQueryInformationThread" );
147         assert( fn, "cannot find NtQueryInformationThread in ntdll" );
148 
149         THREAD_BASIC_INFORMATION tbi;
150         int Status = (*fn)(hnd, ThreadBasicInformation, &tbi, tbi.sizeof, null);
151         assert(Status == 0);
152 
153         return tbi.TebBaseAddress;
154     }
155 
156     // get the thread environment block (TEB) of the thread with the given identifier
getTEBthread_aux157     static void** getTEB( uint id ) nothrow @nogc
158     {
159         HANDLE hnd = OpenThread( THREAD_QUERY_INFORMATION, FALSE, id );
160         assert( hnd, "OpenThread failed" );
161 
162         void** teb = getTEB( hnd );
163         CloseHandle( hnd );
164         return teb;
165     }
166 
167     // get linear address of TEB of current thread
getTEBthread_aux168     static void** getTEB() nothrow @nogc
169     {
170         version (Win32)
171         {
172             version (GNU_InlineAsm)
173             {
174                 void** teb;
175                 asm pure nothrow @nogc { "movl %%fs:0x18, %0;" : "=r" (teb); }
176                 return teb;
177             }
178             else
179             {
180                 asm pure nothrow @nogc
181                 {
182                     naked;
183                     mov EAX,FS:[0x18];
184                     ret;
185                 }
186             }
187         }
188         else version (Win64)
189         {
190             version (GNU_InlineAsm)
191             {
192                 void** teb;
193                 asm pure nothrow @nogc { "movq %%gs:0x30, %0;" : "=r" (teb); }
194                 return teb;
195             }
196             else
197             {
198                 asm pure nothrow @nogc
199                 {
200                     naked;
201                     mov RAX,0x30;
202                     mov RAX,GS:[RAX]; // immediate value causes fixup
203                     ret;
204                 }
205             }
206         }
207         else
208         {
209             static assert(false);
210         }
211     }
212 
213     // get the stack bottom (the top address) of the thread with the given handle
getThreadStackBottomthread_aux214     static void* getThreadStackBottom( HANDLE hnd ) nothrow @nogc
215     {
216         void** teb = getTEB( hnd );
217         return teb[1];
218     }
219 
220     // get the stack bottom (the top address) of the thread with the given identifier
getThreadStackBottomthread_aux221     static void* getThreadStackBottom( uint id ) nothrow @nogc
222     {
223         void** teb = getTEB( id );
224         return teb[1];
225     }
226 
227     // create a thread handle with full access to the thread with the given identifier
OpenThreadHandlethread_aux228     static HANDLE OpenThreadHandle( uint id ) nothrow @nogc
229     {
230         return OpenThread( SYNCHRONIZE|THREAD_GET_CONTEXT|THREAD_QUERY_INFORMATION|THREAD_SUSPEND_RESUME, FALSE, id );
231     }
232 
233     ///////////////////////////////////////////////////////////////////
234     // enumerate threads of the given process calling the passed function on each thread
235     // using function instead of delegate here to avoid allocating closure
enumProcessThreadsthread_aux236     static bool enumProcessThreads( uint procid, bool function( uint id, void* context ) dg, void* context )
237     {
238         HANDLE hnd = GetModuleHandleA( "NTDLL" );
239         fnNtQuerySystemInformation fn = cast(fnNtQuerySystemInformation) GetProcAddress( hnd, "NtQuerySystemInformation" );
240         if ( !fn )
241             return false;
242 
243         uint sz = 16384;
244         uint retLength;
245         HRESULT rc;
246         char* buf;
247         for ( ; ; )
248         {
249             buf = cast(char*) core.stdc.stdlib.malloc(sz);
250             if (!buf)
251                 return false;
252             rc = fn( SystemProcessInformation, buf, sz, &retLength );
253             if ( rc != STATUS_INFO_LENGTH_MISMATCH )
254                 break;
255             core.stdc.stdlib.free( buf );
256             sz *= 2;
257         }
258         scope(exit) core.stdc.stdlib.free( buf );
259 
260         if (rc != 0)
261             return false;
262 
263         auto pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) buf;
264         auto pend  = cast(_SYSTEM_PROCESS_INFORMATION*) (buf + retLength);
265         for ( ; pinfo < pend; )
266         {
267             if ( pinfo.ProcessId == procid )
268             {
269                 auto tinfo = cast(_SYSTEM_THREAD_INFORMATION*)(pinfo + 1);
270                 for ( int i = 0; i < pinfo.NumberOfThreads; i++, tinfo++ )
271                     if ( tinfo.ProcessId == procid )
272                         if ( !dg( cast(uint) tinfo.ThreadId, context ) ) // IDs are actually DWORDs
273                             return false;
274             }
275             if ( pinfo.NextEntryOffset == 0 )
276                 break;
277             pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) (cast(char*) pinfo + pinfo.NextEntryOffset);
278         }
279         return true;
280     }
281 
enumProcessThreadsthread_aux282     static bool enumProcessThreads( bool function( uint id, void* context ) dg, void* context )
283     {
284         return enumProcessThreads( GetCurrentProcessId(), dg, context );
285     }
286 
287     // execute function on the TLS for the given thread
288     alias extern(C) void function() externCVoidFunc;
impersonate_threadthread_aux289     static void impersonate_thread( uint id, externCVoidFunc fn )
290     {
291         impersonate_thread(id, () => fn());
292     }
293 
impersonate_threadthread_aux294     static void impersonate_thread( uint id, scope void delegate() dg)
295     {
296         if ( id == GetCurrentThreadId() )
297         {
298             dg();
299             return;
300         }
301 
302         // temporarily set current TLS array pointer to the array pointer of the referenced thread
303         void** curteb = getTEB();
304         void** teb    = getTEB( id );
305         assert( teb && curteb );
306 
307         void** curtlsarray = cast(void**) curteb[11];
308         void** tlsarray    = cast(void**) teb[11];
309         if ( !curtlsarray || !tlsarray )
310             return;
311 
312         curteb[11] = tlsarray;
313 
314         // swap out the TLS slots aswell
315         version (Win64)
316         {
317             enum TEB_offset_TlsSlots = 0x1480;
318             enum TEB_offset_TlsExpansionSlots = 0x1780;
319         }
320         else
321         {
322             enum TEB_offset_TlsSlots = 0xE10;
323             enum TEB_offset_TlsExpansionSlots = 0xF94;
324         }
325         void* tlsSlotsAdr(void** teb) { return cast(void*) teb + TEB_offset_TlsSlots; }
326         ref void* tlsExpansionSlots(void** teb) { return *cast(void**)(cast(void*) teb + TEB_offset_TlsExpansionSlots); }
327 
328         import core.stdc.string;
329         void*[64] slots = void;
330         memcpy(slots.ptr, tlsSlotsAdr(curteb), slots.sizeof);
331         void* extraSlots = tlsExpansionSlots(curteb);
332 
333         memcpy(tlsSlotsAdr(curteb), tlsSlotsAdr(teb), slots.sizeof);
334         tlsExpansionSlots(curteb) = tlsExpansionSlots(teb);
335 
336         dg();
337 
338         curteb[11] = curtlsarray;
339 
340         // copy the TLS slots back in case they have been changed in dg
341         memcpy(tlsSlotsAdr(teb), tlsSlotsAdr(curteb), slots.sizeof);
342         tlsExpansionSlots(teb) = tlsExpansionSlots(curteb);
343 
344         memcpy(tlsSlotsAdr(curteb), slots.ptr, slots.sizeof);
345         tlsExpansionSlots(curteb) = extraSlots;
346     }
347 }
348 
349 public:
350 // forward as few symbols as possible into the "global" name space
351 alias thread_aux.getTEB getTEB;
352 alias thread_aux.getThreadStackBottom getThreadStackBottom;
353 alias thread_aux.OpenThreadHandle OpenThreadHandle;
354 alias thread_aux.enumProcessThreads enumProcessThreads;
355 alias thread_aux.impersonate_thread impersonate_thread;
356 
357 // get the start of the TLS memory of the thread with the given handle
GetTlsDataAddress(HANDLE hnd)358 void* GetTlsDataAddress( HANDLE hnd ) nothrow
359 {
360     if ( void** teb = getTEB( hnd ) )
361         if ( void** tlsarray = cast(void**) teb[11] )
362             return tlsarray[_tls_index];
363     return null;
364 }
365 
366 // get the start of the TLS memory of the thread with the given identifier
GetTlsDataAddress(uint id)367 void* GetTlsDataAddress( uint id ) nothrow
368 {
369     HANDLE hnd = OpenThread( thread_aux.THREAD_QUERY_INFORMATION, FALSE, id );
370     assert( hnd, "OpenThread failed" );
371 
372     void* tls = GetTlsDataAddress( hnd );
373     CloseHandle( hnd );
374     return tls;
375 }
376 
377 ///////////////////////////////////////////////////////////////////
378 // run rt_moduleTlsCtor in the context of the given thread
thread_moduleTlsCtor(uint id)379 void thread_moduleTlsCtor( uint id )
380 {
381     thread_aux.impersonate_thread(id, &rt_moduleTlsCtor);
382 }
383 
384 ///////////////////////////////////////////////////////////////////
385 // run rt_moduleTlsDtor in the context of the given thread
thread_moduleTlsDtor(uint id)386 void thread_moduleTlsDtor( uint id )
387 {
388     thread_aux.impersonate_thread(id, &rt_moduleTlsDtor);
389 }
390