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 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 size_t PTID; 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 (Win32) return __asm!(void**)("mov %fs:(0x18), $0", "=r"); 171 else version (Win64) return __asm!(void**)("mov %gs:0($1), $0", "=r,r", 0x30); 172 else static assert(false); 173 } 174 } 175 else 176 { 177 static void** getTEB() nothrow @nogc 178 { 179 version (Win32) 180 { 181 asm pure nothrow @nogc 182 { 183 naked; 184 mov EAX,FS:[0x18]; 185 ret; 186 } 187 } 188 else version (Win64) 189 { 190 asm pure nothrow @nogc 191 { 192 naked; 193 mov RAX,0x30; 194 mov RAX,GS:[RAX]; // immediate value causes fixup 195 ret; 196 } 197 } 198 else 199 { 200 static assert(false); 201 } 202 } 203 } 204 205 // get the stack bottom (the top address) of the thread with the given handle 206 static void* getThreadStackBottom( HANDLE hnd ) nothrow @nogc @system 207 { 208 void** teb = getTEB( hnd ); 209 return teb[1]; 210 } 211 212 // get the stack bottom (the top address) of the thread with the given identifier 213 static void* getThreadStackBottom( uint id ) nothrow @nogc @system 214 { 215 void** teb = getTEB( id ); 216 return teb[1]; 217 } 218 219 // create a thread handle with full access to the thread with the given identifier 220 static HANDLE OpenThreadHandle( uint id ) nothrow @nogc 221 { 222 return OpenThread( SYNCHRONIZE|THREAD_GET_CONTEXT|THREAD_QUERY_INFORMATION|THREAD_SUSPEND_RESUME, FALSE, id ); 223 } 224 225 /////////////////////////////////////////////////////////////////// 226 // enumerate threads of the given process calling the passed function on each thread 227 // using function instead of delegate here to avoid allocating closure 228 static bool enumProcessThreads( uint procid, bool function( uint id, void* context ) dg, void* context ) @system 229 { 230 HANDLE hnd = GetModuleHandleA( "NTDLL" ); 231 fnNtQuerySystemInformation fn = cast(fnNtQuerySystemInformation) GetProcAddress( hnd, "NtQuerySystemInformation" ); 232 if ( !fn ) 233 return false; 234 235 uint sz = 16384; 236 uint retLength; 237 HRESULT rc; 238 char* buf; 239 for ( ; ; ) 240 { 241 buf = cast(char*) core.stdc.stdlib.malloc(sz); 242 if (!buf) 243 return false; 244 rc = fn( SystemProcessInformation, buf, sz, &retLength ); 245 if ( rc != STATUS_INFO_LENGTH_MISMATCH ) 246 break; 247 core.stdc.stdlib.free( buf ); 248 sz *= 2; 249 } 250 scope(exit) core.stdc.stdlib.free( buf ); 251 252 if (rc != 0) 253 return false; 254 255 auto pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) buf; 256 auto pend = cast(_SYSTEM_PROCESS_INFORMATION*) (buf + retLength); 257 for ( ; pinfo < pend; ) 258 { 259 if ( pinfo.ProcessId == procid ) 260 { 261 auto tinfo = cast(_SYSTEM_THREAD_INFORMATION*)(pinfo + 1); 262 for ( int i = 0; i < pinfo.NumberOfThreads; i++, tinfo++ ) 263 if ( tinfo.ProcessId == procid ) 264 if ( !dg( cast(uint) tinfo.ThreadId, context ) ) // IDs are actually DWORDs 265 return false; 266 } 267 if ( pinfo.NextEntryOffset == 0 ) 268 break; 269 pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) (cast(char*) pinfo + pinfo.NextEntryOffset); 270 } 271 return true; 272 } 273 274 static bool enumProcessThreads( bool function( uint id, void* context ) dg, void* context ) 275 { 276 return enumProcessThreads( GetCurrentProcessId(), dg, context ); 277 } 278 279 // execute function on the TLS for the given thread 280 alias extern(C) void function() externCVoidFunc; 281 static void impersonate_thread( uint id, externCVoidFunc fn ) 282 { 283 impersonate_thread(id, () => fn()); 284 } 285 286 static void impersonate_thread( uint id, scope void delegate() dg) @system 287 { 288 if ( id == GetCurrentThreadId() ) 289 { 290 dg(); 291 return; 292 } 293 294 // temporarily set current TLS array pointer to the array pointer of the referenced thread 295 void** curteb = getTEB(); 296 void** teb = getTEB( id ); 297 assert( teb && curteb ); 298 299 void** curtlsarray = cast(void**) curteb[11]; 300 void** tlsarray = cast(void**) teb[11]; 301 if ( !curtlsarray || !tlsarray ) 302 return; 303 304 curteb[11] = tlsarray; 305 306 // swap out the TLS slots aswell 307 version (Win64) 308 { 309 enum TEB_offset_TlsSlots = 0x1480; 310 enum TEB_offset_TlsExpansionSlots = 0x1780; 311 } 312 else 313 { 314 enum TEB_offset_TlsSlots = 0xE10; 315 enum TEB_offset_TlsExpansionSlots = 0xF94; 316 } 317 void* tlsSlotsAdr(void** teb) { return cast(void*) teb + TEB_offset_TlsSlots; } 318 ref void* tlsExpansionSlots(void** teb) { return *cast(void**)(cast(void*) teb + TEB_offset_TlsExpansionSlots); } 319 320 import core.stdc.string; 321 void*[64] slots = void; 322 memcpy(slots.ptr, tlsSlotsAdr(curteb), slots.sizeof); 323 void* extraSlots = tlsExpansionSlots(curteb); 324 325 memcpy(tlsSlotsAdr(curteb), tlsSlotsAdr(teb), slots.sizeof); 326 tlsExpansionSlots(curteb) = tlsExpansionSlots(teb); 327 328 dg(); 329 330 curteb[11] = curtlsarray; 331 332 // copy the TLS slots back in case they have been changed in dg 333 memcpy(tlsSlotsAdr(teb), tlsSlotsAdr(curteb), slots.sizeof); 334 tlsExpansionSlots(teb) = tlsExpansionSlots(curteb); 335 336 memcpy(tlsSlotsAdr(curteb), slots.ptr, slots.sizeof); 337 tlsExpansionSlots(curteb) = extraSlots; 338 } 339 } 340 341 public: 342 // forward as few symbols as possible into the "global" name space 343 alias thread_aux.getTEB getTEB; 344 alias thread_aux.getThreadStackBottom getThreadStackBottom; 345 alias thread_aux.OpenThreadHandle OpenThreadHandle; 346 alias thread_aux.enumProcessThreads enumProcessThreads; 347 alias thread_aux.impersonate_thread impersonate_thread; 348 349 // get the start of the TLS memory of the thread with the given handle 350 void* GetTlsDataAddress( HANDLE hnd ) nothrow @system 351 { 352 if ( void** teb = getTEB( hnd ) ) 353 if ( void** tlsarray = cast(void**) teb[11] ) 354 return tlsarray[_tls_index]; 355 return null; 356 } 357 358 // get the start of the TLS memory of the thread with the given identifier 359 void* GetTlsDataAddress( uint id ) nothrow 360 { 361 HANDLE hnd = OpenThread( thread_aux.THREAD_QUERY_INFORMATION, FALSE, id ); 362 assert( hnd, "OpenThread failed" ); 363 364 void* tls = GetTlsDataAddress( hnd ); 365 CloseHandle( hnd ); 366 return tls; 367 } 368 369 // get the address of the entry in the TLS array for the current thread 370 // use C mangling to access it from msvc.c 371 extern(C) void** GetTlsEntryAdr() @system 372 { 373 if( void** teb = getTEB() ) 374 if( void** tlsarray = cast(void**) teb[11] ) 375 return tlsarray + _tls_index; 376 return null; 377 } 378 379 /////////////////////////////////////////////////////////////////// 380 // run rt_moduleTlsCtor in the context of the given thread 381 void thread_moduleTlsCtor( uint id ) 382 { 383 thread_aux.impersonate_thread(id, &rt_moduleTlsCtor); 384 } 385 386 /////////////////////////////////////////////////////////////////// 387 // run rt_moduleTlsDtor in the context of the given thread 388 void thread_moduleTlsDtor( uint id ) 389 { 390 thread_aux.impersonate_thread(id, &rt_moduleTlsDtor); 391 }