From b5bd4946067d3796855e0b4b12219dfc28e15e8f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 6 Feb 2026 04:36:32 -0500 Subject: [PATCH] std.Threaded: replace more kernel32 functions with ntdll --- ci/x86_64-windows-debug.ps1 | 35 +- ci/x86_64-windows-release.ps1 | 37 +- lib/compiler/build_runner.zig | 2 +- lib/fuzzer.zig | 2 +- lib/std/Build/Watch.zig | 278 ++-- lib/std/Io/Threaded.zig | 992 +++++++-------- lib/std/Io/Threaded/test.zig | 2 +- lib/std/Thread.zig | 16 +- lib/std/debug.zig | 45 +- lib/std/debug/Dwarf/SelfUnwinder.zig | 3 +- lib/std/debug/SelfInfo/Elf.zig | 16 +- lib/std/debug/SelfInfo/MachO.zig | 20 +- lib/std/debug/SelfInfo/Windows.zig | 237 ++-- lib/std/heap.zig | 6 +- lib/std/os/windows.zig | 1693 +++++++++---------------- lib/std/os/windows/kernel32.zig | 201 +-- lib/std/os/windows/ntdll.zig | 247 ++-- lib/std/os/windows/win32error.zig | 2 +- lib/std/process.zig | 8 +- lib/std/process/Child.zig | 2 +- lib/std/start.zig | 14 +- lib/std/zig/system/windows.zig | 10 +- lib/zig.h | 28 +- stage1/zig.h | 28 +- test/standalone/coff_dwarf/main.zig | 7 +- test/standalone/windows_argv/fuzz.zig | 18 +- test/standalone/windows_argv/lib.zig | 6 +- 27 files changed, 1694 insertions(+), 2261 deletions(-) diff --git a/ci/x86_64-windows-debug.ps1 b/ci/x86_64-windows-debug.ps1 index e6ba8b0861..b1486f31bc 100644 --- a/ci/x86_64-windows-debug.ps1 +++ b/ci/x86_64-windows-debug.ps1 @@ -35,7 +35,7 @@ Set-Location -Path 'build-debug' # CMake gives a syntax error when file paths with backward slashes are used. # Here, we use forward slashes only to work around this. -& cmake .. ` +cmake .. ` -GNinja ` -DCMAKE_INSTALL_PREFIX="stage3-debug" ` -DCMAKE_PREFIX_PATH="$($PREFIX_PATH -Replace "\\", "/")" ` @@ -54,7 +54,7 @@ ninja install CheckLastExitCode Write-Output "Main test suite..." -& "stage3-debug\bin\zig.exe" build test docs ` +stage3-debug\bin\zig build test docs ` --maxrss $ZSF_MAX_RSS ` --zig-lib-dir "$ZIG_LIB_DIR" ` --search-prefix "$PREFIX_PATH" ` @@ -66,26 +66,25 @@ Write-Output "Main test suite..." CheckLastExitCode Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." -& "stage3-debug\bin\zig.exe" test ` - ..\test\behavior.zig ` - --zig-lib-dir "$ZIG_LIB_DIR" ` - -ofmt=c ` - -femit-bin="test-x86_64-windows-msvc.c" ` - --test-no-exec ` - -target x86_64-windows-msvc ` - -lc -CheckLastExitCode - -& "stage3-debug\bin\zig.exe" build-obj ` +stage3-debug\bin\zig build-obj ` --zig-lib-dir "$ZIG_LIB_DIR" ` -ofmt=c ` -OReleaseSmall ` --name compiler_rt ` -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` - --dep build_options ` -target x86_64-windows-msvc ` - -Mroot="..\lib\compiler_rt.zig" ` - -Mbuild_options="config.zig" + -lc ` + ..\lib\compiler_rt.zig +CheckLastExitCode + +stage3-debug\bin\zig test ` + --zig-lib-dir "$ZIG_LIB_DIR" ` + -ofmt=c ` + -femit-bin="behavior-x86_64-windows-msvc.c" ` + --test-no-exec ` + -target x86_64-windows-msvc ` + -lc ` + ..\test\behavior.zig CheckLastExitCode Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" @@ -97,8 +96,8 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files (x86)\Microsoft Visual Studio\ CheckLastExitCode Write-Output "Build and run behavior tests with msvc..." -& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib +cl /I..\lib /W3 /Z7 behavior-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /link /nologo /debug /subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib CheckLastExitCode -& .\test-x86_64-windows-msvc.exe +.\behavior-x86_64-windows-msvc CheckLastExitCode diff --git a/ci/x86_64-windows-release.ps1 b/ci/x86_64-windows-release.ps1 index e22e3af1c7..a8674d1d3d 100644 --- a/ci/x86_64-windows-release.ps1 +++ b/ci/x86_64-windows-release.ps1 @@ -35,7 +35,7 @@ Set-Location -Path 'build-release' # CMake gives a syntax error when file paths with backward slashes are used. # Here, we use forward slashes only to work around this. -& cmake .. ` +cmake .. ` -GNinja ` -DCMAKE_INSTALL_PREFIX="stage3-release" ` -DCMAKE_PREFIX_PATH="$($PREFIX_PATH -Replace "\\", "/")" ` @@ -54,7 +54,7 @@ ninja install CheckLastExitCode Write-Output "Main test suite..." -& "stage3-release\bin\zig.exe" build test docs ` +stage3-release\bin\zig.exe build test docs ` --maxrss $ZSF_MAX_RSS ` --zig-lib-dir "$ZIG_LIB_DIR" ` --search-prefix "$PREFIX_PATH" ` @@ -67,7 +67,7 @@ CheckLastExitCode # Ensure that stage3 and stage4 are byte-for-byte identical. Write-Output "Build and compare stage4..." -& "stage3-release\bin\zig.exe" build ` +stage3-release\bin\zig.exe build ` --prefix stage4-release ` -Denable-llvm ` -Dno-lib ` @@ -85,26 +85,25 @@ Compare-Object (Get-Content stage3-release\bin\zig.exe) (Get-Content stage4-rele CheckLastExitCode Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." -& "stage3-release\bin\zig.exe" test ` - ..\test\behavior.zig ` - --zig-lib-dir "$ZIG_LIB_DIR" ` - -ofmt=c ` - -femit-bin="test-x86_64-windows-msvc.c" ` - --test-no-exec ` - -target x86_64-windows-msvc ` - -lc -CheckLastExitCode - -& "stage3-release\bin\zig.exe" build-obj ` +stage3-release\bin\zig.exe build-obj ` --zig-lib-dir "$ZIG_LIB_DIR" ` -ofmt=c ` -OReleaseSmall ` --name compiler_rt ` -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` - --dep build_options ` -target x86_64-windows-msvc ` - -Mroot="..\lib\compiler_rt.zig" ` - -Mbuild_options="config.zig" + -lc ` + ..\lib\compiler_rt.zig +CheckLastExitCode + +stage3-release\bin\zig.exe test ` + --zig-lib-dir "$ZIG_LIB_DIR" ` + -ofmt=c ` + -femit-bin="behavior-x86_64-windows-msvc.c" ` + --test-no-exec ` + -target x86_64-windows-msvc ` + -lc ` + ..\test\behavior.zig CheckLastExitCode Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" @@ -116,8 +115,8 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files (x86)\Microsoft Visual Studio\ CheckLastExitCode Write-Output "Build and run behavior tests with msvc..." -& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib +cl /I..\lib /W3 /Z7 behavior-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /link /nologo /debug /subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib CheckLastExitCode -& .\test-x86_64-windows-msvc.exe +.\behavior-x86_64-windows-msvc CheckLastExitCode diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 4aa513ec74..faf81194a9 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -621,7 +621,7 @@ pub fn main(init: process.Init.Minimal) !void { }) catch &caption_buf; var debouncing_node = main_progress_node.start(caption, 0); var in_debounce = false; - while (true) switch (try w.wait(gpa, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) { + while (true) switch (try w.wait(gpa, io, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) { .timeout => { assert(in_debounce); debouncing_node.end(); diff --git a/lib/fuzzer.zig b/lib/fuzzer.zig index 37ca752d1f..9e0c5c6ad6 100644 --- a/lib/fuzzer.zig +++ b/lib/fuzzer.zig @@ -632,7 +632,7 @@ export fn fuzzer_main(limit_kind: abi.LimitKind, amount: u64) void { export fn fuzzer_unslide_address(addr: usize) usize { const si = std.debug.getSelfDebugInfo() catch @compileError("unsupported"); - const slide = si.getModuleSlide(std.debug.getDebugInfoAllocator(), io, addr) catch |err| { + const slide = si.getModuleSlide(io, addr) catch |err| { std.debug.panic("failed to find virtual address slide: {t}", .{err}); }; return addr - slide; diff --git a/lib/std/Build/Watch.zig b/lib/std/Build/Watch.zig index 15ccfcfdf9..c559210b92 100644 --- a/lib/std/Build/Watch.zig +++ b/lib/std/Build/Watch.zig @@ -180,7 +180,7 @@ const Os = switch (builtin.os.tag) { const gop = try w.dir_table.getOrPut(gpa, path); if (!gop.found_existing) { var mount_id: MountId = undefined; - const dir_handle = Os.getDirHandle(gpa, path, &mount_id) catch |err| switch (err) { + const dir_handle = getDirHandle(gpa, path, &mount_id) catch |err| switch (err) { error.FileNotFound => { std.debug.assert(w.dir_table.swapRemove(path)); continue; @@ -291,12 +291,13 @@ const Os = switch (builtin.os.tag) { w.dir_count = w.dir_table.count(); } - fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { + fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { + _ = io; const events_len = try std.posix.poll(w.os.poll_fds.values(), timeout.to_i32_ms()); if (events_len == 0) return .timeout; for (w.os.poll_fds.values()) |poll_fd| { - if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try Os.markDirtySteps(w, gpa, poll_fd.fd)) + if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try markDirtySteps(w, gpa, poll_fd.fd)) return .dirty; } return .clean; @@ -306,12 +307,8 @@ const Os = switch (builtin.os.tag) { const windows = std.os.windows; /// Keyed differently but indexes correspond 1:1 with `dir_table`. - handle_table: HandleTable, - dir_list: std.AutoArrayHashMapUnmanaged(usize, *Directory), - io_cp: ?windows.HANDLE, - counter: usize = 0, - - const HandleTable = std.AutoArrayHashMapUnmanaged(FileId, ReactionSet); + handle_table: std.ArrayHashMapUnmanaged(*Directory, void, Directory.TableAdapter, false), + ready_dirs: std.DoublyLinkedList, const FileId = struct { volumeSerialNumber: windows.ULONG, @@ -319,55 +316,61 @@ const Os = switch (builtin.os.tag) { }; const Directory = struct { - handle: windows.HANDLE, + reaction_set: ReactionSet, id: FileId, - overlapped: windows.OVERLAPPED, + file: Io.File, + state: enum { idle, listening, ready }, + iosb: windows.IO_STATUS_BLOCK, // 64 KB is the packet size limit when monitoring over a network. // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw#remarks - buffer: [64 * 1024]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined, + buffer: [64 * 1024]u8 align(@alignOf(windows.FILE.NOTIFY.INFORMATION)), + ready_node: std.DoublyLinkedList.Node, /// Start listening for events, buffer field will be overwritten eventually. - fn startListening(self: *@This()) !void { - const r = windows.kernel32.ReadDirectoryChangesW( - self.handle, - @ptrCast(&self.buffer), - self.buffer.len, - 0, + fn startListening(dir: *Directory, w: *Watch) !void { + assert(dir.file.flags.nonblocking); + assert(dir.state == .idle); + switch (windows.ntdll.NtNotifyChangeDirectoryFileEx( + dir.file.handle, + null, + ¬ifyApc, + w, + &dir.iosb, + &dir.buffer, + dir.buffer.len, .{ - .creation = true, - .dir_name = true, - .file_name = true, - .last_write = true, - .size = true, + .FILE_NAME = true, + .DIR_NAME = true, + .SIZE = true, + .LAST_WRITE = true, + .CREATION = true, }, - null, - &self.overlapped, - null, - ); - if (r == windows.FALSE) { - switch (windows.GetLastError()) { - .INVALID_FUNCTION => return error.ReadDirectoryChangesUnsupported, - else => |err| return windows.unexpectedError(err), - } + windows.FALSE, + .Notify, + )) { + .SUCCESS, .PENDING => dir.state = .listening, + .ILLEGAL_FUNCTION => return error.ReadDirectoryChangesUnsupported, + else => |status| return windows.unexpectedStatus(status), } } - fn init(gpa: Allocator, path: Cache.Path) !*@This() { + fn notifyApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows.ULONG) callconv(.winapi) void { + const w: *Watch = @ptrCast(@alignCast(apc_context)); + const dir: *Directory = @fieldParentPtr("iosb", iosb); + assert(iosb.u.Status != .PENDING); + assert(dir.state == .listening); + w.os.ready_dirs.append(&dir.ready_node); + dir.state = .ready; + } + + fn init(gpa: Allocator, path: Cache.Path) !*Directory { // The following code is a drawn out NtCreateFile call. (mostly adapted from Io.Dir.makeOpenDirAccessMaskW) // It's necessary in order to get the specific flags that are required when calling ReadDirectoryChangesW. var dir_handle: windows.HANDLE = undefined; const root_fd = path.root_dir.handle.handle; const sub_path = path.subPathOrDot(); - const sub_path_w = try std.Io.Threaded.sliceToPrefixedFileW(root_fd, sub_path); // TODO eliminate this call - const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; - - var nt_name = windows.UNICODE_STRING{ - .Length = @intCast(path_len_bytes), - .MaximumLength = @intCast(path_len_bytes), - .Buffer = @constCast(sub_path_w.span().ptr), - }; + const sub_path_w = try Io.Threaded.sliceToPrefixedFileW(root_fd, sub_path); // TODO eliminate this call var iosb: windows.IO_STATUS_BLOCK = undefined; - switch (windows.ntdll.NtCreateFile( &dir_handle, .{ @@ -379,7 +382,7 @@ const Os = switch (builtin.os.tag) { }, &.{ .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else root_fd, - .ObjectName = &nt_name, + .ObjectName = @constCast(&sub_path_w.string()), }, &iosb, null, @@ -410,20 +413,49 @@ const Os = switch (builtin.os.tag) { const dir_id = try getFileId(dir_handle); - const dir_ptr = try gpa.create(@This()); - dir_ptr.* = .{ - .handle = dir_handle, + const dir = try gpa.create(Directory); + dir.* = .{ + .reaction_set = .empty, .id = dir_id, - .overlapped = std.mem.zeroes(windows.OVERLAPPED), + .file = .{ .handle = dir_handle, .flags = .{ .nonblocking = true } }, + .state = .idle, + .iosb = undefined, + .buffer = undefined, + .ready_node = undefined, }; - return dir_ptr; + return dir; } - fn deinit(self: *@This(), gpa: Allocator) void { - _ = windows.kernel32.CancelIo(self.handle); - windows.CloseHandle(self.handle); - gpa.destroy(self); + fn deinit(dir: *Directory, gpa: Allocator, w: *Watch) void { + state: switch (dir.state) { + .idle => {}, + .listening => { + var cancel_iosb: windows.IO_STATUS_BLOCK = undefined; + _ = windows.ntdll.NtCancelIoFileEx(dir.file.handle, &dir.iosb, &cancel_iosb); + while (switch (dir.state) { + .idle => unreachable, + .listening => true, + .ready => false, + }) Io.Threaded.waitForApcOrAlert(); + continue :state .ready; + }, + .ready => w.os.ready_dirs.remove(&dir.ready_node), + } + windows.CloseHandle(dir.file.handle); + gpa.destroy(dir); } + + /// Useful to make `*Directory` a key in `std.ArrayHashMap`. + const TableAdapter = struct { + pub fn hash(_: TableAdapter, lhs_dir: *Directory) u32 { + return @truncate(Hash.hash(lhs_dir.id.volumeSerialNumber, @ptrCast(&lhs_dir.id.indexNumber))); + } + pub fn eql(_: TableAdapter, lhs_dir: *Directory, rhs_dir: *Directory, rhs_index: usize) bool { + _ = rhs_index; + return lhs_dir.id.volumeSerialNumber == rhs_dir.id.volumeSerialNumber and + lhs_dir.id.indexNumber == rhs_dir.id.indexNumber; + } + }; }; fn init(cwd_path: []const u8) !Watch { @@ -433,9 +465,8 @@ const Os = switch (builtin.os.tag) { .dir_count = 0, .os = switch (builtin.os.tag) { .windows => .{ - .handle_table = .{}, - .dir_list = .{}, - .io_cp = null, + .handle_table = .empty, + .ready_dirs = .{}, }, else => {}, }, @@ -479,29 +510,22 @@ const Os = switch (builtin.os.tag) { fn markDirtySteps(w: *Watch, gpa: Allocator, dir: *Directory) !bool { var any_dirty = false; - const bytes_returned = try windows.GetOverlappedResult(dir.handle, &dir.overlapped, false); + const bytes_returned = dir.iosb.Information; if (bytes_returned == 0) { std.log.warn("file system watch queue overflowed; falling back to fstat", .{}); markAllFilesDirty(w, gpa); - try dir.startListening(); + try dir.startListening(w); return true; } var file_name_buf: [std.fs.max_path_bytes]u8 = undefined; - var notify: *align(1) windows.FILE_NOTIFY_INFORMATION = undefined; var offset: usize = 0; while (true) { - notify = @ptrCast(&dir.buffer[offset]); - const file_name_field: [*]u16 = @ptrFromInt(@intFromPtr(notify) + @sizeOf(windows.FILE_NOTIFY_INFORMATION)); - const file_name_len = std.unicode.wtf16LeToWtf8(&file_name_buf, file_name_field[0 .. notify.FileNameLength / 2]); - const file_name = file_name_buf[0..file_name_len]; - if (w.os.handle_table.getIndex(dir.id)) |reaction_set_i| { - const reaction_set = w.os.handle_table.values()[reaction_set_i]; - if (reaction_set.getPtr(".")) |glob_set| - any_dirty = markStepSetDirty(gpa, glob_set, any_dirty); - if (reaction_set.getPtr(file_name)) |step_set| { - any_dirty = markStepSetDirty(gpa, step_set, any_dirty); - } - } + const notify: *windows.FILE.NOTIFY.INFORMATION = @ptrCast(@alignCast(&dir.buffer[offset])); + const file_name = file_name_buf[0..std.unicode.wtf16LeToWtf8(&file_name_buf, notify.fileName())]; + if (dir.reaction_set.getPtr(".")) |glob_set| + any_dirty = markStepSetDirty(gpa, glob_set, any_dirty); + if (dir.reaction_set.getPtr(file_name)) |step_set| + any_dirty = markStepSetDirty(gpa, step_set, any_dirty); if (notify.NextEntryOffset == 0) break; @@ -509,7 +533,7 @@ const Os = switch (builtin.os.tag) { } // We call this now since at this point we have finished reading dir.buffer. - try dir.startListening(); + try dir.startListening(w); return any_dirty; } @@ -517,41 +541,32 @@ const Os = switch (builtin.os.tag) { // Add missing marks and note persisted ones. for (steps) |step| { for (step.inputs.table.keys(), step.inputs.table.values()) |path, *files| { - const reaction_set = rs: { + const dir = dir: { const gop = try w.dir_table.getOrPut(gpa, path); if (!gop.found_existing) { - const dir = try Os.Directory.init(gpa, path); - errdefer dir.deinit(gpa); + const dir: *Directory = try .init(gpa, path); + errdefer dir.deinit(gpa, w); // `dir.id` may already be present in the table in // the case that we have multiple Cache.Path instances // that compare inequal but ultimately point to the same // directory on the file system. // In such case, we must revert adding this directory, but keep // the additions to the step set. - const dh_gop = try w.os.handle_table.getOrPut(gpa, dir.id); + const dh_gop = try w.os.handle_table.getOrPut(gpa, dir); if (dh_gop.found_existing) { - dir.deinit(gpa); + dir.deinit(gpa, w); _ = w.dir_table.pop(); + break :dir w.os.handle_table.keys()[dh_gop.index]; } else { assert(dh_gop.index == gop.index); - dh_gop.value_ptr.* = .{}; - try dir.startListening(); - const key = w.os.counter; - w.os.counter +%= 1; - try w.os.dir_list.put(gpa, key, dir); - w.os.io_cp = try windows.CreateIoCompletionPort( - dir.handle, - w.os.io_cp, - key, - 0, - ); + try dir.startListening(w); + break :dir dir; } - break :rs &w.os.handle_table.values()[dh_gop.index]; } - break :rs &w.os.handle_table.values()[gop.index]; + break :dir w.os.handle_table.keys()[gop.index]; }; for (files.items) |basename| { - const gop = try reaction_set.getOrPut(gpa, basename); + const gop = try dir.reaction_set.getOrPut(gpa, basename); if (!gop.found_existing) gop.value_ptr.* = .{}; try gop.value_ptr.put(gpa, step, w.generation); } @@ -562,11 +577,11 @@ const Os = switch (builtin.os.tag) { // Remove marks for files that are no longer inputs. var i: usize = 0; while (i < w.os.handle_table.entries.len) { + const dir = w.os.handle_table.keys()[i]; { - const reaction_set = &w.os.handle_table.values()[i]; var step_set_i: usize = 0; - while (step_set_i < reaction_set.entries.len) { - const step_set = &reaction_set.values()[step_set_i]; + while (step_set_i < dir.reaction_set.entries.len) { + const step_set = &dir.reaction_set.values()[step_set_i]; var dirent_i: usize = 0; while (dirent_i < step_set.entries.len) { const generations = step_set.values(); @@ -580,53 +595,45 @@ const Os = switch (builtin.os.tag) { step_set_i += 1; continue; } - reaction_set.swapRemoveAt(step_set_i); + dir.reaction_set.swapRemoveAt(step_set_i); } - if (reaction_set.entries.len > 0) { + if (dir.reaction_set.entries.len > 0) { i += 1; continue; } } - w.os.dir_list.values()[i].deinit(gpa); - w.os.dir_list.swapRemoveAt(i); w.dir_table.swapRemoveAt(i); w.os.handle_table.swapRemoveAt(i); + dir.deinit(gpa, w); } w.generation +%= 1; } w.dir_count = w.dir_table.count(); } - fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { - var bytes_transferred: std.os.windows.DWORD = undefined; - var key: usize = undefined; - var overlapped_ptr: ?*std.os.windows.OVERLAPPED = undefined; - return while (true) switch (std.os.windows.GetQueuedCompletionStatus( - w.os.io_cp.?, - &bytes_transferred, - &key, - &overlapped_ptr, - @bitCast(timeout.to_i32_ms()), - )) { - .Normal => { - if (bytes_transferred == 0) - break error.Unexpected; - - // This 'orelse' detects a race condition that happens when we receive a - // completion notification for a directory that no longer exists in our list. - const dir = w.os.dir_list.get(key) orelse break .clean; - - break if (try Os.markDirtySteps(w, gpa, dir)) - .dirty - else - .clean; - }, - .Timeout => break .timeout, - // This status is issued because CancelIo was called, skip and try again. - .Canceled => continue, - else => break error.Unexpected, - }; + fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { + for (0..2) |attempt| { + while (w.os.ready_dirs.popFirst()) |ready_node| { + const dir: *Directory = @fieldParentPtr("ready_node", ready_node); + assert(dir.state == .ready); + dir.state = .idle; + switch (dir.iosb.u.Status) { + .SUCCESS => return if (try markDirtySteps(w, gpa, dir)) .dirty else .clean, + .PENDING => unreachable, + .CANCELLED => {}, + else => |status| return windows.unexpectedStatus(status), + } + try dir.startListening(w); + } + try io.checkCancel(); + if (attempt == 1) return .timeout; + const delay_interval: windows.LARGE_INTEGER = switch (timeout) { + .none => std.math.minInt(windows.LARGE_INTEGER), + .ms => |ms| -@as(windows.LARGE_INTEGER, ms) * (std.time.ns_per_ms / 100), + }; + _ = windows.ntdll.NtDelayExecution(windows.TRUE, &delay_interval); + } else unreachable; } }, .dragonfly, .freebsd, .netbsd, .openbsd, .ios, .tvos, .visionos, .watchos => struct { @@ -796,7 +803,8 @@ const Os = switch (builtin.os.tag) { w.dir_count = w.dir_table.count(); } - fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { + fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { + _ = io; var timespec_buffer: posix.timespec = undefined; var event_buffer: [100]posix.Kevent = undefined; var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(×pec_buffer)); @@ -852,7 +860,8 @@ const Os = switch (builtin.os.tag) { try w.os.fse.setPaths(gpa, steps); w.dir_count = w.os.fse.watch_roots.len; } - fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { + fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { + _ = io; return w.os.fse.wait(gpa, switch (timeout) { .none => null, .ms => |ms| @as(u64, ms) * std.time.ns_per_ms, @@ -890,10 +899,13 @@ pub const Match = struct { }; fn markAllFilesDirty(w: *Watch, gpa: Allocator) void { - for (w.os.handle_table.values()) |value| { + for (switch (builtin.os.tag) { + .windows => w.os.handle_table.keys(), + else => w.os.handle_table.values(), + }) |item| { const reaction_set = switch (builtin.os.tag) { - .linux => value.reaction_set, - else => value, + .linux, .windows => item.reaction_set, + else => item, }; for (reaction_set.values()) |step_set| { for (step_set.keys()) |step| { @@ -951,6 +963,6 @@ pub const WaitResult = enum { clean, }; -pub fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { - return Os.wait(w, gpa, timeout); +pub fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { + return Os.wait(w, gpa, io, timeout); } diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index f998466e6e..1f450d1dac 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -83,7 +83,7 @@ csprng: Csprng = .{}, system_basic_information: SystemBasicInformation = .{}, const SystemBasicInformation = if (!is_windows) struct {} else struct { - buffer: windows.SYSTEM_BASIC_INFORMATION = undefined, + buffer: windows.SYSTEM.BASIC_INFORMATION = undefined, initialized: std.atomic.Value(bool) = .{ .raw = false }, }; @@ -1392,7 +1392,7 @@ const AlertableSyscall = struct { } }; -fn waitForApcOrAlert() void { +pub fn waitForApcOrAlert() void { const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); _ = windows.ntdll.NtDelayExecution(windows.TRUE, &infinite_timeout); } @@ -2556,9 +2556,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper else => |e| e, }, }, - .device_io_control => |*o| return .{ - .device_io_control = try deviceIoControl(t, o), - }, + .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) }, } } @@ -2970,7 +2968,7 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren break :o; } const buffer = o.data[data_index]; - const short_buffer_len = @min(std.math.maxInt(u32), buffer.len); + const short_buffer_len = std.math.lossyCast(u32, buffer.len); if (o.file.flags.nonblocking) { context.file = o.file.handle; @@ -3259,22 +3257,11 @@ fn dirCreateDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, pe _ = t; _ = permissions; // TODO use this value - const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path); - const sub_path_w = sub_path_w_array.span(); - const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; - - var nt_name: windows.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(sub_path_w.ptr), - }; - const attr: windows.OBJECT_ATTRIBUTES = .{ - .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), - .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, - .Attributes = .{ - .INHERIT = false, - }, - .ObjectName = &nt_name, + const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path); + const attr: windows.OBJECT.ATTRIBUTES = .{ + .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle, + .Attributes = .{ .INHERIT = false }, + .ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w.span())), .SecurityDescriptor = null, .SecurityQualityOfService = null, }; @@ -3438,21 +3425,14 @@ fn dirCreateDirPathOpenWindows( }; components: while (true) { - const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, component.path); - const sub_path_w = sub_path_w_array.span(); - const is_last = it.peekNext() == null; - const create_disposition: w.FILE.CREATE_DISPOSITION = if (is_last) .OPEN_IF else .CREATE; - - var result: Dir = .{ .handle = undefined }; - - const path_len_bytes: u16 = @intCast(sub_path_w.len * 2); - var nt_name: w.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(sub_path_w.ptr), + const sub_path_w = try sliceToPrefixedFileW(dir.handle, component.path); + const attr: windows.OBJECT.ATTRIBUTES = .{ + .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle, + .ObjectName = @constCast(&sub_path_w.string()), }; - var io_status_block: w.IO_STATUS_BLOCK = undefined; - + const is_last = it.peekNext() == null; + var result: Dir = .{ .handle = undefined }; + var iosb: w.IO_STATUS_BLOCK = undefined; const syscall: Syscall = try .start(); while (true) switch (w.ntdll.NtCreateFile( &result.handle, @@ -3468,15 +3448,12 @@ fn dirCreateDirPathOpenWindows( .SYNCHRONIZE = true, }, }, - &.{ - .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, - .ObjectName = &nt_name, - }, - &io_status_block, + &attr, + &iosb, null, .{ .NORMAL = true }, .VALID_FLAGS, - create_disposition, + if (is_last) .OPEN_IF else .CREATE, .{ .DIRECTORY_FILE = true, .IO = .SYNCHRONOUS_NONALERT, @@ -3944,15 +3921,15 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { }; } -fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION { +fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION { if (!t.system_basic_information.initialized.load(.acquire)) { mutexLock(&t.mutex); defer mutexUnlock(&t.mutex); switch (windows.ntdll.NtQuerySystemInformation( - .SystemBasicInformation, + .Basic, &t.system_basic_information.buffer, - @sizeOf(windows.SYSTEM_BASIC_INFORMATION), + @sizeOf(windows.SYSTEM.BASIC_INFORMATION), null, )) { .SUCCESS => {}, @@ -4139,22 +4116,11 @@ fn dirAccessWindows( _ = options; // TODO - const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path); - const sub_path_w = sub_path_w_array.span(); - - if (sub_path_w[0] == '.' and sub_path_w[1] == 0) return; - if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) return; - - const path_len_bytes = std.math.cast(u16, std.mem.sliceTo(sub_path_w, 0).len * 2) orelse - return error.NameTooLong; - var nt_name: windows.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(sub_path_w.ptr), - }; - const attr: windows.OBJECT_ATTRIBUTES = .{ - .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, - .ObjectName = &nt_name, + if (std.mem.eql(u8, sub_path, ".") or std.mem.eql(u8, sub_path, "..")) return; + const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path); + const attr: windows.OBJECT.ATTRIBUTES = .{ + .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle, + .ObjectName = @constCast(&sub_path_w.string()), }; var basic_info: windows.FILE.BASIC_INFORMATION = undefined; const syscall: Syscall = try .start(); @@ -4360,18 +4326,10 @@ fn dirCreateFileWindows( if (std.mem.eql(u8, sub_path, ".")) return error.IsDir; if (std.mem.eql(u8, sub_path, "..")) return error.IsDir; - const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path); - const sub_path_w = sub_path_w_array.span(); - const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; - - var nt_name: windows.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(sub_path_w.ptr), - }; - const attr: windows.OBJECT_ATTRIBUTES = .{ - .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, - .ObjectName = &nt_name, + const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path); + const attr: windows.OBJECT.ATTRIBUTES = .{ + .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle, + .ObjectName = @constCast(&sub_path_w.string()), }; const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive) .CREATE @@ -4966,20 +4924,14 @@ fn dirOpenFileWindows( pub fn dirOpenFileWtf16( dir_handle: ?windows.HANDLE, - sub_path_w: [:0]const u16, + sub_path_w: []const u16, flags: File.OpenFlags, ) File.OpenError!File { const allow_directory = flags.allow_directory and !flags.isWrite(); if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir; if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir; - const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; const w = windows; - var nt_name: w.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(sub_path_w.ptr), - }; var io_status_block: w.IO_STATUS_BLOCK = undefined; var attempt: u5 = 0; var syscall: Syscall = try .start(); @@ -4996,7 +4948,7 @@ pub fn dirOpenFileWtf16( }, &.{ .RootDirectory = dir_handle, - .ObjectName = &nt_name, + .ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)), }, &io_status_block, null, @@ -5329,20 +5281,19 @@ fn dirOpenDirHaiku( pub fn dirOpenDirWindows( dir: Dir, - sub_path_w: [:0]const u16, + sub_path_w: []const u16, options: Dir.OpenOptions, ) Dir.OpenError!Dir { const w = windows; - const path_len_bytes: u16 = @intCast(sub_path_w.len * 2); - var nt_name: w.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(sub_path_w.ptr), - }; var io_status_block: w.IO_STATUS_BLOCK = undefined; var result: Dir = .{ .handle = undefined }; + const attr: w.OBJECT.ATTRIBUTES = .{ + .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, + .ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)), + }; + const syscall: Syscall = try .start(); while (true) switch (w.ntdll.NtCreateFile( &result.handle, @@ -5359,10 +5310,7 @@ pub fn dirOpenDirWindows( .SYNCHRONIZE = true, }, }, - &.{ - .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, - .ObjectName = &nt_name, - }, + &attr, &io_status_block, null, .{ .NORMAL = true }, @@ -6194,13 +6142,15 @@ pub fn GetFinalPathNameByHandle( input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2); @memcpy(input_buf[@sizeOf(windows.MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr))); - { - const rc = windows.DeviceIoControl(mgmt_handle, windows.IOCTL.MOUNTMGR.QUERY_POINTS, .{ .in = &input_buf, .out = &output_buf }); - switch (rc) { - .SUCCESS => {}, - .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, - else => return windows.unexpectedStatus(rc), - } + switch ((try deviceIoControl(&.{ + .file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = false } }, + .code = windows.IOCTL.MOUNTMGR.QUERY_POINTS, + .in = &input_buf, + .out = &output_buf, + })).u.Status) { + .SUCCESS => {}, + .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + else => |status| return windows.unexpectedStatus(status), } const mount_points_struct: *const windows.MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]); @@ -6251,11 +6201,15 @@ pub fn GetFinalPathNameByHandle( vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2); @memcpy(@as([*]windows.WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink); - const rc = windows.DeviceIoControl(mgmt_handle, windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, .{ .in = &vol_input_buf, .out = &vol_output_buf }); - switch (rc) { + switch ((try deviceIoControl(&.{ + .file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = true } }, + .code = windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, + .in = &vol_input_buf, + .out = &vol_output_buf, + })).u.Status) { .SUCCESS => {}, .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume, - else => return windows.unexpectedStatus(rc), + else => |status| return windows.unexpectedStatus(status), } const volume_paths_struct: *const windows.MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]); const volume_path = std.mem.sliceTo(@as( @@ -6352,20 +6306,17 @@ pub const QueryObjectNameError = error{ }; pub fn QueryObjectName(handle: windows.HANDLE, out_buffer: []u16) QueryObjectNameError![]u16 { - const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong; + const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT.NAME_INFORMATION)) orelse return error.NameTooLong; - const info: *windows.OBJECT_NAME_INFORMATION = @ptrCast(out_buffer_aligned); + const info: *windows.OBJECT.NAME_INFORMATION = @ptrCast(out_buffer_aligned); // buffer size is specified in bytes const out_buffer_len = std.math.cast(windows.ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(windows.ULONG); // last argument would return the length required for full_buffer, not exposed here - return switch (windows.ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) { - .SUCCESS => blk: { - // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0) - // if the object was "unnamed", not sure if this can happen for file handles - if (info.Name.MaximumLength == 0) break :blk error.Unexpected; - // resulting string length is specified in bytes - const path_length_unterminated = @divExact(info.Name.Length, 2); - break :blk info.Name.Buffer.?[0..path_length_unterminated]; + return switch (windows.ntdll.NtQueryObject(handle, .Name, info, out_buffer_len, null)) { + .SUCCESS => { + // info.Name from ObQueryNameString is documented to be empty if the object + // was "unnamed", not sure if this can happen for file handles + return if (info.Name.isEmpty()) error.Unexpected else info.Name.slice(); }, .ACCESS_DENIED => error.AccessDenied, .INVALID_HANDLE => error.InvalidHandle, @@ -6620,8 +6571,12 @@ pub const WindowsPathSpace = struct { data: [windows.PATH_MAX_WIDE:0]u16, len: usize, - pub fn span(self: *const WindowsPathSpace) [:0]const u16 { - return self.data[0..self.len :0]; + pub fn span(wps: *const WindowsPathSpace) [:0]const u16 { + return wps.data[0..wps.len :0]; + } + + pub fn string(wps: *const WindowsPathSpace) windows.UNICODE_STRING { + return .init(wps.span()); } }; @@ -7076,25 +7031,19 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov _ = t; const w = windows; - const sub_path_w_buf = try sliceToPrefixedFileW(dir.handle, sub_path); - const sub_path_w = sub_path_w_buf.span(); - - const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2)); - var nt_name: w.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - // The Windows API makes this mutable, but it will not mutate here. - .Buffer = @constCast(sub_path_w.ptr), - }; - - if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { - // Windows does not recognize this, but it does work with empty string. - nt_name.Length = 0; - } - if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { + if (std.mem.eql(u8, sub_path, "..")) { // Can't remove the parent directory with an open handle. return error.FileBusy; } + var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path); + if (std.mem.eql(u8, sub_path, ".")) { + // Windows does not recognize this, but it does work with empty string. + sub_path_w.len = 0; + } + const attr: w.OBJECT.ATTRIBUTES = .{ + .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle, + .ObjectName = @constCast(&sub_path_w.string()), + }; var io_status_block: w.IO_STATUS_BLOCK = undefined; var tmp_handle: w.HANDLE = undefined; @@ -7106,10 +7055,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov .RIGHTS = .{ .DELETE = true }, .SYNCHRONIZE = true, } }, - &.{ - .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, - .ObjectName = &nt_name, - }, + &attr, &io_status_block, null, .{}, @@ -7741,7 +7687,7 @@ fn dirSymLinkWindows( // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw var is_target_absolute = false; const final_target_path = target_path: { - if (windows.hasCommonNtPrefix(u16, target_path_w.span())) { + if (w.hasCommonNtPrefix(u16, target_path_w.span())) { // Already an NT path, no need to do anything to it break :target_path target_path_w.span(); } else { @@ -7785,13 +7731,16 @@ fn dirSymLinkWindows( @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path))); const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2; @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path))); - const rc = w.DeviceIoControl(symlink_handle, .SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] }); - switch (rc) { + switch ((try deviceIoControl(&.{ + .file = .{ .handle = symlink_handle, .flags = .{ .nonblocking = false } }, + .code = .SET_REPARSE_POINT, + .in = buffer[0..buf_len], + })).u.Status) { .SUCCESS => {}, .PRIVILEGE_NOT_HELD => return error.PermissionDenied, .ACCESS_DENIED => return error.AccessDenied, .INVALID_DEVICE_REQUEST => return error.FileSystem, - else => return windows.unexpectedStatus(rc), + else => |status| return w.unexpectedStatus(status), } } @@ -7905,17 +7854,10 @@ fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: [] fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize { // This gets used once for `sub_path` and then reused again temporarily // before converting back to `buffer`. - var sub_path_w_buf = try sliceToPrefixedFileW(dir.handle, sub_path); - const sub_path_w = sub_path_w_buf.span(); - const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; - var nt_name: windows.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(sub_path_w.ptr), - }; - const attr: windows.OBJECT_ATTRIBUTES = .{ - .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, - .ObjectName = &nt_name, + var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path); + const attr: windows.OBJECT.ATTRIBUTES = .{ + .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle, + .ObjectName = @constCast(&sub_path_w.string()), }; var io_status_block: windows.IO_STATUS_BLOCK = undefined; var result_handle: windows.HANDLE = undefined; @@ -8038,14 +7980,14 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink const len = buf.SubstituteNameLength >> 1; const path_buf = @as([*]const u16, &buf.PathBuffer); const is_relative = buf.Flags & windows.SYMLINK_FLAG_RELATIVE != 0; - break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w_buf.data); + break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w.data); }, @as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.MOUNT_POINT)) => r: { const buf: *const windows.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0])); const offset = buf.SubstituteNameOffset >> 1; const len = buf.SubstituteNameLength >> 1; const path_buf = @as([*]const u16, &buf.PathBuffer); - break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w_buf.data); + break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w.data); }, else => return error.UnsupportedReparsePointType, }; @@ -8541,13 +8483,14 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void { fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool { const t: *Threaded = @ptrCast(@alignCast(userdata)); - return t.isTty(file); + _ = t; + return isTty(file); } -fn isTty(t: *Threaded, file: File) Io.Cancelable!bool { +fn isTty(file: File) Io.Cancelable!bool { if (is_windows) { var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE; - switch ((try t.deviceIoControl(&.{ + switch ((try deviceIoControl(&.{ .file = .{ .handle = windows.peb().ProcessParameters.ConsoleHandle, .flags = .{ .nonblocking = false }, @@ -8626,8 +8569,9 @@ fn isTty(t: *Threaded, file: File) Io.Cancelable!bool { fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; - if (!is_windows) return if (!try t.supportsAnsiEscapeCodes(file)) error.NotTerminalDevice; + if (!is_windows) return if (!try supportsAnsiEscapeCodes(file)) error.NotTerminalDevice; // For Windows Terminal, VT Sequences processing is enabled by default. const console: File = .{ @@ -8635,7 +8579,7 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE .flags = .{ .nonblocking = false }, }; var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE; - switch ((try t.deviceIoControl(&.{ + switch ((try deviceIoControl(&.{ .file = console, .code = windows.IOCTL.CONDRV.ISSUE_USER_IO, .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})), @@ -8661,7 +8605,7 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE var set_console_mode = windows.CONSOLE.USER_IO.SET_MODE( get_console_mode.Data | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING, ); - switch ((try t.deviceIoControl(&.{ + switch ((try deviceIoControl(&.{ .file = console, .code = windows.IOCTL.CONDRV.ISSUE_USER_IO, .in = @ptrCast(&set_console_mode.request(file, 0, .{}, 0, .{})), @@ -8673,13 +8617,14 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool { const t: *Threaded = @ptrCast(@alignCast(userdata)); - return t.supportsAnsiEscapeCodes(file); + _ = t; + return supportsAnsiEscapeCodes(file); } -fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool { +fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool { if (is_windows) { var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE; - switch ((try t.deviceIoControl(&.{ + switch ((try deviceIoControl(&.{ .file = .{ .handle = windows.peb().ProcessParameters.ConsoleHandle, .flags = .{ .nonblocking = false }, @@ -8701,7 +8646,7 @@ fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool { return false; } - if (try t.isTty(file)) return true; + if (try isTty(file)) return true; return false; } @@ -8775,7 +8720,7 @@ fn isCygwinPty(file: File) Io.Cancelable!bool { }, }; - const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes); + const name_info: *const windows.FILE.NAME_INFORMATION = @ptrCast(&name_info_bytes); const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength]; const name_wide = std.mem.bytesAsSlice(u16, name_bytes); // The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master @@ -9002,49 +8947,38 @@ fn fileSetTimestamps( _ = t; if (is_windows) { - var access_time_buffer: windows.FILETIME = undefined; - var modify_time_buffer: windows.FILETIME = undefined; - var system_time_buffer: windows.LARGE_INTEGER = undefined; - - if (options.access_timestamp == .now or options.modify_timestamp == .now) { - system_time_buffer = windows.ntdll.RtlGetSystemTimePrecise(); - } - - const access_ptr = switch (options.access_timestamp) { - .unchanged => null, - .now => @panic("TODO do SystemTimeToFileTime logic here"), - .new => |ts| p: { - access_time_buffer = windows.nanoSecondsToFileTime(ts); - break :p &access_time_buffer; + const now_sys = if (options.access_timestamp == .now or options.modify_timestamp == .now) + windows.ntdll.RtlGetSystemTimePrecise() + else + undefined; + var iosb: windows.IO_STATUS_BLOCK = undefined; + var info: windows.FILE.BASIC_INFORMATION = .{ + .CreationTime = 0, + .LastAccessTime = switch (options.access_timestamp) { + .unchanged => 0, + .now => now_sys, + .new => |ts| windows.toSysTime(ts), }, - }; - - const modify_ptr = switch (options.modify_timestamp) { - .unchanged => null, - .now => @panic("TODO do SystemTimeToFileTime logic here"), - .new => |ts| p: { - modify_time_buffer = windows.nanoSecondsToFileTime(ts); - break :p &modify_time_buffer; + .LastWriteTime = switch (options.modify_timestamp) { + .unchanged => 0, + .now => now_sys, + .new => |ts| windows.toSysTime(ts), }, + .ChangeTime = 0, + .FileAttributes = .{}, + }; + var syscall: Syscall = try .start(); + while (true) switch (windows.ntdll.NtSetInformationFile( + file.handle, + &iosb, + &info, + @sizeOf(windows.FILE.BASIC_INFORMATION), + .Basic, + )) { + .SUCCESS => return syscall.finish(), + .CANCELLED => try syscall.checkCancel(), + else => |status| return syscall.unexpectedNtstatus(status), }; - - // https://github.com/ziglang/zig/issues/1840 - const syscall: Syscall = try .start(); - while (true) { - switch (windows.kernel32.SetFileTime(file.handle, null, access_ptr, modify_ptr)) { - 0 => switch (windows.GetLastError()) { - .OPERATION_ABORTED => { - try syscall.checkCancel(); - continue; - }, - else => |err| { - syscall.finish(); - return windows.unexpectedError(err); - }, - }, - else => return syscall.finish(), - } - } } if (native_os == .wasi and !builtin.link_libc) { @@ -9636,15 +9570,43 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.ReadStreamingErro } fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingError!usize { + var iosb: windows.IO_STATUS_BLOCK = undefined; var index: usize = 0; while (data.len - index != 0 and data[index].len == 0) index += 1; if (data.len - index == 0) return 0; const buffer = data[index]; - const short_buffer_len = @min(std.math.maxInt(u32), buffer.len); - - var iosb: windows.IO_STATUS_BLOCK = undefined; - - if (!file.flags.nonblocking) { + const short_buffer_len = std.math.lossyCast(u32, buffer.len); + if (file.flags.nonblocking) { + var done: bool = false; + switch (windows.ntdll.NtReadFile( + file.handle, + null, // event + flagApc, + &done, // APC context + &iosb, + buffer.ptr, + short_buffer_len, + null, // byte offset + null, // key + )) { + // We must wait for the APC routine. + .PENDING, .SUCCESS => while (!done) { + // Once we get here we must not return from the function until the + // operation completes, thereby releasing reference to the iosb. + const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { + error.Canceled => |e| { + var cancel_iosb: windows.IO_STATUS_BLOCK = undefined; + _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb); + while (!done) waitForApcOrAlert(); + return e; + }, + }; + waitForApcOrAlert(); + alertable_syscall.finish(); + }, + else => |status| iosb.u.Status = status, + } + } else { const syscall: Syscall = try .start(); while (true) switch (windows.ntdll.NtReadFile( file.handle, @@ -9665,41 +9627,10 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingEr else => |status| { syscall.finish(); iosb.u.Status = status; - return ntReadFileResult(&iosb); + break; }, }; } - - var done: bool = false; - - switch (windows.ntdll.NtReadFile( - file.handle, - null, // event - flagApc, - &done, // APC context - &iosb, - buffer.ptr, - short_buffer_len, - null, // byte offset - null, // key - )) { - // We must wait for the APC routine. - .PENDING, .SUCCESS => while (!done) { - // Once we get here we must not return from the function until the - // operation completes, thereby releasing reference to io_status_block. - const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { - error.Canceled => |e| { - var cancel_iosb: windows.IO_STATUS_BLOCK = undefined; - _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb); - while (!done) waitForApcOrAlert(); - return e; - }, - }; - waitForApcOrAlert(); - alertable_syscall.finish(); - }, - else => |status| iosb.u.Status = status, - } return ntReadFileResult(&iosb); } @@ -9714,8 +9645,9 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { .CANCELLED => unreachable, .SUCCESS => return io_status_block.Information, .END_OF_FILE, .PIPE_BROKEN => return error.EndOfStream, + .INVALID_HANDLE => return error.NotOpenForReading, .INVALID_DEVICE_REQUEST => return error.IsDir, - .LOCK_NOT_GRANTED => return error.LockViolation, + .FILE_LOCK_CONFLICT => return error.LockViolation, .ACCESS_DENIED => return error.AccessDenied, else => |status| return windows.unexpectedStatus(status), } @@ -9731,7 +9663,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { .QUOTA_EXCEEDED => return error.SystemResources, .PIPE_BROKEN => return error.BrokenPipe, .INVALID_HANDLE => return error.NotOpenForWriting, - .LOCK_NOT_GRANTED => return error.LockViolation, + .FILE_LOCK_CONFLICT => return error.LockViolation, .ACCESS_DENIED => return error.AccessDenied, .WORKING_SET_QUOTA => return error.SystemResources, .DISK_FULL => return error.NoSpaceLeft, @@ -9740,8 +9672,6 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { } fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize { - if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)"); - var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; var i: usize = 0; for (data) |buf| { @@ -9787,9 +9717,44 @@ fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.Rea } } + if (have_preadv) { + const syscall: Syscall = try .start(); + while (true) { + const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset)); + switch (posix.errno(rc)) { + .SUCCESS => { + syscall.finish(); + return @bitCast(rc); + }, + .INTR, .TIMEDOUT => { + try syscall.checkCancel(); + continue; + }, + .NXIO => return syscall.fail(error.Unseekable), + .SPIPE => return syscall.fail(error.Unseekable), + .OVERFLOW => return syscall.fail(error.Unseekable), + .NOBUFS => return syscall.fail(error.SystemResources), + .NOMEM => return syscall.fail(error.SystemResources), + .AGAIN => return syscall.fail(error.WouldBlock), + .IO => return syscall.fail(error.InputOutput), + .ISDIR => return syscall.fail(error.IsDir), + .NOTCONN => |err| return syscall.errnoBug(err), // not a socket + .CONNRESET => |err| return syscall.errnoBug(err), // not a socket + .INVAL => |err| return syscall.errnoBug(err), + .FAULT => |err| return syscall.errnoBug(err), + .BADF => { + syscall.finish(); + if (native_os == .wasi) return error.IsDir; // File operation on directory. + return error.NotOpenForReading; + }, + else => |err| return syscall.unexpectedErrno(err), + } + } + } + const syscall: Syscall = try .start(); while (true) { - const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset)); + const rc = posix.pread(file.handle, dest[0].ptr, @intCast(dest[0].len), @bitCast(offset)); switch (posix.errno(rc)) { .SUCCESS => { syscall.finish(); @@ -9838,116 +9803,119 @@ fn fileReadPositionalWindows(file: File, data: []const []u8, offset: u64) File.R } fn readFilePositionalWindows(file: File, buffer: []u8, offset: u64) File.ReadPositionalError!usize { - const DWORD = windows.DWORD; - const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); - var overlapped: windows.OVERLAPPED = .{ - .Internal = 0, - .InternalHigh = 0, - .DUMMYUNIONNAME = .{ - .DUMMYSTRUCTNAME = .{ - .Offset = @truncate(offset), - .OffsetHigh = @truncate(offset >> 32), + var iosb: windows.IO_STATUS_BLOCK = undefined; + const short_buffer_len = std.math.lossyCast(u32, buffer.len); + const signed_offset: windows.LARGE_INTEGER = @intCast(offset); + if (file.flags.nonblocking) { + var done: bool = false; + switch (windows.ntdll.NtReadFile( + file.handle, + null, // event + flagApc, + &done, // APC context + &iosb, + buffer.ptr, + short_buffer_len, + &signed_offset, + null, // key + )) { + // We must wait for the APC routine. + .PENDING, .SUCCESS => while (!done) { + // Once we get here we must not return from the function until the + // operation completes, thereby releasing reference to the iosb. + const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { + error.Canceled => |e| { + var cancel_iosb: windows.IO_STATUS_BLOCK = undefined; + _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb); + while (!done) waitForApcOrAlert(); + return e; + }, + }; + waitForApcOrAlert(); + alertable_syscall.finish(); }, - }, - .hEvent = null, - }; - - const syscall: Syscall = try .start(); - while (true) { - var n: DWORD = undefined; - if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) != 0) { - syscall.finish(); - return n; + else => |status| iosb.u.Status = status, } - switch (windows.GetLastError()) { - .IO_PENDING => |err| { + } else { + const syscall: Syscall = try .start(); + while (true) switch (windows.ntdll.NtReadFile( + file.handle, + null, // event + null, // APC routine + null, // APC context + &iosb, + buffer.ptr, + short_buffer_len, + &signed_offset, + null, // key + )) { + .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag + .CANCELLED => try syscall.checkCancel(), + else => |status| { syscall.finish(); - return windows.errorBug(err); + iosb.u.Status = status; + break; }, - .OPERATION_ABORTED => { - try syscall.checkCancel(); - continue; - }, - .BROKEN_PIPE, .HANDLE_EOF => { - syscall.finish(); - return 0; - }, - .NETNAME_DELETED => if (is_debug) unreachable else return error.Unexpected, - .LOCK_VIOLATION => return syscall.fail(error.LockViolation), - .ACCESS_DENIED => return syscall.fail(error.AccessDenied), - .INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, - // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing - // a handle to a directory. - .INVALID_FUNCTION => return syscall.fail(error.IsDir), - else => |err| { - syscall.finish(); - return windows.unexpectedError(err); - }, - } + }; } + return ntReadFileResult(&iosb) catch |err| switch (err) { + error.EndOfStream => 0, + else => |e| e, + }; } fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); _ = t; - const fd = file.handle; - - if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { - var result: u64 = undefined; - const syscall: Syscall = try .start(); - while (true) { - switch (posix.errno(posix.system.llseek(fd, @bitCast(offset), &result, posix.SEEK.CUR))) { - .SUCCESS => { - syscall.finish(); - return; - }, - .INTR => { - try syscall.checkCancel(); - continue; - }, - else => |e| { - syscall.finish(); - switch (e) { - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => return error.Unseekable, - .OVERFLOW => return error.Unseekable, - .SPIPE => return error.Unseekable, - .NXIO => return error.Unseekable, - else => |err| return posix.unexpectedErrno(err), - } - }, - } - } - } if (is_windows) { + var iosb: windows.IO_STATUS_BLOCK = undefined; + var info: windows.FILE.POSITION_INFORMATION = undefined; const syscall: Syscall = try .start(); - while (true) { - if (windows.kernel32.SetFilePointerEx(fd, offset, null, windows.FILE_CURRENT) != 0) { - return syscall.finish(); - } - switch (windows.GetLastError()) { - .OPERATION_ABORTED => { - try syscall.checkCancel(); - continue; - }, - .INVALID_FUNCTION => return syscall.fail(error.Unseekable), - .NEGATIVE_SEEK => return syscall.fail(error.Unseekable), - .INVALID_PARAMETER => unreachable, - .INVALID_HANDLE => unreachable, - else => |err| { - syscall.finish(); - return windows.unexpectedError(err); - }, - } - } + while (true) switch (windows.ntdll.NtQueryInformationFile( + file.handle, + &iosb, + &info, + @sizeOf(windows.FILE.POSITION_INFORMATION), + .Position, + )) { + .SUCCESS => break, + .CANCELLED => try syscall.checkCancel(), + .ACCESS_DENIED => return syscall.fail(error.AccessDenied), + .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable), + else => |status| return syscall.unexpectedNtstatus(status), + }; + info.CurrentByteOffset = @bitCast((if (offset >= 0) std.math.add( + u64, + @bitCast(info.CurrentByteOffset), + @intCast(offset), + ) else std.math.sub( + u64, + @bitCast(info.CurrentByteOffset), + @intCast(-offset), + )) catch |err| switch (err) { + error.Overflow => return syscall.fail(error.Unseekable), + }); + while (true) switch (windows.ntdll.NtSetInformationFile( + file.handle, + &iosb, + &info, + @sizeOf(windows.FILE.POSITION_INFORMATION), + .Position, + )) { + .SUCCESS => return syscall.finish(), + .CANCELLED => try syscall.checkCancel(), + .ACCESS_DENIED => return syscall.fail(error.AccessDenied), + .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable), + else => |status| return syscall.unexpectedNtstatus(status), + }; } if (native_os == .wasi and !builtin.link_libc) { var new_offset: std.os.wasi.filesize_t = undefined; const syscall: Syscall = try .start(); while (true) { - switch (std.os.wasi.fd_seek(fd, offset, .CUR, &new_offset)) { + switch (std.os.wasi.fd_seek(file.handle, offset, .CUR, &new_offset)) { .SUCCESS => { syscall.finish(); return; @@ -9974,9 +9942,37 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi if (posix.SEEK == void) return error.Unseekable; + if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { + var result: u64 = undefined; + const syscall: Syscall = try .start(); + while (true) { + switch (posix.errno(posix.system.llseek(file.handle, @bitCast(offset), &result, posix.SEEK.CUR))) { + .SUCCESS => { + syscall.finish(); + return; + }, + .INTR => { + try syscall.checkCancel(); + continue; + }, + else => |e| { + syscall.finish(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => return error.Unseekable, + .OVERFLOW => return error.Unseekable, + .SPIPE => return error.Unseekable, + .NXIO => return error.Unseekable, + else => |err| return posix.unexpectedErrno(err), + } + }, + } + } + } + const syscall: Syscall = try .start(); while (true) { - switch (posix.errno(lseek_sym(fd, offset, posix.SEEK.CUR))) { + switch (posix.errno(lseek_sym(file.handle, offset, posix.SEEK.CUR))) { .SUCCESS => { syscall.finish(); return; @@ -10003,41 +9999,31 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); _ = t; - const fd = file.handle; if (is_windows) { - // "The starting point is zero or the beginning of the file. If [FILE_BEGIN] - // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value." - // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex - const ipos: windows.LARGE_INTEGER = @bitCast(offset); - + var iosb: windows.IO_STATUS_BLOCK = undefined; + var info: windows.FILE.POSITION_INFORMATION = .{ .CurrentByteOffset = @bitCast(offset) }; const syscall: Syscall = try .start(); - while (true) { - if (windows.kernel32.SetFilePointerEx(fd, ipos, null, windows.FILE_BEGIN) != 0) { - return syscall.finish(); - } - switch (windows.GetLastError()) { - .OPERATION_ABORTED => { - try syscall.checkCancel(); - continue; - }, - .INVALID_FUNCTION => return syscall.fail(error.Unseekable), - .NEGATIVE_SEEK => return syscall.fail(error.Unseekable), - .INVALID_PARAMETER => unreachable, - .INVALID_HANDLE => unreachable, - else => |err| { - syscall.finish(); - return windows.unexpectedError(err); - }, - } - } + while (true) switch (windows.ntdll.NtSetInformationFile( + file.handle, + &iosb, + &info, + @sizeOf(windows.FILE.POSITION_INFORMATION), + .Position, + )) { + .SUCCESS => return syscall.finish(), + .CANCELLED => try syscall.checkCancel(), + .ACCESS_DENIED => return syscall.fail(error.AccessDenied), + .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable), + else => |status| return syscall.unexpectedNtstatus(status), + }; } if (native_os == .wasi and !builtin.link_libc) { const syscall: Syscall = try .start(); while (true) { var new_offset: std.os.wasi.filesize_t = undefined; - switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) { + switch (std.os.wasi.fd_seek(file.handle, @bitCast(offset), .SET, &new_offset)) { .SUCCESS => { syscall.finish(); return; @@ -10064,7 +10050,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi if (posix.SEEK == void) return error.Unseekable; - return posixSeekTo(fd, offset); + return posixSeekTo(file.handle, offset); } fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void { @@ -10131,8 +10117,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) process.O // If ImagePathName is a symlink, then it will contain the path of the symlink, // not the path that the symlink points to. However, because we are opening // the file, we can let the openFileW call follow the symlink for us. - const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName; - const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0]; + const image_path_name = windows.peb().ProcessParameters.ImagePathName.sliceZ(); const prefixed_path_w = try wToPrefixedFileW(null, image_path_name); return dirOpenFileWtf16(null, prefixed_path_w.span(), flags); }, @@ -10334,14 +10319,13 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut return error.FileNotFound; }, .windows => { - const w = windows; - const image_path_unicode_string = &w.peb().ProcessParameters.ImagePathName; - const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0]; - // If ImagePathName is a symlink, then it will contain the path of the // symlink, not the path that the symlink points to. We want the path // that the symlink points to, though, so we need to get the realpath. - var path_name_w_buf = try wToPrefixedFileW(null, image_path_name); + var path_name_w_buf = try wToPrefixedFileW( + null, + windows.peb().ProcessParameters.ImagePathName.sliceZ(), + ); const h_file = handle: { if (OpenFile(path_name_w_buf.span(), .{ @@ -10360,7 +10344,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut else => |e| return e, } }; - defer w.CloseHandle(h_file); + defer windows.CloseHandle(h_file); const wide_slice = try GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data); @@ -10388,15 +10372,15 @@ fn fileWritePositional( if (is_windows) { if (header.len != 0) { - return writeFilePositionalWindows(file.handle, header, offset); + return writeFilePositionalWindows(file, header, offset); } for (data[0 .. data.len - 1]) |buf| { if (buf.len == 0) continue; - return writeFilePositionalWindows(file.handle, buf, offset); + return writeFilePositionalWindows(file, buf, offset); } const pattern = data[data.len - 1]; if (pattern.len == 0 or splat == 0) return 0; - return writeFilePositionalWindows(file.handle, pattern, offset); + return writeFilePositionalWindows(file, pattern, offset); } var iovecs: [max_iovecs_len]posix.iovec_const = undefined; @@ -10505,50 +10489,64 @@ fn fileWritePositional( } } -fn writeFilePositionalWindows( - handle: windows.HANDLE, - bytes: []const u8, - offset: u64, -) File.WritePositionalError!usize { - var bytes_written: windows.DWORD = undefined; - var overlapped: windows.OVERLAPPED = .{ - .Internal = 0, - .InternalHigh = 0, - .DUMMYUNIONNAME = .{ - .DUMMYSTRUCTNAME = .{ - .Offset = @truncate(offset), - .OffsetHigh = @truncate(offset >> 32), +fn writeFilePositionalWindows(file: File, buffer: []const u8, offset: u64) File.WritePositionalError!usize { + assert(buffer.len != 0); + var iosb: windows.IO_STATUS_BLOCK = undefined; + const short_buffer_len = std.math.lossyCast(u32, buffer.len); + const signed_offset: windows.LARGE_INTEGER = @intCast(offset); + if (file.flags.nonblocking) { + var done: bool = false; + switch (windows.ntdll.NtWriteFile( + file.handle, + null, // event + flagApc, + &done, // APC context + &iosb, + buffer.ptr, + short_buffer_len, + &signed_offset, + null, // key + )) { + // We must wait for the APC routine. + .PENDING, .SUCCESS => while (!done) { + // Once we get here we must not return from the function until the + // operation completes, thereby releasing reference to the iosb. + const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { + error.Canceled => |e| { + var cancel_iosb: windows.IO_STATUS_BLOCK = undefined; + _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb); + while (!done) waitForApcOrAlert(); + return e; + }, + }; + waitForApcOrAlert(); + alertable_syscall.finish(); }, - }, - .hEvent = null, - }; - const adjusted_len = std.math.lossyCast(u32, bytes.len); - const syscall: Syscall = try .start(); - while (true) { - if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, &overlapped) != 0) { - syscall.finish(); - return bytes_written; + else => |status| iosb.u.Status = status, } - switch (windows.GetLastError()) { - .OPERATION_ABORTED => { - try syscall.checkCancel(); - continue; - }, - .INVALID_USER_BUFFER => return syscall.fail(error.SystemResources), - .NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources), - .NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources), - .NO_DATA => return syscall.fail(error.BrokenPipe), - .INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, // use after free - .LOCK_VIOLATION => return syscall.fail(error.LockViolation), - .ACCESS_DENIED => return syscall.fail(error.AccessDenied), - .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources), - .DISK_FULL => return syscall.fail(error.NoSpaceLeft), - else => |err| { + } else { + const syscall: Syscall = try .start(); + while (true) switch (windows.ntdll.NtWriteFile( + file.handle, + null, // event + null, // APC routine + null, // APC context + &iosb, + buffer.ptr, + short_buffer_len, + &signed_offset, + null, // key + )) { + .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag + .CANCELLED => try syscall.checkCancel(), + else => |status| { syscall.finish(); - return windows.unexpectedError(err); + iosb.u.Status = status; + return ntWriteFileResult(&iosb); }, - } + }; } + return ntWriteFileResult(&iosb); } fn fileWriteStreaming( @@ -10673,9 +10671,7 @@ fn fileWriteStreaming( fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!usize { assert(buffer.len != 0); - var iosb: windows.IO_STATUS_BLOCK = undefined; - if (file.flags.nonblocking) { var done: bool = false; switch (windows.ntdll.NtWriteFile( @@ -10706,7 +10702,6 @@ fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!u }, else => |status| iosb.u.Status = status, } - return ntWriteFileResult(&iosb); } else { const syscall: Syscall = try .start(); while (true) switch (windows.ntdll.NtWriteFile( @@ -10721,17 +10716,15 @@ fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!u null, // key )) { .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag - .CANCELLED => { - try syscall.checkCancel(); - continue; - }, + .CANCELLED => try syscall.checkCancel(), else => |status| { syscall.finish(); iosb.u.Status = status; - return ntWriteFileResult(&iosb); + break; }, }; } + return ntWriteFileResult(&iosb); } fn fileWriteFileStreaming( @@ -11433,13 +11426,22 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp { return .{ .nanoseconds = @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100 + epoch_ns }; }, .awake, .boot => { - // QPC on windows doesn't fail on >= XP/2000 and includes time suspended. - const qpc = windows.QueryPerformanceCounter(); // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA // (a read-only page of info updated and mapped by the kernel to all processes): // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm - const qpf = windows.QueryPerformanceFrequency(); + const qpf: u64 = qpf: { + var qpf: windows.LARGE_INTEGER = undefined; + assert(windows.ntdll.RtlQueryPerformanceFrequency(&qpf) != windows.FALSE); + break :qpf @bitCast(qpf); + }; + + // QPC on windows doesn't fail on >= XP/2000 and includes time suspended. + const qpc: u64 = qpc: { + var qpc: windows.LARGE_INTEGER = undefined; + assert(windows.ntdll.RtlQueryPerformanceCounter(&qpc) != windows.FALSE); + break :qpc @bitCast(qpc); + }; // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it. // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701 @@ -11458,7 +11460,7 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp { // https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L442-L485 if (windows.ntdll.NtQueryInformationProcess( handle, - windows.PROCESSINFOCLASS.Times, + .Times, ×, @sizeOf(windows.KERNEL_USER_TIMES), null, @@ -11474,7 +11476,7 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp { // https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L2971-L3019 if (windows.ntdll.NtQueryInformationThread( handle, - windows.THREADINFOCLASS.Times, + .Times, ×, @sizeOf(windows.KERNEL_USER_TIMES), null, @@ -14132,21 +14134,16 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr } fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirError!void { - if (native_os == .wasi) return error.OperationUnsupported; const t: *Threaded = @ptrCast(@alignCast(userdata)); _ = t; + if (native_os == .wasi) return error.OperationUnsupported; + if (is_windows) { - var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined; - const dir_path = try GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer); - const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong; - var nt_name: windows.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(dir_path.ptr), - }; + var dir_path_buf: [windows.PATH_MAX_WIDE]u16 = undefined; + const dir_path = try GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buf); const syscall: Syscall = try .start(); - while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&nt_name)) { + while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&.init(dir_path))) { .SUCCESS => return syscall.finish(), .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName), .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound), @@ -15465,26 +15462,27 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void { fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT) !void { _ = t; // TODO cancelation const handle = child.id.?; - if (windows.kernel32.TerminateProcess(handle, exit_code) == 0) { - switch (windows.GetLastError()) { - .ACCESS_DENIED => { - // Usually when TerminateProcess triggers a ACCESS_DENIED error, it - // indicates that the process has already exited, but there may be - // some rare edge cases where our process handle no longer has the - // PROCESS_TERMINATE access right, so let's do another check to make - // sure the process is really no longer running: - const minimal_timeout: windows.LARGE_INTEGER = -1; - switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) { - .SUCCESS => return error.AlreadyTerminated, - else => return error.AccessDenied, - } - }, - else => |err| return windows.unexpectedError(err), - } + _ = windows.ntdll.RtlReportSilentProcessExit(handle, @enumFromInt(exit_code)); + switch (windows.ntdll.NtTerminateProcess(handle, @enumFromInt(exit_code))) { + .SUCCESS => { + const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); + _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout); + childCleanupWindows(child); + }, + .ACCESS_DENIED => { + // Usually when TerminateProcess triggers a ACCESS_DENIED error, it + // indicates that the process has already exited, but there may be + // some rare edge cases where our process handle no longer has the + // PROCESS_TERMINATE access right, so let's do another check to make + // sure the process is really no longer running: + const minimal_timeout: windows.LARGE_INTEGER = -1; + return switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) { + windows.NTSTATUS.WAIT_0 => error.AlreadyTerminated, + else => error.AccessDenied, + }; + }, + else => |status| return windows.unexpectedStatus(status), } - const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); - _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout); - childCleanupWindows(child); } fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { @@ -15501,12 +15499,12 @@ fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child else => |status| return alertable_syscall.unexpectedNtstatus(status), }; - var info: windows.PROCESS_BASIC_INFORMATION = undefined; + var info: windows.PROCESS.BASIC_INFORMATION = undefined; const term: process.Child.Term = switch (windows.ntdll.NtQueryInformationProcess( handle, .BasicInformation, &info, - @sizeOf(windows.PROCESS_BASIC_INFORMATION), + @sizeOf(windows.PROCESS.BASIC_INFORMATION), null, )) { .SUCCESS => .{ .exited = @as(u8, @truncate(@intFromEnum(info.ExitStatus))) }, @@ -15521,12 +15519,12 @@ fn childCleanupWindows(child: *process.Child) void { const handle = child.id orelse return; if (child.request_resource_usage_statistics) { - var vmc: windows.VM_COUNTERS = undefined; + var vmc: windows.PROCESS.VM_COUNTERS = undefined; switch (windows.ntdll.NtQueryInformationProcess( handle, .VmCounters, &vmc, - @sizeOf(windows.VM_COUNTERS), + @sizeOf(windows.PROCESS.VM_COUNTERS), null, )) { .SUCCESS => child.resource_usage_statistics.rusage = vmc, @@ -15853,7 +15851,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro .cbReserved2 = 0, .lpReserved2 = null, }; - var piProcInfo: windows.PROCESS_INFORMATION = undefined; + var piProcInfo: windows.PROCESS.INFORMATION = undefined; var arena_allocator = std.heap.ArenaAllocator.init(t.allocator); defer arena_allocator.deinit(); @@ -16062,7 +16060,6 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { if (t.random_file.handle) |handle| return handle; } - const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' }; var fresh_handle: windows.HANDLE = undefined; var io_status_block: windows.IO_STATUS_BLOCK = undefined; var syscall: Syscall = try .start(); @@ -16072,13 +16069,9 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { .STANDARD = .{ .SYNCHRONIZE = true }, .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } }, }, - &.{ - .ObjectName = @constCast(&windows.UNICODE_STRING{ - .Length = @sizeOf(@TypeOf(device_path)), - .MaximumLength = 0, - .Buffer = @constCast(&device_path), - }), - }, + &.{ .ObjectName = @constCast(&windows.UNICODE_STRING.init( + &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' }, + )) }, &io_status_block, .VALID_FLAGS, .{ .IO = .SYNCHRONOUS_NONALERT }, @@ -16111,7 +16104,6 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE { if (t.null_file.handle) |handle| return handle; } - const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }; var fresh_handle: windows.HANDLE = undefined; var io_status_block: windows.IO_STATUS_BLOCK = undefined; var syscall: Syscall = try .start(); @@ -16123,11 +16115,9 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE { }, &.{ .Attributes = .{ .INHERIT = true }, - .ObjectName = @constCast(&windows.UNICODE_STRING{ - .Length = @sizeOf(@TypeOf(device_path)), - .MaximumLength = 0, - .Buffer = @constCast(&device_path), - }), + .ObjectName = @constCast(&windows.UNICODE_STRING.init( + &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, + )), }, &io_status_block, .VALID_FLAGS, @@ -16173,7 +16163,6 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { if (t.pipe_file.handle) |handle| return handle; } - const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' }; var fresh_handle: windows.HANDLE = undefined; var io_status_block: windows.IO_STATUS_BLOCK = undefined; var syscall: Syscall = try .start(); @@ -16181,11 +16170,9 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { &fresh_handle, .{ .STANDARD = .{ .SYNCHRONIZE = true } }, &.{ - .ObjectName = @constCast(&windows.UNICODE_STRING{ - .Length = @sizeOf(@TypeOf(device_path)), - .MaximumLength = 0, - .Buffer = @constCast(&device_path), - }), + .ObjectName = @constCast(&windows.UNICODE_STRING.init( + &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' }, + )), }, &io_status_block, .VALID_FLAGS, @@ -16253,7 +16240,7 @@ fn windowsCreateProcessPathExt( cwd_ptr: ?[*:0]u16, flags: windows.CreateProcessFlags, lpStartupInfo: *windows.STARTUPINFOW, - lpProcessInformation: *windows.PROCESS_INFORMATION, + lpProcessInformation: *windows.PROCESS.INFORMATION, ) !void { const app_name_len = app_buf.items.len; const dir_path_len = dir_buf.items.len; @@ -16341,13 +16328,9 @@ fn windowsCreateProcessPathExt( // On NTFS, `blah.exe*` will always return `blah.exe` first if it exists. // On FAT32, it's possible for something like `blah.exe.obj` to be returned first. while (true) { - const app_name_len_bytes = std.math.cast(u16, app_name_wildcard.len * 2) orelse return error.NameTooLong; - var app_name_unicode_string = windows.UNICODE_STRING{ - .Length = app_name_len_bytes, - .MaximumLength = app_name_len_bytes, - .Buffer = @constCast(app_name_wildcard.ptr), - }; - const rc = windows.ntdll.NtQueryDirectoryFile( + // If we get nothing with the wildcard, then we can just bail out + // as we know appending PATHEXT will not yield anything. + switch (windows.ntdll.NtQueryDirectoryFile( dir.handle, null, null, @@ -16357,18 +16340,14 @@ fn windowsCreateProcessPathExt( file_information_buf.len, .Directory, windows.FALSE, // single result - &app_name_unicode_string, + &.init(app_name_wildcard), windows.FALSE, // restart iteration - ); - - // If we get nothing with the wildcard, then we can just bail out - // as we know appending PATHEXT will not yield anything. - switch (rc) { + )) { .SUCCESS => {}, .NO_SUCH_FILE => return error.FileNotFound, .NO_MORE_FILES => break, .ACCESS_DENIED => return error.AccessDenied, - else => return windows.unexpectedStatus(rc), + else => |status| return windows.unexpectedStatus(status), } // According to the docs, this can only happen if there is not enough room in the @@ -16514,7 +16493,7 @@ fn windowsCreateProcess( cwd_ptr: ?[*:0]u16, flags: windows.CreateProcessFlags, lpStartupInfo: *windows.STARTUPINFOW, - lpProcessInformation: *windows.PROCESS_INFORMATION, + lpProcessInformation: *windows.PROCESS.INFORMATION, ) !void { const syscall: Syscall = try .start(); while (true) { @@ -17138,7 +17117,7 @@ pub const CreatePipeOptions = struct { default_timeout: windows.LARGE_INTEGER = -120 * std.time.ns_per_s / 100, pub const End = struct { - attributes: windows.OBJECT_ATTRIBUTES.ATTRIBUTES = .{}, + attributes: windows.OBJECT.ATTRIBUTES.Flags = .{}, mode: windows.FILE.MODE, }; }; @@ -18373,7 +18352,7 @@ const CreateFileMapError = error{ OutOfMemory, MappingAlreadyExists, Unseekable, - FileLockConflict, + LockViolation, } || Io.Cancelable || Io.UnexpectedError; fn createFileMap( @@ -18408,7 +18387,7 @@ fn createFileMap( file.handle, )) { .SUCCESS => {}, - .FILE_LOCK_CONFLICT => return error.FileLockConflict, + .FILE_LOCK_CONFLICT => return error.LockViolation, .INVALID_FILE_FOR_SECTION => return error.OperationUnsupported, .ACCESS_DENIED => return error.AccessDenied, .SECTION_TOO_BIG => return error.SectionOversize, @@ -18724,7 +18703,7 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError! while (true) { const buf = memory[i..]; if (buf.len == 0) break; - i += try writeFilePositionalWindows(file.handle, memory[i..], offset + i); + i += try writeFilePositionalWindows(file, memory[i..], offset + i); } } else if (native_os == .wasi and !builtin.link_libc) { var i: usize = 0; @@ -18809,8 +18788,7 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError! } } -fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result { - _ = t; +fn deviceIoControl(o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result { if (is_windows) { const NtControlFile = switch (o.code.DeviceType) { .FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile, @@ -19104,16 +19082,10 @@ fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!windows var result: windows.HANDLE = undefined; - const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; - var nt_name: windows.UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(sub_path_w.ptr), - }; - const attr: windows.OBJECT_ATTRIBUTES = .{ + const attr: windows.OBJECT.ATTRIBUTES = .{ .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir, .Attributes = .{ .INHERIT = if (options.sa) |sa| sa.bInheritHandle != windows.FALSE else false }, - .ObjectName = &nt_name, + .ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w)), .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null, }; diff --git a/lib/std/Io/Threaded/test.zig b/lib/std/Io/Threaded/test.zig index 1c9b188584..fff802b191 100644 --- a/lib/std/Io/Threaded/test.zig +++ b/lib/std/Io/Threaded/test.zig @@ -290,7 +290,7 @@ fn RtlDosPathNameToNtPathName_U(path: [:0]const u16) !Io.Threaded.WindowsPathSpa defer windows.ntdll.RtlFreeUnicodeString(&out); var path_space: Io.Threaded.WindowsPathSpace = undefined; - const out_path = out.Buffer.?[0 .. out.Length / 2]; + const out_path = out.slice(); @memcpy(path_space.data[0..out_path.len], out_path); path_space.len = out.Length / 2; path_space.data[path_space.len] = 0; diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 131f57dcf8..11466fb9a2 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -88,20 +88,10 @@ pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { }, .windows => { var buf: [max_name_len]u16 = undefined; - const len = try std.unicode.wtf8ToWtf16Le(&buf, name); - const byte_len = math.cast(c_ushort, len * 2) orelse return error.NameTooLong; - - // Note: NT allocates its own copy, no use-after-free here. - const unicode_string = windows.UNICODE_STRING{ - .Length = byte_len, - .MaximumLength = byte_len, - .Buffer = &buf, - }; - switch (windows.ntdll.NtSetInformationThread( self.getHandle(), .NameInformation, - &unicode_string, + &windows.UNICODE_STRING.init(buf[0..try std.unicode.wtf8ToWtf16Le(&buf, name)]), @sizeOf(windows.UNICODE_STRING), )) { .SUCCESS => return, @@ -217,8 +207,8 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co null, )) { .SUCCESS => { - const string = @as(*const windows.UNICODE_STRING, @ptrCast(&buf)); - const len = std.unicode.wtf16LeToWtf8(buffer, string.Buffer.?[0 .. string.Length / 2]); + const string: *const windows.UNICODE_STRING = @ptrCast(&buf); + const len = std.unicode.wtf16LeToWtf8(buffer, string.slice()); return if (len > 0) buffer[0..len] else null; }, .NOT_IMPLEMENTED => return error.Unsupported, diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 8f83bf19c8..a2b5467c1f 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -37,12 +37,13 @@ pub const cpu_context = @import("debug/cpu_context.zig"); /// /// ``` /// pub const init: SelfInfo; -/// pub fn deinit(si: *SelfInfo, gpa: Allocator) void; +/// pub fn deinit(si: *SelfInfo, io: Io) void; /// /// /// Returns the symbol and source location of the instruction at `address`. -/// pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) SelfInfoError!Symbol; +/// pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) SelfInfoError!Symbol; /// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`. -/// pub fn getModuleName(si: *SelfInfo, gpa: Allocator, address: usize) SelfInfoError![]const u8; +/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8; +/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize; /// /// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available. /// pub const can_unwind: bool; @@ -51,15 +52,15 @@ pub const cpu_context = @import("debug/cpu_context.zig"); /// /// An address representing the instruction pointer in the last frame. /// pc: usize, /// -/// pub fn init(ctx: *cpu_context.Native, gpa: Allocator) Allocator.Error!UnwindContext; -/// pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void; +/// pub fn init(ctx: *cpu_context.Native) Allocator.Error!UnwindContext; +/// pub fn deinit(ctx: *UnwindContext) void; /// /// Returns the frame pointer associated with the last unwound stack frame. /// /// If the frame pointer is unknown, 0 may be returned instead. /// pub fn getFp(uc: *UnwindContext) usize; /// }; /// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's /// /// return address, or 0 if the end of the stack has been reached. -/// pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) SelfInfoError!usize; +/// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize; /// ``` pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo")) root.debug.SelfInfo @@ -669,7 +670,6 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin t.setColor(.reset) catch {}; return; } - const di_gpa = getDebugInfoAllocator(); const di = getSelfDebugInfo() catch |err| switch (err) { error.UnsupportedTarget => { t.setColor(.dim) catch {}; @@ -696,7 +696,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin .useless, .unsafe => {}, .safe, .ideal => continue, // no need to even warn } - const module_name = di.getModuleName(di_gpa, io, unwind_error.address) catch "???"; + const module_name = di.getModuleName(io, unwind_error.address) catch "???"; const caption: []const u8 = switch (unwind_error.err) { error.MissingDebugInfo => "unwind info unavailable", error.InvalidDebugInfo => "unwind info invalid", @@ -741,7 +741,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin } // `ret_addr` is the return address, which is *after* the function call. // Subtract 1 to get an address *in* the function call for a better source location. - try printSourceAtAddress(di_gpa, io, di, t, ret_addr -| StackIterator.ra_call_offset); + try printSourceAtAddress(io, di, t, ret_addr -| StackIterator.ra_call_offset); printed_any_frame = true; }, }; @@ -788,7 +788,6 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void // `st` is `@errorReturnTrace()` and errors are encountered while writing the stack trace. const n_frames = st.index; if (n_frames == 0) return writer.writeAll("(empty stack trace)\n"); - const di_gpa = getDebugInfoAllocator(); const di = getSelfDebugInfo() catch |err| switch (err) { error.UnsupportedTarget => { t.setColor(.dim) catch {}; @@ -802,7 +801,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void for (st.instruction_addresses[0..captured_frames]) |ret_addr| { // `ret_addr` is the return address, which is *after* the function call. // Subtract 1 to get an address *in* the function call for a better source location. - try printSourceAtAddress(di_gpa, io, di, t, ret_addr -| StackIterator.ra_call_offset); + try printSourceAtAddress(io, di, t, ret_addr -| StackIterator.ra_call_offset); } if (n_frames > captured_frames) { t.setColor(.bold) catch {}; @@ -875,7 +874,7 @@ const StackIterator = union(enum) { switch (si.*) { .ctx_first => {}, .fp => {}, - .di => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()), + .di => |*unwind_context| unwind_context.deinit(), } } @@ -980,8 +979,7 @@ const StackIterator = union(enum) { }, .di => |*unwind_context| { const di = getSelfDebugInfo() catch unreachable; - const di_gpa = getDebugInfoAllocator(); - const ret_addr = di.unwindFrame(di_gpa, io, unwind_context) catch |err| { + const ret_addr = di.unwindFrame(io, unwind_context) catch |err| { const pc = unwind_context.pc; const fp = unwind_context.getFp(); it.* = .{ .fp = fp }; @@ -1109,14 +1107,8 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { return ptr; } -fn printSourceAtAddress( - gpa: Allocator, - io: Io, - debug_info: *SelfInfo, - t: Io.Terminal, - address: usize, -) Writer.Error!void { - const symbol: Symbol = debug_info.getSymbol(gpa, io, address) catch |err| switch (err) { +fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void { + const symbol: Symbol = debug_info.getSymbol(io, address) catch |err| switch (err) { error.MissingDebugInfo, error.UnsupportedDebugInfo, error.InvalidDebugInfo, @@ -1134,14 +1126,14 @@ fn printSourceAtAddress( break :s .unknown; }, }; - defer if (symbol.source_location) |sl| gpa.free(sl.file_name); + defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name); return printLineInfo( io, t, symbol.source_location, address, symbol.name orelse "???", - symbol.compile_unit_name orelse debug_info.getModuleName(gpa, io, address) catch "???", + symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", ); } fn printLineInfo( @@ -1608,14 +1600,13 @@ test "manage resources correctly" { return @returnAddress(); } }; - const gpa = testing.allocator; const io = testing.io; var discarding: Writer.Discarding = .init(&.{}); var di: SelfInfo = .init; - defer di.deinit(gpa); + defer di.deinit(io); const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color }; - try printSourceAtAddress(gpa, io, &di, t, S.showMyTrace()); + try printSourceAtAddress(io, &di, t, S.showMyTrace()); } /// This API helps you track where a value originated and where it was mutated, diff --git a/lib/std/debug/Dwarf/SelfUnwinder.zig b/lib/std/debug/Dwarf/SelfUnwinder.zig index 80908e84bb..0f5253844d 100644 --- a/lib/std/debug/Dwarf/SelfUnwinder.zig +++ b/lib/std/debug/Dwarf/SelfUnwinder.zig @@ -55,7 +55,8 @@ pub fn init(cpu_context: *const std.debug.cpu_context.Native) SelfUnwinder { }; } -pub fn deinit(unwinder: *SelfUnwinder, gpa: Allocator) void { +pub fn deinit(unwinder: *SelfUnwinder) void { + const gpa = std.debug.getDebugInfoAllocator(); unwinder.cfi_vm.deinit(gpa); unwinder.expr_vm.deinit(gpa); unwinder.* = undefined; diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 40841bac2c..a264b1170d 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -11,7 +11,9 @@ pub const init: SelfInfo = .{ .ranges = .empty, .unwind_cache = null, }; -pub fn deinit(si: *SelfInfo, gpa: Allocator) void { +pub fn deinit(si: *SelfInfo, io: Io) void { + _ = io; + const gpa = std.debug.getDebugInfoAllocator(); for (si.modules.items) |*mod| { unwind: { const u = &(mod.unwind orelse break :unwind catch break :unwind); @@ -28,7 +30,8 @@ pub fn deinit(si: *SelfInfo, gpa: Allocator) void { if (si.unwind_cache) |cache| gpa.free(cache); } -pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol { +pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol { + const gpa = std.debug.getDebugInfoAllocator(); const module = try si.findModule(gpa, io, address, .exclusive); defer si.rwlock.unlock(io); @@ -73,13 +76,15 @@ pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!st error.OutOfMemory => |e| return e, }; } -pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 { +pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { + const gpa = std.debug.getDebugInfoAllocator(); const module = try si.findModule(gpa, io, address, .shared); defer si.rwlock.unlockShared(io); if (module.name.len == 0) return error.MissingDebugInfo; return module.name; } -pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize { +pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize { + const gpa = std.debug.getDebugInfoAllocator(); const module = try si.findModule(gpa, io, address, .shared); defer si.rwlock.unlockShared(io); return module.load_offset; @@ -179,8 +184,9 @@ comptime { } } pub const UnwindContext = Dwarf.SelfUnwinder; -pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize { +pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize { comptime assert(can_unwind); + const gpa = std.debug.getDebugInfoAllocator(); { si.rwlock.lockSharedUncancelable(io); diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index 1cc2ebed53..6b184fec7a 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -6,7 +6,9 @@ pub const init: SelfInfo = .{ .mutex = .init, .modules = .empty, }; -pub fn deinit(si: *SelfInfo, gpa: Allocator) void { +pub fn deinit(si: *SelfInfo, io: Io) void { + _ = io; + const gpa = std.debug.getDebugInfoAllocator(); for (si.modules.keys()) |*module| { unwind: { const u = &(module.unwind orelse break :unwind catch break :unwind); @@ -20,7 +22,8 @@ pub fn deinit(si: *SelfInfo, gpa: Allocator) void { si.modules.deinit(gpa); } -pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol { +pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol { + const gpa = std.debug.getDebugInfoAllocator(); const module = try si.findModule(gpa, io, address); defer si.mutex.unlock(io); @@ -76,9 +79,8 @@ pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!st ) catch null, }; } -pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 { +pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { _ = si; - _ = gpa; _ = io; // This function is marked as deprecated; however, it is significantly more // performant than `dladdr` (since the latter also does a very slow symbol @@ -87,7 +89,8 @@ pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Erro @ptrFromInt(address), ) orelse return error.MissingDebugInfo); } -pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize { +pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize { + const gpa = std.debug.getDebugInfoAllocator(); const module = try si.findModule(gpa, io, address); defer si.mutex.unlock(io); const header: *std.macho.mach_header_64 = @ptrFromInt(module.text_base); @@ -107,8 +110,8 @@ pub const UnwindContext = std.debug.Dwarf.SelfUnwinder; /// Unwind a frame using MachO compact unwind info (from `__unwind_info`). /// If the compact encoding can't encode a way to unwind a frame, it will /// defer unwinding to DWARF, in which case `__eh_frame` will be used if available. -pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize { - return unwindFrameInner(si, gpa, io, context) catch |err| switch (err) { +pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize { + return unwindFrameInner(si, io, context) catch |err| switch (err) { error.InvalidDebugInfo, error.MissingDebugInfo, error.UnsupportedDebugInfo, @@ -134,7 +137,8 @@ pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContex => return error.InvalidDebugInfo, }; } -fn unwindFrameInner(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) !usize { +fn unwindFrameInner(si: *SelfInfo, io: Io, context: *UnwindContext) !usize { + const gpa = std.debug.getDebugInfoAllocator(); const module = try si.findModule(gpa, io, context.pc); defer si.mutex.unlock(io); diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index 75cc329c3b..7cac79d727 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -1,39 +1,51 @@ mutex: Io.Mutex, +ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn, +notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE, modules: std.ArrayList(Module), -module_name_arena: std.heap.ArenaAllocator.State, pub const init: SelfInfo = .{ .mutex = .init, + .ntdll_handle = null, + .notification_cookie = null, .modules = .empty, - .module_name_arena = .{}, }; -pub fn deinit(si: *SelfInfo, gpa: Allocator) void { - for (si.modules.items) |*module| { - di: { - const di = &(module.di orelse break :di catch break :di); - di.deinit(gpa); +pub fn deinit(si: *SelfInfo, io: Io) void { + const gpa = std.debug.getDebugInfoAllocator(); + if (si.notification_cookie) |cookie| unregister: { + switch ((si.getNtdllProc(.LdrUnregisterDllNotification) catch break :unregister)(cookie)) { + .SUCCESS => {}, + else => |status| windows.unexpectedStatus(status) catch break :unregister, } } + if (si.ntdll_handle) |handle| switch (windows.ntdll.LdrUnloadDll(handle)) { + .SUCCESS => {}, + else => |status| windows.unexpectedStatus(status) catch {}, + }; + for (si.modules.items) |*module| module.deinit(gpa, io); si.modules.deinit(gpa); - - var module_name_arena = si.module_name_arena.promote(gpa); - module_name_arena.deinit(); } -pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol { +pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol { + const gpa = std.debug.getDebugInfoAllocator(); try si.mutex.lock(io); defer si.mutex.unlock(io); const module = try si.findModule(gpa, address); const di = try module.getDebugInfo(gpa, io); - return di.getSymbol(gpa, address - module.base_address); + return di.getSymbol(gpa, address - @intFromPtr(module.entry.DllBase)); } -pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 { +pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { + const gpa = std.debug.getDebugInfoAllocator(); try si.mutex.lock(io); defer si.mutex.unlock(io); const module = try si.findModule(gpa, address); - return module.name; + return module.name orelse { + const name = try std.unicode.wtf16LeToWtf8Alloc(gpa, module.entry.BaseDllName.slice()); + module.name = name; + return name; + }; } -pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize { +pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize { + const gpa = std.debug.getDebugInfoAllocator(); try si.mutex.lock(io); defer si.mutex.unlock(io); const module = try si.findModule(gpa, address); @@ -141,18 +153,16 @@ pub const UnwindContext = struct { .history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE), }; } - pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void { + pub fn deinit(ctx: *UnwindContext) void { _ = ctx; - _ = gpa; } pub fn getFp(ctx: *UnwindContext) usize { return ctx.cur.getRegs().bp; } }; -pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize { +pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize { _ = si; _ = io; - _ = gpa; const current_regs = context.cur.getRegs(); var image_base: usize = undefined; @@ -188,16 +198,12 @@ pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContex } const Module = struct { - base_address: usize, - size: u32, - name: []const u8, - handle: windows.HMODULE, - + entry: *const LDR.DATA_TABLE_ENTRY, + name: ?[]const u8, di: ?(Error!DebugInfo), const DebugInfo = struct { arena: std.heap.ArenaAllocator.State, - io: Io, coff_image_base: u64, mapped_file: ?MappedFile, dwarf: ?Dwarf, @@ -210,14 +216,19 @@ const Module = struct { section_view: []const u8, fn deinit(mf: *const MappedFile, io: Io) void { const process_handle = windows.GetCurrentProcess(); - assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(mf.section_view.ptr)) == .SUCCESS); + switch (windows.ntdll.NtUnmapViewOfSection( + process_handle, + @constCast(mf.section_view.ptr), + )) { + .SUCCESS => {}, + else => |status| windows.unexpectedStatus(status) catch {}, + } windows.CloseHandle(mf.section_handle); mf.file.close(io); } }; - fn deinit(di: *DebugInfo, gpa: Allocator) void { - const io = di.io; + fn deinit(di: *DebugInfo, gpa: Allocator, io: Io) void { if (di.dwarf) |*dwarf| dwarf.deinit(gpa); if (di.pdb) |*pdb| { pdb.file_reader.file.close(io); @@ -262,7 +273,10 @@ const Module = struct { return .{ .name = pdb.getSymbolName(module, vaddr - coff_section.virtual_address), .compile_unit_name = fs.path.basename(module.obj_file_name), - .source_location = pdb.getLineNumberInfo(module, vaddr - coff_section.virtual_address) catch null, + .source_location = pdb.getLineNumberInfo( + module, + vaddr - coff_section.virtual_address, + ) catch null, }; } dwarf: { @@ -286,13 +300,19 @@ const Module = struct { } }; + fn deinit(module: *Module, gpa: Allocator, io: Io) void { + if (module.name) |name| gpa.free(name); + if (module.di) |*di_or_err| if (di_or_err.*) |*di| di.deinit(gpa, io) else |_| {}; + module.* = undefined; + } + fn getDebugInfo(module: *Module, gpa: Allocator, io: Io) Error!*DebugInfo { if (module.di == null) module.di = loadDebugInfo(module, gpa, io); return if (module.di.?) |*di| di else |err| err; } fn loadDebugInfo(module: *const Module, gpa: Allocator, io: Io) Error!DebugInfo { - const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address); - const mapped = mapped_ptr[0..module.size]; + const mapped_ptr: [*]const u8 = @ptrCast(module.entry.DllBase); + const mapped = mapped_ptr[0..module.entry.SizeOfImage]; var coff_obj = coff.Coff.init(mapped, true) catch return error.InvalidDebugInfo; var arena_instance: std.heap.ArenaAllocator = .init(gpa); @@ -304,18 +324,15 @@ const Module = struct { // a binary is produced with -gdwarf, since the section names are longer than 8 bytes. const mapped_file: ?DebugInfo.MappedFile = mapped: { if (!coff_obj.strtabRequired()) break :mapped null; - var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined; - name_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present - const process_handle = windows.GetCurrentProcess(); - const len = windows.kernel32.GetModuleFileNameExW( - process_handle, - module.handle, - name_buffer[4..], - windows.PATH_MAX_WIDE, - ); - if (len == 0) return error.MissingDebugInfo; - const name_w = name_buffer[0 .. len + 4 :0]; - const coff_file = Io.Threaded.dirOpenFileWtf16(null, name_w, .{}) catch |err| switch (err) { + var path_buffer: [4 + windows.PATH_MAX_WIDE]u16 = undefined; + path_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present + const path_slice = module.entry.FullDllName.slice(); + @memcpy(path_buffer[4..][0..path_slice.len], path_slice); + const coff_file = Io.Threaded.dirOpenFileWtf16( + null, + path_buffer[0 .. 4 + path_slice.len], + .{}, + ) catch |err| switch (err) { error.Canceled => |e| return e, error.Unexpected => |e| return e, error.FileNotFound => return error.MissingDebugInfo, @@ -359,7 +376,8 @@ const Module = struct { null, null, .{ .READONLY = true }, - // The documentation states that if no AllocationAttribute is specified, then SEC_COMMIT is the default. + // The documentation states that if no AllocationAttribute is specified, + // then SEC_COMMIT is the default. // In practice, this isn't the case and specifying 0 will result in INVALID_PARAMETER_6. .{ .COMMIT = true }, coff_file.handle, @@ -368,6 +386,7 @@ const Module = struct { errdefer windows.CloseHandle(section_handle); var coff_len: usize = 0; var section_view_ptr: ?[*]const u8 = null; + const process_handle = windows.GetCurrentProcess(); const map_section_rc = windows.ntdll.NtMapViewOfSection( section_handle, process_handle, @@ -381,7 +400,13 @@ const Module = struct { .{ .READONLY = true }, ); if (map_section_rc != .SUCCESS) return error.MissingDebugInfo; - errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(section_view_ptr.?)) == .SUCCESS); + errdefer switch (windows.ntdll.NtUnmapViewOfSection( + process_handle, + @constCast(section_view_ptr.?), + )) { + .SUCCESS => {}, + else => |status| windows.unexpectedStatus(status) catch {}, + }; const section_view = section_view_ptr.?[0..coff_len]; coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo; break :mapped .{ @@ -496,7 +521,6 @@ const Module = struct { return .{ .arena = arena_instance.state, - .io = io, .coff_image_base = coff_image_base, .mapped_file = mapped_file, .dwarf = opt_dwarf, @@ -509,52 +533,82 @@ const Module = struct { /// Assumes we already hold `si.mutex`. fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebugInfo, OutOfMemory, Unexpected }!*Module { for (si.modules.items) |*mod| { - if (address >= mod.base_address and address < mod.base_address + mod.size) { - return mod; + const base = @intFromPtr(mod.entry.DllBase); + if (address >= base and address < base + mod.entry.SizeOfImage) return mod; + } + try si.modules.ensureUnusedCapacity(gpa, 1); + var entry: *LDR.DATA_TABLE_ENTRY = undefined; + switch (windows.ntdll.LdrFindEntryForAddress(@ptrFromInt(address), &entry)) { + .SUCCESS => {}, + .DLL_NOT_FOUND => return error.MissingDebugInfo, + else => |status| return windows.unexpectedStatus(status), + } + if (si.notification_cookie == null) { + var notification_cookie: LDR.DLL_NOTIFICATION.COOKIE = undefined; + switch ((try si.getNtdllProc(.LdrRegisterDllNotification))( + .{}, + &dllNotification, + si, + ¬ification_cookie, + )) { + .SUCCESS => si.notification_cookie = notification_cookie, + else => |status| return windows.unexpectedStatus(status), } } + const mod = si.modules.addOneAssumeCapacity(); + mod.* = .{ .entry = entry, .name = null, .di = null }; + return mod; +} - // A new module might have been loaded; rebuild the list. - { - for (si.modules.items) |*mod| { - const di = &(mod.di orelse continue catch continue); - di.deinit(gpa); - } - si.modules.clearRetainingCapacity(); - - var module_name_arena = si.module_name_arena.promote(gpa); - defer si.module_name_arena = module_name_arena.state; - _ = module_name_arena.reset(.retain_capacity); - - const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0); - if (handle == windows.INVALID_HANDLE_VALUE) { - return windows.unexpectedError(windows.GetLastError()); - } - defer windows.CloseHandle(handle); - var entry: windows.MODULEENTRY32 = undefined; - entry.dwSize = @sizeOf(windows.MODULEENTRY32); - var result = windows.kernel32.Module32First(handle, &entry); - while (result != 0) : (result = windows.kernel32.Module32Next(handle, &entry)) { - try si.modules.append(gpa, .{ - .base_address = @intFromPtr(entry.modBaseAddr), - .size = entry.modBaseSize, - .name = try module_name_arena.allocator().dupe( - u8, - std.mem.sliceTo(&entry.szModule, 0), - ), - .handle = entry.hModule, - .di = null, - }); +inline fn getNtdllProc( + si: *SelfInfo, + comptime proc: std.meta.DeclEnum(windows.ntdll), +) !@TypeOf(&@field(windows.ntdll, @tagName(proc))) { + return if (load_dll_notification_procs) + @ptrCast(try si.loadNtdllProc(@tagName(proc))) + else + &@field(windows.ntdll, @tagName(proc)); +} +fn loadNtdllProc(si: *SelfInfo, name: []const u8) Io.UnexpectedError!*anyopaque { + const ntdll_handle = si.ntdll_handle orelse ntdll_handle: { + var ntdll_handle: *anyopaque = undefined; + switch (windows.ntdll.LdrLoadDll(null, null, &.init( + &.{ 'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l' }, + ), &ntdll_handle)) { + .SUCCESS => {}, + else => |status| return windows.unexpectedStatus(status), } + si.ntdll_handle = ntdll_handle; + break :ntdll_handle ntdll_handle; + }; + var proc_addr: *anyopaque = undefined; + switch (windows.ntdll.LdrGetProcedureAddress(ntdll_handle, &.init(name), 0, &proc_addr)) { + .SUCCESS => {}, + else => |status| return windows.unexpectedStatus(status), } + return proc_addr; +} - for (si.modules.items) |*mod| { - if (address >= mod.base_address and address < mod.base_address + mod.size) { - return mod; - } +fn dllNotification( + reason: LDR.DLL_NOTIFICATION.REASON, + data: *const LDR.DLL_NOTIFICATION.DATA, + context: ?*anyopaque, +) callconv(.winapi) void { + const si: *SelfInfo = @ptrCast(@alignCast(context)); + switch (reason) { + .LOADED => {}, + .UNLOADED => { + const io = std.Options.debug_io; + si.mutex.lockUncancelable(io); + defer si.mutex.unlock(io); + for (si.modules.items, 0..) |*mod, mod_index| { + if (mod.entry.DllBase != data.Unloaded.DllBase) continue; + mod.deinit(std.debug.getDebugInfoAllocator(), io); + _ = si.modules.swapRemove(mod_index); + break; + } + }, } - - return error.MissingDebugInfo; } const std = @import("std"); @@ -563,12 +617,23 @@ const Allocator = std.mem.Allocator; const Dwarf = std.debug.Dwarf; const Pdb = std.debug.Pdb; const Error = std.debug.SelfInfoError; -const assert = std.debug.assert; const coff = std.coff; const fs = std.fs; const windows = std.os.windows; +const LDR = windows.LDR; const builtin = @import("builtin"); const native_endian = builtin.target.cpu.arch.endian(); +const load_dll_notification_procs = builtin.abi == .msvc and switch (builtin.zig_backend) { + .stage2_c => true, + else => switch (builtin.output_mode) { + .Exe => false, + .Lib => switch (builtin.link_mode) { + .static => true, + .dynamic => false, + }, + .Obj => true, + }, +}; const SelfInfo = @This(); diff --git a/lib/std/heap.zig b/lib/std/heap.zig index cfe943fc2b..ba7ef0aabf 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -110,11 +110,11 @@ pub fn defaultQueryPageSize() usize { break :size @intCast(vm_info.page_size); }, .windows => { - var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined; + var sbi: windows.SYSTEM.BASIC_INFORMATION = undefined; switch (windows.ntdll.NtQuerySystemInformation( - .SystemBasicInformation, + .Basic, &sbi, - @sizeOf(windows.SYSTEM_BASIC_INFORMATION), + @sizeOf(windows.SYSTEM.BASIC_INFORMATION), null, )) { .SUCCESS => break :size sbi.PageSize, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 86d4ce0efd..0c91f11ff5 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -24,6 +24,66 @@ pub const nls = @import("windows/nls.zig"); pub const current_process: HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1)))); +pub const OBJECT = struct { + // ref: um/winternl.h + + pub const ATTRIBUTES = extern struct { + Length: ULONG = @sizeOf(ATTRIBUTES), + RootDirectory: ?HANDLE = null, + ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty), + Attributes: Flags = .{}, + SecurityDescriptor: ?*anyopaque = null, + SecurityQualityOfService: ?*anyopaque = null, + + // Valid values for the Attributes field + pub const Flags = packed struct(ULONG) { + Reserved0: u1 = 0, + INHERIT: bool = false, + Reserved2: u2 = 0, + PERMANENT: bool = false, + EXCLUSIVE: bool = false, + /// If name-lookup code should ignore the case of the ObjectName member rather than performing an exact-match search. + CASE_INSENSITIVE: bool = true, + OPENIF: bool = false, + OPENLINK: bool = false, + KERNEL_HANDLE: bool = false, + FORCE_ACCESS_CHECK: bool = false, + IGNORE_IMPERSONATED_DEVICEMAP: bool = false, + DONT_REPARSE: bool = false, + Reserved13: u19 = 0, + + pub const VALID_ATTRIBUTES: ATTRIBUTES = .{ + .INHERIT = true, + .PERMANENT = true, + .EXCLUSIVE = true, + .CASE_INSENSITIVE = true, + .OPENIF = true, + .OPENLINK = true, + .KERNEL_HANDLE = true, + .FORCE_ACCESS_CHECK = true, + .IGNORE_IMPERSONATED_DEVICEMAP = true, + .DONT_REPARSE = true, + }; + }; + }; + + pub const INFORMATION_CLASS = enum(c_int) { + Basic = 0, + Name = 1, + Type = 2, + Types = 3, + HandleFlag = 4, + Session = 5, + _, + + pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".fields.len; + }; + + pub const NAME_INFORMATION = extern struct { + Name: UNICODE_STRING, + }; +}; + pub const FILE = struct { // ref: km/ntddk.h @@ -73,6 +133,94 @@ pub const FILE = struct { // ref: km/ntifs.h + pub const NAME_FLAGS = packed struct(UCHAR) { + NTFS: bool = false, + DOS: bool = false, + Reserved2: u5 = 0, + UNSPECIFIED: bool = false, + }; + + pub const NOTIFY = struct { + pub const CHANGE = packed struct(ULONG) { + FILE_NAME: bool = false, + DIR_NAME: bool = false, + ATTRIBUTES: bool = false, + SIZE: bool = false, + LAST_WRITE: bool = false, + LAST_ACCESS: bool = false, + CREATION: bool = false, + EA: bool = false, + SECURITY: bool = false, + STREAM_NAME: bool = false, + STREAM_SIZE: bool = false, + STREAM_WRITE: bool = false, + Reserved12: u20 = 0, + }; + + pub const INFORMATION = extern struct { + NextEntryOffset: ULONG, + Action: ULONG, + FileNameLength: ULONG, + FileName: [0]WCHAR, + + pub fn fileName(info: *INFORMATION) []WCHAR { + const ptr: [*]WCHAR = @ptrCast(&info.FileName); + return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))]; + } + }; + + pub const EXTENDED_INFORMATION = extern struct { + NextEntryOffset: ULONG, + Action: ULONG, + CreationTime: LARGE_INTEGER, + LastModificationTime: LARGE_INTEGER, + LastChangeTime: LARGE_INTEGER, + LastAccessTime: LARGE_INTEGER, + AllocatedLength: LARGE_INTEGER, + FileSize: LARGE_INTEGER, + FileAttributes: ATTRIBUTE, + u: extern union { + ReparsePointTag: ULONG, + EaSize: ULONG, + }, + FileId: LARGE_INTEGER, + ParentFileId: LARGE_INTEGER, + FileNameLength: ULONG, + FileName: [0]WCHAR, + + pub fn fileName(info: *INFORMATION) []WCHAR { + const ptr: [*]WCHAR = @ptrCast(&info.FileName); + return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))]; + } + }; + + pub const FULL_INFORMATION = extern struct { + NextEntryOffset: ULONG, + Action: ULONG, + CreationTime: LARGE_INTEGER, + LastModificationTime: LARGE_INTEGER, + LastChangeTime: LARGE_INTEGER, + LastAccessTime: LARGE_INTEGER, + AllocatedLength: LARGE_INTEGER, + FileSize: LARGE_INTEGER, + FileAttributes: ATTRIBUTE, + u: extern union { + ReparsePointTag: ULONG, + EaSize: ULONG, + }, + FileId: LARGE_INTEGER, + ParentFileId: LARGE_INTEGER, + FileNameLength: ULONG, + FileNameFlags: NAME_FLAGS, + FileName: [0]WCHAR, + + pub fn fileName(info: *INFORMATION) []WCHAR { + const ptr: [*]WCHAR = @ptrCast(&info.FileName); + return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))]; + } + }; + }; + pub const PIPE = struct { /// Define the `NamedPipeType` flags for `NtCreateNamedPipeFile` pub const TYPE = packed struct(ULONG) { @@ -357,6 +505,7 @@ pub const FILE = struct { IdAllExtdBothDirectory = 81, StreamReservation = 82, MupProvider = 83, + _, pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len; }; @@ -647,6 +796,19 @@ pub const FILE = struct { Mode: MODE, }; }; + + // ref: km/ +}; + +pub const DIRECTORY = struct { + pub const NOTIFY_INFORMATION_CLASS = enum(c_int) { + Notify = 1, + NotifyExtended = 2, + NotifyFull = 3, + _, + + pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len; + }; }; pub const CONSOLE = struct { @@ -880,141 +1042,229 @@ pub const CONSOLE = struct { // ref: km/ntddk.h -pub const PROCESSINFOCLASS = enum(c_int) { - BasicInformation = 0, - QuotaLimits = 1, - IoCounters = 2, - VmCounters = 3, - Times = 4, - BasePriority = 5, - RaisePriority = 6, - DebugPort = 7, - ExceptionPort = 8, - AccessToken = 9, - LdtInformation = 10, - LdtSize = 11, - DefaultHardErrorMode = 12, - IoPortHandlers = 13, - PooledUsageAndLimits = 14, - WorkingSetWatch = 15, - UserModeIOPL = 16, - EnableAlignmentFaultFixup = 17, - PriorityClass = 18, - Wx86Information = 19, - HandleCount = 20, - AffinityMask = 21, - PriorityBoost = 22, - DeviceMap = 23, - SessionInformation = 24, - ForegroundInformation = 25, - Wow64Information = 26, - ImageFileName = 27, - LUIDDeviceMapsEnabled = 28, - BreakOnTermination = 29, - DebugObjectHandle = 30, - DebugFlags = 31, - HandleTracing = 32, - IoPriority = 33, - ExecuteFlags = 34, - TlsInformation = 35, - Cookie = 36, - ImageInformation = 37, - CycleTime = 38, - PagePriority = 39, - InstrumentationCallback = 40, - ThreadStackAllocation = 41, - WorkingSetWatchEx = 42, - ImageFileNameWin32 = 43, - ImageFileMapping = 44, - AffinityUpdateMode = 45, - MemoryAllocationMode = 46, - GroupInformation = 47, - TokenVirtualizationEnabled = 48, - OwnerInformation = 49, - WindowInformation = 50, - HandleInformation = 51, - MitigationPolicy = 52, - DynamicFunctionTableInformation = 53, - HandleCheckingMode = 54, - KeepAliveCount = 55, - RevokeFileHandles = 56, - WorkingSetControl = 57, - HandleTable = 58, - CheckStackExtentsMode = 59, - CommandLineInformation = 60, - ProtectionInformation = 61, - MemoryExhaustion = 62, - FaultInformation = 63, - TelemetryIdInformation = 64, - CommitReleaseInformation = 65, - Reserved1Information = 66, - Reserved2Information = 67, - SubsystemProcess = 68, - InPrivate = 70, - RaiseUMExceptionOnInvalidHandleClose = 71, - SubsystemInformation = 75, - Win32kSyscallFilterInformation = 79, - EnergyTrackingState = 82, - NetworkIoCounters = 114, - _, +pub const SYSTEM = struct { + pub const INFORMATION_CLASS = enum(c_int) { + Basic = 0, + Performance = 2, + TimeOfDay = 3, + Process = 5, + ProcessorPerformance = 8, + Interrupt = 23, + Exception = 33, + RegistryQuota = 37, + Lookaside = 45, + CodeIntegrity = 103, + Policy = 134, + _, + }; - pub const Max: @typeInfo(@This()).@"enum".tag_type = 117; + pub const BASIC_INFORMATION = extern struct { + Reserved: ULONG, + TimerResolution: ULONG, + PageSize: ULONG, + NumberOfPhysicalPages: ULONG, + LowestPhysicalPageNumber: ULONG, + HighestPhysicalPageNumber: ULONG, + AllocationGranularity: ULONG, + MinimumUserModeAddress: ULONG_PTR, + MaximumUserModeAddress: ULONG_PTR, + ActiveProcessorsAffinityMask: KAFFINITY, + NumberOfProcessors: UCHAR, + }; }; -pub const THREADINFOCLASS = enum(c_int) { - BasicInformation = 0, - Times = 1, - Priority = 2, - BasePriority = 3, - AffinityMask = 4, - ImpersonationToken = 5, - DescriptorTableEntry = 6, - EnableAlignmentFaultFixup = 7, - EventPair_Reusable = 8, - QuerySetWin32StartAddress = 9, - ZeroTlsCell = 10, - PerformanceCount = 11, - AmILastThread = 12, - IdealProcessor = 13, - PriorityBoost = 14, - SetTlsArrayAddress = 15, - IsIoPending = 16, - // Windows 2000+ from here - HideFromDebugger = 17, - // Windows XP+ from here - BreakOnTermination = 18, - SwitchLegacyState = 19, - IsTerminated = 20, - // Windows Vista+ from here - LastSystemCall = 21, - IoPriority = 22, - CycleTime = 23, - PagePriority = 24, - ActualBasePriority = 25, - TebInformation = 26, - CSwitchMon = 27, - // Windows 7+ from here - CSwitchPmu = 28, - Wow64Context = 29, - GroupInformation = 30, - UmsInformation = 31, - CounterProfiling = 32, - IdealProcessorEx = 33, - // Windows 8+ from here - CpuAccountingInformation = 34, - // Windows 8.1+ from here - SuspendCount = 35, - // Windows 10+ from here - HeterogeneousCpuPolicy = 36, - ContainerId = 37, - NameInformation = 38, - SelectedCpuSets = 39, - SystemThreadInformation = 40, - ActualGroupAffinity = 41, - DynamicCodePolicyInfo = 42, - SubsystemInformation = 45, +pub const PROCESS = struct { + pub const INFORMATION = extern struct { + hProcess: HANDLE, + hThread: HANDLE, + dwProcessId: DWORD, + dwThreadId: DWORD, + }; - pub const Max: @typeInfo(@This()).@"enum".tag_type = 60; + pub const INFOCLASS = enum(c_int) { + BasicInformation = 0, + QuotaLimits = 1, + IoCounters = 2, + VmCounters = 3, + Times = 4, + BasePriority = 5, + RaisePriority = 6, + DebugPort = 7, + ExceptionPort = 8, + AccessToken = 9, + LdtInformation = 10, + LdtSize = 11, + DefaultHardErrorMode = 12, + IoPortHandlers = 13, + PooledUsageAndLimits = 14, + WorkingSetWatch = 15, + UserModeIOPL = 16, + EnableAlignmentFaultFixup = 17, + PriorityClass = 18, + Wx86Information = 19, + HandleCount = 20, + AffinityMask = 21, + PriorityBoost = 22, + DeviceMap = 23, + SessionInformation = 24, + ForegroundInformation = 25, + Wow64Information = 26, + ImageFileName = 27, + LUIDDeviceMapsEnabled = 28, + BreakOnTermination = 29, + DebugObjectHandle = 30, + DebugFlags = 31, + HandleTracing = 32, + IoPriority = 33, + ExecuteFlags = 34, + TlsInformation = 35, + Cookie = 36, + ImageInformation = 37, + CycleTime = 38, + PagePriority = 39, + InstrumentationCallback = 40, + ThreadStackAllocation = 41, + WorkingSetWatchEx = 42, + ImageFileNameWin32 = 43, + ImageFileMapping = 44, + AffinityUpdateMode = 45, + MemoryAllocationMode = 46, + GroupInformation = 47, + TokenVirtualizationEnabled = 48, + OwnerInformation = 49, + WindowInformation = 50, + HandleInformation = 51, + MitigationPolicy = 52, + DynamicFunctionTableInformation = 53, + HandleCheckingMode = 54, + KeepAliveCount = 55, + RevokeFileHandles = 56, + WorkingSetControl = 57, + HandleTable = 58, + CheckStackExtentsMode = 59, + CommandLineInformation = 60, + ProtectionInformation = 61, + MemoryExhaustion = 62, + FaultInformation = 63, + TelemetryIdInformation = 64, + CommitReleaseInformation = 65, + Reserved1Information = 66, + Reserved2Information = 67, + SubsystemProcess = 68, + InPrivate = 70, + RaiseUMExceptionOnInvalidHandleClose = 71, + SubsystemInformation = 75, + Win32kSyscallFilterInformation = 79, + EnergyTrackingState = 82, + NetworkIoCounters = 114, + _, + + pub const Max: @typeInfo(@This()).@"enum".tag_type = 117; + }; + + pub const BASIC_INFORMATION = extern struct { + ExitStatus: NTSTATUS, + PebBaseAddress: *PEB, + AffinityMask: ULONG_PTR, + BasePriority: KPRIORITY, + UniqueProcessId: ULONG_PTR, + InheritedFromUniqueProcessId: ULONG_PTR, + }; + + pub const VM_COUNTERS = extern struct { + PeakVirtualSize: SIZE_T, + VirtualSize: SIZE_T, + PageFaultCount: ULONG, + PeakWorkingSetSize: SIZE_T, + WorkingSetSize: SIZE_T, + QuotaPeakPagedPoolUsage: SIZE_T, + QuotaPagedPoolUsage: SIZE_T, + QuotaPeakNonPagedPoolUsage: SIZE_T, + QuotaNonPagedPoolUsage: SIZE_T, + PagefileUsage: SIZE_T, + PeakPagefileUsage: SIZE_T, + }; +}; + +pub const THREAD = struct { + pub const INFOCLASS = enum(c_int) { + BasicInformation = 0, + Times = 1, + Priority = 2, + BasePriority = 3, + AffinityMask = 4, + ImpersonationToken = 5, + DescriptorTableEntry = 6, + EnableAlignmentFaultFixup = 7, + EventPair_Reusable = 8, + QuerySetWin32StartAddress = 9, + ZeroTlsCell = 10, + PerformanceCount = 11, + AmILastThread = 12, + IdealProcessor = 13, + PriorityBoost = 14, + SetTlsArrayAddress = 15, + IsIoPending = 16, + // Windows 2000+ from here + HideFromDebugger = 17, + // Windows XP+ from here + BreakOnTermination = 18, + SwitchLegacyState = 19, + IsTerminated = 20, + // Windows Vista+ from here + LastSystemCall = 21, + IoPriority = 22, + CycleTime = 23, + PagePriority = 24, + ActualBasePriority = 25, + TebInformation = 26, + CSwitchMon = 27, + // Windows 7+ from here + CSwitchPmu = 28, + Wow64Context = 29, + GroupInformation = 30, + UmsInformation = 31, + CounterProfiling = 32, + IdealProcessorEx = 33, + // Windows 8+ from here + CpuAccountingInformation = 34, + // Windows 8.1+ from here + SuspendCount = 35, + // Windows 10+ from here + HeterogeneousCpuPolicy = 36, + ContainerId = 37, + NameInformation = 38, + SelectedCpuSets = 39, + SystemThreadInformation = 40, + ActualGroupAffinity = 41, + DynamicCodePolicyInfo = 42, + SubsystemInformation = 45, + _, + + pub const Max: @typeInfo(@This()).@"enum".tag_type = 60; + }; + + pub const BASIC_INFORMATION = extern struct { + ExitStatus: NTSTATUS, + TebBaseAddress: PVOID, + ClientId: CLIENT_ID, + AffinityMask: KAFFINITY, + Priority: KPRIORITY, + BasePriority: KPRIORITY, + }; +}; + +pub const MEMORY = struct { + pub const BASIC_INFORMATION = extern struct { + BaseAddress: PVOID, + AllocationBase: PVOID, + AllocationProtect: DWORD, + PartitionId: WORD, + RegionSize: SIZE_T, + State: DWORD, + Protect: DWORD, + Type: DWORD, + }; }; // ref: km/ntifs.h @@ -2560,48 +2810,6 @@ pub fn GetProcessHeap() ?*HEAP { return peb().ProcessHeap; } -// ref: um/winternl.h - -pub const OBJECT_ATTRIBUTES = extern struct { - Length: ULONG = @sizeOf(OBJECT_ATTRIBUTES), - RootDirectory: ?HANDLE = null, - ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty), - Attributes: ATTRIBUTES = .{}, - SecurityDescriptor: ?*anyopaque = null, - SecurityQualityOfService: ?*anyopaque = null, - - // Valid values for the Attributes field - pub const ATTRIBUTES = packed struct(ULONG) { - Reserved0: u1 = 0, - INHERIT: bool = false, - Reserved2: u2 = 0, - PERMANENT: bool = false, - EXCLUSIVE: bool = false, - /// If name-lookup code should ignore the case of the ObjectName member rather than performing an exact-match search. - CASE_INSENSITIVE: bool = true, - OPENIF: bool = false, - OPENLINK: bool = false, - KERNEL_HANDLE: bool = false, - FORCE_ACCESS_CHECK: bool = false, - IGNORE_IMPERSONATED_DEVICEMAP: bool = false, - DONT_REPARSE: bool = false, - Reserved13: u19 = 0, - - pub const VALID_ATTRIBUTES: ATTRIBUTES = .{ - .INHERIT = true, - .PERMANENT = true, - .EXCLUSIVE = true, - .CASE_INSENSITIVE = true, - .OPENIF = true, - .OPENLINK = true, - .KERNEL_HANDLE = true, - .FORCE_ACCESS_CHECK = true, - .IGNORE_IMPERSONATED_DEVICEMAP = true, - .DONT_REPARSE = true, - }; - }; -}; - // ref none pub fn GetCurrentProcess() HANDLE { @@ -2623,297 +2831,13 @@ pub fn GetCurrentThreadId() DWORD { } pub fn GetLastError() Win32Error { - return @enumFromInt(teb().LastErrorValue); -} -/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls. -/// It implements similar behavior to `DeviceIoControl` and is meant to serve -/// as a direct substitute for that call. -/// TODO work out if we need to expose other arguments to the underlying syscalls. -pub fn DeviceIoControl( - device: HANDLE, - io_control_code: CTL_CODE, - opts: struct { - event: ?HANDLE = null, - apc_routine: ?*const IO_APC_ROUTINE = null, - apc_context: ?*anyopaque = null, - io_status_block: ?*IO_STATUS_BLOCK = null, - in: []const u8 = &.{}, - out: []u8 = &.{}, - }, -) NTSTATUS { - var io_status_block: IO_STATUS_BLOCK = undefined; - return switch (io_control_code.DeviceType) { - .FILE_SYSTEM, .NAMED_PIPE => ntdll.NtFsControlFile( - device, - opts.event, - opts.apc_routine, - opts.apc_context, - opts.io_status_block orelse &io_status_block, - io_control_code, - if (opts.in.len > 0) opts.in.ptr else null, - @intCast(opts.in.len), - if (opts.out.len > 0) opts.out.ptr else null, - @intCast(opts.out.len), - ), - else => ntdll.NtDeviceIoControlFile( - device, - opts.event, - opts.apc_routine, - opts.apc_context, - opts.io_status_block orelse &io_status_block, - io_control_code, - if (opts.in.len > 0) opts.in.ptr else null, - @intCast(opts.in.len), - if (opts.out.len > 0) opts.out.ptr else null, - @intCast(opts.out.len), - ), - }; -} - -pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD { - var bytes: DWORD = undefined; - if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @intFromBool(wait)) == 0) { - switch (GetLastError()) { - .IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable, - else => |err| return unexpectedError(err), - } - } - return bytes; -} - -pub const CreateIoCompletionPortError = error{Unexpected}; - -pub fn CreateIoCompletionPort( - file_handle: HANDLE, - existing_completion_port: ?HANDLE, - completion_key: usize, - concurrent_thread_count: DWORD, -) CreateIoCompletionPortError!HANDLE { - const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse { - switch (GetLastError()) { - .INVALID_PARAMETER => unreachable, - else => |err| return unexpectedError(err), - } - }; - return handle; -} - -pub const PostQueuedCompletionStatusError = error{Unexpected}; - -pub fn PostQueuedCompletionStatus( - completion_port: HANDLE, - bytes_transferred_count: DWORD, - completion_key: usize, - lpOverlapped: ?*OVERLAPPED, -) PostQueuedCompletionStatusError!void { - if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) { - switch (GetLastError()) { - else => |err| return unexpectedError(err), - } - } -} - -pub const GetQueuedCompletionStatusResult = enum { - Normal, - Aborted, - Canceled, - EOF, - Timeout, -}; - -pub fn GetQueuedCompletionStatus( - completion_port: HANDLE, - bytes_transferred_count: *DWORD, - lpCompletionKey: *usize, - lpOverlapped: *?*OVERLAPPED, - dwMilliseconds: DWORD, -) GetQueuedCompletionStatusResult { - if (kernel32.GetQueuedCompletionStatus( - completion_port, - bytes_transferred_count, - lpCompletionKey, - lpOverlapped, - dwMilliseconds, - ) == FALSE) { - switch (GetLastError()) { - .ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted, - .OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Canceled, - .HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF, - .WAIT_TIMEOUT => return GetQueuedCompletionStatusResult.Timeout, - else => |err| { - if (std.debug.runtime_safety) { - @setEvalBranchQuota(2500); - std.debug.panic("unexpected error: {}\n", .{err}); - } - }, - } - } - return GetQueuedCompletionStatusResult.Normal; -} - -pub const GetQueuedCompletionStatusError = error{ - Aborted, - Canceled, - EOF, - Timeout, -} || UnexpectedError; - -pub fn GetQueuedCompletionStatusEx( - completion_port: HANDLE, - completion_port_entries: []OVERLAPPED_ENTRY, - timeout_ms: ?DWORD, - alertable: bool, -) GetQueuedCompletionStatusError!u32 { - var num_entries_removed: u32 = 0; - - const success = kernel32.GetQueuedCompletionStatusEx( - completion_port, - completion_port_entries.ptr, - @as(ULONG, @intCast(completion_port_entries.len)), - &num_entries_removed, - timeout_ms orelse INFINITE, - @intFromBool(alertable), - ); - - if (success == FALSE) { - return switch (GetLastError()) { - .ABANDONED_WAIT_0 => error.Aborted, - .OPERATION_ABORTED => error.Canceled, - .HANDLE_EOF => error.EOF, - .WAIT_TIMEOUT => error.Timeout, - else => |err| unexpectedError(err), - }; - } - - return num_entries_removed; + return teb().LastErrorValue; } pub fn CloseHandle(hObject: HANDLE) void { - assert(ntdll.NtClose(hObject) == .SUCCESS); -} - -pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 { - return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen))); -} - -pub fn sendmsg( - s: ws2_32.SOCKET, - msg: *ws2_32.WSAMSG_const, - flags: u32, -) i32 { - var bytes_send: DWORD = undefined; - if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) { - return ws2_32.SOCKET_ERROR; - } else { - return @as(i32, @as(u31, @intCast(bytes_send))); - } -} - -pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 { - var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = @constCast(buf) }; - var bytes_send: DWORD = undefined; - if (ws2_32.WSASendTo(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_send, flags, to, @as(i32, @intCast(to_len)), null, null) == ws2_32.SOCKET_ERROR) { - return ws2_32.SOCKET_ERROR; - } else { - return @as(i32, @as(u31, @intCast(bytes_send))); - } -} - -pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 { - var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = buf }; - var bytes_received: DWORD = undefined; - var flags_inout = flags; - if (ws2_32.WSARecvFrom(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_received, &flags_inout, from, @as(?*i32, @ptrCast(from_len)), null, null) == ws2_32.SOCKET_ERROR) { - return ws2_32.SOCKET_ERROR; - } else { - return @as(i32, @as(u31, @intCast(bytes_received))); - } -} - -pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 { - return ws2_32.WSAPoll(fds, n, timeout); -} - -pub fn WSAIoctl( - s: ws2_32.SOCKET, - dwIoControlCode: DWORD, - inBuffer: ?[]const u8, - outBuffer: []u8, - overlapped: ?*OVERLAPPED, - completionRoutine: ?ws2_32.LPWSAOVERLAPPED_COMPLETION_ROUTINE, -) !DWORD { - var bytes: DWORD = undefined; - switch (ws2_32.WSAIoctl( - s, - dwIoControlCode, - if (inBuffer) |i| i.ptr else null, - if (inBuffer) |i| @as(DWORD, @intCast(i.len)) else 0, - outBuffer.ptr, - @as(DWORD, @intCast(outBuffer.len)), - &bytes, - overlapped, - completionRoutine, - )) { - 0 => {}, - ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) { - else => |err| return unexpectedWSAError(err), - }, - else => unreachable, - } - return bytes; -} - -const GetModuleFileNameError = error{Unexpected}; - -pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 { - const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len); - if (rc == 0) { - switch (GetLastError()) { - else => |err| return unexpectedError(err), - } - } - return buf_ptr[0..rc :0]; -} - -pub const NtAllocateVirtualMemoryError = error{ - AccessDenied, - InvalidParameter, - NoMemory, - Unexpected, -}; - -pub fn NtAllocateVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, zero_bits: ULONG_PTR, size: ?*SIZE_T, alloc_type: ULONG, protect: ULONG) NtAllocateVirtualMemoryError!void { - return switch (ntdll.NtAllocateVirtualMemory(hProcess, addr, zero_bits, size, alloc_type, protect)) { - .SUCCESS => return, - .ACCESS_DENIED => NtAllocateVirtualMemoryError.AccessDenied, - .INVALID_PARAMETER => NtAllocateVirtualMemoryError.InvalidParameter, - .NO_MEMORY => NtAllocateVirtualMemoryError.NoMemory, - else => |st| unexpectedStatus(st), - }; -} - -pub const NtFreeVirtualMemoryError = error{ - AccessDenied, - InvalidParameter, - Unexpected, -}; - -pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_type: ULONG) NtFreeVirtualMemoryError!void { - // TODO: If the return value is .INVALID_PAGE_PROTECTION, call RtlFlushSecureMemoryCache and try again. - return switch (ntdll.NtFreeVirtualMemory(hProcess, addr, size, free_type)) { - .SUCCESS => return, - .ACCESS_DENIED => NtFreeVirtualMemoryError.AccessDenied, - .INVALID_PARAMETER => NtFreeVirtualMemoryError.InvalidParameter, - else => NtFreeVirtualMemoryError.Unexpected, - }; -} - -pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void { - const success = kernel32.SetFileCompletionNotificationModes(handle, flags); - if (success == FALSE) { - return switch (GetLastError()) { - else => |err| unexpectedError(err), - }; + switch (ntdll.NtClose(hObject)) { + .SUCCESS => {}, + else => |status| unexpectedStatus(status) catch {}, } } @@ -2963,114 +2887,75 @@ pub const CreateProcessFlags = packed struct(u32) { create_ignore_system_default: bool = false, }; -pub const LoadLibraryError = error{ - FileNotFound, - Unexpected, -}; - -pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE { - return kernel32.LoadLibraryW(lpLibFileName) orelse { - switch (GetLastError()) { - .FILE_NOT_FOUND => return error.FileNotFound, - .PATH_NOT_FOUND => return error.FileNotFound, - .MOD_NOT_FOUND => return error.FileNotFound, - else => |err| return unexpectedError(err), - } - }; -} - -pub const LoadLibraryFlags = enum(DWORD) { - none = 0, - dont_resolve_dll_references = 0x00000001, - load_ignore_code_authz_level = 0x00000010, - load_library_as_datafile = 0x00000002, - load_library_as_datafile_exclusive = 0x00000040, - load_library_as_image_resource = 0x00000020, - load_library_search_application_dir = 0x00000200, - load_library_search_default_dirs = 0x00001000, - load_library_search_dll_load_dir = 0x00000100, - load_library_search_system32 = 0x00000800, - load_library_search_user_dirs = 0x00000400, - load_with_altered_search_path = 0x00000008, - load_library_require_signed_target = 0x00000080, - load_library_safe_current_dirs = 0x00002000, -}; - -pub fn LoadLibraryExW(lpLibFileName: [*:0]const u16, dwFlags: LoadLibraryFlags) LoadLibraryError!HMODULE { - return kernel32.LoadLibraryExW(lpLibFileName, null, @intFromEnum(dwFlags)) orelse { - switch (GetLastError()) { - .FILE_NOT_FOUND => return error.FileNotFound, - .PATH_NOT_FOUND => return error.FileNotFound, - .MOD_NOT_FOUND => return error.FileNotFound, - else => |err| return unexpectedError(err), - } - }; -} - -pub fn FreeLibrary(hModule: HMODULE) void { - assert(kernel32.FreeLibrary(hModule) != 0); -} - -pub fn QueryPerformanceFrequency() u64 { - // "On systems that run Windows XP or later, the function will always succeed" - // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency - var result: LARGE_INTEGER = undefined; - assert(ntdll.RtlQueryPerformanceFrequency(&result) != 0); - // The kernel treats this integer as unsigned. - return @as(u64, @bitCast(result)); -} - -pub fn QueryPerformanceCounter() u64 { - // "On systems that run Windows XP or later, the function will always succeed" - // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter - var result: LARGE_INTEGER = undefined; - assert(ntdll.RtlQueryPerformanceCounter(&result) != 0); - // The kernel treats this integer as unsigned. - return @as(u64, @bitCast(result)); -} - -/// This is a workaround for the C backend until zig has the ability to put -/// C code in inline assembly. -extern fn zig_thumb_windows_teb() callconv(.c) *anyopaque; -extern fn zig_aarch64_windows_teb() callconv(.c) *anyopaque; -extern fn zig_x86_windows_teb() callconv(.c) *anyopaque; -extern fn zig_x86_64_windows_teb() callconv(.c) *anyopaque; - pub fn teb() *TEB { - return switch (native_arch) { - .thumb => if (builtin.zig_backend == .stage2_c) - @ptrCast(@alignCast(zig_thumb_windows_teb())) - else - asm ( - \\ mrc p15, 0, %[ptr], c13, c0, 2 - : [ptr] "=r" (-> *TEB), - ), - .aarch64 => if (builtin.zig_backend == .stage2_c) - @ptrCast(@alignCast(zig_aarch64_windows_teb())) - else - asm ( - \\ mov %[ptr], x18 - : [ptr] "=r" (-> *TEB), - ), - .x86 => if (builtin.zig_backend == .stage2_c) - @ptrCast(@alignCast(zig_x86_windows_teb())) - else - asm ( + if (builtin.zig_backend == .stage2_c) return @ptrCast(@alignCast(struct { + /// This is a workaround for the C backend until zig has the ability to put + /// C code in inline assembly. + extern fn zig_windows_teb() callconv(.c) *anyopaque; + }.zig_windows_teb())); + switch (native_arch) { + .thumb => return asm ( + \\ mrc p15, 0, %[ptr], c13, c0, 2 + : [ptr] "=r" (-> *TEB), + ), + .aarch64 => return asm ( + \\ mov %[ptr], x18 + : [ptr] "=r" (-> *TEB), + ), + .x86 => { + comptime assert( + @offsetOf(TEB, "NtTib") + @offsetOf(@FieldType(TEB, "NtTib"), "Self") == 0x18, + ); + return asm ( \\ movl %%fs:0x18, %[ptr] : [ptr] "=r" (-> *TEB), - ), - .x86_64 => if (builtin.zig_backend == .stage2_c) - @ptrCast(@alignCast(zig_x86_64_windows_teb())) - else - asm ( + ); + }, + .x86_64 => { + comptime assert( + @offsetOf(TEB, "NtTib") + @offsetOf(@FieldType(TEB, "NtTib"), "Self") == 0x30, + ); + return asm ( \\ movq %%gs:0x30, %[ptr] : [ptr] "=r" (-> *TEB), - ), + ); + }, else => @compileError("unsupported arch"), - }; + } } pub fn peb() *PEB { + if (builtin.zig_backend == .stage2_c) switch (native_arch) { + .x86, .x86_64 => return @ptrCast(@alignCast(struct { + /// This is a workaround for the C backend until zig has the ability to put + /// C code in inline assembly. + extern fn zig_windows_peb() callconv(.c) *anyopaque; + }.zig_windows_peb())), + else => {}, + } else switch (native_arch) { + .aarch64 => { + comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60); + return asm ( + \\ ldr %[ptr], [x18, #0x60] + : [ptr] "=r" (-> *PEB), + ); + }, + .x86 => { + comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30); + return asm ( + \\ movl %%fs:0x30, %[ptr] + : [ptr] "=r" (-> *PEB), + ); + }, + .x86_64 => { + comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60); + return asm ( + \\ movq %%gs:0x60, %[ptr] + : [ptr] "=r" (-> *PEB), + ); + }, + else => {}, + } return teb().ProcessEnvironmentBlock; } @@ -3089,20 +2974,6 @@ pub fn toSysTime(ns: Io.Timestamp) i64 { return @as(i64, @intCast(hns)) - std.time.epoch.windows * (std.time.ns_per_s / 100); } -pub fn fileTimeToNanoSeconds(ft: FILETIME) Io.Timestamp { - const hns = (@as(i64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; - return fromSysTime(hns); -} - -/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME. -pub fn nanoSecondsToFileTime(ns: Io.Timestamp) FILETIME { - const adjusted: u64 = @bitCast(toSysTime(ns)); - return .{ - .dwHighDateTime = @as(u32, @truncate(adjusted >> 32)), - .dwLowDateTime = @as(u32, @truncate(adjusted)), - }; -} - /// Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a /// redundant copy of the uppercase data. pub inline fn toUpperWtf16(c: u16) u16 { @@ -3126,7 +2997,7 @@ pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool { // endianness for the uppercasing const a_c_native = std.mem.littleToNative(u16, a_c); const b_c_native = std.mem.littleToNative(u16, b_c); - if (a_c != b_c and nls.upcaseW(a_c_native) != nls.upcaseW(b_c_native)) { + if (a_c != b_c and toUpperWtf16(a_c_native) != toUpperWtf16(b_c_native)) { return false; } } @@ -3134,19 +3005,7 @@ pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool { } // Use RtlEqualUnicodeString on Windows when not in comptime to avoid including a // redundant copy of the uppercase data. - const a_bytes = @as(u16, @intCast(a.len * 2)); - const a_string: UNICODE_STRING = .{ - .Length = a_bytes, - .MaximumLength = a_bytes, - .Buffer = @constCast(a.ptr), - }; - const b_bytes = @as(u16, @intCast(b.len * 2)); - const b_string: UNICODE_STRING = .{ - .Length = b_bytes, - .MaximumLength = b_bytes, - .Buffer = @constCast(b.ptr), - }; - return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE; + return ntdll.RtlEqualUnicodeString(&.init(a), &.init(b), TRUE) == TRUE; } /// Compares two WTF-8 strings using the equivalent functionality of @@ -3464,13 +3323,10 @@ pub const LONG = i32; pub const ULONG64 = u64; pub const ULONGLONG = u64; pub const LONGLONG = i64; -pub const HLOCAL = HANDLE; pub const LANGID = c_ushort; pub const COLORREF = DWORD; -pub const WPARAM = usize; pub const LPARAM = LONG_PTR; -pub const LRESULT = LONG_PTR; pub const va_list = *opaque {}; @@ -3480,6 +3336,40 @@ pub const LPCTSTR = @compileError("Deprecated: choose between `LPCSTR` or `LPCWS pub const PTSTR = @compileError("Deprecated: choose between `PSTR` or `PWSTR` directly instead."); pub const PCTSTR = @compileError("Deprecated: choose between `PCSTR` or `PCWSTR` directly instead."); +fn STRING(comptime C: type) type { + return extern struct { + Length: USHORT, + MaximumLength: USHORT, + Buffer: ?[*]C, + + pub const empty: @This() = .{ .Length = 0, .MaximumLength = 0, .Buffer = null }; + + pub fn init(string: []const C) @This() { + const len: USHORT = @intCast(@sizeOf(C) * string.len); + return .{ + .Length = len, + .MaximumLength = len, + .Buffer = @constCast(string.ptr), + }; + } + + pub fn isEmpty(string: *const @This()) bool { + return string.Length == 0; + } + + pub fn slice(string: *const @This()) []C { + return if (string.isEmpty()) &.{} else string.Buffer.?[0..@divExact(string.Length, @sizeOf(C))]; + } + + pub fn sliceZ(string: *const @This()) [:0]C { + assert(string.Length + @sizeOf(C) <= string.MaximumLength); + return string.Buffer.?[0..@divExact(string.Length, @sizeOf(C)) :0]; + } + }; +} +pub const ANSI_STRING = STRING(CHAR); +pub const UNICODE_STRING = STRING(WCHAR); + pub const TRUE = 1; pub const FALSE = 0; @@ -3509,111 +3399,14 @@ pub const OVERLAPPED = extern struct { hEvent: ?HANDLE, }; -pub const OVERLAPPED_ENTRY = extern struct { - lpCompletionKey: ULONG_PTR, - lpOverlapped: *OVERLAPPED, - Internal: ULONG_PTR, - dwNumberOfBytesTransferred: DWORD, -}; - pub const MAX_PATH = 260; -pub const FILE_INFO_BY_HANDLE_CLASS = enum(u32) { - FileBasicInfo = 0, - FileStandardInfo = 1, - FileNameInfo = 2, - FileRenameInfo = 3, - FileDispositionInfo = 4, - FileAllocationInfo = 5, - FileEndOfFileInfo = 6, - FileStreamInfo = 7, - FileCompressionInfo = 8, - FileAttributeTagInfo = 9, - FileIdBothDirectoryInfo = 10, - FileIdBothDirectoryRestartInfo = 11, - FileIoPriorityHintInfo = 12, - FileRemoteProtocolInfo = 13, - FileFullDirectoryInfo = 14, - FileFullDirectoryRestartInfo = 15, - FileStorageInfo = 16, - FileAlignmentInfo = 17, - FileIdInfo = 18, - FileIdExtdDirectoryInfo = 19, - FileIdExtdDirectoryRestartInfo = 20, -}; - -pub const BY_HANDLE_FILE_INFORMATION = extern struct { - dwFileAttributes: DWORD, - ftCreationTime: FILETIME, - ftLastAccessTime: FILETIME, - ftLastWriteTime: FILETIME, - dwVolumeSerialNumber: DWORD, - nFileSizeHigh: DWORD, - nFileSizeLow: DWORD, - nNumberOfLinks: DWORD, - nFileIndexHigh: DWORD, - nFileIndexLow: DWORD, -}; - -pub const FILE_NAME_INFO = extern struct { - FileNameLength: DWORD, - FileName: [1]WCHAR, -}; - -/// Return the normalized drive name. This is the default. -pub const FILE_NAME_NORMALIZED = 0x0; - -/// Return the opened file name (not normalized). -pub const FILE_NAME_OPENED = 0x8; - -/// Return the path with the drive letter. This is the default. -pub const VOLUME_NAME_DOS = 0x0; - -/// Return the path with a volume GUID path instead of the drive name. -pub const VOLUME_NAME_GUID = 0x1; - -/// Return the path with no drive information. -pub const VOLUME_NAME_NONE = 0x4; - -/// Return the path with the volume device path. -pub const VOLUME_NAME_NT = 0x2; - pub const SECURITY_ATTRIBUTES = extern struct { nLength: DWORD, lpSecurityDescriptor: ?*anyopaque, bInheritHandle: BOOL, }; -pub const PIPE_ACCESS_INBOUND = 0x00000001; -pub const PIPE_ACCESS_OUTBOUND = 0x00000002; -pub const PIPE_ACCESS_DUPLEX = 0x00000003; - -pub const PIPE_TYPE_BYTE = 0x00000000; -pub const PIPE_TYPE_MESSAGE = 0x00000004; - -pub const PIPE_READMODE_BYTE = 0x00000000; -pub const PIPE_READMODE_MESSAGE = 0x00000002; - -pub const PIPE_WAIT = 0x00000000; -pub const PIPE_NOWAIT = 0x00000001; - -pub const CREATE_ALWAYS = 2; -pub const CREATE_NEW = 1; -pub const OPEN_ALWAYS = 4; -pub const OPEN_EXISTING = 3; -pub const TRUNCATE_EXISTING = 5; - -// flags for CreateEvent -pub const CREATE_EVENT_INITIAL_SET = 0x00000002; -pub const CREATE_EVENT_MANUAL_RESET = 0x00000001; - -pub const PROCESS_INFORMATION = extern struct { - hProcess: HANDLE, - hThread: HANDLE, - dwProcessId: DWORD, - dwThreadId: DWORD, -}; - pub const STARTUPINFOW = extern struct { cb: DWORD, lpReserved: ?LPWSTR, @@ -3650,50 +3443,7 @@ pub const STARTF_USESHOWWINDOW = 0x00000001; pub const STARTF_USESIZE = 0x00000002; pub const STARTF_USESTDHANDLES = 0x00000100; -pub const INFINITE = 4294967295; - -pub const MAXIMUM_WAIT_OBJECTS = 64; - -pub const WAIT_ABANDONED = 0x00000080; -pub const WAIT_ABANDONED_0 = WAIT_ABANDONED + 0; -pub const WAIT_OBJECT_0 = 0x00000000; -pub const WAIT_TIMEOUT = 0x00000102; -pub const WAIT_FAILED = 0xFFFFFFFF; - -pub const HANDLE_FLAG_INHERIT = 0x00000001; -pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; - -pub const MOVEFILE_COPY_ALLOWED = 2; -pub const MOVEFILE_CREATE_HARDLINK = 16; -pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4; -pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32; -pub const MOVEFILE_REPLACE_EXISTING = 1; -pub const MOVEFILE_WRITE_THROUGH = 8; - -pub const FILE_BEGIN = 0; -pub const FILE_CURRENT = 1; -pub const FILE_END = 2; - -pub const PTHREAD_START_ROUTINE = *const fn (LPVOID) callconv(.winapi) DWORD; -pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; - -pub const WIN32_FIND_DATAW = extern struct { - dwFileAttributes: DWORD, - ftCreationTime: FILETIME, - ftLastAccessTime: FILETIME, - ftLastWriteTime: FILETIME, - nFileSizeHigh: DWORD, - nFileSizeLow: DWORD, - dwReserved0: DWORD, - dwReserved1: DWORD, - cFileName: [260]u16, - cAlternateFileName: [14]u16, -}; - -pub const FILETIME = extern struct { - dwLowDateTime: DWORD, - dwHighDateTime: DWORD, -}; +pub const THREAD_START_ROUTINE = fn (LPVOID) callconv(.winapi) DWORD; pub const SYSTEM_INFO = extern struct { anon1: extern union { @@ -3716,7 +3466,6 @@ pub const SYSTEM_INFO = extern struct { pub const HRESULT = c_long; -pub const KNOWNFOLDERID = GUID; pub const GUID = extern struct { Data1: u32, Data2: u16, @@ -3771,75 +3520,11 @@ test GUID { ); } -pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); - -pub const KF_FLAG_DEFAULT = 0; -pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; -pub const KF_FLAG_CREATE = 32768; -pub const KF_FLAG_DONT_VERIFY = 16384; -pub const KF_FLAG_DONT_UNEXPAND = 8192; -pub const KF_FLAG_NO_ALIAS = 4096; -pub const KF_FLAG_INIT = 2048; -pub const KF_FLAG_DEFAULT_PATH = 1024; -pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; -pub const KF_FLAG_SIMPLE_IDLIST = 256; -pub const KF_FLAG_ALIAS_ONLY = -2147483648; - -pub const S_OK = 0; -pub const S_FALSE = 0x00000001; -pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001))); -pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002))); -pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003))); -pub const E_ABORT = @as(c_long, @bitCast(@as(c_ulong, 0x80004004))); -pub const E_FAIL = @as(c_long, @bitCast(@as(c_ulong, 0x80004005))); -pub const E_UNEXPECTED = @as(c_long, @bitCast(@as(c_ulong, 0x8000FFFF))); -pub const E_ACCESSDENIED = @as(c_long, @bitCast(@as(c_ulong, 0x80070005))); -pub const E_HANDLE = @as(c_long, @bitCast(@as(c_ulong, 0x80070006))); -pub const E_OUTOFMEMORY = @as(c_long, @bitCast(@as(c_ulong, 0x8007000E))); -pub const E_INVALIDARG = @as(c_long, @bitCast(@as(c_ulong, 0x80070057))); - -pub fn HRESULT_CODE(hr: HRESULT) Win32Error { - return @enumFromInt(hr & 0xFFFF); -} - -pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; -pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; -pub const FILE_FLAG_NO_BUFFERING = 0x20000000; -pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000; -pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; -pub const FILE_FLAG_OVERLAPPED = 0x40000000; -pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000; -pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; -pub const FILE_FLAG_SESSION_AWARE = 0x00800000; -pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; -pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; - -pub const RECT = extern struct { - left: LONG, - top: LONG, - right: LONG, - bottom: LONG, -}; - -pub const SMALL_RECT = extern struct { - Left: SHORT, - Top: SHORT, - Right: SHORT, - Bottom: SHORT, -}; - -pub const POINT = extern struct { - x: LONG, - y: LONG, -}; - pub const COORD = extern struct { X: SHORT, Y: SHORT, }; -pub const CREATE_UNICODE_ENVIRONMENT = 1024; - pub const TLS_OUT_OF_INDEXES = 4294967295; pub const IMAGE_TLS_DIRECTORY = extern struct { StartAddressOfRawData: usize, @@ -3854,8 +3539,6 @@ pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; pub const PIMAGE_TLS_CALLBACK = ?*const fn (PVOID, DWORD, PVOID) callconv(.winapi) void; -pub const PROV_RSA_FULL = 1; - pub const REGSAM = ACCESS_MASK; pub const LSTATUS = LONG; @@ -3872,9 +3555,6 @@ pub const HKEY_CURRENT_CONFIG: HKEY = @ptrFromInt(0x80000005); pub const HKEY_DYN_DATA: HKEY = @ptrFromInt(0x80000006); pub const HKEY_CURRENT_USER_LOCAL_SETTINGS: HKEY = @ptrFromInt(0x80000007); -/// Open symbolic link. -pub const REG_OPTION_OPEN_LINK: DWORD = 0x8; - pub const RTL_QUERY_REGISTRY_TABLE = extern struct { QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE, Flags: ULONG, @@ -3975,38 +3655,6 @@ pub const REG = struct { pub const QWORD_LITTLE_ENDIAN: ULONG = 11; }; -pub const FILE_NOTIFY_INFORMATION = extern struct { - NextEntryOffset: DWORD, - Action: DWORD, - FileNameLength: DWORD, - // Flexible array member - // FileName: [1]WCHAR, -}; - -pub const FILE_ACTION_ADDED = 0x00000001; -pub const FILE_ACTION_REMOVED = 0x00000002; -pub const FILE_ACTION_MODIFIED = 0x00000003; -pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004; -pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005; - -pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?*const fn (DWORD, DWORD, *OVERLAPPED) callconv(.winapi) void; - -pub const FileNotifyChangeFilter = packed struct(DWORD) { - file_name: bool = false, - dir_name: bool = false, - attributes: bool = false, - size: bool = false, - last_write: bool = false, - last_access: bool = false, - creation: bool = false, - ea: bool = false, - security: bool = false, - stream_name: bool = false, - stream_size: bool = false, - stream_write: bool = false, - _pad: u20 = 0, -}; - pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4; pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8; @@ -4056,26 +3704,6 @@ pub const RTL_RUN_ONCE = extern struct { pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null }; -pub const COINIT = struct { - pub const APARTMENTTHREADED = 2; - pub const MULTITHREADED = 0; - pub const DISABLE_OLE1DDE = 4; - pub const SPEED_OVER_MEMORY = 8; -}; - -pub const MEMORY_BASIC_INFORMATION = extern struct { - BaseAddress: PVOID, - AllocationBase: PVOID, - AllocationProtect: DWORD, - PartitionId: WORD, - RegionSize: SIZE_T, - State: DWORD, - Protect: DWORD, - Type: DWORD, -}; - -pub const PMEMORY_BASIC_INFORMATION = *MEMORY_BASIC_INFORMATION; - /// > The maximum path of 32,767 characters is approximate, because the "\\?\" /// > prefix may be expanded to a longer string by the system at run time, and /// > this expansion applies to the total length. @@ -4544,14 +4172,6 @@ pub const UNW_FLAG_EHANDLER = 0x1; pub const UNW_FLAG_UHANDLER = 0x2; pub const UNW_FLAG_CHAININFO = 0x4; -pub const UNICODE_STRING = extern struct { - Length: c_ushort, - MaximumLength: c_ushort, - Buffer: ?[*]WCHAR, - - pub const empty: UNICODE_STRING = .{ .Length = 0, .MaximumLength = 0, .Buffer = null }; -}; - pub const ACTIVATION_CONTEXT_DATA = opaque {}; pub const ASSEMBLY_STORAGE_MAP = opaque {}; pub const FLS_CALLBACK_INFO = opaque {}; @@ -4564,15 +4184,6 @@ pub const CLIENT_ID = extern struct { UniqueThread: HANDLE, }; -pub const THREAD_BASIC_INFORMATION = extern struct { - ExitStatus: NTSTATUS, - TebBaseAddress: PVOID, - ClientId: CLIENT_ID, - AffinityMask: KAFFINITY, - Priority: KPRIORITY, - BasePriority: KPRIORITY, -}; - pub const TEB = extern struct { NtTib: NT_TIB, EnvironmentPointer: PVOID, @@ -4580,7 +4191,7 @@ pub const TEB = extern struct { ActiveRpcHandle: PVOID, ThreadLocalStoragePointer: PVOID, ProcessEnvironmentBlock: *PEB, - LastErrorValue: ULONG, + LastErrorValue: Win32Error, Reserved2: [399 * @sizeOf(PVOID) - @sizeOf(ULONG)]u8, Reserved3: [1952]u8, TlsSlots: [64]PVOID, @@ -4793,7 +4404,7 @@ pub const PEB = extern struct { }; /// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process. -/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module. +/// It is essentially the head of three double-linked lists of `LDR.DATA_TABLE_ENTRY` structures which each represent one loaded module. /// /// Microsoft documentation of this is incomplete, the fields here are taken from various resources including: /// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm @@ -4825,24 +4436,89 @@ pub const PEB_LDR_DATA = extern struct { ShutdownThreadId: HANDLE, }; -/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including: -/// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data -/// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm -pub const LDR_DATA_TABLE_ENTRY = extern struct { - InLoadOrderLinks: LIST_ENTRY, - InMemoryOrderLinks: LIST_ENTRY, - InInitializationOrderLinks: LIST_ENTRY, - DllBase: PVOID, - EntryPoint: PVOID, - SizeOfImage: ULONG, - FullDllName: UNICODE_STRING, - BaseDllName: UNICODE_STRING, - Reserved5: [3]PVOID, - DUMMYUNIONNAME: extern union { - CheckSum: ULONG, - Reserved6: PVOID, - }, - TimeDateStamp: ULONG, +pub const LDR = struct { + /// Microsoft documentation of this is incomplete, the fields here are taken from various resources including: + /// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data + /// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm + pub const DATA_TABLE_ENTRY = extern struct { + InLoadOrderLinks: LIST_ENTRY, + InMemoryOrderLinks: LIST_ENTRY, + InInitializationOrderLinks: LIST_ENTRY, + DllBase: PVOID, + EntryPoint: PVOID, + SizeOfImage: ULONG, + FullDllName: UNICODE_STRING, + BaseDllName: UNICODE_STRING, + Reserved5: [3]PVOID, + DUMMYUNIONNAME: extern union { + CheckSum: ULONG, + Reserved6: PVOID, + }, + TimeDateStamp: ULONG, + }; + + pub const DLL_NOTIFICATION = struct { + pub const REASON = enum(ULONG) { LOADED = 1, UNLOADED = 2 }; + + pub const DATA = extern union { + Loaded: LOADED, + Unloaded: UNLOADED, + + pub const LOADED = extern struct { + Flags: REGISTER, + FullDllName: *const UNICODE_STRING, + BaseDllName: *const UNICODE_STRING, + DllBase: PVOID, + SizeOfImage: ULONG, + }; + + pub const UNLOADED = extern struct { + Flags: REGISTER, + FullDllName: *const UNICODE_STRING, + BaseDllName: *const UNICODE_STRING, + DllBase: PVOID, + SizeOfImage: ULONG, + }; + }; + + pub const COOKIE = *opaque {}; + + pub const FUNCTION = fn ( + NotificationReason: REASON, + NotificationData: *const DATA, + Context: ?PVOID, + ) callconv(.winapi) void; + + pub const REGISTER = packed struct(ULONG) { + Reserved0: u32 = 0, + }; + }; + + pub const GET_DLL_HANDLE_EX = packed struct(ULONG) { + UNCHANGED_REFCOUNT: bool = false, + PIN: bool = false, + Reserved2: u30 = 0, + }; + + pub const GET_PROCEDURE_ADDRESS = packed struct(ULONG) { + DONT_RECORD_FORWARDER: bool = false, + Reserved1: u31 = 0, + }; + + pub const LOAD = packed struct(ULONG) { + DONT_RESOLVE_DLL_REFERENCES: bool = false, + LIBRARY_AS_DATAFILE: bool = false, + PACKAGED_LIBRARY: bool = false, + WITH_ALTERED_SEARCH_PATH: bool = false, + IGNORE_CODE_AUTHZ_LEVEL: bool = false, + LIBRARY_AS_IMAGE_RESOURCE: bool = false, + LIBRARY_AS_DATAFILE_EXCLUSIVE: bool = false, + LIBRARY_REQUERE_SIGNED_TARGET: bool = false, + LIBRARY_SEARCH_DLL_LOAD_DIR: bool = false, + LIBRARY_SEARCH_USER_DIRS: bool = false, + LIBRARY_SEARCH_SYSTEM32: bool = false, + LIBRARY_SEARCH_DEFAULT_DIRS: bool = false, + }; }; pub const RTL_USER_PROCESS_PARAMETERS = extern struct { @@ -4956,86 +4632,6 @@ pub const MODULEINFO = extern struct { EntryPoint: LPVOID, }; -pub const PSAPI_WS_WATCH_INFORMATION = extern struct { - FaultingPc: LPVOID, - FaultingVa: LPVOID, -}; - -pub const VM_COUNTERS = extern struct { - PeakVirtualSize: SIZE_T, - VirtualSize: SIZE_T, - PageFaultCount: ULONG, - PeakWorkingSetSize: SIZE_T, - WorkingSetSize: SIZE_T, - QuotaPeakPagedPoolUsage: SIZE_T, - QuotaPagedPoolUsage: SIZE_T, - QuotaPeakNonPagedPoolUsage: SIZE_T, - QuotaNonPagedPoolUsage: SIZE_T, - PagefileUsage: SIZE_T, - PeakPagefileUsage: SIZE_T, -}; - -pub const PROCESS_MEMORY_COUNTERS = extern struct { - cb: DWORD, - PageFaultCount: DWORD, - PeakWorkingSetSize: SIZE_T, - WorkingSetSize: SIZE_T, - QuotaPeakPagedPoolUsage: SIZE_T, - QuotaPagedPoolUsage: SIZE_T, - QuotaPeakNonPagedPoolUsage: SIZE_T, - QuotaNonPagedPoolUsage: SIZE_T, - PagefileUsage: SIZE_T, - PeakPagefileUsage: SIZE_T, -}; - -pub const PROCESS_MEMORY_COUNTERS_EX = extern struct { - cb: DWORD, - PageFaultCount: DWORD, - PeakWorkingSetSize: SIZE_T, - WorkingSetSize: SIZE_T, - QuotaPeakPagedPoolUsage: SIZE_T, - QuotaPagedPoolUsage: SIZE_T, - QuotaPeakNonPagedPoolUsage: SIZE_T, - QuotaNonPagedPoolUsage: SIZE_T, - PagefileUsage: SIZE_T, - PeakPagefileUsage: SIZE_T, - PrivateUsage: SIZE_T, -}; - -pub const PERFORMANCE_INFORMATION = extern struct { - cb: DWORD, - CommitTotal: SIZE_T, - CommitLimit: SIZE_T, - CommitPeak: SIZE_T, - PhysicalTotal: SIZE_T, - PhysicalAvailable: SIZE_T, - SystemCache: SIZE_T, - KernelTotal: SIZE_T, - KernelPaged: SIZE_T, - KernelNonpaged: SIZE_T, - PageSize: SIZE_T, - HandleCount: DWORD, - ProcessCount: DWORD, - ThreadCount: DWORD, -}; - -pub const ENUM_PAGE_FILE_INFORMATION = extern struct { - cb: DWORD, - Reserved: DWORD, - TotalSize: SIZE_T, - TotalInUse: SIZE_T, - PeakUsage: SIZE_T, -}; - -pub const PENUM_PAGE_FILE_CALLBACKW = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.winapi) BOOL; -pub const PENUM_PAGE_FILE_CALLBACKA = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.winapi) BOOL; - -pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct { - BasicInfo: PSAPI_WS_WATCH_INFORMATION, - FaultingThreadId: ULONG_PTR, - Flags: ULONG_PTR, -}; - pub const OSVERSIONINFOW = extern struct { dwOSVersionInfoSize: ULONG, dwMajorVersion: ULONG, @@ -5098,20 +4694,6 @@ pub const MOUNTMGR_VOLUME_PATHS = extern struct { MultiSz: [1]WCHAR, }; -pub const OBJECT_INFORMATION_CLASS = enum(c_int) { - ObjectBasicInformation = 0, - ObjectNameInformation = 1, - ObjectTypeInformation = 2, - ObjectTypesInformation = 3, - ObjectHandleFlagInformation = 4, - ObjectSessionInformation = 5, - MaxObjectInfoClass, -}; - -pub const OBJECT_NAME_INFORMATION = extern struct { - Name: UNICODE_STRING, -}; - pub const SRWLOCK_INIT = SRWLOCK{}; pub const SRWLOCK = extern struct { Ptr: ?PVOID = null, @@ -5122,17 +4704,6 @@ pub const CONDITION_VARIABLE = extern struct { Ptr: ?PVOID = null, }; -pub const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1; -pub const FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2; - -pub const CTRL_C_EVENT: DWORD = 0; -pub const CTRL_BREAK_EVENT: DWORD = 1; -pub const CTRL_CLOSE_EVENT: DWORD = 2; -pub const CTRL_LOGOFF_EVENT: DWORD = 5; -pub const CTRL_SHUTDOWN_EVENT: DWORD = 6; - -pub const HANDLER_ROUTINE = *const fn (dwCtrlType: DWORD) callconv(.winapi) BOOL; - /// Processor feature enumeration. pub const PF = enum(DWORD) { /// On a Pentium, a floating-point precision error can occur in rare circumstances. @@ -5431,72 +5002,13 @@ pub const KUSER_SHARED_DATA = extern struct { /// Read-only user-mode address for the shared data. /// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm /// https://msrc-blog.microsoft.com/2022/04/05/randomizing-the-kuser_shared_data-structure-on-windows/ -pub const SharedUserData: *const KUSER_SHARED_DATA = @as(*const KUSER_SHARED_DATA, @ptrFromInt(0x7FFE0000)); +pub const SharedUserData: *const KUSER_SHARED_DATA = @ptrFromInt(0x7FFE0000); pub fn IsProcessorFeaturePresent(feature: PF) bool { if (@intFromEnum(feature) >= PROCESSOR_FEATURE_MAX) return false; return SharedUserData.ProcessorFeatures[@intFromEnum(feature)] == 1; } -pub const TH32CS_SNAPHEAPLIST = 0x00000001; -pub const TH32CS_SNAPPROCESS = 0x00000002; -pub const TH32CS_SNAPTHREAD = 0x00000004; -pub const TH32CS_SNAPMODULE = 0x00000008; -pub const TH32CS_SNAPMODULE32 = 0x00000010; -pub const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE; -pub const TH32CS_INHERIT = 0x80000000; - -pub const MAX_MODULE_NAME32 = 255; -pub const MODULEENTRY32 = extern struct { - dwSize: DWORD, - th32ModuleID: DWORD, - th32ProcessID: DWORD, - GlblcntUsage: DWORD, - ProccntUsage: DWORD, - modBaseAddr: *BYTE, - modBaseSize: DWORD, - hModule: HMODULE, - szModule: [MAX_MODULE_NAME32 + 1]CHAR, - szExePath: [MAX_PATH]CHAR, -}; - -pub const SYSTEM_INFORMATION_CLASS = enum(c_int) { - SystemBasicInformation = 0, - SystemPerformanceInformation = 2, - SystemTimeOfDayInformation = 3, - SystemProcessInformation = 5, - SystemProcessorPerformanceInformation = 8, - SystemInterruptInformation = 23, - SystemExceptionInformation = 33, - SystemRegistryQuotaInformation = 37, - SystemLookasideInformation = 45, - SystemCodeIntegrityInformation = 103, - SystemPolicyInformation = 134, -}; - -pub const SYSTEM_BASIC_INFORMATION = extern struct { - Reserved: ULONG, - TimerResolution: ULONG, - PageSize: ULONG, - NumberOfPhysicalPages: ULONG, - LowestPhysicalPageNumber: ULONG, - HighestPhysicalPageNumber: ULONG, - AllocationGranularity: ULONG, - MinimumUserModeAddress: ULONG_PTR, - MaximumUserModeAddress: ULONG_PTR, - ActiveProcessorsAffinityMask: KAFFINITY, - NumberOfProcessors: UCHAR, -}; - -pub const PROCESS_BASIC_INFORMATION = extern struct { - ExitStatus: NTSTATUS, - PebBaseAddress: *PEB, - AffinityMask: ULONG_PTR, - BasePriority: KPRIORITY, - UniqueProcessId: ULONG_PTR, - InheritedFromUniqueProcessId: ULONG_PTR, -}; - // https://github.com/reactos/reactos/blob/master/sdk/include/ndk/pstypes.h#L977-L983 pub const KERNEL_USER_TIMES = extern struct { CreationTime: LARGE_INTEGER, @@ -5505,75 +5017,6 @@ pub const KERNEL_USER_TIMES = extern struct { UserTime: LARGE_INTEGER, }; -pub const ReadMemoryError = error{ - Unexpected, -}; - -pub fn ReadProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []u8) ReadMemoryError![]u8 { - var nread: usize = 0; - switch (ntdll.NtReadVirtualMemory( - handle, - addr, - buffer.ptr, - buffer.len, - &nread, - )) { - .SUCCESS => return buffer[0..nread], - // TODO: map errors - else => |rc| return unexpectedStatus(rc), - } -} - -pub const WriteMemoryError = error{ - Unexpected, -}; - -pub fn WriteProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []const u8) WriteMemoryError!usize { - var nwritten: usize = 0; - switch (ntdll.NtWriteVirtualMemory( - handle, - addr, - buffer.ptr, - buffer.len, - &nwritten, - )) { - .SUCCESS => return nwritten, - // TODO: map errors - else => |rc| return unexpectedStatus(rc), - } -} - -pub const ProcessBaseAddressError = error{ - AccessDenied, - InvalidHandle, - Unexpected, -} || ReadMemoryError; - -/// Returns the base address of the process loaded into memory. -pub fn ProcessBaseAddress(handle: HANDLE) ProcessBaseAddressError!HMODULE { - var info: PROCESS_BASIC_INFORMATION = undefined; - var nread: DWORD = 0; - const rc = ntdll.NtQueryInformationProcess( - handle, - .BasicInformation, - &info, - @sizeOf(PROCESS_BASIC_INFORMATION), - &nread, - ); - switch (rc) { - .SUCCESS => {}, - .ACCESS_DENIED => return error.AccessDenied, - .INVALID_HANDLE => return error.InvalidHandle, - .INVALID_PARAMETER => unreachable, - else => return unexpectedStatus(rc), - } - - var peb_buf: [@sizeOf(PEB)]u8 align(@alignOf(PEB)) = undefined; - const peb_out = try ReadProcessMemory(handle, info.PebBaseAddress, &peb_buf); - const ppeb: *const PEB = @ptrCast(@alignCast(peb_out.ptr)); - return ppeb.ImageBaseAddress; -} - pub fn wtf8ToWtf16Le(wtf16le: []u16, wtf8: []const u8) error{ BadPathName, NameTooLong }!usize { // Each u8 in UTF-8/WTF-8 correlates to at most one u16 in UTF-16LE/WTF-16LE. if (wtf16le.len < wtf8.len) { diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index ed9af392a3..30a9a22022 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/lib/std/os/windows/kernel32.zig @@ -1,154 +1,29 @@ const std = @import("../../std.zig"); const windows = std.os.windows; -const ACCESS_MASK = windows.ACCESS_MASK; const BOOL = windows.BOOL; -const CONDITION_VARIABLE = windows.CONDITION_VARIABLE; -const COORD = windows.COORD; const DWORD = windows.DWORD; -const FARPROC = windows.FARPROC; -const FILETIME = windows.FILETIME; const HANDLE = windows.HANDLE; -const HANDLER_ROUTINE = windows.HANDLER_ROUTINE; -const HMODULE = windows.HMODULE; -const LARGE_INTEGER = windows.LARGE_INTEGER; -const LPCSTR = windows.LPCSTR; const LPCVOID = windows.LPCVOID; const LPCWSTR = windows.LPCWSTR; -const LPTHREAD_START_ROUTINE = windows.LPTHREAD_START_ROUTINE; const LPVOID = windows.LPVOID; const LPWSTR = windows.LPWSTR; -const MODULEENTRY32 = windows.MODULEENTRY32; -const OVERLAPPED = windows.OVERLAPPED; -const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY; -const PROCESS_INFORMATION = windows.PROCESS_INFORMATION; +const PROCESS = windows.PROCESS; +const THREAD_START_ROUTINE = windows.THREAD_START_ROUTINE; const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; const SIZE_T = windows.SIZE_T; -const SRWLOCK = windows.SRWLOCK; const STARTUPINFOW = windows.STARTUPINFOW; -const SYSTEM_INFO = windows.SYSTEM_INFO; -const UCHAR = windows.UCHAR; const UINT = windows.UINT; -const ULONG = windows.ULONG; -const ULONG_PTR = windows.ULONG_PTR; const va_list = windows.va_list; -const WCHAR = windows.WCHAR; const Win32Error = windows.Win32Error; -const WORD = windows.WORD; // I/O - Filesystem -pub extern "kernel32" fn ReadDirectoryChangesW( - hDirectory: HANDLE, - lpBuffer: [*]align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) u8, - nBufferLength: DWORD, - bWatchSubtree: BOOL, - dwNotifyFilter: windows.FileNotifyChangeFilter, - lpBytesReturned: ?*DWORD, - lpOverlapped: ?*OVERLAPPED, - lpCompletionRoutine: windows.LPOVERLAPPED_COMPLETION_ROUTINE, -) callconv(.winapi) BOOL; - -// TODO: Wrapper around NtCancelIoFile. -pub extern "kernel32" fn CancelIo( - hFile: HANDLE, -) callconv(.winapi) BOOL; - -// TODO: Wrapper around NtSetInformationFile + `FILE_POSITION_INFORMATION`. -// `FILE_STANDARD_INFORMATION` is also used if dwMoveMethod is `FILE_END` -pub extern "kernel32" fn SetFilePointerEx( - hFile: HANDLE, - liDistanceToMove: LARGE_INTEGER, - lpNewFilePointer: ?*LARGE_INTEGER, - dwMoveMethod: DWORD, -) callconv(.winapi) BOOL; - -// TODO: Wrapper around NtSetInformationFile + `FILE_BASIC_INFORMATION` -pub extern "kernel32" fn SetFileTime( - hFile: HANDLE, - lpCreationTime: ?*const FILETIME, - lpLastAccessTime: ?*const FILETIME, - lpLastWriteTime: ?*const FILETIME, -) callconv(.winapi) BOOL; - -pub extern "kernel32" fn WriteFile( - in_hFile: HANDLE, - in_lpBuffer: [*]const u8, - in_nNumberOfBytesToWrite: DWORD, - out_lpNumberOfBytesWritten: ?*DWORD, - in_out_lpOverlapped: ?*OVERLAPPED, -) callconv(.winapi) BOOL; - -// TODO: Wrapper around NtSetInformationFile + `FILE_IO_COMPLETION_NOTIFICATION_INFORMATION`. -pub extern "kernel32" fn SetFileCompletionNotificationModes( - FileHandle: HANDLE, - Flags: UCHAR, -) callconv(.winapi) BOOL; - -pub extern "kernel32" fn ReadFile( - hFile: HANDLE, - lpBuffer: LPVOID, - nNumberOfBytesToRead: DWORD, - lpNumberOfBytesRead: ?*DWORD, - lpOverlapped: ?*OVERLAPPED, -) callconv(.winapi) BOOL; - pub extern "kernel32" fn GetSystemDirectoryW( lpBuffer: LPWSTR, uSize: UINT, ) callconv(.winapi) UINT; -// I/O - Kernel Objects - -// TODO: Wrapper around NtRemoveIoCompletion. -pub extern "kernel32" fn GetQueuedCompletionStatus( - CompletionPort: HANDLE, - lpNumberOfBytesTransferred: *DWORD, - lpCompletionKey: *ULONG_PTR, - lpOverlapped: *?*OVERLAPPED, - dwMilliseconds: DWORD, -) callconv(.winapi) BOOL; - -// TODO: Wrapper around NtRemoveIoCompletionEx. -pub extern "kernel32" fn GetQueuedCompletionStatusEx( - CompletionPort: HANDLE, - lpCompletionPortEntries: [*]OVERLAPPED_ENTRY, - ulCount: ULONG, - ulNumEntriesRemoved: *ULONG, - dwMilliseconds: DWORD, - fAlertable: BOOL, -) callconv(.winapi) BOOL; - -// TODO: Wrapper around NtSetIoCompletion with `IoStatus = .SUCCESS`. -pub extern "kernel32" fn PostQueuedCompletionStatus( - CompletionPort: HANDLE, - dwNumberOfBytesTransferred: DWORD, - dwCompletionKey: ULONG_PTR, - lpOverlapped: ?*OVERLAPPED, -) callconv(.winapi) BOOL; - -pub extern "kernel32" fn GetOverlappedResult( - hFile: HANDLE, - lpOverlapped: *OVERLAPPED, - lpNumberOfBytesTransferred: *DWORD, - bWait: BOOL, -) callconv(.winapi) BOOL; - -// TODO: Wrapper around NtCreateIoCompletion + NtSetInformationFile with FILE_COMPLETION_INFORMATION. -// This would be better splitting into two functions. -pub extern "kernel32" fn CreateIoCompletionPort( - FileHandle: HANDLE, - ExistingCompletionPort: ?HANDLE, - CompletionKey: ULONG_PTR, - NumberOfConcurrentThreads: DWORD, -) callconv(.winapi) ?HANDLE; - -// TODO: Wrapper around RtlReportSilentProcessExit + NtTerminateProcess. -pub extern "kernel32" fn TerminateProcess( - hProcess: HANDLE, - uExitCode: UINT, -) callconv(.winapi) BOOL; - // Process Management pub extern "kernel32" fn CreateProcessW( @@ -161,86 +36,21 @@ pub extern "kernel32" fn CreateProcessW( lpEnvironment: ?[*:0]const u16, lpCurrentDirectory: ?LPCWSTR, lpStartupInfo: *STARTUPINFOW, - lpProcessInformation: *PROCESS_INFORMATION, + lpProcessInformation: *PROCESS.INFORMATION, ) callconv(.winapi) BOOL; -// TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`. -pub extern "kernel32" fn GetExitCodeProcess( - hProcess: HANDLE, - lpExitCode: *DWORD, -) callconv(.winapi) BOOL; - -pub extern "kernel32" fn CreateToolhelp32Snapshot( - dwFlags: DWORD, - th32ProcessID: DWORD, -) callconv(.winapi) HANDLE; - // Threading -// TODO: Already a wrapper for this, see `windows.GetCurrentThreadId`. -pub extern "kernel32" fn GetCurrentThreadId() callconv(.winapi) DWORD; - // TODO: CreateRemoteThread with hProcess=NtCurrentProcess(). pub extern "kernel32" fn CreateThread( lpThreadAttributes: ?*SECURITY_ATTRIBUTES, dwStackSize: SIZE_T, - lpStartAddress: LPTHREAD_START_ROUTINE, + lpStartAddress: *const THREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?*DWORD, ) callconv(.winapi) ?HANDLE; -// Code Libraries/Modules - -// TODO: Wrapper around LdrGetDllFullName. -pub extern "kernel32" fn GetModuleFileNameW( - hModule: ?HMODULE, - lpFilename: [*]WCHAR, - nSize: DWORD, -) callconv(.winapi) DWORD; - -extern "kernel32" fn K32GetModuleFileNameExW( - hProcess: HANDLE, - hModule: ?HMODULE, - lpFilename: LPWSTR, - nSize: DWORD, -) callconv(.winapi) DWORD; -pub const GetModuleFileNameExW = K32GetModuleFileNameExW; - -// TODO: Wrapper around ntdll.LdrGetDllHandle, which is a wrapper around LdrGetDllHandleEx -pub extern "kernel32" fn GetModuleHandleW( - lpModuleName: ?LPCWSTR, -) callconv(.winapi) ?HMODULE; - -pub extern "kernel32" fn Module32First( - hSnapshot: HANDLE, - lpme: *MODULEENTRY32, -) callconv(.winapi) BOOL; - -pub extern "kernel32" fn Module32Next( - hSnapshot: HANDLE, - lpme: *MODULEENTRY32, -) callconv(.winapi) BOOL; - -pub extern "kernel32" fn LoadLibraryW( - lpLibFileName: LPCWSTR, -) callconv(.winapi) ?HMODULE; - -pub extern "kernel32" fn LoadLibraryExW( - lpLibFileName: LPCWSTR, - hFile: ?HANDLE, - dwFlags: DWORD, -) callconv(.winapi) ?HMODULE; - -pub extern "kernel32" fn GetProcAddress( - hModule: HMODULE, - lpProcName: LPCSTR, -) callconv(.winapi) ?FARPROC; - -pub extern "kernel32" fn FreeLibrary( - hModule: HMODULE, -) callconv(.winapi) BOOL; - // Error Management pub extern "kernel32" fn FormatMessageW( @@ -252,6 +62,3 @@ pub extern "kernel32" fn FormatMessageW( nSize: DWORD, Arguments: ?*va_list, ) callconv(.winapi) DWORD; - -// TODO: Getter for teb().LastErrorValue. -pub extern "kernel32" fn GetLastError() callconv(.winapi) Win32Error; diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index bda9fee828..77799bc1b4 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -2,6 +2,7 @@ const std = @import("../../std.zig"); const windows = std.os.windows; const ACCESS_MASK = windows.ACCESS_MASK; +const ANSI_STRING = windows.ANSI_STRING; const BOOL = windows.BOOL; const BOOLEAN = windows.BOOLEAN; const CONDITION_VARIABLE = windows.CONDITION_VARIABLE; @@ -9,6 +10,7 @@ const CONTEXT = windows.CONTEXT; const CRITICAL_SECTION = windows.CRITICAL_SECTION; const CTL_CODE = windows.CTL_CODE; const CURDIR = windows.CURDIR; +const DIRECTORY = windows.DIRECTORY; const DWORD = windows.DWORD; const DWORD64 = windows.DWORD64; const ERESOURCE = windows.ERESOURCE; @@ -22,18 +24,19 @@ const IO_APC_ROUTINE = windows.IO_APC_ROUTINE; const IO_STATUS_BLOCK = windows.IO_STATUS_BLOCK; const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS; const LARGE_INTEGER = windows.LARGE_INTEGER; +const LDR = windows.LDR; const LOGICAL = windows.LOGICAL; const LONG = windows.LONG; const LPCVOID = windows.LPCVOID; const LPVOID = windows.LPVOID; const MEM = windows.MEM; const NTSTATUS = windows.NTSTATUS; -const OBJECT_ATTRIBUTES = windows.OBJECT_ATTRIBUTES; -const OBJECT_INFORMATION_CLASS = windows.OBJECT_INFORMATION_CLASS; +const OBJECT = windows.OBJECT; const PAGE = windows.PAGE; const PCWSTR = windows.PCWSTR; -const PROCESSINFOCLASS = windows.PROCESSINFOCLASS; +const PROCESS = windows.PROCESS; const PVOID = windows.PVOID; +const PWSTR = windows.PWSTR; const RTL_OSVERSIONINFOW = windows.RTL_OSVERSIONINFOW; const RTL_QUERY_REGISTRY_TABLE = windows.RTL_QUERY_REGISTRY_TABLE; const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION; @@ -41,8 +44,8 @@ const SEC = windows.SEC; const SECTION_INHERIT = windows.SECTION_INHERIT; const SIZE_T = windows.SIZE_T; const SRWLOCK = windows.SRWLOCK; -const SYSTEM_INFORMATION_CLASS = windows.SYSTEM_INFORMATION_CLASS; -const THREADINFOCLASS = windows.THREADINFOCLASS; +const SYSTEM = windows.SYSTEM; +const THREAD = windows.THREAD; const ULONG = windows.ULONG; const ULONG_PTR = windows.ULONG_PTR; const UNICODE_STRING = windows.UNICODE_STRING; @@ -91,7 +94,7 @@ pub extern "ntdll" fn RtlCaptureContext( pub extern "ntdll" fn NtSetInformationThread( ThreadHandle: HANDLE, - ThreadInformationClass: THREADINFOCLASS, + ThreadInformationClass: THREAD.INFOCLASS, ThreadInformation: *const anyopaque, ThreadInformationLength: ULONG, ) callconv(.winapi) NTSTATUS; @@ -99,7 +102,7 @@ pub extern "ntdll" fn NtSetInformationThread( pub extern "ntdll" fn NtCreateFile( FileHandle: *HANDLE, DesiredAccess: ACCESS_MASK, - ObjectAttributes: *const OBJECT_ATTRIBUTES, + ObjectAttributes: *const OBJECT.ATTRIBUTES, IoStatusBlock: *IO_STATUS_BLOCK, AllocationSize: ?*const LARGE_INTEGER, FileAttributes: FILE.ATTRIBUTE, @@ -152,7 +155,7 @@ pub extern "ntdll" fn NtLockFile( pub extern "ntdll" fn NtOpenFile( FileHandle: *HANDLE, DesiredAccess: ACCESS_MASK, - ObjectAttributes: *const OBJECT_ATTRIBUTES, + ObjectAttributes: *const OBJECT.ATTRIBUTES, IoStatusBlock: *IO_STATUS_BLOCK, ShareAccess: FILE.SHARE, OpenOptions: FILE.MODE, @@ -233,7 +236,7 @@ pub extern "ntdll" fn NtUnlockFile( pub extern "ntdll" fn NtQueryObject( Handle: HANDLE, - ObjectInformationClass: OBJECT_INFORMATION_CLASS, + ObjectInformationClass: OBJECT.INFORMATION_CLASS, ObjectInformation: ?PVOID, ObjectInformationLength: ULONG, ReturnLength: ?*ULONG, @@ -246,7 +249,7 @@ pub extern "ntdll" fn NtClose( pub extern "ntdll" fn NtCreateSection( SectionHandle: *HANDLE, DesiredAccess: ACCESS_MASK, - ObjectAttributes: ?*const OBJECT_ATTRIBUTES, + ObjectAttributes: ?*const OBJECT.ATTRIBUTES, MaximumSize: ?*const LARGE_INTEGER, SectionPageProtection: PAGE, AllocationAttributes: SEC, @@ -331,7 +334,7 @@ pub extern "ntdll" fn NtWaitForSingleObject( pub extern "ntdll" fn NtQueryInformationProcess( ProcessHandle: HANDLE, - ProcessInformationClass: PROCESSINFOCLASS, + ProcessInformationClass: PROCESS.INFOCLASS, ProcessInformation: *anyopaque, ProcessInformationLength: ULONG, ReturnLength: ?*ULONG, @@ -339,14 +342,14 @@ pub extern "ntdll" fn NtQueryInformationProcess( pub extern "ntdll" fn NtQueryInformationThread( ThreadHandle: HANDLE, - ThreadInformationClass: THREADINFOCLASS, + ThreadInformationClass: THREAD.INFOCLASS, ThreadInformation: *anyopaque, ThreadInformationLength: ULONG, ReturnLength: ?*ULONG, ) callconv(.winapi) NTSTATUS; pub extern "ntdll" fn NtQuerySystemInformation( - SystemInformationClass: SYSTEM_INFORMATION_CLASS, + SystemInformationClass: SYSTEM.INFORMATION_CLASS, SystemInformation: PVOID, SystemInformationLength: ULONG, ReturnLength: ?*ULONG, @@ -354,15 +357,99 @@ pub extern "ntdll" fn NtQuerySystemInformation( // ref none +pub extern "ntdll" fn LdrAddRefDll( + Flags: ULONG, + DllHandle: PVOID, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrLoadDll( + DllPath: ?PCWSTR, + DllCharacteristics: ?*const ULONG, + DllName: *const UNICODE_STRING, + DllHandle: *PVOID, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrUnloadDll( + DllHandle: PVOID, +) callconv(.winapi) NTSTATUS; + +pub extern "ntdll" fn LdrFindEntryForAddress( + DllHandle: PVOID, + Entry: **LDR.DATA_TABLE_ENTRY, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrGetDllFullName( + DllHandle: ?PVOID, + FullDllName: *UNICODE_STRING, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrGetDllPath( + DllName: PCWSTR, + Flags: LDR.LOAD, + DllPath: *PWSTR, + SearchPaths: *PWSTR, +) callconv(.winapi) NTSTATUS; + +pub extern "ntdll" fn LdrGetDllHandle( + DllPath: ?PCWSTR, + DllCharacteristics: ?*const ULONG, + DllName: *const UNICODE_STRING, + DllHandle: *PVOID, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrGetDllHandleByMapping( + BaseAddress: PVOID, + DllHandle: *PVOID, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrGetDllHandleByName( + BaseDllName: *const UNICODE_STRING, + FullDllName: *const UNICODE_STRING, + DllHandle: *PVOID, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrGetDllHandleEx( + Flags: LDR.GET_DLL_HANDLE_EX, + DllPath: ?PCWSTR, + DllCharacteristics: ?*const ULONG, + DllName: *const UNICODE_STRING, + DllHandle: *PVOID, +) callconv(.winapi) NTSTATUS; + +pub extern "ntdll" fn LdrGetProcedureAddress( + DllHandle: PVOID, + ProcedureName: *const ANSI_STRING, + ProcedureNumber: ULONG, + ProcedureAddress: *PVOID, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrGetProcedureAddressEx( + DllHandle: PVOID, + ProcedureName: *const ANSI_STRING, + ProcedureNumber: ULONG, + ProcedureAddress: *PVOID, + Flags: LDR.GET_PROCEDURE_ADDRESS, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrGetProcedureAddressForCaller( + DllHandle: PVOID, + ProcedureName: *const ANSI_STRING, + ProcedureNumber: ULONG, + ProcedureAddress: *PVOID, + Flags: LDR.GET_PROCEDURE_ADDRESS, + CallerAddress: PVOID, +) callconv(.winapi) NTSTATUS; + +pub extern "ntdll" fn LdrRegisterDllNotification( + Flags: LDR.DLL_NOTIFICATION.REGISTER, + NotificationFunction: *const LDR.DLL_NOTIFICATION.FUNCTION, + Context: ?PVOID, + Cookie: *LDR.DLL_NOTIFICATION.COOKIE, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn LdrUnregisterDllNotification( + Cookie: LDR.DLL_NOTIFICATION.COOKIE, +) callconv(.winapi) NTSTATUS; + pub extern "ntdll" fn NtQueryAttributesFile( - ObjectAttributes: *const OBJECT_ATTRIBUTES, + ObjectAttributes: *const OBJECT.ATTRIBUTES, FileAttributes: *FILE.BASIC_INFORMATION, ) callconv(.winapi) NTSTATUS; pub extern "ntdll" fn NtCreateEvent( EventHandle: *HANDLE, DesiredAccess: ACCESS_MASK, - ObjectAttributes: ?*const OBJECT_ATTRIBUTES, + ObjectAttributes: ?*const OBJECT.ATTRIBUTES, EventType: EVENT_TYPE, InitialState: BOOLEAN, ) callconv(.winapi) NTSTATUS; @@ -374,7 +461,7 @@ pub extern "ntdll" fn NtSetEvent( pub extern "ntdll" fn NtCreateKeyedEvent( KeyedEventHandle: *HANDLE, DesiredAccess: ACCESS_MASK, - ObjectAttributes: ?*const OBJECT_ATTRIBUTES, + ObjectAttributes: ?*const OBJECT.ATTRIBUTES, Flags: ULONG, ) callconv(.winapi) NTSTATUS; pub extern "ntdll" fn NtReleaseKeyedEvent( @@ -390,10 +477,57 @@ pub extern "ntdll" fn NtWaitForKeyedEvent( Timeout: ?*const LARGE_INTEGER, ) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn NtCancelSynchronousIoFile( + ThreadHandle: HANDLE, + IoRequestToCancel: ?*IO_STATUS_BLOCK, + IoStatusBlock: *IO_STATUS_BLOCK, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn NtCancelIoFile( + FileHandle: HANDLE, + IoStatusBlock: *IO_STATUS_BLOCK, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn NtCancelIoFileEx( + FileHandle: HANDLE, + IoRequestToCancel: *const IO_STATUS_BLOCK, + IoStatusBlock: *IO_STATUS_BLOCK, +) callconv(.winapi) NTSTATUS; + +/// This function has been observed to return SUCCESS on timeout on Windows 10 +/// and TIMEOUT on Wine 10.0. +/// +/// This function has been observed on Windows 11 such that positive interval +/// is real time, which can cause waits to be interrupted by changing system +/// time, however negative intervals are not affected by changes to system +/// time. +pub extern "ntdll" fn NtDelayExecution( + Alertable: BOOLEAN, + DelayInterval: *const LARGE_INTEGER, +) callconv(.winapi) NTSTATUS; + +pub extern "ntdll" fn NtNotifyChangeDirectoryFileEx( + FileHandle: HANDLE, + Event: ?HANDLE, + ApcRoutine: ?*const IO_APC_ROUTINE, + ApcContext: ?*anyopaque, + IoStatusBlock: *IO_STATUS_BLOCK, + Buffer: *anyopaque, + Length: ULONG, + CompletionFilter: FILE.NOTIFY.CHANGE, + WatchTree: BOOLEAN, + DirectoryNotifyInformationClass: DIRECTORY.NOTIFY_INFORMATION_CLASS, +) callconv(.winapi) NTSTATUS; + +pub extern "ntdll" fn NtOpenThread( + ThreadHandle: *HANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: *const OBJECT.ATTRIBUTES, + ClientId: *const windows.CLIENT_ID, +) callconv(.winapi) NTSTATUS; + pub extern "ntdll" fn NtCreateNamedPipeFile( FileHandle: *HANDLE, DesiredAccess: ACCESS_MASK, - ObjectAttributes: *const OBJECT_ATTRIBUTES, + ObjectAttributes: *const OBJECT.ATTRIBUTES, IoStatusBlock: *IO_STATUS_BLOCK, ShareAccess: FILE.SHARE, CreateDisposition: FILE.CREATE_DISPOSITION, @@ -437,7 +571,7 @@ pub extern "ntdll" fn NtUnmapViewOfSectionEx( pub extern "ntdll" fn NtOpenKey( KeyHandle: *HANDLE, DesiredAccess: ACCESS_MASK, - ObjectAttributes: *const OBJECT_ATTRIBUTES, + ObjectAttributes: *const OBJECT.ATTRIBUTES, ) callconv(.winapi) NTSTATUS; pub extern "ntdll" fn NtQueueApcThread( @@ -470,6 +604,19 @@ pub extern "ntdll" fn NtProtectVirtualMemory( OldAccessProtection: *PAGE, ) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn NtWaitForAlertByThreadId( + Address: ?*const anyopaque, + Timeout: ?*const LARGE_INTEGER, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn NtAlertThreadByThreadId(ThreadId: DWORD) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn NtAlertThread(ThreadHandle: HANDLE) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn NtAlertMultipleThreadByThreadId( + ThreadIds: [*]const ULONG_PTR, + ThreadCount: ULONG, + Unknown1: ?*const anyopaque, + Unknown2: ?*const anyopaque, +) callconv(.winapi) NTSTATUS; + pub extern "ntdll" fn NtYieldExecution() callconv(.winapi) NTSTATUS; pub extern "ntdll" fn RtlAddVectoredExceptionHandler( @@ -527,10 +674,6 @@ pub extern "ntdll" fn RtlQueryPerformanceCounter( pub extern "ntdll" fn RtlQueryPerformanceFrequency( PerformanceFrequency: *LARGE_INTEGER, ) callconv(.winapi) BOOL; -pub extern "ntdll" fn NtQueryPerformanceCounter( - PerformanceCounter: *LARGE_INTEGER, - PerformanceFrequency: ?*LARGE_INTEGER, -) callconv(.winapi) NTSTATUS; pub extern "ntdll" fn RtlReAllocateHeap( HeapHandle: *HEAP, @@ -539,8 +682,17 @@ pub extern "ntdll" fn RtlReAllocateHeap( Size: SIZE_T, ) callconv(.winapi) ?PVOID; +pub extern "ntdll" fn RtlReportSilentProcessExit( + ProcessHandle: HANDLE, + ExitStatus: NTSTATUS, +) callconv(.winapi) NTSTATUS; +pub extern "ntdll" fn NtTerminateProcess( + ProcessHandle: ?HANDLE, + ExitStatus: NTSTATUS, +) callconv(.winapi) NTSTATUS; + pub extern "ntdll" fn RtlSetCurrentDirectory_U( - PathName: *UNICODE_STRING, + PathName: *const UNICODE_STRING, ) callconv(.winapi) NTSTATUS; pub extern "ntdll" fn RtlTryAcquireSRWLockExclusive( @@ -572,52 +724,3 @@ pub extern "ntdll" fn RtlWakeConditionVariable( pub extern "ntdll" fn RtlWakeAllConditionVariable( ConditionVariable: *CONDITION_VARIABLE, ) callconv(.winapi) void; - -pub extern "ntdll" fn NtWaitForAlertByThreadId( - Address: ?*const anyopaque, - Timeout: ?*const LARGE_INTEGER, -) callconv(.winapi) NTSTATUS; -pub extern "ntdll" fn NtAlertThreadByThreadId(ThreadId: DWORD) callconv(.winapi) NTSTATUS; -pub extern "ntdll" fn NtAlertThread(ThreadHandle: HANDLE) callconv(.winapi) NTSTATUS; -pub extern "ntdll" fn NtAlertMultipleThreadByThreadId( - ThreadIds: [*]const ULONG_PTR, - ThreadCount: ULONG, - Unknown1: ?*const anyopaque, - Unknown2: ?*const anyopaque, -) callconv(.winapi) NTSTATUS; - -pub extern "ntdll" fn NtOpenThread( - ThreadHandle: *HANDLE, - DesiredAccess: ACCESS_MASK, - ObjectAttributes: *const OBJECT_ATTRIBUTES, - ClientId: *const windows.CLIENT_ID, -) callconv(.winapi) NTSTATUS; - -pub extern "ntdll" fn NtCancelSynchronousIoFile( - ThreadHandle: HANDLE, - IoRequestToCancel: ?*IO_STATUS_BLOCK, - IoStatusBlock: *IO_STATUS_BLOCK, -) callconv(.winapi) NTSTATUS; - -/// This function has been observed to return SUCCESS on timeout on Windows 10 -/// and TIMEOUT on Wine 10.0. -/// -/// This function has been observed on Windows 11 such that positive interval -/// is real time, which can cause waits to be interrupted by changing system -/// time, however negative intervals are not affected by changes to system -/// time. -pub extern "ntdll" fn NtDelayExecution( - Alertable: BOOLEAN, - DelayInterval: *const LARGE_INTEGER, -) callconv(.winapi) NTSTATUS; - -pub extern "ntdll" fn NtCancelIoFile( - FileHandle: HANDLE, - IoStatusBlock: *IO_STATUS_BLOCK, -) callconv(.winapi) NTSTATUS; - -pub extern "ntdll" fn NtCancelIoFileEx( - FileHandle: HANDLE, - IoRequestToCancel: *const IO_STATUS_BLOCK, - IoStatusBlock: *IO_STATUS_BLOCK, -) callconv(.winapi) NTSTATUS; diff --git a/lib/std/os/windows/win32error.zig b/lib/std/os/windows/win32error.zig index a71256228c..4dc826cbb9 100644 --- a/lib/std/os/windows/win32error.zig +++ b/lib/std/os/windows/win32error.zig @@ -1,5 +1,5 @@ /// Codes are from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d -pub const Win32Error = enum(u16) { +pub const Win32Error = enum(u32) { /// The operation completed successfully. SUCCESS = 0, /// Incorrect function. diff --git a/lib/std/process.zig b/lib/std/process.zig index 618d7f8f5b..2e3056b960 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -243,7 +243,7 @@ pub fn getBaseAddress() usize { .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => { return @intFromPtr(&std.c._mh_execute_header); }, - .windows => return @intFromPtr(windows.kernel32.GetModuleHandleW(null)), + .windows => return @intFromPtr(windows.peb().ImageBaseAddress), else => @compileError("Unsupported OS"), } } @@ -606,11 +606,11 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { return @as(u64, @bitCast(physmem)); }, .windows => { - var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined; + var sbi: windows.SYSTEM.BASIC_INFORMATION = undefined; const rc = windows.ntdll.NtQuerySystemInformation( - .SystemBasicInformation, + .Basic, &sbi, - @sizeOf(windows.SYSTEM_BASIC_INFORMATION), + @sizeOf(windows.SYSTEM.BASIC_INFORMATION), null, ); if (rc != .SUCCESS) { diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 7ce3143b36..0cc60a23e9 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -86,7 +86,7 @@ pub const ResourceUsageStatistics = struct { .visionos, .watchos, => @as(?std.posix.rusage, null), - .windows => @as(?std.os.windows.VM_COUNTERS, null), + .windows => @as(?std.os.windows.PROCESS.VM_COUNTERS, null), else => {}, }; }; diff --git a/lib/std/start.zig b/lib/std/start.zig index e39465fe9b..9bc001fe59 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -473,10 +473,9 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { std.Thread.maybeAttachSignalStack(); std.debug.maybeEnableSegfaultHandler(); - const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; - const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; - - std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, .global)); + std.os.windows.ntdll.RtlExitUserProcess( + callMain(std.os.windows.peb().ProcessParameters.CommandLine.slice(), .global), + ); } fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { @@ -647,9 +646,7 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal // values in their intended encoding from the PEB instead. std.Thread.maybeAttachSignalStack(); std.debug.maybeEnableSegfaultHandler(); - const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; - const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; - return callMain(cmd_line_w, .global); + return callMain(std.os.windows.peb().ProcessParameters.CommandLine.slice(), .global); }, else => {}, } @@ -776,8 +773,7 @@ fn call_wWinMain() std.os.windows.INT { // - With STARTF_USESHOWWINDOW unset: // - nShowCmd is always SW_SHOWDEFAULT const SW_SHOWDEFAULT = 10; - const STARTF_USESHOWWINDOW = 1; - if (peb.ProcessParameters.dwFlags & STARTF_USESHOWWINDOW != 0) { + if (peb.ProcessParameters.dwFlags & std.os.windows.STARTF_USESHOWWINDOW != 0) { break :nShowCmd @truncate(peb.ProcessParameters.dwShowWindow); } break :nShowCmd SW_SHOWDEFAULT; diff --git a/lib/std/zig/system/windows.zig b/lib/std/zig/system/windows.zig index 43d407c3a0..0a752dd7f9 100644 --- a/lib/std/zig/system/windows.zig +++ b/lib/std/zig/system/windows.zig @@ -100,11 +100,11 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void { REG.MULTI_SZ, => { comptime assert(@sizeOf(std.os.windows.UNICODE_STRING) % 2 == 0); - const unicode = @as(*std.os.windows.UNICODE_STRING, @ptrCast(&tmp_bufs[i])); + const unicode: *std.os.windows.UNICODE_STRING = @ptrCast(&tmp_bufs[i]); unicode.* = .{ .Length = 0, .MaximumLength = max_value_len - @sizeOf(std.os.windows.UNICODE_STRING), - .Buffer = @as([*]u16, @ptrCast(tmp_bufs[i][@sizeOf(std.os.windows.UNICODE_STRING)..])), + .Buffer = @ptrCast(tmp_bufs[i][@sizeOf(std.os.windows.UNICODE_STRING)..]), }; break :blk unicode; }, @@ -159,8 +159,8 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void { REG.MULTI_SZ, => { var buf = @field(args, field.name).value_buf; - const entry = @as(*align(1) const std.os.windows.UNICODE_STRING, @ptrCast(table[i + 1].EntryContext)); - const len = try std.unicode.utf16LeToUtf8(buf, entry.Buffer.?[0 .. entry.Length / 2]); + const entry: *const std.os.windows.UNICODE_STRING = @ptrCast(table[i + 1].EntryContext); + const len = try std.unicode.utf16LeToUtf8(buf, entry.slice()); buf[len] = 0; }, @@ -168,7 +168,7 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void { REG.DWORD_BIG_ENDIAN, REG.QWORD, => { - const entry = @as([*]align(1) const u8, @ptrCast(table[i + 1].EntryContext)); + const entry: [*]const u8 = @ptrCast(table[i + 1].EntryContext); switch (@field(args, field.name).value_type) { REG.DWORD, REG.DWORD_BIG_ENDIAN => { @memcpy(@field(args, field.name).value_buf[0..4], entry[0..4]); diff --git a/lib/zig.h b/lib/zig.h index 7a7a22b17b..81a815ab55 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -4148,7 +4148,7 @@ static inline void zig_msvc_atomic_store_i128(zig_i128 volatile* obj, zig_i128 a #if defined(zig_thumb) -static inline void* zig_thumb_windows_teb(void) { +static inline void* zig_windows_teb(void) { void* teb = 0; #if defined(zig_msvc) teb = (void*)_MoveFromCoprocessor(15, 0, 13, 0, 2); @@ -4160,7 +4160,7 @@ static inline void* zig_thumb_windows_teb(void) { #elif defined(zig_aarch64) -static inline void* zig_aarch64_windows_teb(void) { +static inline void* zig_windows_teb(void) { void* teb = 0; #if defined(zig_msvc) teb = (void*)__readx18qword(0x0); @@ -4172,7 +4172,7 @@ static inline void* zig_aarch64_windows_teb(void) { #elif defined(zig_x86_32) -static inline void* zig_x86_windows_teb(void) { +static inline void* zig_windows_teb(void) { void* teb = 0; #if defined(zig_msvc) teb = (void*)__readfsdword(0x18); @@ -4182,9 +4182,19 @@ static inline void* zig_x86_windows_teb(void) { return teb; } +static inline void* zig_windows_peb(void) { + void* peb = 0; +#if defined(zig_msvc) + peb = (void*)__readfsdword(0x30); +#elif defined(zig_gnuc_asm) + __asm__ ("movl %%fs:0x30, %[ptr]" : [ptr] "=r" (peb)); +#endif + return peb; +} + #elif defined(zig_x86_64) -static inline void* zig_x86_64_windows_teb(void) { +static inline void* zig_windows_teb(void) { void* teb = 0; #if defined(zig_msvc) teb = (void*)__readgsqword(0x30); @@ -4194,6 +4204,16 @@ static inline void* zig_x86_64_windows_teb(void) { return teb; } +static inline void* zig_windows_peb(void) { + void* peb = 0; +#if defined(zig_msvc) + peb = (void*)__readgsqword(0x60); +#elif defined(zig_gnuc_asm) + __asm__ ("movq %%gs:0x60, %[ptr]" : [ptr] "=r" (peb)); +#endif + return peb; +} + #endif #if defined(zig_loongarch) diff --git a/stage1/zig.h b/stage1/zig.h index 7a7a22b17b..81a815ab55 100644 --- a/stage1/zig.h +++ b/stage1/zig.h @@ -4148,7 +4148,7 @@ static inline void zig_msvc_atomic_store_i128(zig_i128 volatile* obj, zig_i128 a #if defined(zig_thumb) -static inline void* zig_thumb_windows_teb(void) { +static inline void* zig_windows_teb(void) { void* teb = 0; #if defined(zig_msvc) teb = (void*)_MoveFromCoprocessor(15, 0, 13, 0, 2); @@ -4160,7 +4160,7 @@ static inline void* zig_thumb_windows_teb(void) { #elif defined(zig_aarch64) -static inline void* zig_aarch64_windows_teb(void) { +static inline void* zig_windows_teb(void) { void* teb = 0; #if defined(zig_msvc) teb = (void*)__readx18qword(0x0); @@ -4172,7 +4172,7 @@ static inline void* zig_aarch64_windows_teb(void) { #elif defined(zig_x86_32) -static inline void* zig_x86_windows_teb(void) { +static inline void* zig_windows_teb(void) { void* teb = 0; #if defined(zig_msvc) teb = (void*)__readfsdword(0x18); @@ -4182,9 +4182,19 @@ static inline void* zig_x86_windows_teb(void) { return teb; } +static inline void* zig_windows_peb(void) { + void* peb = 0; +#if defined(zig_msvc) + peb = (void*)__readfsdword(0x30); +#elif defined(zig_gnuc_asm) + __asm__ ("movl %%fs:0x30, %[ptr]" : [ptr] "=r" (peb)); +#endif + return peb; +} + #elif defined(zig_x86_64) -static inline void* zig_x86_64_windows_teb(void) { +static inline void* zig_windows_teb(void) { void* teb = 0; #if defined(zig_msvc) teb = (void*)__readgsqword(0x30); @@ -4194,6 +4204,16 @@ static inline void* zig_x86_64_windows_teb(void) { return teb; } +static inline void* zig_windows_peb(void) { + void* peb = 0; +#if defined(zig_msvc) + peb = (void*)__readgsqword(0x60); +#elif defined(zig_gnuc_asm) + __asm__ ("movq %%gs:0x60, %[ptr]" : [ptr] "=r" (peb)); +#endif + return peb; +} + #endif #if defined(zig_loongarch) diff --git a/test/standalone/coff_dwarf/main.zig b/test/standalone/coff_dwarf/main.zig index 79caaeb95a..24684d3830 100644 --- a/test/standalone/coff_dwarf/main.zig +++ b/test/standalone/coff_dwarf/main.zig @@ -4,17 +4,16 @@ const fatal = std.process.fatal; extern fn add(a: u32, b: u32, addr: *usize) u32; pub fn main(init: std.process.Init) void { - const gpa = init.gpa; const io = init.io; var di: std.debug.SelfInfo = .init; - defer di.deinit(gpa); + defer di.deinit(io); var add_addr: usize = undefined; _ = add(1, 2, &add_addr); - const symbol = di.getSymbol(gpa, io, add_addr) catch |err| fatal("failed to get symbol: {t}", .{err}); - defer if (symbol.source_location) |sl| gpa.free(sl.file_name); + const symbol = di.getSymbol(io, add_addr) catch |err| fatal("failed to get symbol: {t}", .{err}); + defer if (symbol.source_location) |sl| std.debug.getDebugInfoAllocator().free(sl.file_name); if (symbol.name == null) fatal("failed to resolve symbol name", .{}); if (symbol.compile_unit_name == null) fatal("failed to resolve compile unit", .{}); diff --git a/test/standalone/windows_argv/fuzz.zig b/test/standalone/windows_argv/fuzz.zig index 9227bb6f89..27b0ba3f06 100644 --- a/test/standalone/windows_argv/fuzz.zig +++ b/test/standalone/windows_argv/fuzz.zig @@ -127,7 +127,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO .hStdOutput = null, .hStdError = windows.peb().ProcessParameters.hStdError, }; - var proc_info: windows.PROCESS_INFORMATION = undefined; + var proc_info: windows.PROCESS.INFORMATION = undefined; if (windows.kernel32.CreateProcessW( @constCast(verify_path.ptr), @@ -141,7 +141,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO &startup_info, &proc_info, ) == 0) { - std.process.fatal("kernel32 CreateProcessW failed with {t}", .{windows.kernel32.GetLastError()}); + std.process.fatal("kernel32 CreateProcessW failed with {t}", .{windows.GetLastError()}); } windows.CloseHandle(proc_info.hThread); @@ -156,9 +156,15 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO else => |status| return windows.unexpectedStatus(status), } - var exit_code: windows.DWORD = undefined; - if (windows.kernel32.GetExitCodeProcess(child_proc, &exit_code) == 0) { - return error.UnableToGetExitCode; + var info: windows.PROCESS.BASIC_INFORMATION = undefined; + switch (windows.ntdll.NtQueryInformationProcess( + child_proc, + .BasicInformation, + &info, + @sizeOf(windows.PROCESS.BASIC_INFORMATION), + null, + )) { + .SUCCESS => return @intFromEnum(info.ExitStatus), + else => return error.UnableToGetExitCode, } - return exit_code; } diff --git a/test/standalone/windows_argv/lib.zig b/test/standalone/windows_argv/lib.zig index 750501edd2..370e62b799 100644 --- a/test/standalone/windows_argv/lib.zig +++ b/test/standalone/windows_argv/lib.zig @@ -16,9 +16,9 @@ fn testArgv(expected_args: []const [*:0]const u16) !void { defer arena_state.deinit(); const allocator = arena_state.allocator(); - const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; - const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; - const raw_args: std.process.Args = .{ .vector = cmd_line_w }; + const raw_args: std.process.Args = .{ + .vector = std.os.windows.peb().ProcessParameters.CommandLine.slice(), + }; const args = try raw_args.toSlice(allocator); var wtf8_buf = std.array_list.Managed(u8).init(allocator);