std.Threaded: replace more kernel32 functions with ntdll

This commit is contained in:
Jacob Young
2026-02-06 04:36:32 -05:00
parent 12cb5b9285
commit b5bd494606
27 changed files with 1694 additions and 2261 deletions
+17 -18
View File
@@ -35,7 +35,7 @@ Set-Location -Path 'build-debug'
# CMake gives a syntax error when file paths with backward slashes are used.
# Here, we use forward slashes only to work around this.
& cmake .. `
cmake .. `
-GNinja `
-DCMAKE_INSTALL_PREFIX="stage3-debug" `
-DCMAKE_PREFIX_PATH="$($PREFIX_PATH -Replace "\\", "/")" `
@@ -54,7 +54,7 @@ ninja install
CheckLastExitCode
Write-Output "Main test suite..."
& "stage3-debug\bin\zig.exe" build test docs `
stage3-debug\bin\zig build test docs `
--maxrss $ZSF_MAX_RSS `
--zig-lib-dir "$ZIG_LIB_DIR" `
--search-prefix "$PREFIX_PATH" `
@@ -66,26 +66,25 @@ Write-Output "Main test suite..."
CheckLastExitCode
Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
& "stage3-debug\bin\zig.exe" test `
..\test\behavior.zig `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
-femit-bin="test-x86_64-windows-msvc.c" `
--test-no-exec `
-target x86_64-windows-msvc `
-lc
CheckLastExitCode
& "stage3-debug\bin\zig.exe" build-obj `
stage3-debug\bin\zig build-obj `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
-OReleaseSmall `
--name compiler_rt `
-femit-bin="compiler_rt-x86_64-windows-msvc.c" `
--dep build_options `
-target x86_64-windows-msvc `
-Mroot="..\lib\compiler_rt.zig" `
-Mbuild_options="config.zig"
-lc `
..\lib\compiler_rt.zig
CheckLastExitCode
stage3-debug\bin\zig test `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
-femit-bin="behavior-x86_64-windows-msvc.c" `
--test-no-exec `
-target x86_64-windows-msvc `
-lc `
..\test\behavior.zig
CheckLastExitCode
Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
@@ -97,8 +96,8 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files (x86)\Microsoft Visual Studio\
CheckLastExitCode
Write-Output "Build and run behavior tests with msvc..."
& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib
cl /I..\lib /W3 /Z7 behavior-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /link /nologo /debug /subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib
CheckLastExitCode
& .\test-x86_64-windows-msvc.exe
.\behavior-x86_64-windows-msvc
CheckLastExitCode
+18 -19
View File
@@ -35,7 +35,7 @@ Set-Location -Path 'build-release'
# CMake gives a syntax error when file paths with backward slashes are used.
# Here, we use forward slashes only to work around this.
& cmake .. `
cmake .. `
-GNinja `
-DCMAKE_INSTALL_PREFIX="stage3-release" `
-DCMAKE_PREFIX_PATH="$($PREFIX_PATH -Replace "\\", "/")" `
@@ -54,7 +54,7 @@ ninja install
CheckLastExitCode
Write-Output "Main test suite..."
& "stage3-release\bin\zig.exe" build test docs `
stage3-release\bin\zig.exe build test docs `
--maxrss $ZSF_MAX_RSS `
--zig-lib-dir "$ZIG_LIB_DIR" `
--search-prefix "$PREFIX_PATH" `
@@ -67,7 +67,7 @@ CheckLastExitCode
# Ensure that stage3 and stage4 are byte-for-byte identical.
Write-Output "Build and compare stage4..."
& "stage3-release\bin\zig.exe" build `
stage3-release\bin\zig.exe build `
--prefix stage4-release `
-Denable-llvm `
-Dno-lib `
@@ -85,26 +85,25 @@ Compare-Object (Get-Content stage3-release\bin\zig.exe) (Get-Content stage4-rele
CheckLastExitCode
Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
& "stage3-release\bin\zig.exe" test `
..\test\behavior.zig `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
-femit-bin="test-x86_64-windows-msvc.c" `
--test-no-exec `
-target x86_64-windows-msvc `
-lc
CheckLastExitCode
& "stage3-release\bin\zig.exe" build-obj `
stage3-release\bin\zig.exe build-obj `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
-OReleaseSmall `
--name compiler_rt `
-femit-bin="compiler_rt-x86_64-windows-msvc.c" `
--dep build_options `
-target x86_64-windows-msvc `
-Mroot="..\lib\compiler_rt.zig" `
-Mbuild_options="config.zig"
-lc `
..\lib\compiler_rt.zig
CheckLastExitCode
stage3-release\bin\zig.exe test `
--zig-lib-dir "$ZIG_LIB_DIR" `
-ofmt=c `
-femit-bin="behavior-x86_64-windows-msvc.c" `
--test-no-exec `
-target x86_64-windows-msvc `
-lc `
..\test\behavior.zig
CheckLastExitCode
Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
@@ -116,8 +115,8 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files (x86)\Microsoft Visual Studio\
CheckLastExitCode
Write-Output "Build and run behavior tests with msvc..."
& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib
cl /I..\lib /W3 /Z7 behavior-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /link /nologo /debug /subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib
CheckLastExitCode
& .\test-x86_64-windows-msvc.exe
.\behavior-x86_64-windows-msvc
CheckLastExitCode
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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;
+145 -133
View File
@@ -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,
&notifyApc,
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(&timespec_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);
}
+482 -510
View File
@@ -83,7 +83,7 @@ csprng: Csprng = .{},
system_basic_information: SystemBasicInformation = .{},
const SystemBasicInformation = if (!is_windows) struct {} else struct {
buffer: windows.SYSTEM_BASIC_INFORMATION = undefined,
buffer: windows.SYSTEM.BASIC_INFORMATION = undefined,
initialized: std.atomic.Value(bool) = .{ .raw = false },
};
@@ -1392,7 +1392,7 @@ const AlertableSyscall = struct {
}
};
fn waitForApcOrAlert() void {
pub fn waitForApcOrAlert() void {
const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
_ = windows.ntdll.NtDelayExecution(windows.TRUE, &infinite_timeout);
}
@@ -2556,9 +2556,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
else => |e| e,
},
},
.device_io_control => |*o| return .{
.device_io_control = try deviceIoControl(t, o),
},
.device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) },
}
}
@@ -2970,7 +2968,7 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
break :o;
}
const buffer = o.data[data_index];
const short_buffer_len = @min(std.math.maxInt(u32), buffer.len);
const short_buffer_len = std.math.lossyCast(u32, buffer.len);
if (o.file.flags.nonblocking) {
context.file = o.file.handle;
@@ -3259,22 +3257,11 @@ fn dirCreateDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, pe
_ = t;
_ = permissions; // TODO use this value
const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path);
const sub_path_w = sub_path_w_array.span();
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
var nt_name: windows.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
const attr: windows.OBJECT_ATTRIBUTES = .{
.Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.Attributes = .{
.INHERIT = false,
},
.ObjectName = &nt_name,
const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
const attr: windows.OBJECT.ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
.Attributes = .{ .INHERIT = false },
.ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w.span())),
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
@@ -3438,21 +3425,14 @@ fn dirCreateDirPathOpenWindows(
};
components: while (true) {
const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, component.path);
const sub_path_w = sub_path_w_array.span();
const is_last = it.peekNext() == null;
const create_disposition: w.FILE.CREATE_DISPOSITION = if (is_last) .OPEN_IF else .CREATE;
var result: Dir = .{ .handle = undefined };
const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
var nt_name: w.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
const sub_path_w = try sliceToPrefixedFileW(dir.handle, component.path);
const attr: windows.OBJECT.ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
.ObjectName = @constCast(&sub_path_w.string()),
};
var io_status_block: w.IO_STATUS_BLOCK = undefined;
const is_last = it.peekNext() == null;
var result: Dir = .{ .handle = undefined };
var iosb: w.IO_STATUS_BLOCK = undefined;
const syscall: Syscall = try .start();
while (true) switch (w.ntdll.NtCreateFile(
&result.handle,
@@ -3468,15 +3448,12 @@ fn dirCreateDirPathOpenWindows(
.SYNCHRONIZE = true,
},
},
&.{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.ObjectName = &nt_name,
},
&io_status_block,
&attr,
&iosb,
null,
.{ .NORMAL = true },
.VALID_FLAGS,
create_disposition,
if (is_last) .OPEN_IF else .CREATE,
.{
.DIRECTORY_FILE = true,
.IO = .SYNCHRONOUS_NONALERT,
@@ -3944,15 +3921,15 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
};
}
fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION {
fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION {
if (!t.system_basic_information.initialized.load(.acquire)) {
mutexLock(&t.mutex);
defer mutexUnlock(&t.mutex);
switch (windows.ntdll.NtQuerySystemInformation(
.SystemBasicInformation,
.Basic,
&t.system_basic_information.buffer,
@sizeOf(windows.SYSTEM_BASIC_INFORMATION),
@sizeOf(windows.SYSTEM.BASIC_INFORMATION),
null,
)) {
.SUCCESS => {},
@@ -4139,22 +4116,11 @@ fn dirAccessWindows(
_ = options; // TODO
const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path);
const sub_path_w = sub_path_w_array.span();
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) return;
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) return;
const path_len_bytes = std.math.cast(u16, std.mem.sliceTo(sub_path_w, 0).len * 2) orelse
return error.NameTooLong;
var nt_name: windows.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
const attr: windows.OBJECT_ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.ObjectName = &nt_name,
if (std.mem.eql(u8, sub_path, ".") or std.mem.eql(u8, sub_path, "..")) return;
const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
const attr: windows.OBJECT.ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
.ObjectName = @constCast(&sub_path_w.string()),
};
var basic_info: windows.FILE.BASIC_INFORMATION = undefined;
const syscall: Syscall = try .start();
@@ -4360,18 +4326,10 @@ fn dirCreateFileWindows(
if (std.mem.eql(u8, sub_path, ".")) return error.IsDir;
if (std.mem.eql(u8, sub_path, "..")) return error.IsDir;
const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path);
const sub_path_w = sub_path_w_array.span();
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
var nt_name: windows.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
const attr: windows.OBJECT_ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.ObjectName = &nt_name,
const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
const attr: windows.OBJECT.ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
.ObjectName = @constCast(&sub_path_w.string()),
};
const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive)
.CREATE
@@ -4966,20 +4924,14 @@ fn dirOpenFileWindows(
pub fn dirOpenFileWtf16(
dir_handle: ?windows.HANDLE,
sub_path_w: [:0]const u16,
sub_path_w: []const u16,
flags: File.OpenFlags,
) File.OpenError!File {
const allow_directory = flags.allow_directory and !flags.isWrite();
if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;
if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir;
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
const w = windows;
var nt_name: w.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
var io_status_block: w.IO_STATUS_BLOCK = undefined;
var attempt: u5 = 0;
var syscall: Syscall = try .start();
@@ -4996,7 +4948,7 @@ pub fn dirOpenFileWtf16(
},
&.{
.RootDirectory = dir_handle,
.ObjectName = &nt_name,
.ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)),
},
&io_status_block,
null,
@@ -5329,20 +5281,19 @@ fn dirOpenDirHaiku(
pub fn dirOpenDirWindows(
dir: Dir,
sub_path_w: [:0]const u16,
sub_path_w: []const u16,
options: Dir.OpenOptions,
) Dir.OpenError!Dir {
const w = windows;
const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
var nt_name: w.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
var io_status_block: w.IO_STATUS_BLOCK = undefined;
var result: Dir = .{ .handle = undefined };
const attr: w.OBJECT.ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)),
};
const syscall: Syscall = try .start();
while (true) switch (w.ntdll.NtCreateFile(
&result.handle,
@@ -5359,10 +5310,7 @@ pub fn dirOpenDirWindows(
.SYNCHRONIZE = true,
},
},
&.{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.ObjectName = &nt_name,
},
&attr,
&io_status_block,
null,
.{ .NORMAL = true },
@@ -6194,13 +6142,15 @@ pub fn GetFinalPathNameByHandle(
input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
@memcpy(input_buf[@sizeOf(windows.MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
{
const rc = windows.DeviceIoControl(mgmt_handle, windows.IOCTL.MOUNTMGR.QUERY_POINTS, .{ .in = &input_buf, .out = &output_buf });
switch (rc) {
.SUCCESS => {},
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
else => return windows.unexpectedStatus(rc),
}
switch ((try deviceIoControl(&.{
.file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = false } },
.code = windows.IOCTL.MOUNTMGR.QUERY_POINTS,
.in = &input_buf,
.out = &output_buf,
})).u.Status) {
.SUCCESS => {},
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
else => |status| return windows.unexpectedStatus(status),
}
const mount_points_struct: *const windows.MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
@@ -6251,11 +6201,15 @@ pub fn GetFinalPathNameByHandle(
vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
@memcpy(@as([*]windows.WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);
const rc = windows.DeviceIoControl(mgmt_handle, windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, .{ .in = &vol_input_buf, .out = &vol_output_buf });
switch (rc) {
switch ((try deviceIoControl(&.{
.file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = true } },
.code = windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH,
.in = &vol_input_buf,
.out = &vol_output_buf,
})).u.Status) {
.SUCCESS => {},
.UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
else => return windows.unexpectedStatus(rc),
else => |status| return windows.unexpectedStatus(status),
}
const volume_paths_struct: *const windows.MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
const volume_path = std.mem.sliceTo(@as(
@@ -6352,20 +6306,17 @@ pub const QueryObjectNameError = error{
};
pub fn QueryObjectName(handle: windows.HANDLE, out_buffer: []u16) QueryObjectNameError![]u16 {
const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT.NAME_INFORMATION)) orelse return error.NameTooLong;
const info: *windows.OBJECT_NAME_INFORMATION = @ptrCast(out_buffer_aligned);
const info: *windows.OBJECT.NAME_INFORMATION = @ptrCast(out_buffer_aligned);
// buffer size is specified in bytes
const out_buffer_len = std.math.cast(windows.ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(windows.ULONG);
// last argument would return the length required for full_buffer, not exposed here
return switch (windows.ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) {
.SUCCESS => blk: {
// info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
// if the object was "unnamed", not sure if this can happen for file handles
if (info.Name.MaximumLength == 0) break :blk error.Unexpected;
// resulting string length is specified in bytes
const path_length_unterminated = @divExact(info.Name.Length, 2);
break :blk info.Name.Buffer.?[0..path_length_unterminated];
return switch (windows.ntdll.NtQueryObject(handle, .Name, info, out_buffer_len, null)) {
.SUCCESS => {
// info.Name from ObQueryNameString is documented to be empty if the object
// was "unnamed", not sure if this can happen for file handles
return if (info.Name.isEmpty()) error.Unexpected else info.Name.slice();
},
.ACCESS_DENIED => error.AccessDenied,
.INVALID_HANDLE => error.InvalidHandle,
@@ -6620,8 +6571,12 @@ pub const WindowsPathSpace = struct {
data: [windows.PATH_MAX_WIDE:0]u16,
len: usize,
pub fn span(self: *const WindowsPathSpace) [:0]const u16 {
return self.data[0..self.len :0];
pub fn span(wps: *const WindowsPathSpace) [:0]const u16 {
return wps.data[0..wps.len :0];
}
pub fn string(wps: *const WindowsPathSpace) windows.UNICODE_STRING {
return .init(wps.span());
}
};
@@ -7076,25 +7031,19 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
_ = t;
const w = windows;
const sub_path_w_buf = try sliceToPrefixedFileW(dir.handle, sub_path);
const sub_path_w = sub_path_w_buf.span();
const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
var nt_name: w.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
// The Windows API makes this mutable, but it will not mutate here.
.Buffer = @constCast(sub_path_w.ptr),
};
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
// Windows does not recognize this, but it does work with empty string.
nt_name.Length = 0;
}
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
if (std.mem.eql(u8, sub_path, "..")) {
// Can't remove the parent directory with an open handle.
return error.FileBusy;
}
var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
if (std.mem.eql(u8, sub_path, ".")) {
// Windows does not recognize this, but it does work with empty string.
sub_path_w.len = 0;
}
const attr: w.OBJECT.ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
.ObjectName = @constCast(&sub_path_w.string()),
};
var io_status_block: w.IO_STATUS_BLOCK = undefined;
var tmp_handle: w.HANDLE = undefined;
@@ -7106,10 +7055,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
.RIGHTS = .{ .DELETE = true },
.SYNCHRONIZE = true,
} },
&.{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.ObjectName = &nt_name,
},
&attr,
&io_status_block,
null,
.{},
@@ -7741,7 +7687,7 @@ fn dirSymLinkWindows(
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
var is_target_absolute = false;
const final_target_path = target_path: {
if (windows.hasCommonNtPrefix(u16, target_path_w.span())) {
if (w.hasCommonNtPrefix(u16, target_path_w.span())) {
// Already an NT path, no need to do anything to it
break :target_path target_path_w.span();
} else {
@@ -7785,13 +7731,16 @@ fn dirSymLinkWindows(
@memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
@memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
const rc = w.DeviceIoControl(symlink_handle, .SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
switch (rc) {
switch ((try deviceIoControl(&.{
.file = .{ .handle = symlink_handle, .flags = .{ .nonblocking = false } },
.code = .SET_REPARSE_POINT,
.in = buffer[0..buf_len],
})).u.Status) {
.SUCCESS => {},
.PRIVILEGE_NOT_HELD => return error.PermissionDenied,
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_DEVICE_REQUEST => return error.FileSystem,
else => return windows.unexpectedStatus(rc),
else => |status| return w.unexpectedStatus(status),
}
}
@@ -7905,17 +7854,10 @@ fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []
fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
// This gets used once for `sub_path` and then reused again temporarily
// before converting back to `buffer`.
var sub_path_w_buf = try sliceToPrefixedFileW(dir.handle, sub_path);
const sub_path_w = sub_path_w_buf.span();
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
var nt_name: windows.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
const attr: windows.OBJECT_ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.ObjectName = &nt_name,
var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
const attr: windows.OBJECT.ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
.ObjectName = @constCast(&sub_path_w.string()),
};
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
var result_handle: windows.HANDLE = undefined;
@@ -8038,14 +7980,14 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
const is_relative = buf.Flags & windows.SYMLINK_FLAG_RELATIVE != 0;
break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w_buf.data);
break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w.data);
},
@as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.MOUNT_POINT)) => r: {
const buf: *const windows.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w_buf.data);
break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w.data);
},
else => return error.UnsupportedReparsePointType,
};
@@ -8541,13 +8483,14 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
const t: *Threaded = @ptrCast(@alignCast(userdata));
return t.isTty(file);
_ = t;
return isTty(file);
}
fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
fn isTty(file: File) Io.Cancelable!bool {
if (is_windows) {
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
switch ((try t.deviceIoControl(&.{
switch ((try deviceIoControl(&.{
.file = .{
.handle = windows.peb().ProcessParameters.ConsoleHandle,
.flags = .{ .nonblocking = false },
@@ -8626,8 +8569,9 @@ fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
if (!is_windows) return if (!try t.supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
if (!is_windows) return if (!try supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
// For Windows Terminal, VT Sequences processing is enabled by default.
const console: File = .{
@@ -8635,7 +8579,7 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
.flags = .{ .nonblocking = false },
};
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
switch ((try t.deviceIoControl(&.{
switch ((try deviceIoControl(&.{
.file = console,
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
.in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
@@ -8661,7 +8605,7 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
var set_console_mode = windows.CONSOLE.USER_IO.SET_MODE(
get_console_mode.Data | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
);
switch ((try t.deviceIoControl(&.{
switch ((try deviceIoControl(&.{
.file = console,
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
.in = @ptrCast(&set_console_mode.request(file, 0, .{}, 0, .{})),
@@ -8673,13 +8617,14 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
const t: *Threaded = @ptrCast(@alignCast(userdata));
return t.supportsAnsiEscapeCodes(file);
_ = t;
return supportsAnsiEscapeCodes(file);
}
fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
if (is_windows) {
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
switch ((try t.deviceIoControl(&.{
switch ((try deviceIoControl(&.{
.file = .{
.handle = windows.peb().ProcessParameters.ConsoleHandle,
.flags = .{ .nonblocking = false },
@@ -8701,7 +8646,7 @@ fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
return false;
}
if (try t.isTty(file)) return true;
if (try isTty(file)) return true;
return false;
}
@@ -8775,7 +8720,7 @@ fn isCygwinPty(file: File) Io.Cancelable!bool {
},
};
const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes);
const name_info: *const windows.FILE.NAME_INFORMATION = @ptrCast(&name_info_bytes);
const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];
const name_wide = std.mem.bytesAsSlice(u16, name_bytes);
// The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
@@ -9002,49 +8947,38 @@ fn fileSetTimestamps(
_ = t;
if (is_windows) {
var access_time_buffer: windows.FILETIME = undefined;
var modify_time_buffer: windows.FILETIME = undefined;
var system_time_buffer: windows.LARGE_INTEGER = undefined;
if (options.access_timestamp == .now or options.modify_timestamp == .now) {
system_time_buffer = windows.ntdll.RtlGetSystemTimePrecise();
}
const access_ptr = switch (options.access_timestamp) {
.unchanged => null,
.now => @panic("TODO do SystemTimeToFileTime logic here"),
.new => |ts| p: {
access_time_buffer = windows.nanoSecondsToFileTime(ts);
break :p &access_time_buffer;
const now_sys = if (options.access_timestamp == .now or options.modify_timestamp == .now)
windows.ntdll.RtlGetSystemTimePrecise()
else
undefined;
var iosb: windows.IO_STATUS_BLOCK = undefined;
var info: windows.FILE.BASIC_INFORMATION = .{
.CreationTime = 0,
.LastAccessTime = switch (options.access_timestamp) {
.unchanged => 0,
.now => now_sys,
.new => |ts| windows.toSysTime(ts),
},
};
const modify_ptr = switch (options.modify_timestamp) {
.unchanged => null,
.now => @panic("TODO do SystemTimeToFileTime logic here"),
.new => |ts| p: {
modify_time_buffer = windows.nanoSecondsToFileTime(ts);
break :p &modify_time_buffer;
.LastWriteTime = switch (options.modify_timestamp) {
.unchanged => 0,
.now => now_sys,
.new => |ts| windows.toSysTime(ts),
},
.ChangeTime = 0,
.FileAttributes = .{},
};
var syscall: Syscall = try .start();
while (true) switch (windows.ntdll.NtSetInformationFile(
file.handle,
&iosb,
&info,
@sizeOf(windows.FILE.BASIC_INFORMATION),
.Basic,
)) {
.SUCCESS => return syscall.finish(),
.CANCELLED => try syscall.checkCancel(),
else => |status| return syscall.unexpectedNtstatus(status),
};
// https://github.com/ziglang/zig/issues/1840
const syscall: Syscall = try .start();
while (true) {
switch (windows.kernel32.SetFileTime(file.handle, null, access_ptr, modify_ptr)) {
0 => switch (windows.GetLastError()) {
.OPERATION_ABORTED => {
try syscall.checkCancel();
continue;
},
else => |err| {
syscall.finish();
return windows.unexpectedError(err);
},
},
else => return syscall.finish(),
}
}
}
if (native_os == .wasi and !builtin.link_libc) {
@@ -9636,15 +9570,43 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.ReadStreamingErro
}
fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingError!usize {
var iosb: windows.IO_STATUS_BLOCK = undefined;
var index: usize = 0;
while (data.len - index != 0 and data[index].len == 0) index += 1;
if (data.len - index == 0) return 0;
const buffer = data[index];
const short_buffer_len = @min(std.math.maxInt(u32), buffer.len);
var iosb: windows.IO_STATUS_BLOCK = undefined;
if (!file.flags.nonblocking) {
const short_buffer_len = std.math.lossyCast(u32, buffer.len);
if (file.flags.nonblocking) {
var done: bool = false;
switch (windows.ntdll.NtReadFile(
file.handle,
null, // event
flagApc,
&done, // APC context
&iosb,
buffer.ptr,
short_buffer_len,
null, // byte offset
null, // key
)) {
// We must wait for the APC routine.
.PENDING, .SUCCESS => while (!done) {
// Once we get here we must not return from the function until the
// operation completes, thereby releasing reference to the iosb.
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
error.Canceled => |e| {
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
_ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
while (!done) waitForApcOrAlert();
return e;
},
};
waitForApcOrAlert();
alertable_syscall.finish();
},
else => |status| iosb.u.Status = status,
}
} else {
const syscall: Syscall = try .start();
while (true) switch (windows.ntdll.NtReadFile(
file.handle,
@@ -9665,41 +9627,10 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingEr
else => |status| {
syscall.finish();
iosb.u.Status = status;
return ntReadFileResult(&iosb);
break;
},
};
}
var done: bool = false;
switch (windows.ntdll.NtReadFile(
file.handle,
null, // event
flagApc,
&done, // APC context
&iosb,
buffer.ptr,
short_buffer_len,
null, // byte offset
null, // key
)) {
// We must wait for the APC routine.
.PENDING, .SUCCESS => while (!done) {
// Once we get here we must not return from the function until the
// operation completes, thereby releasing reference to io_status_block.
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
error.Canceled => |e| {
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
_ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
while (!done) waitForApcOrAlert();
return e;
},
};
waitForApcOrAlert();
alertable_syscall.finish();
},
else => |status| iosb.u.Status = status,
}
return ntReadFileResult(&iosb);
}
@@ -9714,8 +9645,9 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
.CANCELLED => unreachable,
.SUCCESS => return io_status_block.Information,
.END_OF_FILE, .PIPE_BROKEN => return error.EndOfStream,
.INVALID_HANDLE => return error.NotOpenForReading,
.INVALID_DEVICE_REQUEST => return error.IsDir,
.LOCK_NOT_GRANTED => return error.LockViolation,
.FILE_LOCK_CONFLICT => return error.LockViolation,
.ACCESS_DENIED => return error.AccessDenied,
else => |status| return windows.unexpectedStatus(status),
}
@@ -9731,7 +9663,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
.QUOTA_EXCEEDED => return error.SystemResources,
.PIPE_BROKEN => return error.BrokenPipe,
.INVALID_HANDLE => return error.NotOpenForWriting,
.LOCK_NOT_GRANTED => return error.LockViolation,
.FILE_LOCK_CONFLICT => return error.LockViolation,
.ACCESS_DENIED => return error.AccessDenied,
.WORKING_SET_QUOTA => return error.SystemResources,
.DISK_FULL => return error.NoSpaceLeft,
@@ -9740,8 +9672,6 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
}
fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)");
var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
var i: usize = 0;
for (data) |buf| {
@@ -9787,9 +9717,44 @@ fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.Rea
}
}
if (have_preadv) {
const syscall: Syscall = try .start();
while (true) {
const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
switch (posix.errno(rc)) {
.SUCCESS => {
syscall.finish();
return @bitCast(rc);
},
.INTR, .TIMEDOUT => {
try syscall.checkCancel();
continue;
},
.NXIO => return syscall.fail(error.Unseekable),
.SPIPE => return syscall.fail(error.Unseekable),
.OVERFLOW => return syscall.fail(error.Unseekable),
.NOBUFS => return syscall.fail(error.SystemResources),
.NOMEM => return syscall.fail(error.SystemResources),
.AGAIN => return syscall.fail(error.WouldBlock),
.IO => return syscall.fail(error.InputOutput),
.ISDIR => return syscall.fail(error.IsDir),
.NOTCONN => |err| return syscall.errnoBug(err), // not a socket
.CONNRESET => |err| return syscall.errnoBug(err), // not a socket
.INVAL => |err| return syscall.errnoBug(err),
.FAULT => |err| return syscall.errnoBug(err),
.BADF => {
syscall.finish();
if (native_os == .wasi) return error.IsDir; // File operation on directory.
return error.NotOpenForReading;
},
else => |err| return syscall.unexpectedErrno(err),
}
}
}
const syscall: Syscall = try .start();
while (true) {
const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
const rc = posix.pread(file.handle, dest[0].ptr, @intCast(dest[0].len), @bitCast(offset));
switch (posix.errno(rc)) {
.SUCCESS => {
syscall.finish();
@@ -9838,116 +9803,119 @@ fn fileReadPositionalWindows(file: File, data: []const []u8, offset: u64) File.R
}
fn readFilePositionalWindows(file: File, buffer: []u8, offset: u64) File.ReadPositionalError!usize {
const DWORD = windows.DWORD;
const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
var overlapped: windows.OVERLAPPED = .{
.Internal = 0,
.InternalHigh = 0,
.DUMMYUNIONNAME = .{
.DUMMYSTRUCTNAME = .{
.Offset = @truncate(offset),
.OffsetHigh = @truncate(offset >> 32),
var iosb: windows.IO_STATUS_BLOCK = undefined;
const short_buffer_len = std.math.lossyCast(u32, buffer.len);
const signed_offset: windows.LARGE_INTEGER = @intCast(offset);
if (file.flags.nonblocking) {
var done: bool = false;
switch (windows.ntdll.NtReadFile(
file.handle,
null, // event
flagApc,
&done, // APC context
&iosb,
buffer.ptr,
short_buffer_len,
&signed_offset,
null, // key
)) {
// We must wait for the APC routine.
.PENDING, .SUCCESS => while (!done) {
// Once we get here we must not return from the function until the
// operation completes, thereby releasing reference to the iosb.
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
error.Canceled => |e| {
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
_ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
while (!done) waitForApcOrAlert();
return e;
},
};
waitForApcOrAlert();
alertable_syscall.finish();
},
},
.hEvent = null,
};
const syscall: Syscall = try .start();
while (true) {
var n: DWORD = undefined;
if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) != 0) {
syscall.finish();
return n;
else => |status| iosb.u.Status = status,
}
switch (windows.GetLastError()) {
.IO_PENDING => |err| {
} else {
const syscall: Syscall = try .start();
while (true) switch (windows.ntdll.NtReadFile(
file.handle,
null, // event
null, // APC routine
null, // APC context
&iosb,
buffer.ptr,
short_buffer_len,
&signed_offset,
null, // key
)) {
.PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
.CANCELLED => try syscall.checkCancel(),
else => |status| {
syscall.finish();
return windows.errorBug(err);
iosb.u.Status = status;
break;
},
.OPERATION_ABORTED => {
try syscall.checkCancel();
continue;
},
.BROKEN_PIPE, .HANDLE_EOF => {
syscall.finish();
return 0;
},
.NETNAME_DELETED => if (is_debug) unreachable else return error.Unexpected,
.LOCK_VIOLATION => return syscall.fail(error.LockViolation),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected,
// TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing
// a handle to a directory.
.INVALID_FUNCTION => return syscall.fail(error.IsDir),
else => |err| {
syscall.finish();
return windows.unexpectedError(err);
},
}
};
}
return ntReadFileResult(&iosb) catch |err| switch (err) {
error.EndOfStream => 0,
else => |e| e,
};
}
fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
const fd = file.handle;
if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined;
const syscall: Syscall = try .start();
while (true) {
switch (posix.errno(posix.system.llseek(fd, @bitCast(offset), &result, posix.SEEK.CUR))) {
.SUCCESS => {
syscall.finish();
return;
},
.INTR => {
try syscall.checkCancel();
continue;
},
else => |e| {
syscall.finish();
switch (e) {
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
.INVAL => return error.Unseekable,
.OVERFLOW => return error.Unseekable,
.SPIPE => return error.Unseekable,
.NXIO => return error.Unseekable,
else => |err| return posix.unexpectedErrno(err),
}
},
}
}
}
if (is_windows) {
var iosb: windows.IO_STATUS_BLOCK = undefined;
var info: windows.FILE.POSITION_INFORMATION = undefined;
const syscall: Syscall = try .start();
while (true) {
if (windows.kernel32.SetFilePointerEx(fd, offset, null, windows.FILE_CURRENT) != 0) {
return syscall.finish();
}
switch (windows.GetLastError()) {
.OPERATION_ABORTED => {
try syscall.checkCancel();
continue;
},
.INVALID_FUNCTION => return syscall.fail(error.Unseekable),
.NEGATIVE_SEEK => return syscall.fail(error.Unseekable),
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| {
syscall.finish();
return windows.unexpectedError(err);
},
}
}
while (true) switch (windows.ntdll.NtQueryInformationFile(
file.handle,
&iosb,
&info,
@sizeOf(windows.FILE.POSITION_INFORMATION),
.Position,
)) {
.SUCCESS => break,
.CANCELLED => try syscall.checkCancel(),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
else => |status| return syscall.unexpectedNtstatus(status),
};
info.CurrentByteOffset = @bitCast((if (offset >= 0) std.math.add(
u64,
@bitCast(info.CurrentByteOffset),
@intCast(offset),
) else std.math.sub(
u64,
@bitCast(info.CurrentByteOffset),
@intCast(-offset),
)) catch |err| switch (err) {
error.Overflow => return syscall.fail(error.Unseekable),
});
while (true) switch (windows.ntdll.NtSetInformationFile(
file.handle,
&iosb,
&info,
@sizeOf(windows.FILE.POSITION_INFORMATION),
.Position,
)) {
.SUCCESS => return syscall.finish(),
.CANCELLED => try syscall.checkCancel(),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
else => |status| return syscall.unexpectedNtstatus(status),
};
}
if (native_os == .wasi and !builtin.link_libc) {
var new_offset: std.os.wasi.filesize_t = undefined;
const syscall: Syscall = try .start();
while (true) {
switch (std.os.wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
switch (std.os.wasi.fd_seek(file.handle, offset, .CUR, &new_offset)) {
.SUCCESS => {
syscall.finish();
return;
@@ -9974,9 +9942,37 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
if (posix.SEEK == void) return error.Unseekable;
if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined;
const syscall: Syscall = try .start();
while (true) {
switch (posix.errno(posix.system.llseek(file.handle, @bitCast(offset), &result, posix.SEEK.CUR))) {
.SUCCESS => {
syscall.finish();
return;
},
.INTR => {
try syscall.checkCancel();
continue;
},
else => |e| {
syscall.finish();
switch (e) {
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
.INVAL => return error.Unseekable,
.OVERFLOW => return error.Unseekable,
.SPIPE => return error.Unseekable,
.NXIO => return error.Unseekable,
else => |err| return posix.unexpectedErrno(err),
}
},
}
}
}
const syscall: Syscall = try .start();
while (true) {
switch (posix.errno(lseek_sym(fd, offset, posix.SEEK.CUR))) {
switch (posix.errno(lseek_sym(file.handle, offset, posix.SEEK.CUR))) {
.SUCCESS => {
syscall.finish();
return;
@@ -10003,41 +9999,31 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
const fd = file.handle;
if (is_windows) {
// "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
// is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
const ipos: windows.LARGE_INTEGER = @bitCast(offset);
var iosb: windows.IO_STATUS_BLOCK = undefined;
var info: windows.FILE.POSITION_INFORMATION = .{ .CurrentByteOffset = @bitCast(offset) };
const syscall: Syscall = try .start();
while (true) {
if (windows.kernel32.SetFilePointerEx(fd, ipos, null, windows.FILE_BEGIN) != 0) {
return syscall.finish();
}
switch (windows.GetLastError()) {
.OPERATION_ABORTED => {
try syscall.checkCancel();
continue;
},
.INVALID_FUNCTION => return syscall.fail(error.Unseekable),
.NEGATIVE_SEEK => return syscall.fail(error.Unseekable),
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| {
syscall.finish();
return windows.unexpectedError(err);
},
}
}
while (true) switch (windows.ntdll.NtSetInformationFile(
file.handle,
&iosb,
&info,
@sizeOf(windows.FILE.POSITION_INFORMATION),
.Position,
)) {
.SUCCESS => return syscall.finish(),
.CANCELLED => try syscall.checkCancel(),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
else => |status| return syscall.unexpectedNtstatus(status),
};
}
if (native_os == .wasi and !builtin.link_libc) {
const syscall: Syscall = try .start();
while (true) {
var new_offset: std.os.wasi.filesize_t = undefined;
switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
switch (std.os.wasi.fd_seek(file.handle, @bitCast(offset), .SET, &new_offset)) {
.SUCCESS => {
syscall.finish();
return;
@@ -10064,7 +10050,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi
if (posix.SEEK == void) return error.Unseekable;
return posixSeekTo(fd, offset);
return posixSeekTo(file.handle, offset);
}
fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void {
@@ -10131,8 +10117,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) process.O
// If ImagePathName is a symlink, then it will contain the path of the symlink,
// not the path that the symlink points to. However, because we are opening
// the file, we can let the openFileW call follow the symlink for us.
const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
const image_path_name = windows.peb().ProcessParameters.ImagePathName.sliceZ();
const prefixed_path_w = try wToPrefixedFileW(null, image_path_name);
return dirOpenFileWtf16(null, prefixed_path_w.span(), flags);
},
@@ -10334,14 +10319,13 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
return error.FileNotFound;
},
.windows => {
const w = windows;
const image_path_unicode_string = &w.peb().ProcessParameters.ImagePathName;
const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
// If ImagePathName is a symlink, then it will contain the path of the
// symlink, not the path that the symlink points to. We want the path
// that the symlink points to, though, so we need to get the realpath.
var path_name_w_buf = try wToPrefixedFileW(null, image_path_name);
var path_name_w_buf = try wToPrefixedFileW(
null,
windows.peb().ProcessParameters.ImagePathName.sliceZ(),
);
const h_file = handle: {
if (OpenFile(path_name_w_buf.span(), .{
@@ -10360,7 +10344,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
else => |e| return e,
}
};
defer w.CloseHandle(h_file);
defer windows.CloseHandle(h_file);
const wide_slice = try GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data);
@@ -10388,15 +10372,15 @@ fn fileWritePositional(
if (is_windows) {
if (header.len != 0) {
return writeFilePositionalWindows(file.handle, header, offset);
return writeFilePositionalWindows(file, header, offset);
}
for (data[0 .. data.len - 1]) |buf| {
if (buf.len == 0) continue;
return writeFilePositionalWindows(file.handle, buf, offset);
return writeFilePositionalWindows(file, buf, offset);
}
const pattern = data[data.len - 1];
if (pattern.len == 0 or splat == 0) return 0;
return writeFilePositionalWindows(file.handle, pattern, offset);
return writeFilePositionalWindows(file, pattern, offset);
}
var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
@@ -10505,50 +10489,64 @@ fn fileWritePositional(
}
}
fn writeFilePositionalWindows(
handle: windows.HANDLE,
bytes: []const u8,
offset: u64,
) File.WritePositionalError!usize {
var bytes_written: windows.DWORD = undefined;
var overlapped: windows.OVERLAPPED = .{
.Internal = 0,
.InternalHigh = 0,
.DUMMYUNIONNAME = .{
.DUMMYSTRUCTNAME = .{
.Offset = @truncate(offset),
.OffsetHigh = @truncate(offset >> 32),
fn writeFilePositionalWindows(file: File, buffer: []const u8, offset: u64) File.WritePositionalError!usize {
assert(buffer.len != 0);
var iosb: windows.IO_STATUS_BLOCK = undefined;
const short_buffer_len = std.math.lossyCast(u32, buffer.len);
const signed_offset: windows.LARGE_INTEGER = @intCast(offset);
if (file.flags.nonblocking) {
var done: bool = false;
switch (windows.ntdll.NtWriteFile(
file.handle,
null, // event
flagApc,
&done, // APC context
&iosb,
buffer.ptr,
short_buffer_len,
&signed_offset,
null, // key
)) {
// We must wait for the APC routine.
.PENDING, .SUCCESS => while (!done) {
// Once we get here we must not return from the function until the
// operation completes, thereby releasing reference to the iosb.
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
error.Canceled => |e| {
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
_ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
while (!done) waitForApcOrAlert();
return e;
},
};
waitForApcOrAlert();
alertable_syscall.finish();
},
},
.hEvent = null,
};
const adjusted_len = std.math.lossyCast(u32, bytes.len);
const syscall: Syscall = try .start();
while (true) {
if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, &overlapped) != 0) {
syscall.finish();
return bytes_written;
else => |status| iosb.u.Status = status,
}
switch (windows.GetLastError()) {
.OPERATION_ABORTED => {
try syscall.checkCancel();
continue;
},
.INVALID_USER_BUFFER => return syscall.fail(error.SystemResources),
.NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources),
.NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources),
.NO_DATA => return syscall.fail(error.BrokenPipe),
.INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, // use after free
.LOCK_VIOLATION => return syscall.fail(error.LockViolation),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
.DISK_FULL => return syscall.fail(error.NoSpaceLeft),
else => |err| {
} else {
const syscall: Syscall = try .start();
while (true) switch (windows.ntdll.NtWriteFile(
file.handle,
null, // event
null, // APC routine
null, // APC context
&iosb,
buffer.ptr,
short_buffer_len,
&signed_offset,
null, // key
)) {
.PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
.CANCELLED => try syscall.checkCancel(),
else => |status| {
syscall.finish();
return windows.unexpectedError(err);
iosb.u.Status = status;
return ntWriteFileResult(&iosb);
},
}
};
}
return ntWriteFileResult(&iosb);
}
fn fileWriteStreaming(
@@ -10673,9 +10671,7 @@ fn fileWriteStreaming(
fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!usize {
assert(buffer.len != 0);
var iosb: windows.IO_STATUS_BLOCK = undefined;
if (file.flags.nonblocking) {
var done: bool = false;
switch (windows.ntdll.NtWriteFile(
@@ -10706,7 +10702,6 @@ fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!u
},
else => |status| iosb.u.Status = status,
}
return ntWriteFileResult(&iosb);
} else {
const syscall: Syscall = try .start();
while (true) switch (windows.ntdll.NtWriteFile(
@@ -10721,17 +10716,15 @@ fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!u
null, // key
)) {
.PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
.CANCELLED => {
try syscall.checkCancel();
continue;
},
.CANCELLED => try syscall.checkCancel(),
else => |status| {
syscall.finish();
iosb.u.Status = status;
return ntWriteFileResult(&iosb);
break;
},
};
}
return ntWriteFileResult(&iosb);
}
fn fileWriteFileStreaming(
@@ -11433,13 +11426,22 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp {
return .{ .nanoseconds = @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100 + epoch_ns };
},
.awake, .boot => {
// QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
const qpc = windows.QueryPerformanceCounter();
// We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA
// (a read-only page of info updated and mapped by the kernel to all processes):
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
const qpf = windows.QueryPerformanceFrequency();
const qpf: u64 = qpf: {
var qpf: windows.LARGE_INTEGER = undefined;
assert(windows.ntdll.RtlQueryPerformanceFrequency(&qpf) != windows.FALSE);
break :qpf @bitCast(qpf);
};
// QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
const qpc: u64 = qpc: {
var qpc: windows.LARGE_INTEGER = undefined;
assert(windows.ntdll.RtlQueryPerformanceCounter(&qpc) != windows.FALSE);
break :qpc @bitCast(qpc);
};
// 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
// https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
@@ -11458,7 +11460,7 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp {
// https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L442-L485
if (windows.ntdll.NtQueryInformationProcess(
handle,
windows.PROCESSINFOCLASS.Times,
.Times,
&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,
&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,
};
+1 -1
View File
@@ -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;
+3 -13
View File
@@ -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,
+18 -27
View File
@@ -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,
+2 -1
View File
@@ -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;
+11 -5
View File
@@ -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);
+12 -8
View File
@@ -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);
+151 -86
View File
@@ -1,39 +1,51 @@
mutex: Io.Mutex,
ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn,
notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE,
modules: std.ArrayList(Module),
module_name_arena: std.heap.ArenaAllocator.State,
pub const init: SelfInfo = .{
.mutex = .init,
.ntdll_handle = null,
.notification_cookie = null,
.modules = .empty,
.module_name_arena = .{},
};
pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
for (si.modules.items) |*module| {
di: {
const di = &(module.di orelse break :di catch break :di);
di.deinit(gpa);
pub fn deinit(si: *SelfInfo, io: Io) void {
const gpa = std.debug.getDebugInfoAllocator();
if (si.notification_cookie) |cookie| unregister: {
switch ((si.getNtdllProc(.LdrUnregisterDllNotification) catch break :unregister)(cookie)) {
.SUCCESS => {},
else => |status| windows.unexpectedStatus(status) catch break :unregister,
}
}
if (si.ntdll_handle) |handle| switch (windows.ntdll.LdrUnloadDll(handle)) {
.SUCCESS => {},
else => |status| windows.unexpectedStatus(status) catch {},
};
for (si.modules.items) |*module| module.deinit(gpa, io);
si.modules.deinit(gpa);
var module_name_arena = si.module_name_arena.promote(gpa);
module_name_arena.deinit();
}
pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol {
pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {
const gpa = std.debug.getDebugInfoAllocator();
try si.mutex.lock(io);
defer si.mutex.unlock(io);
const module = try si.findModule(gpa, address);
const di = try module.getDebugInfo(gpa, io);
return di.getSymbol(gpa, address - module.base_address);
return di.getSymbol(gpa, address - @intFromPtr(module.entry.DllBase));
}
pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 {
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
const gpa = std.debug.getDebugInfoAllocator();
try si.mutex.lock(io);
defer si.mutex.unlock(io);
const module = try si.findModule(gpa, address);
return module.name;
return module.name orelse {
const name = try std.unicode.wtf16LeToWtf8Alloc(gpa, module.entry.BaseDllName.slice());
module.name = name;
return name;
};
}
pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize {
pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
const gpa = std.debug.getDebugInfoAllocator();
try si.mutex.lock(io);
defer si.mutex.unlock(io);
const module = try si.findModule(gpa, address);
@@ -141,18 +153,16 @@ pub const UnwindContext = struct {
.history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE),
};
}
pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void {
pub fn deinit(ctx: *UnwindContext) void {
_ = ctx;
_ = gpa;
}
pub fn getFp(ctx: *UnwindContext) usize {
return ctx.cur.getRegs().bp;
}
};
pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize {
pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize {
_ = si;
_ = io;
_ = gpa;
const current_regs = context.cur.getRegs();
var image_base: usize = undefined;
@@ -188,16 +198,12 @@ pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContex
}
const Module = struct {
base_address: usize,
size: u32,
name: []const u8,
handle: windows.HMODULE,
entry: *const LDR.DATA_TABLE_ENTRY,
name: ?[]const u8,
di: ?(Error!DebugInfo),
const DebugInfo = struct {
arena: std.heap.ArenaAllocator.State,
io: Io,
coff_image_base: u64,
mapped_file: ?MappedFile,
dwarf: ?Dwarf,
@@ -210,14 +216,19 @@ const Module = struct {
section_view: []const u8,
fn deinit(mf: *const MappedFile, io: Io) void {
const process_handle = windows.GetCurrentProcess();
assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(mf.section_view.ptr)) == .SUCCESS);
switch (windows.ntdll.NtUnmapViewOfSection(
process_handle,
@constCast(mf.section_view.ptr),
)) {
.SUCCESS => {},
else => |status| windows.unexpectedStatus(status) catch {},
}
windows.CloseHandle(mf.section_handle);
mf.file.close(io);
}
};
fn deinit(di: *DebugInfo, gpa: Allocator) void {
const io = di.io;
fn deinit(di: *DebugInfo, gpa: Allocator, io: Io) void {
if (di.dwarf) |*dwarf| dwarf.deinit(gpa);
if (di.pdb) |*pdb| {
pdb.file_reader.file.close(io);
@@ -262,7 +273,10 @@ const Module = struct {
return .{
.name = pdb.getSymbolName(module, vaddr - coff_section.virtual_address),
.compile_unit_name = fs.path.basename(module.obj_file_name),
.source_location = pdb.getLineNumberInfo(module, vaddr - coff_section.virtual_address) catch null,
.source_location = pdb.getLineNumberInfo(
module,
vaddr - coff_section.virtual_address,
) catch null,
};
}
dwarf: {
@@ -286,13 +300,19 @@ const Module = struct {
}
};
fn deinit(module: *Module, gpa: Allocator, io: Io) void {
if (module.name) |name| gpa.free(name);
if (module.di) |*di_or_err| if (di_or_err.*) |*di| di.deinit(gpa, io) else |_| {};
module.* = undefined;
}
fn getDebugInfo(module: *Module, gpa: Allocator, io: Io) Error!*DebugInfo {
if (module.di == null) module.di = loadDebugInfo(module, gpa, io);
return if (module.di.?) |*di| di else |err| err;
}
fn loadDebugInfo(module: *const Module, gpa: Allocator, io: Io) Error!DebugInfo {
const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address);
const mapped = mapped_ptr[0..module.size];
const mapped_ptr: [*]const u8 = @ptrCast(module.entry.DllBase);
const mapped = mapped_ptr[0..module.entry.SizeOfImage];
var coff_obj = coff.Coff.init(mapped, true) catch return error.InvalidDebugInfo;
var arena_instance: std.heap.ArenaAllocator = .init(gpa);
@@ -304,18 +324,15 @@ const Module = struct {
// a binary is produced with -gdwarf, since the section names are longer than 8 bytes.
const mapped_file: ?DebugInfo.MappedFile = mapped: {
if (!coff_obj.strtabRequired()) break :mapped null;
var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
name_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present
const process_handle = windows.GetCurrentProcess();
const len = windows.kernel32.GetModuleFileNameExW(
process_handle,
module.handle,
name_buffer[4..],
windows.PATH_MAX_WIDE,
);
if (len == 0) return error.MissingDebugInfo;
const name_w = name_buffer[0 .. len + 4 :0];
const coff_file = Io.Threaded.dirOpenFileWtf16(null, name_w, .{}) catch |err| switch (err) {
var path_buffer: [4 + windows.PATH_MAX_WIDE]u16 = undefined;
path_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present
const path_slice = module.entry.FullDllName.slice();
@memcpy(path_buffer[4..][0..path_slice.len], path_slice);
const coff_file = Io.Threaded.dirOpenFileWtf16(
null,
path_buffer[0 .. 4 + path_slice.len],
.{},
) catch |err| switch (err) {
error.Canceled => |e| return e,
error.Unexpected => |e| return e,
error.FileNotFound => return error.MissingDebugInfo,
@@ -359,7 +376,8 @@ const Module = struct {
null,
null,
.{ .READONLY = true },
// The documentation states that if no AllocationAttribute is specified, then SEC_COMMIT is the default.
// The documentation states that if no AllocationAttribute is specified,
// then SEC_COMMIT is the default.
// In practice, this isn't the case and specifying 0 will result in INVALID_PARAMETER_6.
.{ .COMMIT = true },
coff_file.handle,
@@ -368,6 +386,7 @@ const Module = struct {
errdefer windows.CloseHandle(section_handle);
var coff_len: usize = 0;
var section_view_ptr: ?[*]const u8 = null;
const process_handle = windows.GetCurrentProcess();
const map_section_rc = windows.ntdll.NtMapViewOfSection(
section_handle,
process_handle,
@@ -381,7 +400,13 @@ const Module = struct {
.{ .READONLY = true },
);
if (map_section_rc != .SUCCESS) return error.MissingDebugInfo;
errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(section_view_ptr.?)) == .SUCCESS);
errdefer switch (windows.ntdll.NtUnmapViewOfSection(
process_handle,
@constCast(section_view_ptr.?),
)) {
.SUCCESS => {},
else => |status| windows.unexpectedStatus(status) catch {},
};
const section_view = section_view_ptr.?[0..coff_len];
coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo;
break :mapped .{
@@ -496,7 +521,6 @@ const Module = struct {
return .{
.arena = arena_instance.state,
.io = io,
.coff_image_base = coff_image_base,
.mapped_file = mapped_file,
.dwarf = opt_dwarf,
@@ -509,52 +533,82 @@ const Module = struct {
/// Assumes we already hold `si.mutex`.
fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebugInfo, OutOfMemory, Unexpected }!*Module {
for (si.modules.items) |*mod| {
if (address >= mod.base_address and address < mod.base_address + mod.size) {
return mod;
const base = @intFromPtr(mod.entry.DllBase);
if (address >= base and address < base + mod.entry.SizeOfImage) return mod;
}
try si.modules.ensureUnusedCapacity(gpa, 1);
var entry: *LDR.DATA_TABLE_ENTRY = undefined;
switch (windows.ntdll.LdrFindEntryForAddress(@ptrFromInt(address), &entry)) {
.SUCCESS => {},
.DLL_NOT_FOUND => return error.MissingDebugInfo,
else => |status| return windows.unexpectedStatus(status),
}
if (si.notification_cookie == null) {
var notification_cookie: LDR.DLL_NOTIFICATION.COOKIE = undefined;
switch ((try si.getNtdllProc(.LdrRegisterDllNotification))(
.{},
&dllNotification,
si,
&notification_cookie,
)) {
.SUCCESS => si.notification_cookie = notification_cookie,
else => |status| return windows.unexpectedStatus(status),
}
}
const mod = si.modules.addOneAssumeCapacity();
mod.* = .{ .entry = entry, .name = null, .di = null };
return mod;
}
// A new module might have been loaded; rebuild the list.
{
for (si.modules.items) |*mod| {
const di = &(mod.di orelse continue catch continue);
di.deinit(gpa);
}
si.modules.clearRetainingCapacity();
var module_name_arena = si.module_name_arena.promote(gpa);
defer si.module_name_arena = module_name_arena.state;
_ = module_name_arena.reset(.retain_capacity);
const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0);
if (handle == windows.INVALID_HANDLE_VALUE) {
return windows.unexpectedError(windows.GetLastError());
}
defer windows.CloseHandle(handle);
var entry: windows.MODULEENTRY32 = undefined;
entry.dwSize = @sizeOf(windows.MODULEENTRY32);
var result = windows.kernel32.Module32First(handle, &entry);
while (result != 0) : (result = windows.kernel32.Module32Next(handle, &entry)) {
try si.modules.append(gpa, .{
.base_address = @intFromPtr(entry.modBaseAddr),
.size = entry.modBaseSize,
.name = try module_name_arena.allocator().dupe(
u8,
std.mem.sliceTo(&entry.szModule, 0),
),
.handle = entry.hModule,
.di = null,
});
inline fn getNtdllProc(
si: *SelfInfo,
comptime proc: std.meta.DeclEnum(windows.ntdll),
) !@TypeOf(&@field(windows.ntdll, @tagName(proc))) {
return if (load_dll_notification_procs)
@ptrCast(try si.loadNtdllProc(@tagName(proc)))
else
&@field(windows.ntdll, @tagName(proc));
}
fn loadNtdllProc(si: *SelfInfo, name: []const u8) Io.UnexpectedError!*anyopaque {
const ntdll_handle = si.ntdll_handle orelse ntdll_handle: {
var ntdll_handle: *anyopaque = undefined;
switch (windows.ntdll.LdrLoadDll(null, null, &.init(
&.{ 'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l' },
), &ntdll_handle)) {
.SUCCESS => {},
else => |status| return windows.unexpectedStatus(status),
}
si.ntdll_handle = ntdll_handle;
break :ntdll_handle ntdll_handle;
};
var proc_addr: *anyopaque = undefined;
switch (windows.ntdll.LdrGetProcedureAddress(ntdll_handle, &.init(name), 0, &proc_addr)) {
.SUCCESS => {},
else => |status| return windows.unexpectedStatus(status),
}
return proc_addr;
}
for (si.modules.items) |*mod| {
if (address >= mod.base_address and address < mod.base_address + mod.size) {
return mod;
}
fn dllNotification(
reason: LDR.DLL_NOTIFICATION.REASON,
data: *const LDR.DLL_NOTIFICATION.DATA,
context: ?*anyopaque,
) callconv(.winapi) void {
const si: *SelfInfo = @ptrCast(@alignCast(context));
switch (reason) {
.LOADED => {},
.UNLOADED => {
const io = std.Options.debug_io;
si.mutex.lockUncancelable(io);
defer si.mutex.unlock(io);
for (si.modules.items, 0..) |*mod, mod_index| {
if (mod.entry.DllBase != data.Unloaded.DllBase) continue;
mod.deinit(std.debug.getDebugInfoAllocator(), io);
_ = si.modules.swapRemove(mod_index);
break;
}
},
}
return error.MissingDebugInfo;
}
const std = @import("std");
@@ -563,12 +617,23 @@ const Allocator = std.mem.Allocator;
const Dwarf = std.debug.Dwarf;
const Pdb = std.debug.Pdb;
const Error = std.debug.SelfInfoError;
const assert = std.debug.assert;
const coff = std.coff;
const fs = std.fs;
const windows = std.os.windows;
const LDR = windows.LDR;
const builtin = @import("builtin");
const native_endian = builtin.target.cpu.arch.endian();
const load_dll_notification_procs = builtin.abi == .msvc and switch (builtin.zig_backend) {
.stage2_c => true,
else => switch (builtin.output_mode) {
.Exe => false,
.Lib => switch (builtin.link_mode) {
.static => true,
.dynamic => false,
},
.Obj => true,
},
};
const SelfInfo = @This();
+3 -3
View File
@@ -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,
+568 -1125
View File
@@ -24,6 +24,66 @@ pub const nls = @import("windows/nls.zig");
pub const current_process: HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))));
pub const OBJECT = struct {
// ref: um/winternl.h
pub const ATTRIBUTES = extern struct {
Length: ULONG = @sizeOf(ATTRIBUTES),
RootDirectory: ?HANDLE = null,
ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty),
Attributes: Flags = .{},
SecurityDescriptor: ?*anyopaque = null,
SecurityQualityOfService: ?*anyopaque = null,
// Valid values for the Attributes field
pub const Flags = packed struct(ULONG) {
Reserved0: u1 = 0,
INHERIT: bool = false,
Reserved2: u2 = 0,
PERMANENT: bool = false,
EXCLUSIVE: bool = false,
/// If name-lookup code should ignore the case of the ObjectName member rather than performing an exact-match search.
CASE_INSENSITIVE: bool = true,
OPENIF: bool = false,
OPENLINK: bool = false,
KERNEL_HANDLE: bool = false,
FORCE_ACCESS_CHECK: bool = false,
IGNORE_IMPERSONATED_DEVICEMAP: bool = false,
DONT_REPARSE: bool = false,
Reserved13: u19 = 0,
pub const VALID_ATTRIBUTES: ATTRIBUTES = .{
.INHERIT = true,
.PERMANENT = true,
.EXCLUSIVE = true,
.CASE_INSENSITIVE = true,
.OPENIF = true,
.OPENLINK = true,
.KERNEL_HANDLE = true,
.FORCE_ACCESS_CHECK = true,
.IGNORE_IMPERSONATED_DEVICEMAP = true,
.DONT_REPARSE = true,
};
};
};
pub const INFORMATION_CLASS = enum(c_int) {
Basic = 0,
Name = 1,
Type = 2,
Types = 3,
HandleFlag = 4,
Session = 5,
_,
pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".fields.len;
};
pub const NAME_INFORMATION = extern struct {
Name: UNICODE_STRING,
};
};
pub const FILE = struct {
// ref: km/ntddk.h
@@ -73,6 +133,94 @@ pub const FILE = struct {
// ref: km/ntifs.h
pub const NAME_FLAGS = packed struct(UCHAR) {
NTFS: bool = false,
DOS: bool = false,
Reserved2: u5 = 0,
UNSPECIFIED: bool = false,
};
pub const NOTIFY = struct {
pub const CHANGE = packed struct(ULONG) {
FILE_NAME: bool = false,
DIR_NAME: bool = false,
ATTRIBUTES: bool = false,
SIZE: bool = false,
LAST_WRITE: bool = false,
LAST_ACCESS: bool = false,
CREATION: bool = false,
EA: bool = false,
SECURITY: bool = false,
STREAM_NAME: bool = false,
STREAM_SIZE: bool = false,
STREAM_WRITE: bool = false,
Reserved12: u20 = 0,
};
pub const INFORMATION = extern struct {
NextEntryOffset: ULONG,
Action: ULONG,
FileNameLength: ULONG,
FileName: [0]WCHAR,
pub fn fileName(info: *INFORMATION) []WCHAR {
const ptr: [*]WCHAR = @ptrCast(&info.FileName);
return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
}
};
pub const EXTENDED_INFORMATION = extern struct {
NextEntryOffset: ULONG,
Action: ULONG,
CreationTime: LARGE_INTEGER,
LastModificationTime: LARGE_INTEGER,
LastChangeTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
AllocatedLength: LARGE_INTEGER,
FileSize: LARGE_INTEGER,
FileAttributes: ATTRIBUTE,
u: extern union {
ReparsePointTag: ULONG,
EaSize: ULONG,
},
FileId: LARGE_INTEGER,
ParentFileId: LARGE_INTEGER,
FileNameLength: ULONG,
FileName: [0]WCHAR,
pub fn fileName(info: *INFORMATION) []WCHAR {
const ptr: [*]WCHAR = @ptrCast(&info.FileName);
return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
}
};
pub const FULL_INFORMATION = extern struct {
NextEntryOffset: ULONG,
Action: ULONG,
CreationTime: LARGE_INTEGER,
LastModificationTime: LARGE_INTEGER,
LastChangeTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
AllocatedLength: LARGE_INTEGER,
FileSize: LARGE_INTEGER,
FileAttributes: ATTRIBUTE,
u: extern union {
ReparsePointTag: ULONG,
EaSize: ULONG,
},
FileId: LARGE_INTEGER,
ParentFileId: LARGE_INTEGER,
FileNameLength: ULONG,
FileNameFlags: NAME_FLAGS,
FileName: [0]WCHAR,
pub fn fileName(info: *INFORMATION) []WCHAR {
const ptr: [*]WCHAR = @ptrCast(&info.FileName);
return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
}
};
};
pub const PIPE = struct {
/// Define the `NamedPipeType` flags for `NtCreateNamedPipeFile`
pub const TYPE = packed struct(ULONG) {
@@ -357,6 +505,7 @@ pub const FILE = struct {
IdAllExtdBothDirectory = 81,
StreamReservation = 82,
MupProvider = 83,
_,
pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len;
};
@@ -647,6 +796,19 @@ pub const FILE = struct {
Mode: MODE,
};
};
// ref: km/
};
pub const DIRECTORY = struct {
pub const NOTIFY_INFORMATION_CLASS = enum(c_int) {
Notify = 1,
NotifyExtended = 2,
NotifyFull = 3,
_,
pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len;
};
};
pub const CONSOLE = struct {
@@ -880,141 +1042,229 @@ pub const CONSOLE = struct {
// ref: km/ntddk.h
pub const PROCESSINFOCLASS = enum(c_int) {
BasicInformation = 0,
QuotaLimits = 1,
IoCounters = 2,
VmCounters = 3,
Times = 4,
BasePriority = 5,
RaisePriority = 6,
DebugPort = 7,
ExceptionPort = 8,
AccessToken = 9,
LdtInformation = 10,
LdtSize = 11,
DefaultHardErrorMode = 12,
IoPortHandlers = 13,
PooledUsageAndLimits = 14,
WorkingSetWatch = 15,
UserModeIOPL = 16,
EnableAlignmentFaultFixup = 17,
PriorityClass = 18,
Wx86Information = 19,
HandleCount = 20,
AffinityMask = 21,
PriorityBoost = 22,
DeviceMap = 23,
SessionInformation = 24,
ForegroundInformation = 25,
Wow64Information = 26,
ImageFileName = 27,
LUIDDeviceMapsEnabled = 28,
BreakOnTermination = 29,
DebugObjectHandle = 30,
DebugFlags = 31,
HandleTracing = 32,
IoPriority = 33,
ExecuteFlags = 34,
TlsInformation = 35,
Cookie = 36,
ImageInformation = 37,
CycleTime = 38,
PagePriority = 39,
InstrumentationCallback = 40,
ThreadStackAllocation = 41,
WorkingSetWatchEx = 42,
ImageFileNameWin32 = 43,
ImageFileMapping = 44,
AffinityUpdateMode = 45,
MemoryAllocationMode = 46,
GroupInformation = 47,
TokenVirtualizationEnabled = 48,
OwnerInformation = 49,
WindowInformation = 50,
HandleInformation = 51,
MitigationPolicy = 52,
DynamicFunctionTableInformation = 53,
HandleCheckingMode = 54,
KeepAliveCount = 55,
RevokeFileHandles = 56,
WorkingSetControl = 57,
HandleTable = 58,
CheckStackExtentsMode = 59,
CommandLineInformation = 60,
ProtectionInformation = 61,
MemoryExhaustion = 62,
FaultInformation = 63,
TelemetryIdInformation = 64,
CommitReleaseInformation = 65,
Reserved1Information = 66,
Reserved2Information = 67,
SubsystemProcess = 68,
InPrivate = 70,
RaiseUMExceptionOnInvalidHandleClose = 71,
SubsystemInformation = 75,
Win32kSyscallFilterInformation = 79,
EnergyTrackingState = 82,
NetworkIoCounters = 114,
_,
pub const SYSTEM = struct {
pub const INFORMATION_CLASS = enum(c_int) {
Basic = 0,
Performance = 2,
TimeOfDay = 3,
Process = 5,
ProcessorPerformance = 8,
Interrupt = 23,
Exception = 33,
RegistryQuota = 37,
Lookaside = 45,
CodeIntegrity = 103,
Policy = 134,
_,
};
pub const Max: @typeInfo(@This()).@"enum".tag_type = 117;
pub const BASIC_INFORMATION = extern struct {
Reserved: ULONG,
TimerResolution: ULONG,
PageSize: ULONG,
NumberOfPhysicalPages: ULONG,
LowestPhysicalPageNumber: ULONG,
HighestPhysicalPageNumber: ULONG,
AllocationGranularity: ULONG,
MinimumUserModeAddress: ULONG_PTR,
MaximumUserModeAddress: ULONG_PTR,
ActiveProcessorsAffinityMask: KAFFINITY,
NumberOfProcessors: UCHAR,
};
};
pub const THREADINFOCLASS = enum(c_int) {
BasicInformation = 0,
Times = 1,
Priority = 2,
BasePriority = 3,
AffinityMask = 4,
ImpersonationToken = 5,
DescriptorTableEntry = 6,
EnableAlignmentFaultFixup = 7,
EventPair_Reusable = 8,
QuerySetWin32StartAddress = 9,
ZeroTlsCell = 10,
PerformanceCount = 11,
AmILastThread = 12,
IdealProcessor = 13,
PriorityBoost = 14,
SetTlsArrayAddress = 15,
IsIoPending = 16,
// Windows 2000+ from here
HideFromDebugger = 17,
// Windows XP+ from here
BreakOnTermination = 18,
SwitchLegacyState = 19,
IsTerminated = 20,
// Windows Vista+ from here
LastSystemCall = 21,
IoPriority = 22,
CycleTime = 23,
PagePriority = 24,
ActualBasePriority = 25,
TebInformation = 26,
CSwitchMon = 27,
// Windows 7+ from here
CSwitchPmu = 28,
Wow64Context = 29,
GroupInformation = 30,
UmsInformation = 31,
CounterProfiling = 32,
IdealProcessorEx = 33,
// Windows 8+ from here
CpuAccountingInformation = 34,
// Windows 8.1+ from here
SuspendCount = 35,
// Windows 10+ from here
HeterogeneousCpuPolicy = 36,
ContainerId = 37,
NameInformation = 38,
SelectedCpuSets = 39,
SystemThreadInformation = 40,
ActualGroupAffinity = 41,
DynamicCodePolicyInfo = 42,
SubsystemInformation = 45,
pub const PROCESS = struct {
pub const INFORMATION = extern struct {
hProcess: HANDLE,
hThread: HANDLE,
dwProcessId: DWORD,
dwThreadId: DWORD,
};
pub const Max: @typeInfo(@This()).@"enum".tag_type = 60;
pub const INFOCLASS = enum(c_int) {
BasicInformation = 0,
QuotaLimits = 1,
IoCounters = 2,
VmCounters = 3,
Times = 4,
BasePriority = 5,
RaisePriority = 6,
DebugPort = 7,
ExceptionPort = 8,
AccessToken = 9,
LdtInformation = 10,
LdtSize = 11,
DefaultHardErrorMode = 12,
IoPortHandlers = 13,
PooledUsageAndLimits = 14,
WorkingSetWatch = 15,
UserModeIOPL = 16,
EnableAlignmentFaultFixup = 17,
PriorityClass = 18,
Wx86Information = 19,
HandleCount = 20,
AffinityMask = 21,
PriorityBoost = 22,
DeviceMap = 23,
SessionInformation = 24,
ForegroundInformation = 25,
Wow64Information = 26,
ImageFileName = 27,
LUIDDeviceMapsEnabled = 28,
BreakOnTermination = 29,
DebugObjectHandle = 30,
DebugFlags = 31,
HandleTracing = 32,
IoPriority = 33,
ExecuteFlags = 34,
TlsInformation = 35,
Cookie = 36,
ImageInformation = 37,
CycleTime = 38,
PagePriority = 39,
InstrumentationCallback = 40,
ThreadStackAllocation = 41,
WorkingSetWatchEx = 42,
ImageFileNameWin32 = 43,
ImageFileMapping = 44,
AffinityUpdateMode = 45,
MemoryAllocationMode = 46,
GroupInformation = 47,
TokenVirtualizationEnabled = 48,
OwnerInformation = 49,
WindowInformation = 50,
HandleInformation = 51,
MitigationPolicy = 52,
DynamicFunctionTableInformation = 53,
HandleCheckingMode = 54,
KeepAliveCount = 55,
RevokeFileHandles = 56,
WorkingSetControl = 57,
HandleTable = 58,
CheckStackExtentsMode = 59,
CommandLineInformation = 60,
ProtectionInformation = 61,
MemoryExhaustion = 62,
FaultInformation = 63,
TelemetryIdInformation = 64,
CommitReleaseInformation = 65,
Reserved1Information = 66,
Reserved2Information = 67,
SubsystemProcess = 68,
InPrivate = 70,
RaiseUMExceptionOnInvalidHandleClose = 71,
SubsystemInformation = 75,
Win32kSyscallFilterInformation = 79,
EnergyTrackingState = 82,
NetworkIoCounters = 114,
_,
pub const Max: @typeInfo(@This()).@"enum".tag_type = 117;
};
pub const BASIC_INFORMATION = extern struct {
ExitStatus: NTSTATUS,
PebBaseAddress: *PEB,
AffinityMask: ULONG_PTR,
BasePriority: KPRIORITY,
UniqueProcessId: ULONG_PTR,
InheritedFromUniqueProcessId: ULONG_PTR,
};
pub const VM_COUNTERS = extern struct {
PeakVirtualSize: SIZE_T,
VirtualSize: SIZE_T,
PageFaultCount: ULONG,
PeakWorkingSetSize: SIZE_T,
WorkingSetSize: SIZE_T,
QuotaPeakPagedPoolUsage: SIZE_T,
QuotaPagedPoolUsage: SIZE_T,
QuotaPeakNonPagedPoolUsage: SIZE_T,
QuotaNonPagedPoolUsage: SIZE_T,
PagefileUsage: SIZE_T,
PeakPagefileUsage: SIZE_T,
};
};
pub const THREAD = struct {
pub const INFOCLASS = enum(c_int) {
BasicInformation = 0,
Times = 1,
Priority = 2,
BasePriority = 3,
AffinityMask = 4,
ImpersonationToken = 5,
DescriptorTableEntry = 6,
EnableAlignmentFaultFixup = 7,
EventPair_Reusable = 8,
QuerySetWin32StartAddress = 9,
ZeroTlsCell = 10,
PerformanceCount = 11,
AmILastThread = 12,
IdealProcessor = 13,
PriorityBoost = 14,
SetTlsArrayAddress = 15,
IsIoPending = 16,
// Windows 2000+ from here
HideFromDebugger = 17,
// Windows XP+ from here
BreakOnTermination = 18,
SwitchLegacyState = 19,
IsTerminated = 20,
// Windows Vista+ from here
LastSystemCall = 21,
IoPriority = 22,
CycleTime = 23,
PagePriority = 24,
ActualBasePriority = 25,
TebInformation = 26,
CSwitchMon = 27,
// Windows 7+ from here
CSwitchPmu = 28,
Wow64Context = 29,
GroupInformation = 30,
UmsInformation = 31,
CounterProfiling = 32,
IdealProcessorEx = 33,
// Windows 8+ from here
CpuAccountingInformation = 34,
// Windows 8.1+ from here
SuspendCount = 35,
// Windows 10+ from here
HeterogeneousCpuPolicy = 36,
ContainerId = 37,
NameInformation = 38,
SelectedCpuSets = 39,
SystemThreadInformation = 40,
ActualGroupAffinity = 41,
DynamicCodePolicyInfo = 42,
SubsystemInformation = 45,
_,
pub const Max: @typeInfo(@This()).@"enum".tag_type = 60;
};
pub const BASIC_INFORMATION = extern struct {
ExitStatus: NTSTATUS,
TebBaseAddress: PVOID,
ClientId: CLIENT_ID,
AffinityMask: KAFFINITY,
Priority: KPRIORITY,
BasePriority: KPRIORITY,
};
};
pub const MEMORY = struct {
pub const BASIC_INFORMATION = extern struct {
BaseAddress: PVOID,
AllocationBase: PVOID,
AllocationProtect: DWORD,
PartitionId: WORD,
RegionSize: SIZE_T,
State: DWORD,
Protect: DWORD,
Type: DWORD,
};
};
// ref: km/ntifs.h
@@ -2560,48 +2810,6 @@ pub fn GetProcessHeap() ?*HEAP {
return peb().ProcessHeap;
}
// ref: um/winternl.h
pub const OBJECT_ATTRIBUTES = extern struct {
Length: ULONG = @sizeOf(OBJECT_ATTRIBUTES),
RootDirectory: ?HANDLE = null,
ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty),
Attributes: ATTRIBUTES = .{},
SecurityDescriptor: ?*anyopaque = null,
SecurityQualityOfService: ?*anyopaque = null,
// Valid values for the Attributes field
pub const ATTRIBUTES = packed struct(ULONG) {
Reserved0: u1 = 0,
INHERIT: bool = false,
Reserved2: u2 = 0,
PERMANENT: bool = false,
EXCLUSIVE: bool = false,
/// If name-lookup code should ignore the case of the ObjectName member rather than performing an exact-match search.
CASE_INSENSITIVE: bool = true,
OPENIF: bool = false,
OPENLINK: bool = false,
KERNEL_HANDLE: bool = false,
FORCE_ACCESS_CHECK: bool = false,
IGNORE_IMPERSONATED_DEVICEMAP: bool = false,
DONT_REPARSE: bool = false,
Reserved13: u19 = 0,
pub const VALID_ATTRIBUTES: ATTRIBUTES = .{
.INHERIT = true,
.PERMANENT = true,
.EXCLUSIVE = true,
.CASE_INSENSITIVE = true,
.OPENIF = true,
.OPENLINK = true,
.KERNEL_HANDLE = true,
.FORCE_ACCESS_CHECK = true,
.IGNORE_IMPERSONATED_DEVICEMAP = true,
.DONT_REPARSE = true,
};
};
};
// ref none
pub fn GetCurrentProcess() HANDLE {
@@ -2623,297 +2831,13 @@ pub fn GetCurrentThreadId() DWORD {
}
pub fn GetLastError() Win32Error {
return @enumFromInt(teb().LastErrorValue);
}
/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
/// It implements similar behavior to `DeviceIoControl` and is meant to serve
/// as a direct substitute for that call.
/// TODO work out if we need to expose other arguments to the underlying syscalls.
pub fn DeviceIoControl(
device: HANDLE,
io_control_code: CTL_CODE,
opts: struct {
event: ?HANDLE = null,
apc_routine: ?*const IO_APC_ROUTINE = null,
apc_context: ?*anyopaque = null,
io_status_block: ?*IO_STATUS_BLOCK = null,
in: []const u8 = &.{},
out: []u8 = &.{},
},
) NTSTATUS {
var io_status_block: IO_STATUS_BLOCK = undefined;
return switch (io_control_code.DeviceType) {
.FILE_SYSTEM, .NAMED_PIPE => ntdll.NtFsControlFile(
device,
opts.event,
opts.apc_routine,
opts.apc_context,
opts.io_status_block orelse &io_status_block,
io_control_code,
if (opts.in.len > 0) opts.in.ptr else null,
@intCast(opts.in.len),
if (opts.out.len > 0) opts.out.ptr else null,
@intCast(opts.out.len),
),
else => ntdll.NtDeviceIoControlFile(
device,
opts.event,
opts.apc_routine,
opts.apc_context,
opts.io_status_block orelse &io_status_block,
io_control_code,
if (opts.in.len > 0) opts.in.ptr else null,
@intCast(opts.in.len),
if (opts.out.len > 0) opts.out.ptr else null,
@intCast(opts.out.len),
),
};
}
pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
var bytes: DWORD = undefined;
if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @intFromBool(wait)) == 0) {
switch (GetLastError()) {
.IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable,
else => |err| return unexpectedError(err),
}
}
return bytes;
}
pub const CreateIoCompletionPortError = error{Unexpected};
pub fn CreateIoCompletionPort(
file_handle: HANDLE,
existing_completion_port: ?HANDLE,
completion_key: usize,
concurrent_thread_count: DWORD,
) CreateIoCompletionPortError!HANDLE {
const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
switch (GetLastError()) {
.INVALID_PARAMETER => unreachable,
else => |err| return unexpectedError(err),
}
};
return handle;
}
pub const PostQueuedCompletionStatusError = error{Unexpected};
pub fn PostQueuedCompletionStatus(
completion_port: HANDLE,
bytes_transferred_count: DWORD,
completion_key: usize,
lpOverlapped: ?*OVERLAPPED,
) PostQueuedCompletionStatusError!void {
if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub const GetQueuedCompletionStatusResult = enum {
Normal,
Aborted,
Canceled,
EOF,
Timeout,
};
pub fn GetQueuedCompletionStatus(
completion_port: HANDLE,
bytes_transferred_count: *DWORD,
lpCompletionKey: *usize,
lpOverlapped: *?*OVERLAPPED,
dwMilliseconds: DWORD,
) GetQueuedCompletionStatusResult {
if (kernel32.GetQueuedCompletionStatus(
completion_port,
bytes_transferred_count,
lpCompletionKey,
lpOverlapped,
dwMilliseconds,
) == FALSE) {
switch (GetLastError()) {
.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Canceled,
.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
.WAIT_TIMEOUT => return GetQueuedCompletionStatusResult.Timeout,
else => |err| {
if (std.debug.runtime_safety) {
@setEvalBranchQuota(2500);
std.debug.panic("unexpected error: {}\n", .{err});
}
},
}
}
return GetQueuedCompletionStatusResult.Normal;
}
pub const GetQueuedCompletionStatusError = error{
Aborted,
Canceled,
EOF,
Timeout,
} || UnexpectedError;
pub fn GetQueuedCompletionStatusEx(
completion_port: HANDLE,
completion_port_entries: []OVERLAPPED_ENTRY,
timeout_ms: ?DWORD,
alertable: bool,
) GetQueuedCompletionStatusError!u32 {
var num_entries_removed: u32 = 0;
const success = kernel32.GetQueuedCompletionStatusEx(
completion_port,
completion_port_entries.ptr,
@as(ULONG, @intCast(completion_port_entries.len)),
&num_entries_removed,
timeout_ms orelse INFINITE,
@intFromBool(alertable),
);
if (success == FALSE) {
return switch (GetLastError()) {
.ABANDONED_WAIT_0 => error.Aborted,
.OPERATION_ABORTED => error.Canceled,
.HANDLE_EOF => error.EOF,
.WAIT_TIMEOUT => error.Timeout,
else => |err| unexpectedError(err),
};
}
return num_entries_removed;
return teb().LastErrorValue;
}
pub fn CloseHandle(hObject: HANDLE) void {
assert(ntdll.NtClose(hObject) == .SUCCESS);
}
pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen)));
}
pub fn sendmsg(
s: ws2_32.SOCKET,
msg: *ws2_32.WSAMSG_const,
flags: u32,
) i32 {
var bytes_send: DWORD = undefined;
if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_send)));
}
}
pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = @constCast(buf) };
var bytes_send: DWORD = undefined;
if (ws2_32.WSASendTo(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_send, flags, to, @as(i32, @intCast(to_len)), null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_send)));
}
}
pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 {
var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = buf };
var bytes_received: DWORD = undefined;
var flags_inout = flags;
if (ws2_32.WSARecvFrom(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_received, &flags_inout, from, @as(?*i32, @ptrCast(from_len)), null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_received)));
}
}
pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 {
return ws2_32.WSAPoll(fds, n, timeout);
}
pub fn WSAIoctl(
s: ws2_32.SOCKET,
dwIoControlCode: DWORD,
inBuffer: ?[]const u8,
outBuffer: []u8,
overlapped: ?*OVERLAPPED,
completionRoutine: ?ws2_32.LPWSAOVERLAPPED_COMPLETION_ROUTINE,
) !DWORD {
var bytes: DWORD = undefined;
switch (ws2_32.WSAIoctl(
s,
dwIoControlCode,
if (inBuffer) |i| i.ptr else null,
if (inBuffer) |i| @as(DWORD, @intCast(i.len)) else 0,
outBuffer.ptr,
@as(DWORD, @intCast(outBuffer.len)),
&bytes,
overlapped,
completionRoutine,
)) {
0 => {},
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
else => |err| return unexpectedWSAError(err),
},
else => unreachable,
}
return bytes;
}
const GetModuleFileNameError = error{Unexpected};
pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 {
const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len);
if (rc == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return buf_ptr[0..rc :0];
}
pub const NtAllocateVirtualMemoryError = error{
AccessDenied,
InvalidParameter,
NoMemory,
Unexpected,
};
pub fn NtAllocateVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, zero_bits: ULONG_PTR, size: ?*SIZE_T, alloc_type: ULONG, protect: ULONG) NtAllocateVirtualMemoryError!void {
return switch (ntdll.NtAllocateVirtualMemory(hProcess, addr, zero_bits, size, alloc_type, protect)) {
.SUCCESS => return,
.ACCESS_DENIED => NtAllocateVirtualMemoryError.AccessDenied,
.INVALID_PARAMETER => NtAllocateVirtualMemoryError.InvalidParameter,
.NO_MEMORY => NtAllocateVirtualMemoryError.NoMemory,
else => |st| unexpectedStatus(st),
};
}
pub const NtFreeVirtualMemoryError = error{
AccessDenied,
InvalidParameter,
Unexpected,
};
pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_type: ULONG) NtFreeVirtualMemoryError!void {
// TODO: If the return value is .INVALID_PAGE_PROTECTION, call RtlFlushSecureMemoryCache and try again.
return switch (ntdll.NtFreeVirtualMemory(hProcess, addr, size, free_type)) {
.SUCCESS => return,
.ACCESS_DENIED => NtFreeVirtualMemoryError.AccessDenied,
.INVALID_PARAMETER => NtFreeVirtualMemoryError.InvalidParameter,
else => NtFreeVirtualMemoryError.Unexpected,
};
}
pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
if (success == FALSE) {
return switch (GetLastError()) {
else => |err| unexpectedError(err),
};
switch (ntdll.NtClose(hObject)) {
.SUCCESS => {},
else => |status| unexpectedStatus(status) catch {},
}
}
@@ -2963,114 +2887,75 @@ pub const CreateProcessFlags = packed struct(u32) {
create_ignore_system_default: bool = false,
};
pub const LoadLibraryError = error{
FileNotFound,
Unexpected,
};
pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE {
return kernel32.LoadLibraryW(lpLibFileName) orelse {
switch (GetLastError()) {
.FILE_NOT_FOUND => return error.FileNotFound,
.PATH_NOT_FOUND => return error.FileNotFound,
.MOD_NOT_FOUND => return error.FileNotFound,
else => |err| return unexpectedError(err),
}
};
}
pub const LoadLibraryFlags = enum(DWORD) {
none = 0,
dont_resolve_dll_references = 0x00000001,
load_ignore_code_authz_level = 0x00000010,
load_library_as_datafile = 0x00000002,
load_library_as_datafile_exclusive = 0x00000040,
load_library_as_image_resource = 0x00000020,
load_library_search_application_dir = 0x00000200,
load_library_search_default_dirs = 0x00001000,
load_library_search_dll_load_dir = 0x00000100,
load_library_search_system32 = 0x00000800,
load_library_search_user_dirs = 0x00000400,
load_with_altered_search_path = 0x00000008,
load_library_require_signed_target = 0x00000080,
load_library_safe_current_dirs = 0x00002000,
};
pub fn LoadLibraryExW(lpLibFileName: [*:0]const u16, dwFlags: LoadLibraryFlags) LoadLibraryError!HMODULE {
return kernel32.LoadLibraryExW(lpLibFileName, null, @intFromEnum(dwFlags)) orelse {
switch (GetLastError()) {
.FILE_NOT_FOUND => return error.FileNotFound,
.PATH_NOT_FOUND => return error.FileNotFound,
.MOD_NOT_FOUND => return error.FileNotFound,
else => |err| return unexpectedError(err),
}
};
}
pub fn FreeLibrary(hModule: HMODULE) void {
assert(kernel32.FreeLibrary(hModule) != 0);
}
pub fn QueryPerformanceFrequency() u64 {
// "On systems that run Windows XP or later, the function will always succeed"
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency
var result: LARGE_INTEGER = undefined;
assert(ntdll.RtlQueryPerformanceFrequency(&result) != 0);
// The kernel treats this integer as unsigned.
return @as(u64, @bitCast(result));
}
pub fn QueryPerformanceCounter() u64 {
// "On systems that run Windows XP or later, the function will always succeed"
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter
var result: LARGE_INTEGER = undefined;
assert(ntdll.RtlQueryPerformanceCounter(&result) != 0);
// The kernel treats this integer as unsigned.
return @as(u64, @bitCast(result));
}
/// This is a workaround for the C backend until zig has the ability to put
/// C code in inline assembly.
extern fn zig_thumb_windows_teb() callconv(.c) *anyopaque;
extern fn zig_aarch64_windows_teb() callconv(.c) *anyopaque;
extern fn zig_x86_windows_teb() callconv(.c) *anyopaque;
extern fn zig_x86_64_windows_teb() callconv(.c) *anyopaque;
pub fn teb() *TEB {
return switch (native_arch) {
.thumb => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_thumb_windows_teb()))
else
asm (
\\ mrc p15, 0, %[ptr], c13, c0, 2
: [ptr] "=r" (-> *TEB),
),
.aarch64 => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_aarch64_windows_teb()))
else
asm (
\\ mov %[ptr], x18
: [ptr] "=r" (-> *TEB),
),
.x86 => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_x86_windows_teb()))
else
asm (
if (builtin.zig_backend == .stage2_c) return @ptrCast(@alignCast(struct {
/// This is a workaround for the C backend until zig has the ability to put
/// C code in inline assembly.
extern fn zig_windows_teb() callconv(.c) *anyopaque;
}.zig_windows_teb()));
switch (native_arch) {
.thumb => return asm (
\\ mrc p15, 0, %[ptr], c13, c0, 2
: [ptr] "=r" (-> *TEB),
),
.aarch64 => return asm (
\\ mov %[ptr], x18
: [ptr] "=r" (-> *TEB),
),
.x86 => {
comptime assert(
@offsetOf(TEB, "NtTib") + @offsetOf(@FieldType(TEB, "NtTib"), "Self") == 0x18,
);
return asm (
\\ movl %%fs:0x18, %[ptr]
: [ptr] "=r" (-> *TEB),
),
.x86_64 => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_x86_64_windows_teb()))
else
asm (
);
},
.x86_64 => {
comptime assert(
@offsetOf(TEB, "NtTib") + @offsetOf(@FieldType(TEB, "NtTib"), "Self") == 0x30,
);
return asm (
\\ movq %%gs:0x30, %[ptr]
: [ptr] "=r" (-> *TEB),
),
);
},
else => @compileError("unsupported arch"),
};
}
}
pub fn peb() *PEB {
if (builtin.zig_backend == .stage2_c) switch (native_arch) {
.x86, .x86_64 => return @ptrCast(@alignCast(struct {
/// This is a workaround for the C backend until zig has the ability to put
/// C code in inline assembly.
extern fn zig_windows_peb() callconv(.c) *anyopaque;
}.zig_windows_peb())),
else => {},
} else switch (native_arch) {
.aarch64 => {
comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
return asm (
\\ ldr %[ptr], [x18, #0x60]
: [ptr] "=r" (-> *PEB),
);
},
.x86 => {
comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30);
return asm (
\\ movl %%fs:0x30, %[ptr]
: [ptr] "=r" (-> *PEB),
);
},
.x86_64 => {
comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
return asm (
\\ movq %%gs:0x60, %[ptr]
: [ptr] "=r" (-> *PEB),
);
},
else => {},
}
return teb().ProcessEnvironmentBlock;
}
@@ -3089,20 +2974,6 @@ pub fn toSysTime(ns: Io.Timestamp) i64 {
return @as(i64, @intCast(hns)) - std.time.epoch.windows * (std.time.ns_per_s / 100);
}
pub fn fileTimeToNanoSeconds(ft: FILETIME) Io.Timestamp {
const hns = (@as(i64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
return fromSysTime(hns);
}
/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
pub fn nanoSecondsToFileTime(ns: Io.Timestamp) FILETIME {
const adjusted: u64 = @bitCast(toSysTime(ns));
return .{
.dwHighDateTime = @as(u32, @truncate(adjusted >> 32)),
.dwLowDateTime = @as(u32, @truncate(adjusted)),
};
}
/// Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a
/// redundant copy of the uppercase data.
pub inline fn toUpperWtf16(c: u16) u16 {
@@ -3126,7 +2997,7 @@ pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool {
// endianness for the uppercasing
const a_c_native = std.mem.littleToNative(u16, a_c);
const b_c_native = std.mem.littleToNative(u16, b_c);
if (a_c != b_c and nls.upcaseW(a_c_native) != nls.upcaseW(b_c_native)) {
if (a_c != b_c and toUpperWtf16(a_c_native) != toUpperWtf16(b_c_native)) {
return false;
}
}
@@ -3134,19 +3005,7 @@ pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool {
}
// Use RtlEqualUnicodeString on Windows when not in comptime to avoid including a
// redundant copy of the uppercase data.
const a_bytes = @as(u16, @intCast(a.len * 2));
const a_string: UNICODE_STRING = .{
.Length = a_bytes,
.MaximumLength = a_bytes,
.Buffer = @constCast(a.ptr),
};
const b_bytes = @as(u16, @intCast(b.len * 2));
const b_string: UNICODE_STRING = .{
.Length = b_bytes,
.MaximumLength = b_bytes,
.Buffer = @constCast(b.ptr),
};
return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
return ntdll.RtlEqualUnicodeString(&.init(a), &.init(b), TRUE) == TRUE;
}
/// Compares two WTF-8 strings using the equivalent functionality of
@@ -3464,13 +3323,10 @@ pub const LONG = i32;
pub const ULONG64 = u64;
pub const ULONGLONG = u64;
pub const LONGLONG = i64;
pub const HLOCAL = HANDLE;
pub const LANGID = c_ushort;
pub const COLORREF = DWORD;
pub const WPARAM = usize;
pub const LPARAM = LONG_PTR;
pub const LRESULT = LONG_PTR;
pub const va_list = *opaque {};
@@ -3480,6 +3336,40 @@ pub const LPCTSTR = @compileError("Deprecated: choose between `LPCSTR` or `LPCWS
pub const PTSTR = @compileError("Deprecated: choose between `PSTR` or `PWSTR` directly instead.");
pub const PCTSTR = @compileError("Deprecated: choose between `PCSTR` or `PCWSTR` directly instead.");
fn STRING(comptime C: type) type {
return extern struct {
Length: USHORT,
MaximumLength: USHORT,
Buffer: ?[*]C,
pub const empty: @This() = .{ .Length = 0, .MaximumLength = 0, .Buffer = null };
pub fn init(string: []const C) @This() {
const len: USHORT = @intCast(@sizeOf(C) * string.len);
return .{
.Length = len,
.MaximumLength = len,
.Buffer = @constCast(string.ptr),
};
}
pub fn isEmpty(string: *const @This()) bool {
return string.Length == 0;
}
pub fn slice(string: *const @This()) []C {
return if (string.isEmpty()) &.{} else string.Buffer.?[0..@divExact(string.Length, @sizeOf(C))];
}
pub fn sliceZ(string: *const @This()) [:0]C {
assert(string.Length + @sizeOf(C) <= string.MaximumLength);
return string.Buffer.?[0..@divExact(string.Length, @sizeOf(C)) :0];
}
};
}
pub const ANSI_STRING = STRING(CHAR);
pub const UNICODE_STRING = STRING(WCHAR);
pub const TRUE = 1;
pub const FALSE = 0;
@@ -3509,111 +3399,14 @@ pub const OVERLAPPED = extern struct {
hEvent: ?HANDLE,
};
pub const OVERLAPPED_ENTRY = extern struct {
lpCompletionKey: ULONG_PTR,
lpOverlapped: *OVERLAPPED,
Internal: ULONG_PTR,
dwNumberOfBytesTransferred: DWORD,
};
pub const MAX_PATH = 260;
pub const FILE_INFO_BY_HANDLE_CLASS = enum(u32) {
FileBasicInfo = 0,
FileStandardInfo = 1,
FileNameInfo = 2,
FileRenameInfo = 3,
FileDispositionInfo = 4,
FileAllocationInfo = 5,
FileEndOfFileInfo = 6,
FileStreamInfo = 7,
FileCompressionInfo = 8,
FileAttributeTagInfo = 9,
FileIdBothDirectoryInfo = 10,
FileIdBothDirectoryRestartInfo = 11,
FileIoPriorityHintInfo = 12,
FileRemoteProtocolInfo = 13,
FileFullDirectoryInfo = 14,
FileFullDirectoryRestartInfo = 15,
FileStorageInfo = 16,
FileAlignmentInfo = 17,
FileIdInfo = 18,
FileIdExtdDirectoryInfo = 19,
FileIdExtdDirectoryRestartInfo = 20,
};
pub const BY_HANDLE_FILE_INFORMATION = extern struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
ftLastWriteTime: FILETIME,
dwVolumeSerialNumber: DWORD,
nFileSizeHigh: DWORD,
nFileSizeLow: DWORD,
nNumberOfLinks: DWORD,
nFileIndexHigh: DWORD,
nFileIndexLow: DWORD,
};
pub const FILE_NAME_INFO = extern struct {
FileNameLength: DWORD,
FileName: [1]WCHAR,
};
/// Return the normalized drive name. This is the default.
pub const FILE_NAME_NORMALIZED = 0x0;
/// Return the opened file name (not normalized).
pub const FILE_NAME_OPENED = 0x8;
/// Return the path with the drive letter. This is the default.
pub const VOLUME_NAME_DOS = 0x0;
/// Return the path with a volume GUID path instead of the drive name.
pub const VOLUME_NAME_GUID = 0x1;
/// Return the path with no drive information.
pub const VOLUME_NAME_NONE = 0x4;
/// Return the path with the volume device path.
pub const VOLUME_NAME_NT = 0x2;
pub const SECURITY_ATTRIBUTES = extern struct {
nLength: DWORD,
lpSecurityDescriptor: ?*anyopaque,
bInheritHandle: BOOL,
};
pub const PIPE_ACCESS_INBOUND = 0x00000001;
pub const PIPE_ACCESS_OUTBOUND = 0x00000002;
pub const PIPE_ACCESS_DUPLEX = 0x00000003;
pub const PIPE_TYPE_BYTE = 0x00000000;
pub const PIPE_TYPE_MESSAGE = 0x00000004;
pub const PIPE_READMODE_BYTE = 0x00000000;
pub const PIPE_READMODE_MESSAGE = 0x00000002;
pub const PIPE_WAIT = 0x00000000;
pub const PIPE_NOWAIT = 0x00000001;
pub const CREATE_ALWAYS = 2;
pub const CREATE_NEW = 1;
pub const OPEN_ALWAYS = 4;
pub const OPEN_EXISTING = 3;
pub const TRUNCATE_EXISTING = 5;
// flags for CreateEvent
pub const CREATE_EVENT_INITIAL_SET = 0x00000002;
pub const CREATE_EVENT_MANUAL_RESET = 0x00000001;
pub const PROCESS_INFORMATION = extern struct {
hProcess: HANDLE,
hThread: HANDLE,
dwProcessId: DWORD,
dwThreadId: DWORD,
};
pub const STARTUPINFOW = extern struct {
cb: DWORD,
lpReserved: ?LPWSTR,
@@ -3650,50 +3443,7 @@ pub const STARTF_USESHOWWINDOW = 0x00000001;
pub const STARTF_USESIZE = 0x00000002;
pub const STARTF_USESTDHANDLES = 0x00000100;
pub const INFINITE = 4294967295;
pub const MAXIMUM_WAIT_OBJECTS = 64;
pub const WAIT_ABANDONED = 0x00000080;
pub const WAIT_ABANDONED_0 = WAIT_ABANDONED + 0;
pub const WAIT_OBJECT_0 = 0x00000000;
pub const WAIT_TIMEOUT = 0x00000102;
pub const WAIT_FAILED = 0xFFFFFFFF;
pub const HANDLE_FLAG_INHERIT = 0x00000001;
pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
pub const MOVEFILE_COPY_ALLOWED = 2;
pub const MOVEFILE_CREATE_HARDLINK = 16;
pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4;
pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32;
pub const MOVEFILE_REPLACE_EXISTING = 1;
pub const MOVEFILE_WRITE_THROUGH = 8;
pub const FILE_BEGIN = 0;
pub const FILE_CURRENT = 1;
pub const FILE_END = 2;
pub const PTHREAD_START_ROUTINE = *const fn (LPVOID) callconv(.winapi) DWORD;
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
pub const WIN32_FIND_DATAW = extern struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
ftLastWriteTime: FILETIME,
nFileSizeHigh: DWORD,
nFileSizeLow: DWORD,
dwReserved0: DWORD,
dwReserved1: DWORD,
cFileName: [260]u16,
cAlternateFileName: [14]u16,
};
pub const FILETIME = extern struct {
dwLowDateTime: DWORD,
dwHighDateTime: DWORD,
};
pub const THREAD_START_ROUTINE = fn (LPVOID) callconv(.winapi) DWORD;
pub const SYSTEM_INFO = extern struct {
anon1: extern union {
@@ -3716,7 +3466,6 @@ pub const SYSTEM_INFO = extern struct {
pub const HRESULT = c_long;
pub const KNOWNFOLDERID = GUID;
pub const GUID = extern struct {
Data1: u32,
Data2: u16,
@@ -3771,75 +3520,11 @@ test GUID {
);
}
pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
pub const KF_FLAG_DEFAULT = 0;
pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
pub const KF_FLAG_CREATE = 32768;
pub const KF_FLAG_DONT_VERIFY = 16384;
pub const KF_FLAG_DONT_UNEXPAND = 8192;
pub const KF_FLAG_NO_ALIAS = 4096;
pub const KF_FLAG_INIT = 2048;
pub const KF_FLAG_DEFAULT_PATH = 1024;
pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
pub const KF_FLAG_SIMPLE_IDLIST = 256;
pub const KF_FLAG_ALIAS_ONLY = -2147483648;
pub const S_OK = 0;
pub const S_FALSE = 0x00000001;
pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001)));
pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002)));
pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003)));
pub const E_ABORT = @as(c_long, @bitCast(@as(c_ulong, 0x80004004)));
pub const E_FAIL = @as(c_long, @bitCast(@as(c_ulong, 0x80004005)));
pub const E_UNEXPECTED = @as(c_long, @bitCast(@as(c_ulong, 0x8000FFFF)));
pub const E_ACCESSDENIED = @as(c_long, @bitCast(@as(c_ulong, 0x80070005)));
pub const E_HANDLE = @as(c_long, @bitCast(@as(c_ulong, 0x80070006)));
pub const E_OUTOFMEMORY = @as(c_long, @bitCast(@as(c_ulong, 0x8007000E)));
pub const E_INVALIDARG = @as(c_long, @bitCast(@as(c_ulong, 0x80070057)));
pub fn HRESULT_CODE(hr: HRESULT) Win32Error {
return @enumFromInt(hr & 0xFFFF);
}
pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
pub const FILE_FLAG_OVERLAPPED = 0x40000000;
pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
pub const RECT = extern struct {
left: LONG,
top: LONG,
right: LONG,
bottom: LONG,
};
pub const SMALL_RECT = extern struct {
Left: SHORT,
Top: SHORT,
Right: SHORT,
Bottom: SHORT,
};
pub const POINT = extern struct {
x: LONG,
y: LONG,
};
pub const COORD = extern struct {
X: SHORT,
Y: SHORT,
};
pub const CREATE_UNICODE_ENVIRONMENT = 1024;
pub const TLS_OUT_OF_INDEXES = 4294967295;
pub const IMAGE_TLS_DIRECTORY = extern struct {
StartAddressOfRawData: usize,
@@ -3854,8 +3539,6 @@ pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
pub const PIMAGE_TLS_CALLBACK = ?*const fn (PVOID, DWORD, PVOID) callconv(.winapi) void;
pub const PROV_RSA_FULL = 1;
pub const REGSAM = ACCESS_MASK;
pub const LSTATUS = LONG;
@@ -3872,9 +3555,6 @@ pub const HKEY_CURRENT_CONFIG: HKEY = @ptrFromInt(0x80000005);
pub const HKEY_DYN_DATA: HKEY = @ptrFromInt(0x80000006);
pub const HKEY_CURRENT_USER_LOCAL_SETTINGS: HKEY = @ptrFromInt(0x80000007);
/// Open symbolic link.
pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
pub const RTL_QUERY_REGISTRY_TABLE = extern struct {
QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE,
Flags: ULONG,
@@ -3975,38 +3655,6 @@ pub const REG = struct {
pub const QWORD_LITTLE_ENDIAN: ULONG = 11;
};
pub const FILE_NOTIFY_INFORMATION = extern struct {
NextEntryOffset: DWORD,
Action: DWORD,
FileNameLength: DWORD,
// Flexible array member
// FileName: [1]WCHAR,
};
pub const FILE_ACTION_ADDED = 0x00000001;
pub const FILE_ACTION_REMOVED = 0x00000002;
pub const FILE_ACTION_MODIFIED = 0x00000003;
pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?*const fn (DWORD, DWORD, *OVERLAPPED) callconv(.winapi) void;
pub const FileNotifyChangeFilter = packed struct(DWORD) {
file_name: bool = false,
dir_name: bool = false,
attributes: bool = false,
size: bool = false,
last_write: bool = false,
last_access: bool = false,
creation: bool = false,
ea: bool = false,
security: bool = false,
stream_name: bool = false,
stream_size: bool = false,
stream_write: bool = false,
_pad: u20 = 0,
};
pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
@@ -4056,26 +3704,6 @@ pub const RTL_RUN_ONCE = extern struct {
pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
pub const COINIT = struct {
pub const APARTMENTTHREADED = 2;
pub const MULTITHREADED = 0;
pub const DISABLE_OLE1DDE = 4;
pub const SPEED_OVER_MEMORY = 8;
};
pub const MEMORY_BASIC_INFORMATION = extern struct {
BaseAddress: PVOID,
AllocationBase: PVOID,
AllocationProtect: DWORD,
PartitionId: WORD,
RegionSize: SIZE_T,
State: DWORD,
Protect: DWORD,
Type: DWORD,
};
pub const PMEMORY_BASIC_INFORMATION = *MEMORY_BASIC_INFORMATION;
/// > The maximum path of 32,767 characters is approximate, because the "\\?\"
/// > prefix may be expanded to a longer string by the system at run time, and
/// > this expansion applies to the total length.
@@ -4544,14 +4172,6 @@ pub const UNW_FLAG_EHANDLER = 0x1;
pub const UNW_FLAG_UHANDLER = 0x2;
pub const UNW_FLAG_CHAININFO = 0x4;
pub const UNICODE_STRING = extern struct {
Length: c_ushort,
MaximumLength: c_ushort,
Buffer: ?[*]WCHAR,
pub const empty: UNICODE_STRING = .{ .Length = 0, .MaximumLength = 0, .Buffer = null };
};
pub const ACTIVATION_CONTEXT_DATA = opaque {};
pub const ASSEMBLY_STORAGE_MAP = opaque {};
pub const FLS_CALLBACK_INFO = opaque {};
@@ -4564,15 +4184,6 @@ pub const CLIENT_ID = extern struct {
UniqueThread: HANDLE,
};
pub const THREAD_BASIC_INFORMATION = extern struct {
ExitStatus: NTSTATUS,
TebBaseAddress: PVOID,
ClientId: CLIENT_ID,
AffinityMask: KAFFINITY,
Priority: KPRIORITY,
BasePriority: KPRIORITY,
};
pub const TEB = extern struct {
NtTib: NT_TIB,
EnvironmentPointer: PVOID,
@@ -4580,7 +4191,7 @@ pub const TEB = extern struct {
ActiveRpcHandle: PVOID,
ThreadLocalStoragePointer: PVOID,
ProcessEnvironmentBlock: *PEB,
LastErrorValue: ULONG,
LastErrorValue: Win32Error,
Reserved2: [399 * @sizeOf(PVOID) - @sizeOf(ULONG)]u8,
Reserved3: [1952]u8,
TlsSlots: [64]PVOID,
@@ -4793,7 +4404,7 @@ pub const PEB = extern struct {
};
/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.
/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module.
/// It is essentially the head of three double-linked lists of `LDR.DATA_TABLE_ENTRY` structures which each represent one loaded module.
///
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm
@@ -4825,24 +4436,89 @@ pub const PEB_LDR_DATA = extern struct {
ShutdownThreadId: HANDLE,
};
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
/// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
/// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
pub const LDR_DATA_TABLE_ENTRY = extern struct {
InLoadOrderLinks: LIST_ENTRY,
InMemoryOrderLinks: LIST_ENTRY,
InInitializationOrderLinks: LIST_ENTRY,
DllBase: PVOID,
EntryPoint: PVOID,
SizeOfImage: ULONG,
FullDllName: UNICODE_STRING,
BaseDllName: UNICODE_STRING,
Reserved5: [3]PVOID,
DUMMYUNIONNAME: extern union {
CheckSum: ULONG,
Reserved6: PVOID,
},
TimeDateStamp: ULONG,
pub const LDR = struct {
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
/// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
/// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
pub const DATA_TABLE_ENTRY = extern struct {
InLoadOrderLinks: LIST_ENTRY,
InMemoryOrderLinks: LIST_ENTRY,
InInitializationOrderLinks: LIST_ENTRY,
DllBase: PVOID,
EntryPoint: PVOID,
SizeOfImage: ULONG,
FullDllName: UNICODE_STRING,
BaseDllName: UNICODE_STRING,
Reserved5: [3]PVOID,
DUMMYUNIONNAME: extern union {
CheckSum: ULONG,
Reserved6: PVOID,
},
TimeDateStamp: ULONG,
};
pub const DLL_NOTIFICATION = struct {
pub const REASON = enum(ULONG) { LOADED = 1, UNLOADED = 2 };
pub const DATA = extern union {
Loaded: LOADED,
Unloaded: UNLOADED,
pub const LOADED = extern struct {
Flags: REGISTER,
FullDllName: *const UNICODE_STRING,
BaseDllName: *const UNICODE_STRING,
DllBase: PVOID,
SizeOfImage: ULONG,
};
pub const UNLOADED = extern struct {
Flags: REGISTER,
FullDllName: *const UNICODE_STRING,
BaseDllName: *const UNICODE_STRING,
DllBase: PVOID,
SizeOfImage: ULONG,
};
};
pub const COOKIE = *opaque {};
pub const FUNCTION = fn (
NotificationReason: REASON,
NotificationData: *const DATA,
Context: ?PVOID,
) callconv(.winapi) void;
pub const REGISTER = packed struct(ULONG) {
Reserved0: u32 = 0,
};
};
pub const GET_DLL_HANDLE_EX = packed struct(ULONG) {
UNCHANGED_REFCOUNT: bool = false,
PIN: bool = false,
Reserved2: u30 = 0,
};
pub const GET_PROCEDURE_ADDRESS = packed struct(ULONG) {
DONT_RECORD_FORWARDER: bool = false,
Reserved1: u31 = 0,
};
pub const LOAD = packed struct(ULONG) {
DONT_RESOLVE_DLL_REFERENCES: bool = false,
LIBRARY_AS_DATAFILE: bool = false,
PACKAGED_LIBRARY: bool = false,
WITH_ALTERED_SEARCH_PATH: bool = false,
IGNORE_CODE_AUTHZ_LEVEL: bool = false,
LIBRARY_AS_IMAGE_RESOURCE: bool = false,
LIBRARY_AS_DATAFILE_EXCLUSIVE: bool = false,
LIBRARY_REQUERE_SIGNED_TARGET: bool = false,
LIBRARY_SEARCH_DLL_LOAD_DIR: bool = false,
LIBRARY_SEARCH_USER_DIRS: bool = false,
LIBRARY_SEARCH_SYSTEM32: bool = false,
LIBRARY_SEARCH_DEFAULT_DIRS: bool = false,
};
};
pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
@@ -4956,86 +4632,6 @@ pub const MODULEINFO = extern struct {
EntryPoint: LPVOID,
};
pub const PSAPI_WS_WATCH_INFORMATION = extern struct {
FaultingPc: LPVOID,
FaultingVa: LPVOID,
};
pub const VM_COUNTERS = extern struct {
PeakVirtualSize: SIZE_T,
VirtualSize: SIZE_T,
PageFaultCount: ULONG,
PeakWorkingSetSize: SIZE_T,
WorkingSetSize: SIZE_T,
QuotaPeakPagedPoolUsage: SIZE_T,
QuotaPagedPoolUsage: SIZE_T,
QuotaPeakNonPagedPoolUsage: SIZE_T,
QuotaNonPagedPoolUsage: SIZE_T,
PagefileUsage: SIZE_T,
PeakPagefileUsage: SIZE_T,
};
pub const PROCESS_MEMORY_COUNTERS = extern struct {
cb: DWORD,
PageFaultCount: DWORD,
PeakWorkingSetSize: SIZE_T,
WorkingSetSize: SIZE_T,
QuotaPeakPagedPoolUsage: SIZE_T,
QuotaPagedPoolUsage: SIZE_T,
QuotaPeakNonPagedPoolUsage: SIZE_T,
QuotaNonPagedPoolUsage: SIZE_T,
PagefileUsage: SIZE_T,
PeakPagefileUsage: SIZE_T,
};
pub const PROCESS_MEMORY_COUNTERS_EX = extern struct {
cb: DWORD,
PageFaultCount: DWORD,
PeakWorkingSetSize: SIZE_T,
WorkingSetSize: SIZE_T,
QuotaPeakPagedPoolUsage: SIZE_T,
QuotaPagedPoolUsage: SIZE_T,
QuotaPeakNonPagedPoolUsage: SIZE_T,
QuotaNonPagedPoolUsage: SIZE_T,
PagefileUsage: SIZE_T,
PeakPagefileUsage: SIZE_T,
PrivateUsage: SIZE_T,
};
pub const PERFORMANCE_INFORMATION = extern struct {
cb: DWORD,
CommitTotal: SIZE_T,
CommitLimit: SIZE_T,
CommitPeak: SIZE_T,
PhysicalTotal: SIZE_T,
PhysicalAvailable: SIZE_T,
SystemCache: SIZE_T,
KernelTotal: SIZE_T,
KernelPaged: SIZE_T,
KernelNonpaged: SIZE_T,
PageSize: SIZE_T,
HandleCount: DWORD,
ProcessCount: DWORD,
ThreadCount: DWORD,
};
pub const ENUM_PAGE_FILE_INFORMATION = extern struct {
cb: DWORD,
Reserved: DWORD,
TotalSize: SIZE_T,
TotalInUse: SIZE_T,
PeakUsage: SIZE_T,
};
pub const PENUM_PAGE_FILE_CALLBACKW = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.winapi) BOOL;
pub const PENUM_PAGE_FILE_CALLBACKA = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.winapi) BOOL;
pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct {
BasicInfo: PSAPI_WS_WATCH_INFORMATION,
FaultingThreadId: ULONG_PTR,
Flags: ULONG_PTR,
};
pub const OSVERSIONINFOW = extern struct {
dwOSVersionInfoSize: ULONG,
dwMajorVersion: ULONG,
@@ -5098,20 +4694,6 @@ pub const MOUNTMGR_VOLUME_PATHS = extern struct {
MultiSz: [1]WCHAR,
};
pub const OBJECT_INFORMATION_CLASS = enum(c_int) {
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectTypesInformation = 3,
ObjectHandleFlagInformation = 4,
ObjectSessionInformation = 5,
MaxObjectInfoClass,
};
pub const OBJECT_NAME_INFORMATION = extern struct {
Name: UNICODE_STRING,
};
pub const SRWLOCK_INIT = SRWLOCK{};
pub const SRWLOCK = extern struct {
Ptr: ?PVOID = null,
@@ -5122,17 +4704,6 @@ pub const CONDITION_VARIABLE = extern struct {
Ptr: ?PVOID = null,
};
pub const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1;
pub const FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2;
pub const CTRL_C_EVENT: DWORD = 0;
pub const CTRL_BREAK_EVENT: DWORD = 1;
pub const CTRL_CLOSE_EVENT: DWORD = 2;
pub const CTRL_LOGOFF_EVENT: DWORD = 5;
pub const CTRL_SHUTDOWN_EVENT: DWORD = 6;
pub const HANDLER_ROUTINE = *const fn (dwCtrlType: DWORD) callconv(.winapi) BOOL;
/// Processor feature enumeration.
pub const PF = enum(DWORD) {
/// On a Pentium, a floating-point precision error can occur in rare circumstances.
@@ -5431,72 +5002,13 @@ pub const KUSER_SHARED_DATA = extern struct {
/// Read-only user-mode address for the shared data.
/// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
/// https://msrc-blog.microsoft.com/2022/04/05/randomizing-the-kuser_shared_data-structure-on-windows/
pub const SharedUserData: *const KUSER_SHARED_DATA = @as(*const KUSER_SHARED_DATA, @ptrFromInt(0x7FFE0000));
pub const SharedUserData: *const KUSER_SHARED_DATA = @ptrFromInt(0x7FFE0000);
pub fn IsProcessorFeaturePresent(feature: PF) bool {
if (@intFromEnum(feature) >= PROCESSOR_FEATURE_MAX) return false;
return SharedUserData.ProcessorFeatures[@intFromEnum(feature)] == 1;
}
pub const TH32CS_SNAPHEAPLIST = 0x00000001;
pub const TH32CS_SNAPPROCESS = 0x00000002;
pub const TH32CS_SNAPTHREAD = 0x00000004;
pub const TH32CS_SNAPMODULE = 0x00000008;
pub const TH32CS_SNAPMODULE32 = 0x00000010;
pub const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE;
pub const TH32CS_INHERIT = 0x80000000;
pub const MAX_MODULE_NAME32 = 255;
pub const MODULEENTRY32 = extern struct {
dwSize: DWORD,
th32ModuleID: DWORD,
th32ProcessID: DWORD,
GlblcntUsage: DWORD,
ProccntUsage: DWORD,
modBaseAddr: *BYTE,
modBaseSize: DWORD,
hModule: HMODULE,
szModule: [MAX_MODULE_NAME32 + 1]CHAR,
szExePath: [MAX_PATH]CHAR,
};
pub const SYSTEM_INFORMATION_CLASS = enum(c_int) {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45,
SystemCodeIntegrityInformation = 103,
SystemPolicyInformation = 134,
};
pub const SYSTEM_BASIC_INFORMATION = extern struct {
Reserved: ULONG,
TimerResolution: ULONG,
PageSize: ULONG,
NumberOfPhysicalPages: ULONG,
LowestPhysicalPageNumber: ULONG,
HighestPhysicalPageNumber: ULONG,
AllocationGranularity: ULONG,
MinimumUserModeAddress: ULONG_PTR,
MaximumUserModeAddress: ULONG_PTR,
ActiveProcessorsAffinityMask: KAFFINITY,
NumberOfProcessors: UCHAR,
};
pub const PROCESS_BASIC_INFORMATION = extern struct {
ExitStatus: NTSTATUS,
PebBaseAddress: *PEB,
AffinityMask: ULONG_PTR,
BasePriority: KPRIORITY,
UniqueProcessId: ULONG_PTR,
InheritedFromUniqueProcessId: ULONG_PTR,
};
// https://github.com/reactos/reactos/blob/master/sdk/include/ndk/pstypes.h#L977-L983
pub const KERNEL_USER_TIMES = extern struct {
CreationTime: LARGE_INTEGER,
@@ -5505,75 +5017,6 @@ pub const KERNEL_USER_TIMES = extern struct {
UserTime: LARGE_INTEGER,
};
pub const ReadMemoryError = error{
Unexpected,
};
pub fn ReadProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []u8) ReadMemoryError![]u8 {
var nread: usize = 0;
switch (ntdll.NtReadVirtualMemory(
handle,
addr,
buffer.ptr,
buffer.len,
&nread,
)) {
.SUCCESS => return buffer[0..nread],
// TODO: map errors
else => |rc| return unexpectedStatus(rc),
}
}
pub const WriteMemoryError = error{
Unexpected,
};
pub fn WriteProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []const u8) WriteMemoryError!usize {
var nwritten: usize = 0;
switch (ntdll.NtWriteVirtualMemory(
handle,
addr,
buffer.ptr,
buffer.len,
&nwritten,
)) {
.SUCCESS => return nwritten,
// TODO: map errors
else => |rc| return unexpectedStatus(rc),
}
}
pub const ProcessBaseAddressError = error{
AccessDenied,
InvalidHandle,
Unexpected,
} || ReadMemoryError;
/// Returns the base address of the process loaded into memory.
pub fn ProcessBaseAddress(handle: HANDLE) ProcessBaseAddressError!HMODULE {
var info: PROCESS_BASIC_INFORMATION = undefined;
var nread: DWORD = 0;
const rc = ntdll.NtQueryInformationProcess(
handle,
.BasicInformation,
&info,
@sizeOf(PROCESS_BASIC_INFORMATION),
&nread,
);
switch (rc) {
.SUCCESS => {},
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_HANDLE => return error.InvalidHandle,
.INVALID_PARAMETER => unreachable,
else => return unexpectedStatus(rc),
}
var peb_buf: [@sizeOf(PEB)]u8 align(@alignOf(PEB)) = undefined;
const peb_out = try ReadProcessMemory(handle, info.PebBaseAddress, &peb_buf);
const ppeb: *const PEB = @ptrCast(@alignCast(peb_out.ptr));
return ppeb.ImageBaseAddress;
}
pub fn wtf8ToWtf16Le(wtf16le: []u16, wtf8: []const u8) error{ BadPathName, NameTooLong }!usize {
// Each u8 in UTF-8/WTF-8 correlates to at most one u16 in UTF-16LE/WTF-16LE.
if (wtf16le.len < wtf8.len) {
+4 -197
View File
@@ -1,154 +1,29 @@
const std = @import("../../std.zig");
const windows = std.os.windows;
const ACCESS_MASK = windows.ACCESS_MASK;
const BOOL = windows.BOOL;
const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
const COORD = windows.COORD;
const DWORD = windows.DWORD;
const FARPROC = windows.FARPROC;
const FILETIME = windows.FILETIME;
const HANDLE = windows.HANDLE;
const HANDLER_ROUTINE = windows.HANDLER_ROUTINE;
const HMODULE = windows.HMODULE;
const LARGE_INTEGER = windows.LARGE_INTEGER;
const LPCSTR = windows.LPCSTR;
const LPCVOID = windows.LPCVOID;
const LPCWSTR = windows.LPCWSTR;
const LPTHREAD_START_ROUTINE = windows.LPTHREAD_START_ROUTINE;
const LPVOID = windows.LPVOID;
const LPWSTR = windows.LPWSTR;
const MODULEENTRY32 = windows.MODULEENTRY32;
const OVERLAPPED = windows.OVERLAPPED;
const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY;
const PROCESS_INFORMATION = windows.PROCESS_INFORMATION;
const PROCESS = windows.PROCESS;
const THREAD_START_ROUTINE = windows.THREAD_START_ROUTINE;
const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;
const SIZE_T = windows.SIZE_T;
const SRWLOCK = windows.SRWLOCK;
const STARTUPINFOW = windows.STARTUPINFOW;
const SYSTEM_INFO = windows.SYSTEM_INFO;
const UCHAR = windows.UCHAR;
const UINT = windows.UINT;
const ULONG = windows.ULONG;
const ULONG_PTR = windows.ULONG_PTR;
const va_list = windows.va_list;
const WCHAR = windows.WCHAR;
const Win32Error = windows.Win32Error;
const WORD = windows.WORD;
// I/O - Filesystem
pub extern "kernel32" fn ReadDirectoryChangesW(
hDirectory: HANDLE,
lpBuffer: [*]align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) u8,
nBufferLength: DWORD,
bWatchSubtree: BOOL,
dwNotifyFilter: windows.FileNotifyChangeFilter,
lpBytesReturned: ?*DWORD,
lpOverlapped: ?*OVERLAPPED,
lpCompletionRoutine: windows.LPOVERLAPPED_COMPLETION_ROUTINE,
) callconv(.winapi) BOOL;
// TODO: Wrapper around NtCancelIoFile.
pub extern "kernel32" fn CancelIo(
hFile: HANDLE,
) callconv(.winapi) BOOL;
// TODO: Wrapper around NtSetInformationFile + `FILE_POSITION_INFORMATION`.
// `FILE_STANDARD_INFORMATION` is also used if dwMoveMethod is `FILE_END`
pub extern "kernel32" fn SetFilePointerEx(
hFile: HANDLE,
liDistanceToMove: LARGE_INTEGER,
lpNewFilePointer: ?*LARGE_INTEGER,
dwMoveMethod: DWORD,
) callconv(.winapi) BOOL;
// TODO: Wrapper around NtSetInformationFile + `FILE_BASIC_INFORMATION`
pub extern "kernel32" fn SetFileTime(
hFile: HANDLE,
lpCreationTime: ?*const FILETIME,
lpLastAccessTime: ?*const FILETIME,
lpLastWriteTime: ?*const FILETIME,
) callconv(.winapi) BOOL;
pub extern "kernel32" fn WriteFile(
in_hFile: HANDLE,
in_lpBuffer: [*]const u8,
in_nNumberOfBytesToWrite: DWORD,
out_lpNumberOfBytesWritten: ?*DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) callconv(.winapi) BOOL;
// TODO: Wrapper around NtSetInformationFile + `FILE_IO_COMPLETION_NOTIFICATION_INFORMATION`.
pub extern "kernel32" fn SetFileCompletionNotificationModes(
FileHandle: HANDLE,
Flags: UCHAR,
) callconv(.winapi) BOOL;
pub extern "kernel32" fn ReadFile(
hFile: HANDLE,
lpBuffer: LPVOID,
nNumberOfBytesToRead: DWORD,
lpNumberOfBytesRead: ?*DWORD,
lpOverlapped: ?*OVERLAPPED,
) callconv(.winapi) BOOL;
pub extern "kernel32" fn GetSystemDirectoryW(
lpBuffer: LPWSTR,
uSize: UINT,
) callconv(.winapi) UINT;
// I/O - Kernel Objects
// TODO: Wrapper around NtRemoveIoCompletion.
pub extern "kernel32" fn GetQueuedCompletionStatus(
CompletionPort: HANDLE,
lpNumberOfBytesTransferred: *DWORD,
lpCompletionKey: *ULONG_PTR,
lpOverlapped: *?*OVERLAPPED,
dwMilliseconds: DWORD,
) callconv(.winapi) BOOL;
// TODO: Wrapper around NtRemoveIoCompletionEx.
pub extern "kernel32" fn GetQueuedCompletionStatusEx(
CompletionPort: HANDLE,
lpCompletionPortEntries: [*]OVERLAPPED_ENTRY,
ulCount: ULONG,
ulNumEntriesRemoved: *ULONG,
dwMilliseconds: DWORD,
fAlertable: BOOL,
) callconv(.winapi) BOOL;
// TODO: Wrapper around NtSetIoCompletion with `IoStatus = .SUCCESS`.
pub extern "kernel32" fn PostQueuedCompletionStatus(
CompletionPort: HANDLE,
dwNumberOfBytesTransferred: DWORD,
dwCompletionKey: ULONG_PTR,
lpOverlapped: ?*OVERLAPPED,
) callconv(.winapi) BOOL;
pub extern "kernel32" fn GetOverlappedResult(
hFile: HANDLE,
lpOverlapped: *OVERLAPPED,
lpNumberOfBytesTransferred: *DWORD,
bWait: BOOL,
) callconv(.winapi) BOOL;
// TODO: Wrapper around NtCreateIoCompletion + NtSetInformationFile with FILE_COMPLETION_INFORMATION.
// This would be better splitting into two functions.
pub extern "kernel32" fn CreateIoCompletionPort(
FileHandle: HANDLE,
ExistingCompletionPort: ?HANDLE,
CompletionKey: ULONG_PTR,
NumberOfConcurrentThreads: DWORD,
) callconv(.winapi) ?HANDLE;
// TODO: Wrapper around RtlReportSilentProcessExit + NtTerminateProcess.
pub extern "kernel32" fn TerminateProcess(
hProcess: HANDLE,
uExitCode: UINT,
) callconv(.winapi) BOOL;
// Process Management
pub extern "kernel32" fn CreateProcessW(
@@ -161,86 +36,21 @@ pub extern "kernel32" fn CreateProcessW(
lpEnvironment: ?[*:0]const u16,
lpCurrentDirectory: ?LPCWSTR,
lpStartupInfo: *STARTUPINFOW,
lpProcessInformation: *PROCESS_INFORMATION,
lpProcessInformation: *PROCESS.INFORMATION,
) callconv(.winapi) BOOL;
// TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`.
pub extern "kernel32" fn GetExitCodeProcess(
hProcess: HANDLE,
lpExitCode: *DWORD,
) callconv(.winapi) BOOL;
pub extern "kernel32" fn CreateToolhelp32Snapshot(
dwFlags: DWORD,
th32ProcessID: DWORD,
) callconv(.winapi) HANDLE;
// Threading
// TODO: Already a wrapper for this, see `windows.GetCurrentThreadId`.
pub extern "kernel32" fn GetCurrentThreadId() callconv(.winapi) DWORD;
// TODO: CreateRemoteThread with hProcess=NtCurrentProcess().
pub extern "kernel32" fn CreateThread(
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
dwStackSize: SIZE_T,
lpStartAddress: LPTHREAD_START_ROUTINE,
lpStartAddress: *const THREAD_START_ROUTINE,
lpParameter: ?LPVOID,
dwCreationFlags: DWORD,
lpThreadId: ?*DWORD,
) callconv(.winapi) ?HANDLE;
// Code Libraries/Modules
// TODO: Wrapper around LdrGetDllFullName.
pub extern "kernel32" fn GetModuleFileNameW(
hModule: ?HMODULE,
lpFilename: [*]WCHAR,
nSize: DWORD,
) callconv(.winapi) DWORD;
extern "kernel32" fn K32GetModuleFileNameExW(
hProcess: HANDLE,
hModule: ?HMODULE,
lpFilename: LPWSTR,
nSize: DWORD,
) callconv(.winapi) DWORD;
pub const GetModuleFileNameExW = K32GetModuleFileNameExW;
// TODO: Wrapper around ntdll.LdrGetDllHandle, which is a wrapper around LdrGetDllHandleEx
pub extern "kernel32" fn GetModuleHandleW(
lpModuleName: ?LPCWSTR,
) callconv(.winapi) ?HMODULE;
pub extern "kernel32" fn Module32First(
hSnapshot: HANDLE,
lpme: *MODULEENTRY32,
) callconv(.winapi) BOOL;
pub extern "kernel32" fn Module32Next(
hSnapshot: HANDLE,
lpme: *MODULEENTRY32,
) callconv(.winapi) BOOL;
pub extern "kernel32" fn LoadLibraryW(
lpLibFileName: LPCWSTR,
) callconv(.winapi) ?HMODULE;
pub extern "kernel32" fn LoadLibraryExW(
lpLibFileName: LPCWSTR,
hFile: ?HANDLE,
dwFlags: DWORD,
) callconv(.winapi) ?HMODULE;
pub extern "kernel32" fn GetProcAddress(
hModule: HMODULE,
lpProcName: LPCSTR,
) callconv(.winapi) ?FARPROC;
pub extern "kernel32" fn FreeLibrary(
hModule: HMODULE,
) callconv(.winapi) BOOL;
// Error Management
pub extern "kernel32" fn FormatMessageW(
@@ -252,6 +62,3 @@ pub extern "kernel32" fn FormatMessageW(
nSize: DWORD,
Arguments: ?*va_list,
) callconv(.winapi) DWORD;
// TODO: Getter for teb().LastErrorValue.
pub extern "kernel32" fn GetLastError() callconv(.winapi) Win32Error;
+175 -72
View File
@@ -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;
+1 -1
View File
@@ -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.
+4 -4
View File
@@ -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) {
+1 -1
View File
@@ -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 => {},
};
};
+5 -9
View File
@@ -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;
+5 -5
View File
@@ -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]);
+24 -4
View File
@@ -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)
+24 -4
View File
@@ -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)
+3 -4
View File
@@ -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", .{});
+12 -6
View File
@@ -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;
}
+3 -3
View File
@@ -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);