| 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,14 +405,19 @@ const Completion = std.atomic.Value(enum(if (builtin.zig_backend == .stage2_risc |
| 405 | /// Performs implementation-agnostic thread setup (`maybeAttachSignalStack`), then calls the given | 405 | /// Performs implementation-agnostic thread setup (`maybeAttachSignalStack`), then calls the given |
| 406 | /// thread entry point `f` with `args` and handles the result. | 406 | /// thread entry point `f` with `args` and handles the result. |
| 407 | fn callFn(comptime f: anytype, args: anytype) switch (Impl) { | 407 | fn callFn(comptime f: anytype, args: anytype) switch (Impl) { |
| 408 | WindowsThreadImpl => windows.DWORD, | 408 | WindowsThreadImpl => windows.NTSTATUS, |
| 409 | LinuxThreadImpl => u8, | 409 | LinuxThreadImpl => u8, |
| 410 | PosixThreadImpl => ?*anyopaque, | 410 | PosixThreadImpl => ?*anyopaque, |
| 411 | else => unreachable, | 411 | else => unreachable, |
| 412 | } { | 412 | } { |
| 413 | maybeAttachSignalStack(); | 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 | const bad_fn_ret = "expected return type of startFn to be 'u8', 'noreturn', '!noreturn', 'void', or '!void'"; | 421 | const bad_fn_ret = "expected return type of startFn to be 'u8', 'noreturn', '!noreturn', 'void', or '!void'"; |
| 417 | 422 | ||
| 418 | switch (@typeInfo(@typeInfo(@TypeOf(f)).@"fn".return_type.?)) { | 423 | switch (@typeInfo(@typeInfo(@TypeOf(f)).@"fn".return_type.?)) { |
| ... | @@ -429,12 +434,13 @@ fn callFn(comptime f: anytype, args: anytype) switch (Impl) { | ... | @@ -429,12 +434,13 @@ fn callFn(comptime f: anytype, args: anytype) switch (Impl) { |
| 429 | } | 434 | } |
| 430 | 435 | ||
| 431 | const status = @call(.auto, f, args); | 436 | const status = @call(.auto, f, args); |
| 432 | if (Impl != PosixThreadImpl) { | 437 | switch (Impl) { |
| 433 | return status; | 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 | .error_union => |info| { | 445 | .error_union => |info| { |
| 440 | switch (info.payload) { | 446 | switch (info.payload) { |
| ... | @@ -526,7 +532,7 @@ const WindowsThreadImpl = struct { | ... | @@ -526,7 +532,7 @@ const WindowsThreadImpl = struct { |
| 526 | fn_args: Args, | 532 | fn_args: Args, |
| 527 | thread: ThreadCompletion, | 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 | const self: *@This() = @ptrCast(@alignCast(raw_ptr)); | 536 | const self: *@This() = @ptrCast(@alignCast(raw_ptr)); |
| 531 | defer switch (self.thread.completion.swap(.completed, .seq_cst)) { | 537 | defer switch (self.thread.completion.swap(.completed, .seq_cst)) { |
| 532 | .running => {}, | 538 | .running => {}, |
| ... | @@ -559,16 +565,67 @@ const WindowsThreadImpl = struct { | ... | @@ -559,16 +565,67 @@ const WindowsThreadImpl = struct { |
| 559 | // Its also fine if the limit here is incorrect as stack size is only a hint. | 565 | // Its also fine if the limit here is incorrect as stack size is only a hint. |
| 560 | const stack_size = @max(64 * 1024, std.math.lossyCast(u32, config.stack_size)); | 566 | const stack_size = @max(64 * 1024, std.math.lossyCast(u32, config.stack_size)); |
| 561 | 567 | ||
| 562 | instance.thread.thread_handle = windows.kernel32.CreateThread( | 568 | // Intended to be equivalent to a kernel32.CreateThread call with no flags set. |
| 563 | null, | 569 | // However, CreateThread is just a wrapper around CreateRemoteThreadEx, |
| 564 | stack_size, | 570 | // so that's the more relevant function in this context. |
| 565 | Instance.entryFn, | 571 | // |
| 566 | instance, | 572 | // https://github.com/wine-mirror/wine/blob/3d128be6400b3869119d293d0c8fa9e7702978f8/dlls/kernelbase/thread.c#L85 |
| 567 | 0, | 573 | instance.thread.thread_handle = blk: { |
| 568 | null, | 574 | var active_ctx: ?windows.HANDLE = undefined; |
| 569 | ) orelse { | 575 | // Note: Can return null on SUCCESS |
| 570 | const errno = windows.GetLastError(); | 576 | switch (windows.ntdll.RtlGetActiveActivationContext(&active_ctx)) { |
| 571 | return windows.unexpectedError(errno); | 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 | return Impl{ .thread = &instance.thread }; | 631 | return Impl{ .thread = &instance.thread }; |
lib/std/os/windows.zig+88| ... | @@ -23,6 +23,75 @@ pub const nls = @import("windows/nls.zig"); | ... | @@ -23,6 +23,75 @@ pub const nls = @import("windows/nls.zig"); |
| 23 | 23 | ||
| 24 | pub const current_process: HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1)))); | 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 | pub const OBJECT = struct { | 95 | pub const OBJECT = struct { |
| 27 | // ref: um/winternl.h | 96 | // ref: um/winternl.h |
| 28 | 97 | ||
| ... | @@ -1251,6 +1320,24 @@ pub const THREAD = struct { | ... | @@ -1251,6 +1320,24 @@ pub const THREAD = struct { |
| 1251 | Priority: KPRIORITY, | 1320 | Priority: KPRIORITY, |
| 1252 | BasePriority: KPRIORITY, | 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 | pub const MEMORY = struct { | 1343 | pub const MEMORY = struct { |
| ... | @@ -3436,6 +3523,7 @@ pub const STARTF_USESIZE = 0x00000002; | ... | @@ -3436,6 +3523,7 @@ pub const STARTF_USESIZE = 0x00000002; |
| 3436 | pub const STARTF_USESTDHANDLES = 0x00000100; | 3523 | pub const STARTF_USESTDHANDLES = 0x00000100; |
| 3437 | 3524 | ||
| 3438 | pub const THREAD_START_ROUTINE = fn (LPVOID) callconv(.winapi) DWORD; | 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 | pub const SYSTEM_INFO = extern struct { | 3528 | pub const SYSTEM_INFO = extern struct { |
| 3441 | anon1: extern union { | 3529 | anon1: extern union { |
lib/std/os/windows/kernel32.zig-10| ... | @@ -25,13 +25,3 @@ pub extern "kernel32" fn CreateProcessW( | ... | @@ -25,13 +25,3 @@ pub extern "kernel32" fn CreateProcessW( |
| 25 | lpStartupInfo: *STARTUPINFOW, | 25 | lpStartupInfo: *STARTUPINFOW, |
| 26 | lpProcessInformation: *PROCESS.INFORMATION, | 26 | lpProcessInformation: *PROCESS.INFORMATION, |
| 27 | ) callconv(.winapi) BOOL; | 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,6 +55,9 @@ const UNWIND_HISTORY_TABLE = windows.UNWIND_HISTORY_TABLE; |
| 55 | const USHORT = windows.USHORT; | 55 | const USHORT = windows.USHORT; |
| 56 | const VECTORED_EXCEPTION_HANDLER = windows.VECTORED_EXCEPTION_HANDLER; | 56 | const VECTORED_EXCEPTION_HANDLER = windows.VECTORED_EXCEPTION_HANDLER; |
| 57 | const WORD = windows.WORD; | 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 | // ref: km/ntifs.h | 62 | // ref: km/ntifs.h |
| 60 | 63 | ||
| ... | @@ -359,6 +362,21 @@ pub extern "ntdll" fn NtQuerySystemInformation( | ... | @@ -359,6 +362,21 @@ pub extern "ntdll" fn NtQuerySystemInformation( |
| 359 | 362 | ||
| 360 | // ref none | 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 | pub extern "ntdll" fn LdrAddRefDll( | 380 | pub extern "ntdll" fn LdrAddRefDll( |
| 363 | Flags: ULONG, | 381 | Flags: ULONG, |
| 364 | DllHandle: PVOID, | 382 | DllHandle: PVOID, |
| ... | @@ -759,3 +777,26 @@ pub extern "ntdll" fn NtLoadKeyEx( | ... | @@ -759,3 +777,26 @@ pub extern "ntdll" fn NtLoadKeyEx( |
| 759 | RootHandle: ?*HANDLE, | 777 | RootHandle: ?*HANDLE, |
| 760 | Reserved: ?*anyopaque, | 778 | Reserved: ?*anyopaque, |
| 761 | ) callconv(.winapi) NTSTATUS; | 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; |