commit b5bd4946067d3796855e0b4b12219dfc28e15e8f (tree)
parent 12cb5b92851f601c44d48deadea9934146edcffa
Author: Jacob Young <jacobly0@users.noreply.github.com>
Date: Fri, 6 Feb 2026 04:36:32 -0500
std.Threaded: replace more kernel32 functions with ntdll
Diffstat:
27 files changed, 1674 insertions(+), 2241 deletions(-)
diff --git 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 `
+stage3-debug\bin\zig build-obj `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
- -femit-bin="test-x86_64-windows-msvc.c" `
- --test-no-exec `
+ -OReleaseSmall `
+ --name compiler_rt `
+ -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
-target x86_64-windows-msvc `
- -lc
+ -lc `
+ ..\lib\compiler_rt.zig
CheckLastExitCode
-& "stage3-debug\bin\zig.exe" build-obj `
+stage3-debug\bin\zig test `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
- -OReleaseSmall `
- --name compiler_rt `
- -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
- --dep build_options `
+ -femit-bin="behavior-x86_64-windows-msvc.c" `
+ --test-no-exec `
-target x86_64-windows-msvc `
- -Mroot="..\lib\compiler_rt.zig" `
- -Mbuild_options="config.zig"
+ -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
@@ -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 `
+stage3-release\bin\zig.exe build-obj `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
- -femit-bin="test-x86_64-windows-msvc.c" `
- --test-no-exec `
+ -OReleaseSmall `
+ --name compiler_rt `
+ -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
-target x86_64-windows-msvc `
- -lc
+ -lc `
+ ..\lib\compiler_rt.zig
CheckLastExitCode
-& "stage3-release\bin\zig.exe" build-obj `
+stage3-release\bin\zig.exe test `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
- -OReleaseSmall `
- --name compiler_rt `
- -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
- --dep build_options `
+ -femit-bin="behavior-x86_64-windows-msvc.c" `
+ --test-no-exec `
-target x86_64-windows-msvc `
- -Mroot="..\lib\compiler_rt.zig" `
- -Mbuild_options="config.zig"
+ -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
@@ -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
@@ -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
@@ -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
@@ -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 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()),
+ };
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),
- };
- var io_status_block: w.IO_STATUS_BLOCK = 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),
+ },
+ .LastWriteTime = switch (options.modify_timestamp) {
+ .unchanged => 0,
+ .now => now_sys,
+ .new => |ts| windows.toSysTime(ts),
+ },
+ .ChangeTime = 0,
+ .FileAttributes = .{},
};
-
- 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;
- },
+ 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,65 +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| {
- syscall.finish();
- return windows.errorBug(err);
- },
- .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| {
+ } 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.unexpectedError(err);
+ iosb.u.Status = status;
+ break;
},
- }
+ };
}
+ 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;
+ if (is_windows) {
+ var iosb: windows.IO_STATUS_BLOCK = undefined;
+ var info: windows.FILE.POSITION_INFORMATION = undefined;
+ const syscall: Syscall = try .start();
+ 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 (posix.errno(posix.system.llseek(fd, @bitCast(offset), &result, posix.SEEK.CUR))) {
+ switch (std.os.wasi.fd_seek(file.handle, offset, .CUR, &new_offset)) {
.SUCCESS => {
syscall.finish();
return;
@@ -9913,6 +9932,7 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
.OVERFLOW => return error.Unseekable,
.SPIPE => return error.Unseekable,
.NXIO => return error.Unseekable,
+ .NOTCAPABLE => return error.AccessDenied,
else => |err| return posix.unexpectedErrno(err),
}
},
@@ -9920,34 +9940,13 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
}
}
- if (is_windows) {
- 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);
- },
- }
- }
- }
+ if (posix.SEEK == void) return error.Unseekable;
- if (native_os == .wasi and !builtin.link_libc) {
- var new_offset: std.os.wasi.filesize_t = undefined;
+ if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
+ var result: u64 = undefined;
const syscall: Syscall = try .start();
while (true) {
- switch (std.os.wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
+ switch (posix.errno(posix.system.llseek(file.handle, @bitCast(offset), &result, posix.SEEK.CUR))) {
.SUCCESS => {
syscall.finish();
return;
@@ -9964,7 +9963,6 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
.OVERFLOW => return error.Unseekable,
.SPIPE => return error.Unseekable,
.NXIO => return error.Unseekable,
- .NOTCAPABLE => return error.AccessDenied,
else => |err| return posix.unexpectedErrno(err),
}
},
@@ -9972,11 +9970,9 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
}
}
- if (posix.SEEK == void) return error.Unseekable;
-
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
}
-
- // 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,
- });
+ 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;
+}
- for (si.modules.items) |*mod| {
- if (address >= mod.base_address and address < mod.base_address + mod.size) {
- return mod;
+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;
+}
- return error.MissingDebugInfo;
+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;
+ }
+ },
+ }
}
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
@@ -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
@@ -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 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 = struct {
+ pub const INFORMATION = extern struct {
+ hProcess: HANDLE,
+ hThread: HANDLE,
+ dwProcessId: DWORD,
+ dwThreadId: DWORD,
+ };
+
+ 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 Max: @typeInfo(@This()).@"enum".tag_type = 117;
+ pub const BASIC_INFORMATION = extern struct {
+ ExitStatus: NTSTATUS,
+ TebBaseAddress: PVOID,
+ ClientId: CLIENT_ID,
+ AffinityMask: KAFFINITY,
+ Priority: KPRIORITY,
+ BasePriority: KPRIORITY,
+ };
};
-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 Max: @typeInfo(@This()).@"enum".tag_type = 60;
+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
@@ -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,
-) callconv(.winapi) BOOL;
-
-// TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`.
-pub extern "kernel32" fn GetExitCodeProcess(
- hProcess: HANDLE,
- lpExitCode: *DWORD,
+ lpProcessInformation: *PROCESS.INFORMATION,
) 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);