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