| author | |
| committer | |
| log | c38e6ed6862c4abfb6c3ab6c8cdd1ea13961969b |
| tree | 27ec7ca64848b85e5c75c3d1c191ac5076725e86 |
| parent | 4d5721214f31684e3bed3624878d8903fabe8e39 |
Co-authored-by: Ryan Liptak <squeek502@hotmail.com>
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31519
Reviewed-by: Ryan Liptak <squeek502@noreply.codeberg.org>
Co-authored-by: inf <infirms@protonmail.com>
Co-committed-by: inf <infirms@protonmail.com>4 files changed, 204 insertions(+), 28 deletions(-)
lib/std/Thread.zig+75-18| ... | ... | @@ -405,14 +405,19 @@ const Completion = std.atomic.Value(enum(if (builtin.zig_backend == .stage2_risc |
| 405 | 405 | /// Performs implementation-agnostic thread setup (`maybeAttachSignalStack`), then calls the given |
| 406 | 406 | /// thread entry point `f` with `args` and handles the result. |
| 407 | 407 | fn callFn(comptime f: anytype, args: anytype) switch (Impl) { |
| 408 | WindowsThreadImpl => windows.DWORD, | |
| 408 | WindowsThreadImpl => windows.NTSTATUS, | |
| 409 | 409 | LinuxThreadImpl => u8, |
| 410 | 410 | PosixThreadImpl => ?*anyopaque, |
| 411 | 411 | else => unreachable, |
| 412 | 412 | } { |
| 413 | 413 | maybeAttachSignalStack(); |
| 414 | 414 | |
| 415 | const default_value = if (Impl == PosixThreadImpl) null else 0; | |
| 415 | const default_value = switch (Impl) { | |
| 416 | WindowsThreadImpl => .SUCCESS, | |
| 417 | LinuxThreadImpl => 0, | |
| 418 | PosixThreadImpl => null, | |
| 419 | else => unreachable, | |
| 420 | }; | |
| 416 | 421 | const bad_fn_ret = "expected return type of startFn to be 'u8', 'noreturn', '!noreturn', 'void', or '!void'"; |
| 417 | 422 | |
| 418 | 423 | switch (@typeInfo(@typeInfo(@TypeOf(f)).@"fn".return_type.?)) { |
| ... | ... | @@ -429,12 +434,13 @@ fn callFn(comptime f: anytype, args: anytype) switch (Impl) { |
| 429 | 434 | } |
| 430 | 435 | |
| 431 | 436 | const status = @call(.auto, f, args); |
| 432 | if (Impl != PosixThreadImpl) { | |
| 433 | return status; | |
| 437 | switch (Impl) { | |
| 438 | WindowsThreadImpl => return @enumFromInt(status), | |
| 439 | LinuxThreadImpl => return status, | |
| 440 | // pthreads don't support exit status, ignore value | |
| 441 | PosixThreadImpl => return default_value, | |
| 442 | else => unreachable, | |
| 434 | 443 | } |
| 435 | ||
| 436 | // pthreads don't support exit status, ignore value | |
| 437 | return default_value; | |
| 438 | 444 | }, |
| 439 | 445 | .error_union => |info| { |
| 440 | 446 | switch (info.payload) { |
| ... | ... | @@ -526,7 +532,7 @@ const WindowsThreadImpl = struct { |
| 526 | 532 | fn_args: Args, |
| 527 | 533 | thread: ThreadCompletion, |
| 528 | 534 | |
| 529 | fn entryFn(raw_ptr: windows.PVOID) callconv(.winapi) windows.DWORD { | |
| 535 | fn entryFn(raw_ptr: windows.PVOID) callconv(.winapi) windows.NTSTATUS { | |
| 530 | 536 | const self: *@This() = @ptrCast(@alignCast(raw_ptr)); |
| 531 | 537 | defer switch (self.thread.completion.swap(.completed, .seq_cst)) { |
| 532 | 538 | .running => {}, |
| ... | ... | @@ -559,16 +565,67 @@ const WindowsThreadImpl = struct { |
| 559 | 565 | // Its also fine if the limit here is incorrect as stack size is only a hint. |
| 560 | 566 | const stack_size = @max(64 * 1024, std.math.lossyCast(u32, config.stack_size)); |
| 561 | 567 | |
| 562 | instance.thread.thread_handle = windows.kernel32.CreateThread( | |
| 563 | null, | |
| 564 | stack_size, | |
| 565 | Instance.entryFn, | |
| 566 | instance, | |
| 567 | 0, | |
| 568 | null, | |
| 569 | ) orelse { | |
| 570 | const errno = windows.GetLastError(); | |
| 571 | return windows.unexpectedError(errno); | |
| 568 | // Intended to be equivalent to a kernel32.CreateThread call with no flags set. | |
| 569 | // However, CreateThread is just a wrapper around CreateRemoteThreadEx, | |
| 570 | // so that's the more relevant function in this context. | |
| 571 | // | |
| 572 | // https://github.com/wine-mirror/wine/blob/3d128be6400b3869119d293d0c8fa9e7702978f8/dlls/kernelbase/thread.c#L85 | |
| 573 | instance.thread.thread_handle = blk: { | |
| 574 | var active_ctx: ?windows.HANDLE = undefined; | |
| 575 | // Note: Can return null on SUCCESS | |
| 576 | switch (windows.ntdll.RtlGetActiveActivationContext(&active_ctx)) { | |
| 577 | .SUCCESS => {}, | |
| 578 | else => |status| return windows.unexpectedStatus(status), | |
| 579 | } | |
| 580 | defer if (active_ctx) |ctx| windows.ntdll.RtlReleaseActivationContext(ctx); | |
| 581 | ||
| 582 | var teb: *windows.TEB = undefined; | |
| 583 | var attr_list = windows.PS.ATTRIBUTE.LIST{ | |
| 584 | .TotalLength = @sizeOf(windows.PS.ATTRIBUTE.LIST), | |
| 585 | .Attributes = .{ | |
| 586 | .{ | |
| 587 | .Attribute = .TEB_ADDRESS, | |
| 588 | .Size = @sizeOf(*windows.TEB), | |
| 589 | .u = .{ | |
| 590 | .ValuePtr = @ptrCast(&teb), | |
| 591 | }, | |
| 592 | .ReturnLength = null, | |
| 593 | }, | |
| 594 | }, | |
| 595 | }; | |
| 596 | ||
| 597 | var thread_handle: windows.HANDLE = undefined; | |
| 598 | switch (windows.ntdll.NtCreateThreadEx( | |
| 599 | &thread_handle, | |
| 600 | .{ .MAXIMUM_ALLOWED = true }, | |
| 601 | &.{}, | |
| 602 | windows.GetCurrentProcess(), | |
| 603 | Instance.entryFn, | |
| 604 | instance, | |
| 605 | .{ .CREATE_SUSPENDED = true }, | |
| 606 | 0, | |
| 607 | @enumFromInt(stack_size), | |
| 608 | .default, | |
| 609 | &attr_list, | |
| 610 | )) { | |
| 611 | .SUCCESS => {}, | |
| 612 | else => |status| return windows.unexpectedStatus(status), | |
| 613 | } | |
| 614 | ||
| 615 | if (active_ctx) |ctx| { | |
| 616 | var cookie: windows.ULONG = 0; | |
| 617 | switch (windows.ntdll.RtlActivateActivationContextEx(0, teb, ctx, &cookie)) { | |
| 618 | .SUCCESS => {}, | |
| 619 | else => |status| return windows.unexpectedStatus(status), | |
| 620 | } | |
| 621 | } | |
| 622 | ||
| 623 | switch (windows.ntdll.NtResumeThread(thread_handle, null)) { | |
| 624 | .SUCCESS => {}, | |
| 625 | else => |status| return windows.unexpectedStatus(status), | |
| 626 | } | |
| 627 | ||
| 628 | break :blk thread_handle; | |
| 572 | 629 | }; |
| 573 | 630 | |
| 574 | 631 | return Impl{ .thread = &instance.thread }; |
lib/std/os/windows.zig+88| ... | ... | @@ -23,6 +23,75 @@ pub const nls = @import("windows/nls.zig"); |
| 23 | 23 | |
| 24 | 24 | pub const current_process: HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1)))); |
| 25 | 25 | |
| 26 | pub const PS = struct { | |
| 27 | pub const ATTRIBUTE = extern struct { | |
| 28 | Attribute: Type, | |
| 29 | Size: SIZE_T, | |
| 30 | u: extern union { | |
| 31 | Value: ULONG_PTR, | |
| 32 | ValuePtr: PVOID, | |
| 33 | }, | |
| 34 | ReturnLength: ?*SIZE_T, | |
| 35 | ||
| 36 | /// https://ntdoc.m417z.com/ps_attribute_num | |
| 37 | /// Tag type is `u16` based on PS_ATTRIBUTE_NUMBER_MASK being 0xFFFF | |
| 38 | pub const NUM = enum(u16) { | |
| 39 | ParentProcess = 0, | |
| 40 | DebugObject, | |
| 41 | Token, | |
| 42 | ClientId, | |
| 43 | TebAddress, | |
| 44 | ImageName, | |
| 45 | ImageInfo, | |
| 46 | MemoryReserve, | |
| 47 | PriorityClass, | |
| 48 | ErrorMode, | |
| 49 | StdHandleInfo, | |
| 50 | HandleList, | |
| 51 | GroupAffinity, | |
| 52 | PreferredNode, | |
| 53 | IdealProcessor, | |
| 54 | UmsThread, | |
| 55 | MitigationOptions, | |
| 56 | ProtectionLevel, | |
| 57 | SecureProcess, | |
| 58 | JobList, | |
| 59 | ChildProcessPolicy, | |
| 60 | AllApplicationPackagesPolicy, | |
| 61 | Win32kFilter, | |
| 62 | SafeOpenPromptOriginClaim, | |
| 63 | BnoIsolation, | |
| 64 | DesktopAppPolicy, | |
| 65 | Chpe, | |
| 66 | MitigationAuditOptions, | |
| 67 | MachineType, | |
| 68 | ComponentFilter, | |
| 69 | EnableOptionalXStateFeatures, | |
| 70 | SupportedMachines, | |
| 71 | SveVectorLength, | |
| 72 | }; | |
| 73 | ||
| 74 | /// https://ntdoc.m417z.com/psattributevalue | |
| 75 | pub const Type = enum(ULONG_PTR) { | |
| 76 | TEB_ADDRESS = construct(.TebAddress, true, false, false), | |
| 77 | _, | |
| 78 | ||
| 79 | pub fn construct(num: NUM, thread: bool, input: bool, additive: bool) ULONG_PTR { | |
| 80 | var val: ULONG_PTR = @intFromEnum(num); | |
| 81 | if (thread) val |= 0x10000; | |
| 82 | if (input) val |= 0x20000; | |
| 83 | if (additive) val |= 0x40000; | |
| 84 | return val; | |
| 85 | } | |
| 86 | }; | |
| 87 | ||
| 88 | pub const LIST = extern struct { | |
| 89 | TotalLength: SIZE_T, | |
| 90 | Attributes: [1]ATTRIBUTE, | |
| 91 | }; | |
| 92 | }; | |
| 93 | }; | |
| 94 | ||
| 26 | 95 | pub const OBJECT = struct { |
| 27 | 96 | // ref: um/winternl.h |
| 28 | 97 | |
| ... | ... | @@ -1251,6 +1320,24 @@ pub const THREAD = struct { |
| 1251 | 1320 | Priority: KPRIORITY, |
| 1252 | 1321 | BasePriority: KPRIORITY, |
| 1253 | 1322 | }; |
| 1323 | ||
| 1324 | pub const CREATE_FLAGS = packed struct(ULONG) { | |
| 1325 | CREATE_SUSPENDED: bool = false, | |
| 1326 | SKIP_THREAD_ATTACH: bool = false, | |
| 1327 | HIDE_FROM_DEBUGGER: bool = false, | |
| 1328 | LOADER_WORKER: bool = false, | |
| 1329 | SKIP_LOADER_INIT: bool = false, | |
| 1330 | BYPASS_PROCESS_FREEZE: bool = false, | |
| 1331 | Reserved6: u26 = 0, | |
| 1332 | ||
| 1333 | pub const NONE: CREATE_FLAGS = .{}; | |
| 1334 | }; | |
| 1335 | ||
| 1336 | pub const StackSize = enum(SIZE_T) { | |
| 1337 | /// The default size specified in the executable header | |
| 1338 | default = 0, | |
| 1339 | _, | |
| 1340 | }; | |
| 1254 | 1341 | }; |
| 1255 | 1342 | |
| 1256 | 1343 | pub const MEMORY = struct { |
| ... | ... | @@ -3436,6 +3523,7 @@ pub const STARTF_USESIZE = 0x00000002; |
| 3436 | 3523 | pub const STARTF_USESTDHANDLES = 0x00000100; |
| 3437 | 3524 | |
| 3438 | 3525 | pub const THREAD_START_ROUTINE = fn (LPVOID) callconv(.winapi) DWORD; |
| 3526 | pub const USER_THREAD_START_ROUTINE = fn (LPVOID) callconv(.winapi) NTSTATUS; | |
| 3439 | 3527 | |
| 3440 | 3528 | pub const SYSTEM_INFO = extern struct { |
| 3441 | 3529 | anon1: extern union { |
lib/std/os/windows/kernel32.zig-10| ... | ... | @@ -25,13 +25,3 @@ pub extern "kernel32" fn CreateProcessW( |
| 25 | 25 | lpStartupInfo: *STARTUPINFOW, |
| 26 | 26 | lpProcessInformation: *PROCESS.INFORMATION, |
| 27 | 27 | ) callconv(.winapi) BOOL; |
| 28 | ||
| 29 | // TODO: CreateRemoteThread with hProcess=NtCurrentProcess(). | |
| 30 | pub extern "kernel32" fn CreateThread( | |
| 31 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, | |
| 32 | dwStackSize: SIZE_T, | |
| 33 | lpStartAddress: *const THREAD_START_ROUTINE, | |
| 34 | lpParameter: ?LPVOID, | |
| 35 | dwCreationFlags: DWORD, | |
| 36 | lpThreadId: ?*DWORD, | |
| 37 | ) callconv(.winapi) ?HANDLE; |
lib/std/os/windows/ntdll.zig+41| ... | ... | @@ -55,6 +55,9 @@ const UNWIND_HISTORY_TABLE = windows.UNWIND_HISTORY_TABLE; |
| 55 | 55 | const USHORT = windows.USHORT; |
| 56 | 56 | const VECTORED_EXCEPTION_HANDLER = windows.VECTORED_EXCEPTION_HANDLER; |
| 57 | 57 | const WORD = windows.WORD; |
| 58 | const USER_THREAD_START_ROUTINE = windows.USER_THREAD_START_ROUTINE; | |
| 59 | const PS = windows.PS; | |
| 60 | const TEB = windows.TEB; | |
| 58 | 61 | |
| 59 | 62 | // ref: km/ntifs.h |
| 60 | 63 | |
| ... | ... | @@ -359,6 +362,21 @@ pub extern "ntdll" fn NtQuerySystemInformation( |
| 359 | 362 | |
| 360 | 363 | // ref none |
| 361 | 364 | |
| 365 | pub extern "ntdll" fn RtlGetActiveActivationContext( | |
| 366 | ActivationContext: *?HANDLE, | |
| 367 | ) callconv(.winapi) NTSTATUS; | |
| 368 | ||
| 369 | pub extern "ntdll" fn RtlActivateActivationContextEx( | |
| 370 | Flags: ULONG, | |
| 371 | Teb: *TEB, | |
| 372 | ActivationContext: HANDLE, | |
| 373 | Cookie: *ULONG, | |
| 374 | ) callconv(.winapi) NTSTATUS; | |
| 375 | ||
| 376 | pub extern "ntdll" fn RtlReleaseActivationContext( | |
| 377 | ActivationContext: HANDLE, | |
| 378 | ) callconv(.winapi) void; | |
| 379 | ||
| 362 | 380 | pub extern "ntdll" fn LdrAddRefDll( |
| 363 | 381 | Flags: ULONG, |
| 364 | 382 | DllHandle: PVOID, |
| ... | ... | @@ -759,3 +777,26 @@ pub extern "ntdll" fn NtLoadKeyEx( |
| 759 | 777 | RootHandle: ?*HANDLE, |
| 760 | 778 | Reserved: ?*anyopaque, |
| 761 | 779 | ) callconv(.winapi) NTSTATUS; |
| 780 | ||
| 781 | pub extern "ntdll" fn NtCreateThreadEx( | |
| 782 | ThreadHandle: *HANDLE, | |
| 783 | DesiredAccess: ACCESS_MASK, | |
| 784 | ObjectAttributes: *const OBJECT.ATTRIBUTES, | |
| 785 | ProcessHandle: HANDLE, | |
| 786 | StartRoutine: *const USER_THREAD_START_ROUTINE, | |
| 787 | Argument: ?PVOID, | |
| 788 | CreateFlags: THREAD.CREATE_FLAGS, | |
| 789 | ZeroBits: SIZE_T, | |
| 790 | /// This value is rounded up to the nearest page. | |
| 791 | /// If this value is larger than `StackReserve`, the reserved stack | |
| 792 | /// size will be the rounded value of this parameter. | |
| 793 | /// https://learn.microsoft.com/en-us/windows/win32/procthread/thread-stack-size | |
| 794 | StackCommit: THREAD.StackSize, | |
| 795 | StackReserve: THREAD.StackSize, | |
| 796 | AttributeList: ?*PS.ATTRIBUTE.LIST, | |
| 797 | ) callconv(.winapi) NTSTATUS; | |
| 798 | ||
| 799 | pub extern "ntdll" fn NtResumeThread( | |
| 800 | ThreadHandle: HANDLE, | |
| 801 | PreviousSuspendCount: ?*ULONG, | |
| 802 | ) callconv(.winapi) NTSTATUS; |