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