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