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