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 }