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