authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-02-06 04:36:32-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-02-07 00:02:50-05:00
logb5bd4946067d3796855e0b4b12219dfc28e15e8f
tree5fc2b5e02c308f0b9123c24e8c7c88925899d180
parent12cb5b92851f601c44d48deadea9934146edcffa

std.Threaded: replace more kernel32 functions with ntdll


27 files changed, 1674 insertions(+), 2241 deletions(-)

ci/x86_64-windows-debug.ps1+15-16
......@@ -35,7 +35,7 @@ Set-Location -Path 'build-debug'
3535
3636# CMake gives a syntax error when file paths with backward slashes are used.
3737# Here, we use forward slashes only to work around this.
38& cmake .. `
38cmake .. `
3939 -GNinja `
4040 -DCMAKE_INSTALL_PREFIX="stage3-debug" `
4141 -DCMAKE_PREFIX_PATH="$($PREFIX_PATH -Replace "\\", "/")" `
......@@ -54,7 +54,7 @@ ninja install
5454CheckLastExitCode
5555
5656Write-Output "Main test suite..."
57& "stage3-debug\bin\zig.exe" build test docs `
57stage3-debug\bin\zig build test docs `
5858 --maxrss $ZSF_MAX_RSS `
5959 --zig-lib-dir "$ZIG_LIB_DIR" `
6060 --search-prefix "$PREFIX_PATH" `
......@@ -66,26 +66,25 @@ Write-Output "Main test suite..."
6666CheckLastExitCode
6767
6868Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
69& "stage3-debug\bin\zig.exe" test `
70 ..\test\behavior.zig `
69stage3-debug\bin\zig build-obj `
7170 --zig-lib-dir "$ZIG_LIB_DIR" `
7271 -ofmt=c `
73 -femit-bin="test-x86_64-windows-msvc.c" `
74 --test-no-exec `
72 -OReleaseSmall `
73 --name compiler_rt `
74 -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
7575 -target x86_64-windows-msvc `
76 -lc
76 -lc `
77 ..\lib\compiler_rt.zig
7778CheckLastExitCode
7879
79& "stage3-debug\bin\zig.exe" build-obj `
80stage3-debug\bin\zig test `
8081 --zig-lib-dir "$ZIG_LIB_DIR" `
8182 -ofmt=c `
82 -OReleaseSmall `
83 --name compiler_rt `
84 -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
85 --dep build_options `
83 -femit-bin="behavior-x86_64-windows-msvc.c" `
84 --test-no-exec `
8685 -target x86_64-windows-msvc `
87 -Mroot="..\lib\compiler_rt.zig" `
88 -Mbuild_options="config.zig"
86 -lc `
87 ..\test\behavior.zig
8988CheckLastExitCode
9089
9190Import-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\
9796CheckLastExitCode
9897
9998Write-Output "Build and run behavior tests with msvc..."
100& 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
99cl /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
101100CheckLastExitCode
102101
103& .\test-x86_64-windows-msvc.exe
102.\behavior-x86_64-windows-msvc
104103CheckLastExitCode
ci/x86_64-windows-release.ps1+16-17
......@@ -35,7 +35,7 @@ Set-Location -Path 'build-release'
3535
3636# CMake gives a syntax error when file paths with backward slashes are used.
3737# Here, we use forward slashes only to work around this.
38& cmake .. `
38cmake .. `
3939 -GNinja `
4040 -DCMAKE_INSTALL_PREFIX="stage3-release" `
4141 -DCMAKE_PREFIX_PATH="$($PREFIX_PATH -Replace "\\", "/")" `
......@@ -54,7 +54,7 @@ ninja install
5454CheckLastExitCode
5555
5656Write-Output "Main test suite..."
57& "stage3-release\bin\zig.exe" build test docs `
57stage3-release\bin\zig.exe build test docs `
5858 --maxrss $ZSF_MAX_RSS `
5959 --zig-lib-dir "$ZIG_LIB_DIR" `
6060 --search-prefix "$PREFIX_PATH" `
......@@ -67,7 +67,7 @@ CheckLastExitCode
6767
6868# Ensure that stage3 and stage4 are byte-for-byte identical.
6969Write-Output "Build and compare stage4..."
70& "stage3-release\bin\zig.exe" build `
70stage3-release\bin\zig.exe build `
7171 --prefix stage4-release `
7272 -Denable-llvm `
7373 -Dno-lib `
......@@ -85,26 +85,25 @@ Compare-Object (Get-Content stage3-release\bin\zig.exe) (Get-Content stage4-rele
8585CheckLastExitCode
8686
8787Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
88& "stage3-release\bin\zig.exe" test `
89 ..\test\behavior.zig `
88stage3-release\bin\zig.exe build-obj `
9089 --zig-lib-dir "$ZIG_LIB_DIR" `
9190 -ofmt=c `
92 -femit-bin="test-x86_64-windows-msvc.c" `
93 --test-no-exec `
91 -OReleaseSmall `
92 --name compiler_rt `
93 -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
9494 -target x86_64-windows-msvc `
95 -lc
95 -lc `
96 ..\lib\compiler_rt.zig
9697CheckLastExitCode
9798
98& "stage3-release\bin\zig.exe" build-obj `
99stage3-release\bin\zig.exe test `
99100 --zig-lib-dir "$ZIG_LIB_DIR" `
100101 -ofmt=c `
101 -OReleaseSmall `
102 --name compiler_rt `
103 -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
104 --dep build_options `
102 -femit-bin="behavior-x86_64-windows-msvc.c" `
103 --test-no-exec `
105104 -target x86_64-windows-msvc `
106 -Mroot="..\lib\compiler_rt.zig" `
107 -Mbuild_options="config.zig"
105 -lc `
106 ..\test\behavior.zig
108107CheckLastExitCode
109108
110109Import-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\
116115CheckLastExitCode
117116
118117Write-Output "Build and run behavior tests with msvc..."
119& 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
118cl /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
120119CheckLastExitCode
121120
122& .\test-x86_64-windows-msvc.exe
121.\behavior-x86_64-windows-msvc
123122CheckLastExitCode
lib/compiler/build_runner.zig+1-1
......@@ -621,7 +621,7 @@ pub fn main(init: process.Init.Minimal) !void {
621621 }) catch &caption_buf;
622622 var debouncing_node = main_progress_node.start(caption, 0);
623623 var in_debounce = false;
624 while (true) switch (try w.wait(gpa, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) {
624 while (true) switch (try w.wait(gpa, io, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) {
625625 .timeout => {
626626 assert(in_debounce);
627627 debouncing_node.end();
lib/fuzzer.zig+1-1
......@@ -632,7 +632,7 @@ export fn fuzzer_main(limit_kind: abi.LimitKind, amount: u64) void {
632632
633633export fn fuzzer_unslide_address(addr: usize) usize {
634634 const si = std.debug.getSelfDebugInfo() catch @compileError("unsupported");
635 const slide = si.getModuleSlide(std.debug.getDebugInfoAllocator(), io, addr) catch |err| {
635 const slide = si.getModuleSlide(io, addr) catch |err| {
636636 std.debug.panic("failed to find virtual address slide: {t}", .{err});
637637 };
638638 return addr - slide;
lib/std/Build/Watch.zig+145-133
......@@ -180,7 +180,7 @@ const Os = switch (builtin.os.tag) {
180180 const gop = try w.dir_table.getOrPut(gpa, path);
181181 if (!gop.found_existing) {
182182 var mount_id: MountId = undefined;
183 const dir_handle = Os.getDirHandle(gpa, path, &mount_id) catch |err| switch (err) {
183 const dir_handle = getDirHandle(gpa, path, &mount_id) catch |err| switch (err) {
184184 error.FileNotFound => {
185185 std.debug.assert(w.dir_table.swapRemove(path));
186186 continue;
......@@ -291,12 +291,13 @@ const Os = switch (builtin.os.tag) {
291291 w.dir_count = w.dir_table.count();
292292 }
293293
294 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
294 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
295 _ = io;
295296 const events_len = try std.posix.poll(w.os.poll_fds.values(), timeout.to_i32_ms());
296297 if (events_len == 0)
297298 return .timeout;
298299 for (w.os.poll_fds.values()) |poll_fd| {
299 if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try Os.markDirtySteps(w, gpa, poll_fd.fd))
300 if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try markDirtySteps(w, gpa, poll_fd.fd))
300301 return .dirty;
301302 }
302303 return .clean;
......@@ -306,12 +307,8 @@ const Os = switch (builtin.os.tag) {
306307 const windows = std.os.windows;
307308
308309 /// Keyed differently but indexes correspond 1:1 with `dir_table`.
309 handle_table: HandleTable,
310 dir_list: std.AutoArrayHashMapUnmanaged(usize, *Directory),
311 io_cp: ?windows.HANDLE,
312 counter: usize = 0,
313
314 const HandleTable = std.AutoArrayHashMapUnmanaged(FileId, ReactionSet);
310 handle_table: std.ArrayHashMapUnmanaged(*Directory, void, Directory.TableAdapter, false),
311 ready_dirs: std.DoublyLinkedList,
315312
316313 const FileId = struct {
317314 volumeSerialNumber: windows.ULONG,
......@@ -319,55 +316,61 @@ const Os = switch (builtin.os.tag) {
319316 };
320317
321318 const Directory = struct {
322 handle: windows.HANDLE,
319 reaction_set: ReactionSet,
323320 id: FileId,
324 overlapped: windows.OVERLAPPED,
321 file: Io.File,
322 state: enum { idle, listening, ready },
323 iosb: windows.IO_STATUS_BLOCK,
325324 // 64 KB is the packet size limit when monitoring over a network.
326325 // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw#remarks
327 buffer: [64 * 1024]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined,
326 buffer: [64 * 1024]u8 align(@alignOf(windows.FILE.NOTIFY.INFORMATION)),
327 ready_node: std.DoublyLinkedList.Node,
328328
329329 /// Start listening for events, buffer field will be overwritten eventually.
330 fn startListening(self: *@This()) !void {
331 const r = windows.kernel32.ReadDirectoryChangesW(
332 self.handle,
333 @ptrCast(&self.buffer),
334 self.buffer.len,
335 0,
330 fn startListening(dir: *Directory, w: *Watch) !void {
331 assert(dir.file.flags.nonblocking);
332 assert(dir.state == .idle);
333 switch (windows.ntdll.NtNotifyChangeDirectoryFileEx(
334 dir.file.handle,
335 null,
336 &notifyApc,
337 w,
338 &dir.iosb,
339 &dir.buffer,
340 dir.buffer.len,
336341 .{
337 .creation = true,
338 .dir_name = true,
339 .file_name = true,
340 .last_write = true,
341 .size = true,
342 .FILE_NAME = true,
343 .DIR_NAME = true,
344 .SIZE = true,
345 .LAST_WRITE = true,
346 .CREATION = true,
342347 },
343 null,
344 &self.overlapped,
345 null,
346 );
347 if (r == windows.FALSE) {
348 switch (windows.GetLastError()) {
349 .INVALID_FUNCTION => return error.ReadDirectoryChangesUnsupported,
350 else => |err| return windows.unexpectedError(err),
351 }
348 windows.FALSE,
349 .Notify,
350 )) {
351 .SUCCESS, .PENDING => dir.state = .listening,
352 .ILLEGAL_FUNCTION => return error.ReadDirectoryChangesUnsupported,
353 else => |status| return windows.unexpectedStatus(status),
352354 }
353355 }
354356
355 fn init(gpa: Allocator, path: Cache.Path) !*@This() {
357 fn notifyApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows.ULONG) callconv(.winapi) void {
358 const w: *Watch = @ptrCast(@alignCast(apc_context));
359 const dir: *Directory = @fieldParentPtr("iosb", iosb);
360 assert(iosb.u.Status != .PENDING);
361 assert(dir.state == .listening);
362 w.os.ready_dirs.append(&dir.ready_node);
363 dir.state = .ready;
364 }
365
366 fn init(gpa: Allocator, path: Cache.Path) !*Directory {
356367 // The following code is a drawn out NtCreateFile call. (mostly adapted from Io.Dir.makeOpenDirAccessMaskW)
357368 // It's necessary in order to get the specific flags that are required when calling ReadDirectoryChangesW.
358369 var dir_handle: windows.HANDLE = undefined;
359370 const root_fd = path.root_dir.handle.handle;
360371 const sub_path = path.subPathOrDot();
361 const sub_path_w = try std.Io.Threaded.sliceToPrefixedFileW(root_fd, sub_path); // TODO eliminate this call
362 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
363
364 var nt_name = windows.UNICODE_STRING{
365 .Length = @intCast(path_len_bytes),
366 .MaximumLength = @intCast(path_len_bytes),
367 .Buffer = @constCast(sub_path_w.span().ptr),
368 };
372 const sub_path_w = try Io.Threaded.sliceToPrefixedFileW(root_fd, sub_path); // TODO eliminate this call
369373 var iosb: windows.IO_STATUS_BLOCK = undefined;
370
371374 switch (windows.ntdll.NtCreateFile(
372375 &dir_handle,
373376 .{
......@@ -379,7 +382,7 @@ const Os = switch (builtin.os.tag) {
379382 },
380383 &.{
381384 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else root_fd,
382 .ObjectName = &nt_name,
385 .ObjectName = @constCast(&sub_path_w.string()),
383386 },
384387 &iosb,
385388 null,
......@@ -410,20 +413,49 @@ const Os = switch (builtin.os.tag) {
410413
411414 const dir_id = try getFileId(dir_handle);
412415
413 const dir_ptr = try gpa.create(@This());
414 dir_ptr.* = .{
415 .handle = dir_handle,
416 const dir = try gpa.create(Directory);
417 dir.* = .{
418 .reaction_set = .empty,
416419 .id = dir_id,
417 .overlapped = std.mem.zeroes(windows.OVERLAPPED),
420 .file = .{ .handle = dir_handle, .flags = .{ .nonblocking = true } },
421 .state = .idle,
422 .iosb = undefined,
423 .buffer = undefined,
424 .ready_node = undefined,
418425 };
419 return dir_ptr;
426 return dir;
420427 }
421428
422 fn deinit(self: *@This(), gpa: Allocator) void {
423 _ = windows.kernel32.CancelIo(self.handle);
424 windows.CloseHandle(self.handle);
425 gpa.destroy(self);
429 fn deinit(dir: *Directory, gpa: Allocator, w: *Watch) void {
430 state: switch (dir.state) {
431 .idle => {},
432 .listening => {
433 var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
434 _ = windows.ntdll.NtCancelIoFileEx(dir.file.handle, &dir.iosb, &cancel_iosb);
435 while (switch (dir.state) {
436 .idle => unreachable,
437 .listening => true,
438 .ready => false,
439 }) Io.Threaded.waitForApcOrAlert();
440 continue :state .ready;
441 },
442 .ready => w.os.ready_dirs.remove(&dir.ready_node),
443 }
444 windows.CloseHandle(dir.file.handle);
445 gpa.destroy(dir);
426446 }
447
448 /// Useful to make `*Directory` a key in `std.ArrayHashMap`.
449 const TableAdapter = struct {
450 pub fn hash(_: TableAdapter, lhs_dir: *Directory) u32 {
451 return @truncate(Hash.hash(lhs_dir.id.volumeSerialNumber, @ptrCast(&lhs_dir.id.indexNumber)));
452 }
453 pub fn eql(_: TableAdapter, lhs_dir: *Directory, rhs_dir: *Directory, rhs_index: usize) bool {
454 _ = rhs_index;
455 return lhs_dir.id.volumeSerialNumber == rhs_dir.id.volumeSerialNumber and
456 lhs_dir.id.indexNumber == rhs_dir.id.indexNumber;
457 }
458 };
427459 };
428460
429461 fn init(cwd_path: []const u8) !Watch {
......@@ -433,9 +465,8 @@ const Os = switch (builtin.os.tag) {
433465 .dir_count = 0,
434466 .os = switch (builtin.os.tag) {
435467 .windows => .{
436 .handle_table = .{},
437 .dir_list = .{},
438 .io_cp = null,
468 .handle_table = .empty,
469 .ready_dirs = .{},
439470 },
440471 else => {},
441472 },
......@@ -479,29 +510,22 @@ const Os = switch (builtin.os.tag) {
479510
480511 fn markDirtySteps(w: *Watch, gpa: Allocator, dir: *Directory) !bool {
481512 var any_dirty = false;
482 const bytes_returned = try windows.GetOverlappedResult(dir.handle, &dir.overlapped, false);
513 const bytes_returned = dir.iosb.Information;
483514 if (bytes_returned == 0) {
484515 std.log.warn("file system watch queue overflowed; falling back to fstat", .{});
485516 markAllFilesDirty(w, gpa);
486 try dir.startListening();
517 try dir.startListening(w);
487518 return true;
488519 }
489520 var file_name_buf: [std.fs.max_path_bytes]u8 = undefined;
490 var notify: *align(1) windows.FILE_NOTIFY_INFORMATION = undefined;
491521 var offset: usize = 0;
492522 while (true) {
493 notify = @ptrCast(&dir.buffer[offset]);
494 const file_name_field: [*]u16 = @ptrFromInt(@intFromPtr(notify) + @sizeOf(windows.FILE_NOTIFY_INFORMATION));
495 const file_name_len = std.unicode.wtf16LeToWtf8(&file_name_buf, file_name_field[0 .. notify.FileNameLength / 2]);
496 const file_name = file_name_buf[0..file_name_len];
497 if (w.os.handle_table.getIndex(dir.id)) |reaction_set_i| {
498 const reaction_set = w.os.handle_table.values()[reaction_set_i];
499 if (reaction_set.getPtr(".")) |glob_set|
500 any_dirty = markStepSetDirty(gpa, glob_set, any_dirty);
501 if (reaction_set.getPtr(file_name)) |step_set| {
502 any_dirty = markStepSetDirty(gpa, step_set, any_dirty);
503 }
504 }
523 const notify: *windows.FILE.NOTIFY.INFORMATION = @ptrCast(@alignCast(&dir.buffer[offset]));
524 const file_name = file_name_buf[0..std.unicode.wtf16LeToWtf8(&file_name_buf, notify.fileName())];
525 if (dir.reaction_set.getPtr(".")) |glob_set|
526 any_dirty = markStepSetDirty(gpa, glob_set, any_dirty);
527 if (dir.reaction_set.getPtr(file_name)) |step_set|
528 any_dirty = markStepSetDirty(gpa, step_set, any_dirty);
505529 if (notify.NextEntryOffset == 0)
506530 break;
507531
......@@ -509,7 +533,7 @@ const Os = switch (builtin.os.tag) {
509533 }
510534
511535 // We call this now since at this point we have finished reading dir.buffer.
512 try dir.startListening();
536 try dir.startListening(w);
513537 return any_dirty;
514538 }
515539
......@@ -517,41 +541,32 @@ const Os = switch (builtin.os.tag) {
517541 // Add missing marks and note persisted ones.
518542 for (steps) |step| {
519543 for (step.inputs.table.keys(), step.inputs.table.values()) |path, *files| {
520 const reaction_set = rs: {
544 const dir = dir: {
521545 const gop = try w.dir_table.getOrPut(gpa, path);
522546 if (!gop.found_existing) {
523 const dir = try Os.Directory.init(gpa, path);
524 errdefer dir.deinit(gpa);
547 const dir: *Directory = try .init(gpa, path);
548 errdefer dir.deinit(gpa, w);
525549 // `dir.id` may already be present in the table in
526550 // the case that we have multiple Cache.Path instances
527551 // that compare inequal but ultimately point to the same
528552 // directory on the file system.
529553 // In such case, we must revert adding this directory, but keep
530554 // the additions to the step set.
531 const dh_gop = try w.os.handle_table.getOrPut(gpa, dir.id);
555 const dh_gop = try w.os.handle_table.getOrPut(gpa, dir);
532556 if (dh_gop.found_existing) {
533 dir.deinit(gpa);
557 dir.deinit(gpa, w);
534558 _ = w.dir_table.pop();
559 break :dir w.os.handle_table.keys()[dh_gop.index];
535560 } else {
536561 assert(dh_gop.index == gop.index);
537 dh_gop.value_ptr.* = .{};
538 try dir.startListening();
539 const key = w.os.counter;
540 w.os.counter +%= 1;
541 try w.os.dir_list.put(gpa, key, dir);
542 w.os.io_cp = try windows.CreateIoCompletionPort(
543 dir.handle,
544 w.os.io_cp,
545 key,
546 0,
547 );
562 try dir.startListening(w);
563 break :dir dir;
548564 }
549 break :rs &w.os.handle_table.values()[dh_gop.index];
550565 }
551 break :rs &w.os.handle_table.values()[gop.index];
566 break :dir w.os.handle_table.keys()[gop.index];
552567 };
553568 for (files.items) |basename| {
554 const gop = try reaction_set.getOrPut(gpa, basename);
569 const gop = try dir.reaction_set.getOrPut(gpa, basename);
555570 if (!gop.found_existing) gop.value_ptr.* = .{};
556571 try gop.value_ptr.put(gpa, step, w.generation);
557572 }
......@@ -562,11 +577,11 @@ const Os = switch (builtin.os.tag) {
562577 // Remove marks for files that are no longer inputs.
563578 var i: usize = 0;
564579 while (i < w.os.handle_table.entries.len) {
580 const dir = w.os.handle_table.keys()[i];
565581 {
566 const reaction_set = &w.os.handle_table.values()[i];
567582 var step_set_i: usize = 0;
568 while (step_set_i < reaction_set.entries.len) {
569 const step_set = &reaction_set.values()[step_set_i];
583 while (step_set_i < dir.reaction_set.entries.len) {
584 const step_set = &dir.reaction_set.values()[step_set_i];
570585 var dirent_i: usize = 0;
571586 while (dirent_i < step_set.entries.len) {
572587 const generations = step_set.values();
......@@ -580,53 +595,45 @@ const Os = switch (builtin.os.tag) {
580595 step_set_i += 1;
581596 continue;
582597 }
583 reaction_set.swapRemoveAt(step_set_i);
598 dir.reaction_set.swapRemoveAt(step_set_i);
584599 }
585 if (reaction_set.entries.len > 0) {
600 if (dir.reaction_set.entries.len > 0) {
586601 i += 1;
587602 continue;
588603 }
589604 }
590605
591 w.os.dir_list.values()[i].deinit(gpa);
592 w.os.dir_list.swapRemoveAt(i);
593606 w.dir_table.swapRemoveAt(i);
594607 w.os.handle_table.swapRemoveAt(i);
608 dir.deinit(gpa, w);
595609 }
596610 w.generation +%= 1;
597611 }
598612 w.dir_count = w.dir_table.count();
599613 }
600614
601 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
602 var bytes_transferred: std.os.windows.DWORD = undefined;
603 var key: usize = undefined;
604 var overlapped_ptr: ?*std.os.windows.OVERLAPPED = undefined;
605 return while (true) switch (std.os.windows.GetQueuedCompletionStatus(
606 w.os.io_cp.?,
607 &bytes_transferred,
608 &key,
609 &overlapped_ptr,
610 @bitCast(timeout.to_i32_ms()),
611 )) {
612 .Normal => {
613 if (bytes_transferred == 0)
614 break error.Unexpected;
615
616 // This 'orelse' detects a race condition that happens when we receive a
617 // completion notification for a directory that no longer exists in our list.
618 const dir = w.os.dir_list.get(key) orelse break .clean;
619
620 break if (try Os.markDirtySteps(w, gpa, dir))
621 .dirty
622 else
623 .clean;
624 },
625 .Timeout => break .timeout,
626 // This status is issued because CancelIo was called, skip and try again.
627 .Canceled => continue,
628 else => break error.Unexpected,
629 };
615 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
616 for (0..2) |attempt| {
617 while (w.os.ready_dirs.popFirst()) |ready_node| {
618 const dir: *Directory = @fieldParentPtr("ready_node", ready_node);
619 assert(dir.state == .ready);
620 dir.state = .idle;
621 switch (dir.iosb.u.Status) {
622 .SUCCESS => return if (try markDirtySteps(w, gpa, dir)) .dirty else .clean,
623 .PENDING => unreachable,
624 .CANCELLED => {},
625 else => |status| return windows.unexpectedStatus(status),
626 }
627 try dir.startListening(w);
628 }
629 try io.checkCancel();
630 if (attempt == 1) return .timeout;
631 const delay_interval: windows.LARGE_INTEGER = switch (timeout) {
632 .none => std.math.minInt(windows.LARGE_INTEGER),
633 .ms => |ms| -@as(windows.LARGE_INTEGER, ms) * (std.time.ns_per_ms / 100),
634 };
635 _ = windows.ntdll.NtDelayExecution(windows.TRUE, &delay_interval);
636 } else unreachable;
630637 }
631638 },
632639 .dragonfly, .freebsd, .netbsd, .openbsd, .ios, .tvos, .visionos, .watchos => struct {
......@@ -796,7 +803,8 @@ const Os = switch (builtin.os.tag) {
796803 w.dir_count = w.dir_table.count();
797804 }
798805
799 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
806 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
807 _ = io;
800808 var timespec_buffer: posix.timespec = undefined;
801809 var event_buffer: [100]posix.Kevent = undefined;
802810 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) {
852860 try w.os.fse.setPaths(gpa, steps);
853861 w.dir_count = w.os.fse.watch_roots.len;
854862 }
855 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
863 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
864 _ = io;
856865 return w.os.fse.wait(gpa, switch (timeout) {
857866 .none => null,
858867 .ms => |ms| @as(u64, ms) * std.time.ns_per_ms,
......@@ -890,10 +899,13 @@ pub const Match = struct {
890899};
891900
892901fn markAllFilesDirty(w: *Watch, gpa: Allocator) void {
893 for (w.os.handle_table.values()) |value| {
902 for (switch (builtin.os.tag) {
903 .windows => w.os.handle_table.keys(),
904 else => w.os.handle_table.values(),
905 }) |item| {
894906 const reaction_set = switch (builtin.os.tag) {
895 .linux => value.reaction_set,
896 else => value,
907 .linux, .windows => item.reaction_set,
908 else => item,
897909 };
898910 for (reaction_set.values()) |step_set| {
899911 for (step_set.keys()) |step| {
......@@ -951,6 +963,6 @@ pub const WaitResult = enum {
951963 clean,
952964};
953965
954pub fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
955 return Os.wait(w, gpa, timeout);
966pub fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
967 return Os.wait(w, gpa, io, timeout);
956968}
lib/std/Io/Threaded.zig+465-493
......@@ -83,7 +83,7 @@ csprng: Csprng = .{},
8383system_basic_information: SystemBasicInformation = .{},
8484
8585const SystemBasicInformation = if (!is_windows) struct {} else struct {
86 buffer: windows.SYSTEM_BASIC_INFORMATION = undefined,
86 buffer: windows.SYSTEM.BASIC_INFORMATION = undefined,
8787 initialized: std.atomic.Value(bool) = .{ .raw = false },
8888};
8989
......@@ -1392,7 +1392,7 @@ const AlertableSyscall = struct {
13921392 }
13931393};
13941394
1395fn waitForApcOrAlert() void {
1395pub fn waitForApcOrAlert() void {
13961396 const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
13971397 _ = windows.ntdll.NtDelayExecution(windows.TRUE, &infinite_timeout);
13981398}
......@@ -2556,9 +2556,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
25562556 else => |e| e,
25572557 },
25582558 },
2559 .device_io_control => |*o| return .{
2560 .device_io_control = try deviceIoControl(t, o),
2561 },
2559 .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) },
25622560 }
25632561}
25642562
......@@ -2970,7 +2968,7 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
29702968 break :o;
29712969 }
29722970 const buffer = o.data[data_index];
2973 const short_buffer_len = @min(std.math.maxInt(u32), buffer.len);
2971 const short_buffer_len = std.math.lossyCast(u32, buffer.len);
29742972
29752973 if (o.file.flags.nonblocking) {
29762974 context.file = o.file.handle;
......@@ -3259,22 +3257,11 @@ fn dirCreateDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, pe
32593257 _ = t;
32603258 _ = permissions; // TODO use this value
32613259
3262 const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path);
3263 const sub_path_w = sub_path_w_array.span();
3264 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
3265
3266 var nt_name: windows.UNICODE_STRING = .{
3267 .Length = path_len_bytes,
3268 .MaximumLength = path_len_bytes,
3269 .Buffer = @constCast(sub_path_w.ptr),
3270 };
3271 const attr: windows.OBJECT_ATTRIBUTES = .{
3272 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
3273 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
3274 .Attributes = .{
3275 .INHERIT = false,
3276 },
3277 .ObjectName = &nt_name,
3260 const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
3261 const attr: windows.OBJECT.ATTRIBUTES = .{
3262 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
3263 .Attributes = .{ .INHERIT = false },
3264 .ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w.span())),
32783265 .SecurityDescriptor = null,
32793266 .SecurityQualityOfService = null,
32803267 };
......@@ -3438,21 +3425,14 @@ fn dirCreateDirPathOpenWindows(
34383425 };
34393426
34403427 components: while (true) {
3441 const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, component.path);
3442 const sub_path_w = sub_path_w_array.span();
3428 const sub_path_w = try sliceToPrefixedFileW(dir.handle, component.path);
3429 const attr: windows.OBJECT.ATTRIBUTES = .{
3430 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
3431 .ObjectName = @constCast(&sub_path_w.string()),
3432 };
34433433 const is_last = it.peekNext() == null;
3444 const create_disposition: w.FILE.CREATE_DISPOSITION = if (is_last) .OPEN_IF else .CREATE;
3445
34463434 var result: Dir = .{ .handle = undefined };
3447
3448 const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
3449 var nt_name: w.UNICODE_STRING = .{
3450 .Length = path_len_bytes,
3451 .MaximumLength = path_len_bytes,
3452 .Buffer = @constCast(sub_path_w.ptr),
3453 };
3454 var io_status_block: w.IO_STATUS_BLOCK = undefined;
3455
3435 var iosb: w.IO_STATUS_BLOCK = undefined;
34563436 const syscall: Syscall = try .start();
34573437 while (true) switch (w.ntdll.NtCreateFile(
34583438 &result.handle,
......@@ -3468,15 +3448,12 @@ fn dirCreateDirPathOpenWindows(
34683448 .SYNCHRONIZE = true,
34693449 },
34703450 },
3471 &.{
3472 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
3473 .ObjectName = &nt_name,
3474 },
3475 &io_status_block,
3451 &attr,
3452 &iosb,
34763453 null,
34773454 .{ .NORMAL = true },
34783455 .VALID_FLAGS,
3479 create_disposition,
3456 if (is_last) .OPEN_IF else .CREATE,
34803457 .{
34813458 .DIRECTORY_FILE = true,
34823459 .IO = .SYNCHRONOUS_NONALERT,
......@@ -3944,15 +3921,15 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
39443921 };
39453922}
39463923
3947fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION {
3924fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION {
39483925 if (!t.system_basic_information.initialized.load(.acquire)) {
39493926 mutexLock(&t.mutex);
39503927 defer mutexUnlock(&t.mutex);
39513928
39523929 switch (windows.ntdll.NtQuerySystemInformation(
3953 .SystemBasicInformation,
3930 .Basic,
39543931 &t.system_basic_information.buffer,
3955 @sizeOf(windows.SYSTEM_BASIC_INFORMATION),
3932 @sizeOf(windows.SYSTEM.BASIC_INFORMATION),
39563933 null,
39573934 )) {
39583935 .SUCCESS => {},
......@@ -4139,22 +4116,11 @@ fn dirAccessWindows(
41394116
41404117 _ = options; // TODO
41414118
4142 const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path);
4143 const sub_path_w = sub_path_w_array.span();
4144
4145 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) return;
4146 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) return;
4147
4148 const path_len_bytes = std.math.cast(u16, std.mem.sliceTo(sub_path_w, 0).len * 2) orelse
4149 return error.NameTooLong;
4150 var nt_name: windows.UNICODE_STRING = .{
4151 .Length = path_len_bytes,
4152 .MaximumLength = path_len_bytes,
4153 .Buffer = @constCast(sub_path_w.ptr),
4154 };
4155 const attr: windows.OBJECT_ATTRIBUTES = .{
4156 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
4157 .ObjectName = &nt_name,
4119 if (std.mem.eql(u8, sub_path, ".") or std.mem.eql(u8, sub_path, "..")) return;
4120 const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
4121 const attr: windows.OBJECT.ATTRIBUTES = .{
4122 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
4123 .ObjectName = @constCast(&sub_path_w.string()),
41584124 };
41594125 var basic_info: windows.FILE.BASIC_INFORMATION = undefined;
41604126 const syscall: Syscall = try .start();
......@@ -4360,18 +4326,10 @@ fn dirCreateFileWindows(
43604326 if (std.mem.eql(u8, sub_path, ".")) return error.IsDir;
43614327 if (std.mem.eql(u8, sub_path, "..")) return error.IsDir;
43624328
4363 const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path);
4364 const sub_path_w = sub_path_w_array.span();
4365 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
4366
4367 var nt_name: windows.UNICODE_STRING = .{
4368 .Length = path_len_bytes,
4369 .MaximumLength = path_len_bytes,
4370 .Buffer = @constCast(sub_path_w.ptr),
4371 };
4372 const attr: windows.OBJECT_ATTRIBUTES = .{
4373 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
4374 .ObjectName = &nt_name,
4329 const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
4330 const attr: windows.OBJECT.ATTRIBUTES = .{
4331 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
4332 .ObjectName = @constCast(&sub_path_w.string()),
43754333 };
43764334 const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive)
43774335 .CREATE
......@@ -4966,20 +4924,14 @@ fn dirOpenFileWindows(
49664924
49674925pub fn dirOpenFileWtf16(
49684926 dir_handle: ?windows.HANDLE,
4969 sub_path_w: [:0]const u16,
4927 sub_path_w: []const u16,
49704928 flags: File.OpenFlags,
49714929) File.OpenError!File {
49724930 const allow_directory = flags.allow_directory and !flags.isWrite();
49734931 if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;
49744932 if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir;
4975 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
49764933 const w = windows;
49774934
4978 var nt_name: w.UNICODE_STRING = .{
4979 .Length = path_len_bytes,
4980 .MaximumLength = path_len_bytes,
4981 .Buffer = @constCast(sub_path_w.ptr),
4982 };
49834935 var io_status_block: w.IO_STATUS_BLOCK = undefined;
49844936 var attempt: u5 = 0;
49854937 var syscall: Syscall = try .start();
......@@ -4996,7 +4948,7 @@ pub fn dirOpenFileWtf16(
49964948 },
49974949 &.{
49984950 .RootDirectory = dir_handle,
4999 .ObjectName = &nt_name,
4951 .ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)),
50004952 },
50014953 &io_status_block,
50024954 null,
......@@ -5329,20 +5281,19 @@ fn dirOpenDirHaiku(
53295281
53305282pub fn dirOpenDirWindows(
53315283 dir: Dir,
5332 sub_path_w: [:0]const u16,
5284 sub_path_w: []const u16,
53335285 options: Dir.OpenOptions,
53345286) Dir.OpenError!Dir {
53355287 const w = windows;
53365288
5337 const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
5338 var nt_name: w.UNICODE_STRING = .{
5339 .Length = path_len_bytes,
5340 .MaximumLength = path_len_bytes,
5341 .Buffer = @constCast(sub_path_w.ptr),
5342 };
53435289 var io_status_block: w.IO_STATUS_BLOCK = undefined;
53445290 var result: Dir = .{ .handle = undefined };
53455291
5292 const attr: w.OBJECT.ATTRIBUTES = .{
5293 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
5294 .ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)),
5295 };
5296
53465297 const syscall: Syscall = try .start();
53475298 while (true) switch (w.ntdll.NtCreateFile(
53485299 &result.handle,
......@@ -5359,10 +5310,7 @@ pub fn dirOpenDirWindows(
53595310 .SYNCHRONIZE = true,
53605311 },
53615312 },
5362 &.{
5363 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
5364 .ObjectName = &nt_name,
5365 },
5313 &attr,
53665314 &io_status_block,
53675315 null,
53685316 .{ .NORMAL = true },
......@@ -6194,13 +6142,15 @@ pub fn GetFinalPathNameByHandle(
61946142 input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
61956143 @memcpy(input_buf[@sizeOf(windows.MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
61966144
6197 {
6198 const rc = windows.DeviceIoControl(mgmt_handle, windows.IOCTL.MOUNTMGR.QUERY_POINTS, .{ .in = &input_buf, .out = &output_buf });
6199 switch (rc) {
6200 .SUCCESS => {},
6201 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
6202 else => return windows.unexpectedStatus(rc),
6203 }
6145 switch ((try deviceIoControl(&.{
6146 .file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = false } },
6147 .code = windows.IOCTL.MOUNTMGR.QUERY_POINTS,
6148 .in = &input_buf,
6149 .out = &output_buf,
6150 })).u.Status) {
6151 .SUCCESS => {},
6152 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
6153 else => |status| return windows.unexpectedStatus(status),
62046154 }
62056155 const mount_points_struct: *const windows.MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
62066156
......@@ -6251,11 +6201,15 @@ pub fn GetFinalPathNameByHandle(
62516201 vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
62526202 @memcpy(@as([*]windows.WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);
62536203
6254 const rc = windows.DeviceIoControl(mgmt_handle, windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, .{ .in = &vol_input_buf, .out = &vol_output_buf });
6255 switch (rc) {
6204 switch ((try deviceIoControl(&.{
6205 .file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = true } },
6206 .code = windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH,
6207 .in = &vol_input_buf,
6208 .out = &vol_output_buf,
6209 })).u.Status) {
62566210 .SUCCESS => {},
62576211 .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
6258 else => return windows.unexpectedStatus(rc),
6212 else => |status| return windows.unexpectedStatus(status),
62596213 }
62606214 const volume_paths_struct: *const windows.MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
62616215 const volume_path = std.mem.sliceTo(@as(
......@@ -6352,20 +6306,17 @@ pub const QueryObjectNameError = error{
63526306};
63536307
63546308pub fn QueryObjectName(handle: windows.HANDLE, out_buffer: []u16) QueryObjectNameError![]u16 {
6355 const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
6309 const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT.NAME_INFORMATION)) orelse return error.NameTooLong;
63566310
6357 const info: *windows.OBJECT_NAME_INFORMATION = @ptrCast(out_buffer_aligned);
6311 const info: *windows.OBJECT.NAME_INFORMATION = @ptrCast(out_buffer_aligned);
63586312 // buffer size is specified in bytes
63596313 const out_buffer_len = std.math.cast(windows.ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(windows.ULONG);
63606314 // last argument would return the length required for full_buffer, not exposed here
6361 return switch (windows.ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) {
6362 .SUCCESS => blk: {
6363 // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
6364 // if the object was "unnamed", not sure if this can happen for file handles
6365 if (info.Name.MaximumLength == 0) break :blk error.Unexpected;
6366 // resulting string length is specified in bytes
6367 const path_length_unterminated = @divExact(info.Name.Length, 2);
6368 break :blk info.Name.Buffer.?[0..path_length_unterminated];
6315 return switch (windows.ntdll.NtQueryObject(handle, .Name, info, out_buffer_len, null)) {
6316 .SUCCESS => {
6317 // info.Name from ObQueryNameString is documented to be empty if the object
6318 // was "unnamed", not sure if this can happen for file handles
6319 return if (info.Name.isEmpty()) error.Unexpected else info.Name.slice();
63696320 },
63706321 .ACCESS_DENIED => error.AccessDenied,
63716322 .INVALID_HANDLE => error.InvalidHandle,
......@@ -6620,8 +6571,12 @@ pub const WindowsPathSpace = struct {
66206571 data: [windows.PATH_MAX_WIDE:0]u16,
66216572 len: usize,
66226573
6623 pub fn span(self: *const WindowsPathSpace) [:0]const u16 {
6624 return self.data[0..self.len :0];
6574 pub fn span(wps: *const WindowsPathSpace) [:0]const u16 {
6575 return wps.data[0..wps.len :0];
6576 }
6577
6578 pub fn string(wps: *const WindowsPathSpace) windows.UNICODE_STRING {
6579 return .init(wps.span());
66256580 }
66266581};
66276582
......@@ -7076,25 +7031,19 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
70767031 _ = t;
70777032 const w = windows;
70787033
7079 const sub_path_w_buf = try sliceToPrefixedFileW(dir.handle, sub_path);
7080 const sub_path_w = sub_path_w_buf.span();
7081
7082 const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
7083 var nt_name: w.UNICODE_STRING = .{
7084 .Length = path_len_bytes,
7085 .MaximumLength = path_len_bytes,
7086 // The Windows API makes this mutable, but it will not mutate here.
7087 .Buffer = @constCast(sub_path_w.ptr),
7088 };
7089
7090 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
7091 // Windows does not recognize this, but it does work with empty string.
7092 nt_name.Length = 0;
7093 }
7094 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
7034 if (std.mem.eql(u8, sub_path, "..")) {
70957035 // Can't remove the parent directory with an open handle.
70967036 return error.FileBusy;
70977037 }
7038 var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
7039 if (std.mem.eql(u8, sub_path, ".")) {
7040 // Windows does not recognize this, but it does work with empty string.
7041 sub_path_w.len = 0;
7042 }
7043 const attr: w.OBJECT.ATTRIBUTES = .{
7044 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
7045 .ObjectName = @constCast(&sub_path_w.string()),
7046 };
70987047
70997048 var io_status_block: w.IO_STATUS_BLOCK = undefined;
71007049 var tmp_handle: w.HANDLE = undefined;
......@@ -7106,10 +7055,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
71067055 .RIGHTS = .{ .DELETE = true },
71077056 .SYNCHRONIZE = true,
71087057 } },
7109 &.{
7110 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
7111 .ObjectName = &nt_name,
7112 },
7058 &attr,
71137059 &io_status_block,
71147060 null,
71157061 .{},
......@@ -7741,7 +7687,7 @@ fn dirSymLinkWindows(
77417687 // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
77427688 var is_target_absolute = false;
77437689 const final_target_path = target_path: {
7744 if (windows.hasCommonNtPrefix(u16, target_path_w.span())) {
7690 if (w.hasCommonNtPrefix(u16, target_path_w.span())) {
77457691 // Already an NT path, no need to do anything to it
77467692 break :target_path target_path_w.span();
77477693 } else {
......@@ -7785,13 +7731,16 @@ fn dirSymLinkWindows(
77857731 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
77867732 const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
77877733 @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
7788 const rc = w.DeviceIoControl(symlink_handle, .SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
7789 switch (rc) {
7734 switch ((try deviceIoControl(&.{
7735 .file = .{ .handle = symlink_handle, .flags = .{ .nonblocking = false } },
7736 .code = .SET_REPARSE_POINT,
7737 .in = buffer[0..buf_len],
7738 })).u.Status) {
77907739 .SUCCESS => {},
77917740 .PRIVILEGE_NOT_HELD => return error.PermissionDenied,
77927741 .ACCESS_DENIED => return error.AccessDenied,
77937742 .INVALID_DEVICE_REQUEST => return error.FileSystem,
7794 else => return windows.unexpectedStatus(rc),
7743 else => |status| return w.unexpectedStatus(status),
77957744 }
77967745}
77977746
......@@ -7905,17 +7854,10 @@ fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []
79057854fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
79067855 // This gets used once for `sub_path` and then reused again temporarily
79077856 // before converting back to `buffer`.
7908 var sub_path_w_buf = try sliceToPrefixedFileW(dir.handle, sub_path);
7909 const sub_path_w = sub_path_w_buf.span();
7910 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
7911 var nt_name: windows.UNICODE_STRING = .{
7912 .Length = path_len_bytes,
7913 .MaximumLength = path_len_bytes,
7914 .Buffer = @constCast(sub_path_w.ptr),
7915 };
7916 const attr: windows.OBJECT_ATTRIBUTES = .{
7917 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
7918 .ObjectName = &nt_name,
7857 var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
7858 const attr: windows.OBJECT.ATTRIBUTES = .{
7859 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
7860 .ObjectName = @constCast(&sub_path_w.string()),
79197861 };
79207862 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
79217863 var result_handle: windows.HANDLE = undefined;
......@@ -8038,14 +7980,14 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink
80387980 const len = buf.SubstituteNameLength >> 1;
80397981 const path_buf = @as([*]const u16, &buf.PathBuffer);
80407982 const is_relative = buf.Flags & windows.SYMLINK_FLAG_RELATIVE != 0;
8041 break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w_buf.data);
7983 break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w.data);
80427984 },
80437985 @as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.MOUNT_POINT)) => r: {
80447986 const buf: *const windows.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
80457987 const offset = buf.SubstituteNameOffset >> 1;
80467988 const len = buf.SubstituteNameLength >> 1;
80477989 const path_buf = @as([*]const u16, &buf.PathBuffer);
8048 break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w_buf.data);
7990 break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w.data);
80497991 },
80507992 else => return error.UnsupportedReparsePointType,
80517993 };
......@@ -8541,13 +8483,14 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
85418483
85428484fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
85438485 const t: *Threaded = @ptrCast(@alignCast(userdata));
8544 return t.isTty(file);
8486 _ = t;
8487 return isTty(file);
85458488}
85468489
8547fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
8490fn isTty(file: File) Io.Cancelable!bool {
85488491 if (is_windows) {
85498492 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
8550 switch ((try t.deviceIoControl(&.{
8493 switch ((try deviceIoControl(&.{
85518494 .file = .{
85528495 .handle = windows.peb().ProcessParameters.ConsoleHandle,
85538496 .flags = .{ .nonblocking = false },
......@@ -8626,8 +8569,9 @@ fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
86268569
86278570fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
86288571 const t: *Threaded = @ptrCast(@alignCast(userdata));
8572 _ = t;
86298573
8630 if (!is_windows) return if (!try t.supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
8574 if (!is_windows) return if (!try supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
86318575
86328576 // For Windows Terminal, VT Sequences processing is enabled by default.
86338577 const console: File = .{
......@@ -8635,7 +8579,7 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
86358579 .flags = .{ .nonblocking = false },
86368580 };
86378581 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
8638 switch ((try t.deviceIoControl(&.{
8582 switch ((try deviceIoControl(&.{
86398583 .file = console,
86408584 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
86418585 .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
......@@ -8661,7 +8605,7 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
86618605 var set_console_mode = windows.CONSOLE.USER_IO.SET_MODE(
86628606 get_console_mode.Data | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
86638607 );
8664 switch ((try t.deviceIoControl(&.{
8608 switch ((try deviceIoControl(&.{
86658609 .file = console,
86668610 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
86678611 .in = @ptrCast(&set_console_mode.request(file, 0, .{}, 0, .{})),
......@@ -8673,13 +8617,14 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
86738617
86748618fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
86758619 const t: *Threaded = @ptrCast(@alignCast(userdata));
8676 return t.supportsAnsiEscapeCodes(file);
8620 _ = t;
8621 return supportsAnsiEscapeCodes(file);
86778622}
86788623
8679fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
8624fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
86808625 if (is_windows) {
86818626 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
8682 switch ((try t.deviceIoControl(&.{
8627 switch ((try deviceIoControl(&.{
86838628 .file = .{
86848629 .handle = windows.peb().ProcessParameters.ConsoleHandle,
86858630 .flags = .{ .nonblocking = false },
......@@ -8701,7 +8646,7 @@ fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
87018646 return false;
87028647 }
87038648
8704 if (try t.isTty(file)) return true;
8649 if (try isTty(file)) return true;
87058650
87068651 return false;
87078652}
......@@ -8775,7 +8720,7 @@ fn isCygwinPty(file: File) Io.Cancelable!bool {
87758720 },
87768721 };
87778722
8778 const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes);
8723 const name_info: *const windows.FILE.NAME_INFORMATION = @ptrCast(&name_info_bytes);
87798724 const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];
87808725 const name_wide = std.mem.bytesAsSlice(u16, name_bytes);
87818726 // The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
......@@ -9002,49 +8947,38 @@ fn fileSetTimestamps(
90028947 _ = t;
90038948
90048949 if (is_windows) {
9005 var access_time_buffer: windows.FILETIME = undefined;
9006 var modify_time_buffer: windows.FILETIME = undefined;
9007 var system_time_buffer: windows.LARGE_INTEGER = undefined;
9008
9009 if (options.access_timestamp == .now or options.modify_timestamp == .now) {
9010 system_time_buffer = windows.ntdll.RtlGetSystemTimePrecise();
9011 }
9012
9013 const access_ptr = switch (options.access_timestamp) {
9014 .unchanged => null,
9015 .now => @panic("TODO do SystemTimeToFileTime logic here"),
9016 .new => |ts| p: {
9017 access_time_buffer = windows.nanoSecondsToFileTime(ts);
9018 break :p &access_time_buffer;
9019 },
8950 const now_sys = if (options.access_timestamp == .now or options.modify_timestamp == .now)
8951 windows.ntdll.RtlGetSystemTimePrecise()
8952 else
8953 undefined;
8954 var iosb: windows.IO_STATUS_BLOCK = undefined;
8955 var info: windows.FILE.BASIC_INFORMATION = .{
8956 .CreationTime = 0,
8957 .LastAccessTime = switch (options.access_timestamp) {
8958 .unchanged => 0,
8959 .now => now_sys,
8960 .new => |ts| windows.toSysTime(ts),
8961 },
8962 .LastWriteTime = switch (options.modify_timestamp) {
8963 .unchanged => 0,
8964 .now => now_sys,
8965 .new => |ts| windows.toSysTime(ts),
8966 },
8967 .ChangeTime = 0,
8968 .FileAttributes = .{},
90208969 };
9021
9022 const modify_ptr = switch (options.modify_timestamp) {
9023 .unchanged => null,
9024 .now => @panic("TODO do SystemTimeToFileTime logic here"),
9025 .new => |ts| p: {
9026 modify_time_buffer = windows.nanoSecondsToFileTime(ts);
9027 break :p &modify_time_buffer;
9028 },
8970 var syscall: Syscall = try .start();
8971 while (true) switch (windows.ntdll.NtSetInformationFile(
8972 file.handle,
8973 &iosb,
8974 &info,
8975 @sizeOf(windows.FILE.BASIC_INFORMATION),
8976 .Basic,
8977 )) {
8978 .SUCCESS => return syscall.finish(),
8979 .CANCELLED => try syscall.checkCancel(),
8980 else => |status| return syscall.unexpectedNtstatus(status),
90298981 };
9030
9031 // https://github.com/ziglang/zig/issues/1840
9032 const syscall: Syscall = try .start();
9033 while (true) {
9034 switch (windows.kernel32.SetFileTime(file.handle, null, access_ptr, modify_ptr)) {
9035 0 => switch (windows.GetLastError()) {
9036 .OPERATION_ABORTED => {
9037 try syscall.checkCancel();
9038 continue;
9039 },
9040 else => |err| {
9041 syscall.finish();
9042 return windows.unexpectedError(err);
9043 },
9044 },
9045 else => return syscall.finish(),
9046 }
9047 }
90488982 }
90498983
90508984 if (native_os == .wasi and !builtin.link_libc) {
......@@ -9636,15 +9570,43 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.ReadStreamingErro
96369570}
96379571
96389572fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingError!usize {
9573 var iosb: windows.IO_STATUS_BLOCK = undefined;
96399574 var index: usize = 0;
96409575 while (data.len - index != 0 and data[index].len == 0) index += 1;
96419576 if (data.len - index == 0) return 0;
96429577 const buffer = data[index];
9643 const short_buffer_len = @min(std.math.maxInt(u32), buffer.len);
9644
9645 var iosb: windows.IO_STATUS_BLOCK = undefined;
9646
9647 if (!file.flags.nonblocking) {
9578 const short_buffer_len = std.math.lossyCast(u32, buffer.len);
9579 if (file.flags.nonblocking) {
9580 var done: bool = false;
9581 switch (windows.ntdll.NtReadFile(
9582 file.handle,
9583 null, // event
9584 flagApc,
9585 &done, // APC context
9586 &iosb,
9587 buffer.ptr,
9588 short_buffer_len,
9589 null, // byte offset
9590 null, // key
9591 )) {
9592 // We must wait for the APC routine.
9593 .PENDING, .SUCCESS => while (!done) {
9594 // Once we get here we must not return from the function until the
9595 // operation completes, thereby releasing reference to the iosb.
9596 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
9597 error.Canceled => |e| {
9598 var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
9599 _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
9600 while (!done) waitForApcOrAlert();
9601 return e;
9602 },
9603 };
9604 waitForApcOrAlert();
9605 alertable_syscall.finish();
9606 },
9607 else => |status| iosb.u.Status = status,
9608 }
9609 } else {
96489610 const syscall: Syscall = try .start();
96499611 while (true) switch (windows.ntdll.NtReadFile(
96509612 file.handle,
......@@ -9665,41 +9627,10 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingEr
96659627 else => |status| {
96669628 syscall.finish();
96679629 iosb.u.Status = status;
9668 return ntReadFileResult(&iosb);
9630 break;
96699631 },
96709632 };
96719633 }
9672
9673 var done: bool = false;
9674
9675 switch (windows.ntdll.NtReadFile(
9676 file.handle,
9677 null, // event
9678 flagApc,
9679 &done, // APC context
9680 &iosb,
9681 buffer.ptr,
9682 short_buffer_len,
9683 null, // byte offset
9684 null, // key
9685 )) {
9686 // We must wait for the APC routine.
9687 .PENDING, .SUCCESS => while (!done) {
9688 // Once we get here we must not return from the function until the
9689 // operation completes, thereby releasing reference to io_status_block.
9690 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
9691 error.Canceled => |e| {
9692 var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
9693 _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
9694 while (!done) waitForApcOrAlert();
9695 return e;
9696 },
9697 };
9698 waitForApcOrAlert();
9699 alertable_syscall.finish();
9700 },
9701 else => |status| iosb.u.Status = status,
9702 }
97039634 return ntReadFileResult(&iosb);
97049635}
97059636
......@@ -9714,8 +9645,9 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
97149645 .CANCELLED => unreachable,
97159646 .SUCCESS => return io_status_block.Information,
97169647 .END_OF_FILE, .PIPE_BROKEN => return error.EndOfStream,
9648 .INVALID_HANDLE => return error.NotOpenForReading,
97179649 .INVALID_DEVICE_REQUEST => return error.IsDir,
9718 .LOCK_NOT_GRANTED => return error.LockViolation,
9650 .FILE_LOCK_CONFLICT => return error.LockViolation,
97199651 .ACCESS_DENIED => return error.AccessDenied,
97209652 else => |status| return windows.unexpectedStatus(status),
97219653 }
......@@ -9731,7 +9663,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
97319663 .QUOTA_EXCEEDED => return error.SystemResources,
97329664 .PIPE_BROKEN => return error.BrokenPipe,
97339665 .INVALID_HANDLE => return error.NotOpenForWriting,
9734 .LOCK_NOT_GRANTED => return error.LockViolation,
9666 .FILE_LOCK_CONFLICT => return error.LockViolation,
97359667 .ACCESS_DENIED => return error.AccessDenied,
97369668 .WORKING_SET_QUOTA => return error.SystemResources,
97379669 .DISK_FULL => return error.NoSpaceLeft,
......@@ -9740,8 +9672,6 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
97409672}
97419673
97429674fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
9743 if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)");
9744
97459675 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
97469676 var i: usize = 0;
97479677 for (data) |buf| {
......@@ -9787,9 +9717,44 @@ fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.Rea
97879717 }
97889718 }
97899719
9720 if (have_preadv) {
9721 const syscall: Syscall = try .start();
9722 while (true) {
9723 const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
9724 switch (posix.errno(rc)) {
9725 .SUCCESS => {
9726 syscall.finish();
9727 return @bitCast(rc);
9728 },
9729 .INTR, .TIMEDOUT => {
9730 try syscall.checkCancel();
9731 continue;
9732 },
9733 .NXIO => return syscall.fail(error.Unseekable),
9734 .SPIPE => return syscall.fail(error.Unseekable),
9735 .OVERFLOW => return syscall.fail(error.Unseekable),
9736 .NOBUFS => return syscall.fail(error.SystemResources),
9737 .NOMEM => return syscall.fail(error.SystemResources),
9738 .AGAIN => return syscall.fail(error.WouldBlock),
9739 .IO => return syscall.fail(error.InputOutput),
9740 .ISDIR => return syscall.fail(error.IsDir),
9741 .NOTCONN => |err| return syscall.errnoBug(err), // not a socket
9742 .CONNRESET => |err| return syscall.errnoBug(err), // not a socket
9743 .INVAL => |err| return syscall.errnoBug(err),
9744 .FAULT => |err| return syscall.errnoBug(err),
9745 .BADF => {
9746 syscall.finish();
9747 if (native_os == .wasi) return error.IsDir; // File operation on directory.
9748 return error.NotOpenForReading;
9749 },
9750 else => |err| return syscall.unexpectedErrno(err),
9751 }
9752 }
9753 }
9754
97909755 const syscall: Syscall = try .start();
97919756 while (true) {
9792 const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
9757 const rc = posix.pread(file.handle, dest[0].ptr, @intCast(dest[0].len), @bitCast(offset));
97939758 switch (posix.errno(rc)) {
97949759 .SUCCESS => {
97959760 syscall.finish();
......@@ -9838,65 +9803,119 @@ fn fileReadPositionalWindows(file: File, data: []const []u8, offset: u64) File.R
98389803}
98399804
98409805fn readFilePositionalWindows(file: File, buffer: []u8, offset: u64) File.ReadPositionalError!usize {
9841 const DWORD = windows.DWORD;
9842 const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
9843 var overlapped: windows.OVERLAPPED = .{
9844 .Internal = 0,
9845 .InternalHigh = 0,
9846 .DUMMYUNIONNAME = .{
9847 .DUMMYSTRUCTNAME = .{
9848 .Offset = @truncate(offset),
9849 .OffsetHigh = @truncate(offset >> 32),
9806 var iosb: windows.IO_STATUS_BLOCK = undefined;
9807 const short_buffer_len = std.math.lossyCast(u32, buffer.len);
9808 const signed_offset: windows.LARGE_INTEGER = @intCast(offset);
9809 if (file.flags.nonblocking) {
9810 var done: bool = false;
9811 switch (windows.ntdll.NtReadFile(
9812 file.handle,
9813 null, // event
9814 flagApc,
9815 &done, // APC context
9816 &iosb,
9817 buffer.ptr,
9818 short_buffer_len,
9819 &signed_offset,
9820 null, // key
9821 )) {
9822 // We must wait for the APC routine.
9823 .PENDING, .SUCCESS => while (!done) {
9824 // Once we get here we must not return from the function until the
9825 // operation completes, thereby releasing reference to the iosb.
9826 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
9827 error.Canceled => |e| {
9828 var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
9829 _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
9830 while (!done) waitForApcOrAlert();
9831 return e;
9832 },
9833 };
9834 waitForApcOrAlert();
9835 alertable_syscall.finish();
98509836 },
9851 },
9852 .hEvent = null,
9853 };
9854
9855 const syscall: Syscall = try .start();
9856 while (true) {
9857 var n: DWORD = undefined;
9858 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) != 0) {
9859 syscall.finish();
9860 return n;
9837 else => |status| iosb.u.Status = status,
98619838 }
9862 switch (windows.GetLastError()) {
9863 .IO_PENDING => |err| {
9864 syscall.finish();
9865 return windows.errorBug(err);
9866 },
9867 .OPERATION_ABORTED => {
9868 try syscall.checkCancel();
9869 continue;
9870 },
9871 .BROKEN_PIPE, .HANDLE_EOF => {
9872 syscall.finish();
9873 return 0;
9874 },
9875 .NETNAME_DELETED => if (is_debug) unreachable else return error.Unexpected,
9876 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
9877 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
9878 .INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected,
9879 // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing
9880 // a handle to a directory.
9881 .INVALID_FUNCTION => return syscall.fail(error.IsDir),
9882 else => |err| {
9839 } else {
9840 const syscall: Syscall = try .start();
9841 while (true) switch (windows.ntdll.NtReadFile(
9842 file.handle,
9843 null, // event
9844 null, // APC routine
9845 null, // APC context
9846 &iosb,
9847 buffer.ptr,
9848 short_buffer_len,
9849 &signed_offset,
9850 null, // key
9851 )) {
9852 .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
9853 .CANCELLED => try syscall.checkCancel(),
9854 else => |status| {
98839855 syscall.finish();
9884 return windows.unexpectedError(err);
9856 iosb.u.Status = status;
9857 break;
98859858 },
9886 }
9859 };
98879860 }
9861 return ntReadFileResult(&iosb) catch |err| switch (err) {
9862 error.EndOfStream => 0,
9863 else => |e| e,
9864 };
98889865}
98899866
98909867fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void {
98919868 const t: *Threaded = @ptrCast(@alignCast(userdata));
98929869 _ = t;
9893 const fd = file.handle;
98949870
9895 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
9896 var result: u64 = undefined;
9871 if (is_windows) {
9872 var iosb: windows.IO_STATUS_BLOCK = undefined;
9873 var info: windows.FILE.POSITION_INFORMATION = undefined;
9874 const syscall: Syscall = try .start();
9875 while (true) switch (windows.ntdll.NtQueryInformationFile(
9876 file.handle,
9877 &iosb,
9878 &info,
9879 @sizeOf(windows.FILE.POSITION_INFORMATION),
9880 .Position,
9881 )) {
9882 .SUCCESS => break,
9883 .CANCELLED => try syscall.checkCancel(),
9884 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
9885 .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
9886 else => |status| return syscall.unexpectedNtstatus(status),
9887 };
9888 info.CurrentByteOffset = @bitCast((if (offset >= 0) std.math.add(
9889 u64,
9890 @bitCast(info.CurrentByteOffset),
9891 @intCast(offset),
9892 ) else std.math.sub(
9893 u64,
9894 @bitCast(info.CurrentByteOffset),
9895 @intCast(-offset),
9896 )) catch |err| switch (err) {
9897 error.Overflow => return syscall.fail(error.Unseekable),
9898 });
9899 while (true) switch (windows.ntdll.NtSetInformationFile(
9900 file.handle,
9901 &iosb,
9902 &info,
9903 @sizeOf(windows.FILE.POSITION_INFORMATION),
9904 .Position,
9905 )) {
9906 .SUCCESS => return syscall.finish(),
9907 .CANCELLED => try syscall.checkCancel(),
9908 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
9909 .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
9910 else => |status| return syscall.unexpectedNtstatus(status),
9911 };
9912 }
9913
9914 if (native_os == .wasi and !builtin.link_libc) {
9915 var new_offset: std.os.wasi.filesize_t = undefined;
98979916 const syscall: Syscall = try .start();
98989917 while (true) {
9899 switch (posix.errno(posix.system.llseek(fd, @bitCast(offset), &result, posix.SEEK.CUR))) {
9918 switch (std.os.wasi.fd_seek(file.handle, offset, .CUR, &new_offset)) {
99009919 .SUCCESS => {
99019920 syscall.finish();
99029921 return;
......@@ -9913,6 +9932,7 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
99139932 .OVERFLOW => return error.Unseekable,
99149933 .SPIPE => return error.Unseekable,
99159934 .NXIO => return error.Unseekable,
9935 .NOTCAPABLE => return error.AccessDenied,
99169936 else => |err| return posix.unexpectedErrno(err),
99179937 }
99189938 },
......@@ -9920,34 +9940,13 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
99209940 }
99219941 }
99229942
9923 if (is_windows) {
9924 const syscall: Syscall = try .start();
9925 while (true) {
9926 if (windows.kernel32.SetFilePointerEx(fd, offset, null, windows.FILE_CURRENT) != 0) {
9927 return syscall.finish();
9928 }
9929 switch (windows.GetLastError()) {
9930 .OPERATION_ABORTED => {
9931 try syscall.checkCancel();
9932 continue;
9933 },
9934 .INVALID_FUNCTION => return syscall.fail(error.Unseekable),
9935 .NEGATIVE_SEEK => return syscall.fail(error.Unseekable),
9936 .INVALID_PARAMETER => unreachable,
9937 .INVALID_HANDLE => unreachable,
9938 else => |err| {
9939 syscall.finish();
9940 return windows.unexpectedError(err);
9941 },
9942 }
9943 }
9944 }
9943 if (posix.SEEK == void) return error.Unseekable;
99459944
9946 if (native_os == .wasi and !builtin.link_libc) {
9947 var new_offset: std.os.wasi.filesize_t = undefined;
9945 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
9946 var result: u64 = undefined;
99489947 const syscall: Syscall = try .start();
99499948 while (true) {
9950 switch (std.os.wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
9949 switch (posix.errno(posix.system.llseek(file.handle, @bitCast(offset), &result, posix.SEEK.CUR))) {
99519950 .SUCCESS => {
99529951 syscall.finish();
99539952 return;
......@@ -9964,7 +9963,6 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
99649963 .OVERFLOW => return error.Unseekable,
99659964 .SPIPE => return error.Unseekable,
99669965 .NXIO => return error.Unseekable,
9967 .NOTCAPABLE => return error.AccessDenied,
99689966 else => |err| return posix.unexpectedErrno(err),
99699967 }
99709968 },
......@@ -9972,11 +9970,9 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
99729970 }
99739971 }
99749972
9975 if (posix.SEEK == void) return error.Unseekable;
9976
99779973 const syscall: Syscall = try .start();
99789974 while (true) {
9979 switch (posix.errno(lseek_sym(fd, offset, posix.SEEK.CUR))) {
9975 switch (posix.errno(lseek_sym(file.handle, offset, posix.SEEK.CUR))) {
99809976 .SUCCESS => {
99819977 syscall.finish();
99829978 return;
......@@ -10003,41 +9999,31 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
100039999fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void {
1000410000 const t: *Threaded = @ptrCast(@alignCast(userdata));
1000510001 _ = t;
10006 const fd = file.handle;
1000710002
1000810003 if (is_windows) {
10009 // "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
10010 // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
10011 // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
10012 const ipos: windows.LARGE_INTEGER = @bitCast(offset);
10013
10004 var iosb: windows.IO_STATUS_BLOCK = undefined;
10005 var info: windows.FILE.POSITION_INFORMATION = .{ .CurrentByteOffset = @bitCast(offset) };
1001410006 const syscall: Syscall = try .start();
10015 while (true) {
10016 if (windows.kernel32.SetFilePointerEx(fd, ipos, null, windows.FILE_BEGIN) != 0) {
10017 return syscall.finish();
10018 }
10019 switch (windows.GetLastError()) {
10020 .OPERATION_ABORTED => {
10021 try syscall.checkCancel();
10022 continue;
10023 },
10024 .INVALID_FUNCTION => return syscall.fail(error.Unseekable),
10025 .NEGATIVE_SEEK => return syscall.fail(error.Unseekable),
10026 .INVALID_PARAMETER => unreachable,
10027 .INVALID_HANDLE => unreachable,
10028 else => |err| {
10029 syscall.finish();
10030 return windows.unexpectedError(err);
10031 },
10032 }
10033 }
10007 while (true) switch (windows.ntdll.NtSetInformationFile(
10008 file.handle,
10009 &iosb,
10010 &info,
10011 @sizeOf(windows.FILE.POSITION_INFORMATION),
10012 .Position,
10013 )) {
10014 .SUCCESS => return syscall.finish(),
10015 .CANCELLED => try syscall.checkCancel(),
10016 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
10017 .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
10018 else => |status| return syscall.unexpectedNtstatus(status),
10019 };
1003410020 }
1003510021
1003610022 if (native_os == .wasi and !builtin.link_libc) {
1003710023 const syscall: Syscall = try .start();
1003810024 while (true) {
1003910025 var new_offset: std.os.wasi.filesize_t = undefined;
10040 switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
10026 switch (std.os.wasi.fd_seek(file.handle, @bitCast(offset), .SET, &new_offset)) {
1004110027 .SUCCESS => {
1004210028 syscall.finish();
1004310029 return;
......@@ -10064,7 +10050,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi
1006410050
1006510051 if (posix.SEEK == void) return error.Unseekable;
1006610052
10067 return posixSeekTo(fd, offset);
10053 return posixSeekTo(file.handle, offset);
1006810054}
1006910055
1007010056fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void {
......@@ -10131,8 +10117,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) process.O
1013110117 // If ImagePathName is a symlink, then it will contain the path of the symlink,
1013210118 // not the path that the symlink points to. However, because we are opening
1013310119 // the file, we can let the openFileW call follow the symlink for us.
10134 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
10135 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
10120 const image_path_name = windows.peb().ProcessParameters.ImagePathName.sliceZ();
1013610121 const prefixed_path_w = try wToPrefixedFileW(null, image_path_name);
1013710122 return dirOpenFileWtf16(null, prefixed_path_w.span(), flags);
1013810123 },
......@@ -10334,14 +10319,13 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
1033410319 return error.FileNotFound;
1033510320 },
1033610321 .windows => {
10337 const w = windows;
10338 const image_path_unicode_string = &w.peb().ProcessParameters.ImagePathName;
10339 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
10340
1034110322 // If ImagePathName is a symlink, then it will contain the path of the
1034210323 // symlink, not the path that the symlink points to. We want the path
1034310324 // that the symlink points to, though, so we need to get the realpath.
10344 var path_name_w_buf = try wToPrefixedFileW(null, image_path_name);
10325 var path_name_w_buf = try wToPrefixedFileW(
10326 null,
10327 windows.peb().ProcessParameters.ImagePathName.sliceZ(),
10328 );
1034510329
1034610330 const h_file = handle: {
1034710331 if (OpenFile(path_name_w_buf.span(), .{
......@@ -10360,7 +10344,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
1036010344 else => |e| return e,
1036110345 }
1036210346 };
10363 defer w.CloseHandle(h_file);
10347 defer windows.CloseHandle(h_file);
1036410348
1036510349 const wide_slice = try GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data);
1036610350
......@@ -10388,15 +10372,15 @@ fn fileWritePositional(
1038810372
1038910373 if (is_windows) {
1039010374 if (header.len != 0) {
10391 return writeFilePositionalWindows(file.handle, header, offset);
10375 return writeFilePositionalWindows(file, header, offset);
1039210376 }
1039310377 for (data[0 .. data.len - 1]) |buf| {
1039410378 if (buf.len == 0) continue;
10395 return writeFilePositionalWindows(file.handle, buf, offset);
10379 return writeFilePositionalWindows(file, buf, offset);
1039610380 }
1039710381 const pattern = data[data.len - 1];
1039810382 if (pattern.len == 0 or splat == 0) return 0;
10399 return writeFilePositionalWindows(file.handle, pattern, offset);
10383 return writeFilePositionalWindows(file, pattern, offset);
1040010384 }
1040110385
1040210386 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
......@@ -10505,50 +10489,64 @@ fn fileWritePositional(
1050510489 }
1050610490}
1050710491
10508fn writeFilePositionalWindows(
10509 handle: windows.HANDLE,
10510 bytes: []const u8,
10511 offset: u64,
10512) File.WritePositionalError!usize {
10513 var bytes_written: windows.DWORD = undefined;
10514 var overlapped: windows.OVERLAPPED = .{
10515 .Internal = 0,
10516 .InternalHigh = 0,
10517 .DUMMYUNIONNAME = .{
10518 .DUMMYSTRUCTNAME = .{
10519 .Offset = @truncate(offset),
10520 .OffsetHigh = @truncate(offset >> 32),
10492fn writeFilePositionalWindows(file: File, buffer: []const u8, offset: u64) File.WritePositionalError!usize {
10493 assert(buffer.len != 0);
10494 var iosb: windows.IO_STATUS_BLOCK = undefined;
10495 const short_buffer_len = std.math.lossyCast(u32, buffer.len);
10496 const signed_offset: windows.LARGE_INTEGER = @intCast(offset);
10497 if (file.flags.nonblocking) {
10498 var done: bool = false;
10499 switch (windows.ntdll.NtWriteFile(
10500 file.handle,
10501 null, // event
10502 flagApc,
10503 &done, // APC context
10504 &iosb,
10505 buffer.ptr,
10506 short_buffer_len,
10507 &signed_offset,
10508 null, // key
10509 )) {
10510 // We must wait for the APC routine.
10511 .PENDING, .SUCCESS => while (!done) {
10512 // Once we get here we must not return from the function until the
10513 // operation completes, thereby releasing reference to the iosb.
10514 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
10515 error.Canceled => |e| {
10516 var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
10517 _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
10518 while (!done) waitForApcOrAlert();
10519 return e;
10520 },
10521 };
10522 waitForApcOrAlert();
10523 alertable_syscall.finish();
1052110524 },
10522 },
10523 .hEvent = null,
10524 };
10525 const adjusted_len = std.math.lossyCast(u32, bytes.len);
10526 const syscall: Syscall = try .start();
10527 while (true) {
10528 if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, &overlapped) != 0) {
10529 syscall.finish();
10530 return bytes_written;
10525 else => |status| iosb.u.Status = status,
1053110526 }
10532 switch (windows.GetLastError()) {
10533 .OPERATION_ABORTED => {
10534 try syscall.checkCancel();
10535 continue;
10536 },
10537 .INVALID_USER_BUFFER => return syscall.fail(error.SystemResources),
10538 .NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources),
10539 .NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources),
10540 .NO_DATA => return syscall.fail(error.BrokenPipe),
10541 .INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, // use after free
10542 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
10543 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
10544 .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
10545 .DISK_FULL => return syscall.fail(error.NoSpaceLeft),
10546 else => |err| {
10527 } else {
10528 const syscall: Syscall = try .start();
10529 while (true) switch (windows.ntdll.NtWriteFile(
10530 file.handle,
10531 null, // event
10532 null, // APC routine
10533 null, // APC context
10534 &iosb,
10535 buffer.ptr,
10536 short_buffer_len,
10537 &signed_offset,
10538 null, // key
10539 )) {
10540 .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
10541 .CANCELLED => try syscall.checkCancel(),
10542 else => |status| {
1054710543 syscall.finish();
10548 return windows.unexpectedError(err);
10544 iosb.u.Status = status;
10545 return ntWriteFileResult(&iosb);
1054910546 },
10550 }
10547 };
1055110548 }
10549 return ntWriteFileResult(&iosb);
1055210550}
1055310551
1055410552fn fileWriteStreaming(
......@@ -10673,9 +10671,7 @@ fn fileWriteStreaming(
1067310671
1067410672fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!usize {
1067510673 assert(buffer.len != 0);
10676
1067710674 var iosb: windows.IO_STATUS_BLOCK = undefined;
10678
1067910675 if (file.flags.nonblocking) {
1068010676 var done: bool = false;
1068110677 switch (windows.ntdll.NtWriteFile(
......@@ -10706,7 +10702,6 @@ fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!u
1070610702 },
1070710703 else => |status| iosb.u.Status = status,
1070810704 }
10709 return ntWriteFileResult(&iosb);
1071010705 } else {
1071110706 const syscall: Syscall = try .start();
1071210707 while (true) switch (windows.ntdll.NtWriteFile(
......@@ -10721,17 +10716,15 @@ fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!u
1072110716 null, // key
1072210717 )) {
1072310718 .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
10724 .CANCELLED => {
10725 try syscall.checkCancel();
10726 continue;
10727 },
10719 .CANCELLED => try syscall.checkCancel(),
1072810720 else => |status| {
1072910721 syscall.finish();
1073010722 iosb.u.Status = status;
10731 return ntWriteFileResult(&iosb);
10723 break;
1073210724 },
1073310725 };
1073410726 }
10727 return ntWriteFileResult(&iosb);
1073510728}
1073610729
1073710730fn fileWriteFileStreaming(
......@@ -11433,13 +11426,22 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp {
1143311426 return .{ .nanoseconds = @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100 + epoch_ns };
1143411427 },
1143511428 .awake, .boot => {
11436 // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
11437 const qpc = windows.QueryPerformanceCounter();
1143811429 // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA
1143911430 // (a read-only page of info updated and mapped by the kernel to all processes):
1144011431 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
1144111432 // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
11442 const qpf = windows.QueryPerformanceFrequency();
11433 const qpf: u64 = qpf: {
11434 var qpf: windows.LARGE_INTEGER = undefined;
11435 assert(windows.ntdll.RtlQueryPerformanceFrequency(&qpf) != windows.FALSE);
11436 break :qpf @bitCast(qpf);
11437 };
11438
11439 // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
11440 const qpc: u64 = qpc: {
11441 var qpc: windows.LARGE_INTEGER = undefined;
11442 assert(windows.ntdll.RtlQueryPerformanceCounter(&qpc) != windows.FALSE);
11443 break :qpc @bitCast(qpc);
11444 };
1144311445
1144411446 // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
1144511447 // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
......@@ -11458,7 +11460,7 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp {
1145811460 // https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L442-L485
1145911461 if (windows.ntdll.NtQueryInformationProcess(
1146011462 handle,
11461 windows.PROCESSINFOCLASS.Times,
11463 .Times,
1146211464 &times,
1146311465 @sizeOf(windows.KERNEL_USER_TIMES),
1146411466 null,
......@@ -11474,7 +11476,7 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp {
1147411476 // https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L2971-L3019
1147511477 if (windows.ntdll.NtQueryInformationThread(
1147611478 handle,
11477 windows.THREADINFOCLASS.Times,
11479 .Times,
1147811480 &times,
1147911481 @sizeOf(windows.KERNEL_USER_TIMES),
1148011482 null,
......@@ -14132,21 +14134,16 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr
1413214134}
1413314135
1413414136fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirError!void {
14135 if (native_os == .wasi) return error.OperationUnsupported;
1413614137 const t: *Threaded = @ptrCast(@alignCast(userdata));
1413714138 _ = t;
1413814139
14140 if (native_os == .wasi) return error.OperationUnsupported;
14141
1413914142 if (is_windows) {
14140 var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined;
14141 const dir_path = try GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer);
14142 const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong;
14143 var nt_name: windows.UNICODE_STRING = .{
14144 .Length = path_len_bytes,
14145 .MaximumLength = path_len_bytes,
14146 .Buffer = @constCast(dir_path.ptr),
14147 };
14143 var dir_path_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
14144 const dir_path = try GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buf);
1414814145 const syscall: Syscall = try .start();
14149 while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&nt_name)) {
14146 while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&.init(dir_path))) {
1415014147 .SUCCESS => return syscall.finish(),
1415114148 .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
1415214149 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
......@@ -15465,26 +15462,27 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
1546515462fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT) !void {
1546615463 _ = t; // TODO cancelation
1546715464 const handle = child.id.?;
15468 if (windows.kernel32.TerminateProcess(handle, exit_code) == 0) {
15469 switch (windows.GetLastError()) {
15470 .ACCESS_DENIED => {
15471 // Usually when TerminateProcess triggers a ACCESS_DENIED error, it
15472 // indicates that the process has already exited, but there may be
15473 // some rare edge cases where our process handle no longer has the
15474 // PROCESS_TERMINATE access right, so let's do another check to make
15475 // sure the process is really no longer running:
15476 const minimal_timeout: windows.LARGE_INTEGER = -1;
15477 switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) {
15478 .SUCCESS => return error.AlreadyTerminated,
15479 else => return error.AccessDenied,
15480 }
15481 },
15482 else => |err| return windows.unexpectedError(err),
15483 }
15465 _ = windows.ntdll.RtlReportSilentProcessExit(handle, @enumFromInt(exit_code));
15466 switch (windows.ntdll.NtTerminateProcess(handle, @enumFromInt(exit_code))) {
15467 .SUCCESS => {
15468 const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
15469 _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout);
15470 childCleanupWindows(child);
15471 },
15472 .ACCESS_DENIED => {
15473 // Usually when TerminateProcess triggers a ACCESS_DENIED error, it
15474 // indicates that the process has already exited, but there may be
15475 // some rare edge cases where our process handle no longer has the
15476 // PROCESS_TERMINATE access right, so let's do another check to make
15477 // sure the process is really no longer running:
15478 const minimal_timeout: windows.LARGE_INTEGER = -1;
15479 return switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) {
15480 windows.NTSTATUS.WAIT_0 => error.AlreadyTerminated,
15481 else => error.AccessDenied,
15482 };
15483 },
15484 else => |status| return windows.unexpectedStatus(status),
1548415485 }
15485 const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
15486 _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout);
15487 childCleanupWindows(child);
1548815486}
1548915487
1549015488fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term {
......@@ -15501,12 +15499,12 @@ fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child
1550115499 else => |status| return alertable_syscall.unexpectedNtstatus(status),
1550215500 };
1550315501
15504 var info: windows.PROCESS_BASIC_INFORMATION = undefined;
15502 var info: windows.PROCESS.BASIC_INFORMATION = undefined;
1550515503 const term: process.Child.Term = switch (windows.ntdll.NtQueryInformationProcess(
1550615504 handle,
1550715505 .BasicInformation,
1550815506 &info,
15509 @sizeOf(windows.PROCESS_BASIC_INFORMATION),
15507 @sizeOf(windows.PROCESS.BASIC_INFORMATION),
1551015508 null,
1551115509 )) {
1551215510 .SUCCESS => .{ .exited = @as(u8, @truncate(@intFromEnum(info.ExitStatus))) },
......@@ -15521,12 +15519,12 @@ fn childCleanupWindows(child: *process.Child) void {
1552115519 const handle = child.id orelse return;
1552215520
1552315521 if (child.request_resource_usage_statistics) {
15524 var vmc: windows.VM_COUNTERS = undefined;
15522 var vmc: windows.PROCESS.VM_COUNTERS = undefined;
1552515523 switch (windows.ntdll.NtQueryInformationProcess(
1552615524 handle,
1552715525 .VmCounters,
1552815526 &vmc,
15529 @sizeOf(windows.VM_COUNTERS),
15527 @sizeOf(windows.PROCESS.VM_COUNTERS),
1553015528 null,
1553115529 )) {
1553215530 .SUCCESS => child.resource_usage_statistics.rusage = vmc,
......@@ -15853,7 +15851,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
1585315851 .cbReserved2 = 0,
1585415852 .lpReserved2 = null,
1585515853 };
15856 var piProcInfo: windows.PROCESS_INFORMATION = undefined;
15854 var piProcInfo: windows.PROCESS.INFORMATION = undefined;
1585715855
1585815856 var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
1585915857 defer arena_allocator.deinit();
......@@ -16062,7 +16060,6 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
1606216060 if (t.random_file.handle) |handle| return handle;
1606316061 }
1606416062
16065 const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' };
1606616063 var fresh_handle: windows.HANDLE = undefined;
1606716064 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
1606816065 var syscall: Syscall = try .start();
......@@ -16072,13 +16069,9 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
1607216069 .STANDARD = .{ .SYNCHRONIZE = true },
1607316070 .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } },
1607416071 },
16075 &.{
16076 .ObjectName = @constCast(&windows.UNICODE_STRING{
16077 .Length = @sizeOf(@TypeOf(device_path)),
16078 .MaximumLength = 0,
16079 .Buffer = @constCast(&device_path),
16080 }),
16081 },
16072 &.{ .ObjectName = @constCast(&windows.UNICODE_STRING.init(
16073 &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' },
16074 )) },
1608216075 &io_status_block,
1608316076 .VALID_FLAGS,
1608416077 .{ .IO = .SYNCHRONOUS_NONALERT },
......@@ -16111,7 +16104,6 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE {
1611116104 if (t.null_file.handle) |handle| return handle;
1611216105 }
1611316106
16114 const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' };
1611516107 var fresh_handle: windows.HANDLE = undefined;
1611616108 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
1611716109 var syscall: Syscall = try .start();
......@@ -16123,11 +16115,9 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE {
1612316115 },
1612416116 &.{
1612516117 .Attributes = .{ .INHERIT = true },
16126 .ObjectName = @constCast(&windows.UNICODE_STRING{
16127 .Length = @sizeOf(@TypeOf(device_path)),
16128 .MaximumLength = 0,
16129 .Buffer = @constCast(&device_path),
16130 }),
16118 .ObjectName = @constCast(&windows.UNICODE_STRING.init(
16119 &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' },
16120 )),
1613116121 },
1613216122 &io_status_block,
1613316123 .VALID_FLAGS,
......@@ -16173,7 +16163,6 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {
1617316163 if (t.pipe_file.handle) |handle| return handle;
1617416164 }
1617516165
16176 const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' };
1617716166 var fresh_handle: windows.HANDLE = undefined;
1617816167 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
1617916168 var syscall: Syscall = try .start();
......@@ -16181,11 +16170,9 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {
1618116170 &fresh_handle,
1618216171 .{ .STANDARD = .{ .SYNCHRONIZE = true } },
1618316172 &.{
16184 .ObjectName = @constCast(&windows.UNICODE_STRING{
16185 .Length = @sizeOf(@TypeOf(device_path)),
16186 .MaximumLength = 0,
16187 .Buffer = @constCast(&device_path),
16188 }),
16173 .ObjectName = @constCast(&windows.UNICODE_STRING.init(
16174 &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' },
16175 )),
1618916176 },
1619016177 &io_status_block,
1619116178 .VALID_FLAGS,
......@@ -16253,7 +16240,7 @@ fn windowsCreateProcessPathExt(
1625316240 cwd_ptr: ?[*:0]u16,
1625416241 flags: windows.CreateProcessFlags,
1625516242 lpStartupInfo: *windows.STARTUPINFOW,
16256 lpProcessInformation: *windows.PROCESS_INFORMATION,
16243 lpProcessInformation: *windows.PROCESS.INFORMATION,
1625716244) !void {
1625816245 const app_name_len = app_buf.items.len;
1625916246 const dir_path_len = dir_buf.items.len;
......@@ -16341,13 +16328,9 @@ fn windowsCreateProcessPathExt(
1634116328 // On NTFS, `blah.exe*` will always return `blah.exe` first if it exists.
1634216329 // On FAT32, it's possible for something like `blah.exe.obj` to be returned first.
1634316330 while (true) {
16344 const app_name_len_bytes = std.math.cast(u16, app_name_wildcard.len * 2) orelse return error.NameTooLong;
16345 var app_name_unicode_string = windows.UNICODE_STRING{
16346 .Length = app_name_len_bytes,
16347 .MaximumLength = app_name_len_bytes,
16348 .Buffer = @constCast(app_name_wildcard.ptr),
16349 };
16350 const rc = windows.ntdll.NtQueryDirectoryFile(
16331 // If we get nothing with the wildcard, then we can just bail out
16332 // as we know appending PATHEXT will not yield anything.
16333 switch (windows.ntdll.NtQueryDirectoryFile(
1635116334 dir.handle,
1635216335 null,
1635316336 null,
......@@ -16357,18 +16340,14 @@ fn windowsCreateProcessPathExt(
1635716340 file_information_buf.len,
1635816341 .Directory,
1635916342 windows.FALSE, // single result
16360 &app_name_unicode_string,
16343 &.init(app_name_wildcard),
1636116344 windows.FALSE, // restart iteration
16362 );
16363
16364 // If we get nothing with the wildcard, then we can just bail out
16365 // as we know appending PATHEXT will not yield anything.
16366 switch (rc) {
16345 )) {
1636716346 .SUCCESS => {},
1636816347 .NO_SUCH_FILE => return error.FileNotFound,
1636916348 .NO_MORE_FILES => break,
1637016349 .ACCESS_DENIED => return error.AccessDenied,
16371 else => return windows.unexpectedStatus(rc),
16350 else => |status| return windows.unexpectedStatus(status),
1637216351 }
1637316352
1637416353 // According to the docs, this can only happen if there is not enough room in the
......@@ -16514,7 +16493,7 @@ fn windowsCreateProcess(
1651416493 cwd_ptr: ?[*:0]u16,
1651516494 flags: windows.CreateProcessFlags,
1651616495 lpStartupInfo: *windows.STARTUPINFOW,
16517 lpProcessInformation: *windows.PROCESS_INFORMATION,
16496 lpProcessInformation: *windows.PROCESS.INFORMATION,
1651816497) !void {
1651916498 const syscall: Syscall = try .start();
1652016499 while (true) {
......@@ -17138,7 +17117,7 @@ pub const CreatePipeOptions = struct {
1713817117 default_timeout: windows.LARGE_INTEGER = -120 * std.time.ns_per_s / 100,
1713917118
1714017119 pub const End = struct {
17141 attributes: windows.OBJECT_ATTRIBUTES.ATTRIBUTES = .{},
17120 attributes: windows.OBJECT.ATTRIBUTES.Flags = .{},
1714217121 mode: windows.FILE.MODE,
1714317122 };
1714417123};
......@@ -18373,7 +18352,7 @@ const CreateFileMapError = error{
1837318352 OutOfMemory,
1837418353 MappingAlreadyExists,
1837518354 Unseekable,
18376 FileLockConflict,
18355 LockViolation,
1837718356} || Io.Cancelable || Io.UnexpectedError;
1837818357
1837918358fn createFileMap(
......@@ -18408,7 +18387,7 @@ fn createFileMap(
1840818387 file.handle,
1840918388 )) {
1841018389 .SUCCESS => {},
18411 .FILE_LOCK_CONFLICT => return error.FileLockConflict,
18390 .FILE_LOCK_CONFLICT => return error.LockViolation,
1841218391 .INVALID_FILE_FOR_SECTION => return error.OperationUnsupported,
1841318392 .ACCESS_DENIED => return error.AccessDenied,
1841418393 .SECTION_TOO_BIG => return error.SectionOversize,
......@@ -18724,7 +18703,7 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!
1872418703 while (true) {
1872518704 const buf = memory[i..];
1872618705 if (buf.len == 0) break;
18727 i += try writeFilePositionalWindows(file.handle, memory[i..], offset + i);
18706 i += try writeFilePositionalWindows(file, memory[i..], offset + i);
1872818707 }
1872918708 } else if (native_os == .wasi and !builtin.link_libc) {
1873018709 var i: usize = 0;
......@@ -18809,8 +18788,7 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!
1880918788 }
1881018789}
1881118790
18812fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
18813 _ = t;
18791fn deviceIoControl(o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
1881418792 if (is_windows) {
1881518793 const NtControlFile = switch (o.code.DeviceType) {
1881618794 .FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
......@@ -19104,16 +19082,10 @@ fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!windows
1910419082
1910519083 var result: windows.HANDLE = undefined;
1910619084
19107 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
19108 var nt_name: windows.UNICODE_STRING = .{
19109 .Length = path_len_bytes,
19110 .MaximumLength = path_len_bytes,
19111 .Buffer = @constCast(sub_path_w.ptr),
19112 };
19113 const attr: windows.OBJECT_ATTRIBUTES = .{
19085 const attr: windows.OBJECT.ATTRIBUTES = .{
1911419086 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
1911519087 .Attributes = .{ .INHERIT = if (options.sa) |sa| sa.bInheritHandle != windows.FALSE else false },
19116 .ObjectName = &nt_name,
19088 .ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w)),
1911719089 .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null,
1911819090 };
1911919091
lib/std/Io/Threaded/test.zig+1-1
......@@ -290,7 +290,7 @@ fn RtlDosPathNameToNtPathName_U(path: [:0]const u16) !Io.Threaded.WindowsPathSpa
290290 defer windows.ntdll.RtlFreeUnicodeString(&out);
291291
292292 var path_space: Io.Threaded.WindowsPathSpace = undefined;
293 const out_path = out.Buffer.?[0 .. out.Length / 2];
293 const out_path = out.slice();
294294 @memcpy(path_space.data[0..out_path.len], out_path);
295295 path_space.len = out.Length / 2;
296296 path_space.data[path_space.len] = 0;
lib/std/Thread.zig+3-13
......@@ -88,20 +88,10 @@ pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void {
8888 },
8989 .windows => {
9090 var buf: [max_name_len]u16 = undefined;
91 const len = try std.unicode.wtf8ToWtf16Le(&buf, name);
92 const byte_len = math.cast(c_ushort, len * 2) orelse return error.NameTooLong;
93
94 // Note: NT allocates its own copy, no use-after-free here.
95 const unicode_string = windows.UNICODE_STRING{
96 .Length = byte_len,
97 .MaximumLength = byte_len,
98 .Buffer = &buf,
99 };
100
10191 switch (windows.ntdll.NtSetInformationThread(
10292 self.getHandle(),
10393 .NameInformation,
104 &unicode_string,
94 &windows.UNICODE_STRING.init(buf[0..try std.unicode.wtf8ToWtf16Le(&buf, name)]),
10595 @sizeOf(windows.UNICODE_STRING),
10696 )) {
10797 .SUCCESS => return,
......@@ -217,8 +207,8 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
217207 null,
218208 )) {
219209 .SUCCESS => {
220 const string = @as(*const windows.UNICODE_STRING, @ptrCast(&buf));
221 const len = std.unicode.wtf16LeToWtf8(buffer, string.Buffer.?[0 .. string.Length / 2]);
210 const string: *const windows.UNICODE_STRING = @ptrCast(&buf);
211 const len = std.unicode.wtf16LeToWtf8(buffer, string.slice());
222212 return if (len > 0) buffer[0..len] else null;
223213 },
224214 .NOT_IMPLEMENTED => return error.Unsupported,
lib/std/debug.zig+18-27
......@@ -37,12 +37,13 @@ pub const cpu_context = @import("debug/cpu_context.zig");
3737///
3838/// ```
3939/// pub const init: SelfInfo;
40/// pub fn deinit(si: *SelfInfo, gpa: Allocator) void;
40/// pub fn deinit(si: *SelfInfo, io: Io) void;
4141///
4242/// /// Returns the symbol and source location of the instruction at `address`.
43/// pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) SelfInfoError!Symbol;
43/// pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) SelfInfoError!Symbol;
4444/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
45/// pub fn getModuleName(si: *SelfInfo, gpa: Allocator, address: usize) SelfInfoError![]const u8;
45/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
46/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
4647///
4748/// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available.
4849/// pub const can_unwind: bool;
......@@ -51,15 +52,15 @@ pub const cpu_context = @import("debug/cpu_context.zig");
5152/// /// An address representing the instruction pointer in the last frame.
5253/// pc: usize,
5354///
54/// pub fn init(ctx: *cpu_context.Native, gpa: Allocator) Allocator.Error!UnwindContext;
55/// pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void;
55/// pub fn init(ctx: *cpu_context.Native) Allocator.Error!UnwindContext;
56/// pub fn deinit(ctx: *UnwindContext) void;
5657/// /// Returns the frame pointer associated with the last unwound stack frame.
5758/// /// If the frame pointer is unknown, 0 may be returned instead.
5859/// pub fn getFp(uc: *UnwindContext) usize;
5960/// };
6061/// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's
6162/// /// return address, or 0 if the end of the stack has been reached.
62/// pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) SelfInfoError!usize;
63/// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize;
6364/// ```
6465pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo"))
6566 root.debug.SelfInfo
......@@ -669,7 +670,6 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin
669670 t.setColor(.reset) catch {};
670671 return;
671672 }
672 const di_gpa = getDebugInfoAllocator();
673673 const di = getSelfDebugInfo() catch |err| switch (err) {
674674 error.UnsupportedTarget => {
675675 t.setColor(.dim) catch {};
......@@ -696,7 +696,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin
696696 .useless, .unsafe => {},
697697 .safe, .ideal => continue, // no need to even warn
698698 }
699 const module_name = di.getModuleName(di_gpa, io, unwind_error.address) catch "???";
699 const module_name = di.getModuleName(io, unwind_error.address) catch "???";
700700 const caption: []const u8 = switch (unwind_error.err) {
701701 error.MissingDebugInfo => "unwind info unavailable",
702702 error.InvalidDebugInfo => "unwind info invalid",
......@@ -741,7 +741,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin
741741 }
742742 // `ret_addr` is the return address, which is *after* the function call.
743743 // Subtract 1 to get an address *in* the function call for a better source location.
744 try printSourceAtAddress(di_gpa, io, di, t, ret_addr -| StackIterator.ra_call_offset);
744 try printSourceAtAddress(io, di, t, ret_addr -| StackIterator.ra_call_offset);
745745 printed_any_frame = true;
746746 },
747747 };
......@@ -788,7 +788,6 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void
788788 // `st` is `@errorReturnTrace()` and errors are encountered while writing the stack trace.
789789 const n_frames = st.index;
790790 if (n_frames == 0) return writer.writeAll("(empty stack trace)\n");
791 const di_gpa = getDebugInfoAllocator();
792791 const di = getSelfDebugInfo() catch |err| switch (err) {
793792 error.UnsupportedTarget => {
794793 t.setColor(.dim) catch {};
......@@ -802,7 +801,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void
802801 for (st.instruction_addresses[0..captured_frames]) |ret_addr| {
803802 // `ret_addr` is the return address, which is *after* the function call.
804803 // Subtract 1 to get an address *in* the function call for a better source location.
805 try printSourceAtAddress(di_gpa, io, di, t, ret_addr -| StackIterator.ra_call_offset);
804 try printSourceAtAddress(io, di, t, ret_addr -| StackIterator.ra_call_offset);
806805 }
807806 if (n_frames > captured_frames) {
808807 t.setColor(.bold) catch {};
......@@ -875,7 +874,7 @@ const StackIterator = union(enum) {
875874 switch (si.*) {
876875 .ctx_first => {},
877876 .fp => {},
878 .di => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()),
877 .di => |*unwind_context| unwind_context.deinit(),
879878 }
880879 }
881880
......@@ -980,8 +979,7 @@ const StackIterator = union(enum) {
980979 },
981980 .di => |*unwind_context| {
982981 const di = getSelfDebugInfo() catch unreachable;
983 const di_gpa = getDebugInfoAllocator();
984 const ret_addr = di.unwindFrame(di_gpa, io, unwind_context) catch |err| {
982 const ret_addr = di.unwindFrame(io, unwind_context) catch |err| {
985983 const pc = unwind_context.pc;
986984 const fp = unwind_context.getFp();
987985 it.* = .{ .fp = fp };
......@@ -1109,14 +1107,8 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
11091107 return ptr;
11101108}
11111109
1112fn printSourceAtAddress(
1113 gpa: Allocator,
1114 io: Io,
1115 debug_info: *SelfInfo,
1116 t: Io.Terminal,
1117 address: usize,
1118) Writer.Error!void {
1119 const symbol: Symbol = debug_info.getSymbol(gpa, io, address) catch |err| switch (err) {
1110fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void {
1111 const symbol: Symbol = debug_info.getSymbol(io, address) catch |err| switch (err) {
11201112 error.MissingDebugInfo,
11211113 error.UnsupportedDebugInfo,
11221114 error.InvalidDebugInfo,
......@@ -1134,14 +1126,14 @@ fn printSourceAtAddress(
11341126 break :s .unknown;
11351127 },
11361128 };
1137 defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
1129 defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name);
11381130 return printLineInfo(
11391131 io,
11401132 t,
11411133 symbol.source_location,
11421134 address,
11431135 symbol.name orelse "???",
1144 symbol.compile_unit_name orelse debug_info.getModuleName(gpa, io, address) catch "???",
1136 symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???",
11451137 );
11461138}
11471139fn printLineInfo(
......@@ -1608,14 +1600,13 @@ test "manage resources correctly" {
16081600 return @returnAddress();
16091601 }
16101602 };
1611 const gpa = testing.allocator;
16121603 const io = testing.io;
16131604
16141605 var discarding: Writer.Discarding = .init(&.{});
16151606 var di: SelfInfo = .init;
1616 defer di.deinit(gpa);
1607 defer di.deinit(io);
16171608 const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color };
1618 try printSourceAtAddress(gpa, io, &di, t, S.showMyTrace());
1609 try printSourceAtAddress(io, &di, t, S.showMyTrace());
16191610}
16201611
16211612/// This API helps you track where a value originated and where it was mutated,
lib/std/debug/Dwarf/SelfUnwinder.zig+2-1
......@@ -55,7 +55,8 @@ pub fn init(cpu_context: *const std.debug.cpu_context.Native) SelfUnwinder {
5555 };
5656}
5757
58pub fn deinit(unwinder: *SelfUnwinder, gpa: Allocator) void {
58pub fn deinit(unwinder: *SelfUnwinder) void {
59 const gpa = std.debug.getDebugInfoAllocator();
5960 unwinder.cfi_vm.deinit(gpa);
6061 unwinder.expr_vm.deinit(gpa);
6162 unwinder.* = undefined;
lib/std/debug/SelfInfo/Elf.zig+11-5
......@@ -11,7 +11,9 @@ pub const init: SelfInfo = .{
1111 .ranges = .empty,
1212 .unwind_cache = null,
1313};
14pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
14pub fn deinit(si: *SelfInfo, io: Io) void {
15 _ = io;
16 const gpa = std.debug.getDebugInfoAllocator();
1517 for (si.modules.items) |*mod| {
1618 unwind: {
1719 const u = &(mod.unwind orelse break :unwind catch break :unwind);
......@@ -28,7 +30,8 @@ pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
2830 if (si.unwind_cache) |cache| gpa.free(cache);
2931}
3032
31pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol {
33pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {
34 const gpa = std.debug.getDebugInfoAllocator();
3235 const module = try si.findModule(gpa, io, address, .exclusive);
3336 defer si.rwlock.unlock(io);
3437
......@@ -73,13 +76,15 @@ pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!st
7376 error.OutOfMemory => |e| return e,
7477 };
7578}
76pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 {
79pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
80 const gpa = std.debug.getDebugInfoAllocator();
7781 const module = try si.findModule(gpa, io, address, .shared);
7882 defer si.rwlock.unlockShared(io);
7983 if (module.name.len == 0) return error.MissingDebugInfo;
8084 return module.name;
8185}
82pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize {
86pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
87 const gpa = std.debug.getDebugInfoAllocator();
8388 const module = try si.findModule(gpa, io, address, .shared);
8489 defer si.rwlock.unlockShared(io);
8590 return module.load_offset;
......@@ -179,8 +184,9 @@ comptime {
179184 }
180185}
181186pub const UnwindContext = Dwarf.SelfUnwinder;
182pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize {
187pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize {
183188 comptime assert(can_unwind);
189 const gpa = std.debug.getDebugInfoAllocator();
184190
185191 {
186192 si.rwlock.lockSharedUncancelable(io);
lib/std/debug/SelfInfo/MachO.zig+12-8
......@@ -6,7 +6,9 @@ pub const init: SelfInfo = .{
66 .mutex = .init,
77 .modules = .empty,
88};
9pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
9pub fn deinit(si: *SelfInfo, io: Io) void {
10 _ = io;
11 const gpa = std.debug.getDebugInfoAllocator();
1012 for (si.modules.keys()) |*module| {
1113 unwind: {
1214 const u = &(module.unwind orelse break :unwind catch break :unwind);
......@@ -20,7 +22,8 @@ pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
2022 si.modules.deinit(gpa);
2123}
2224
23pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol {
25pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {
26 const gpa = std.debug.getDebugInfoAllocator();
2427 const module = try si.findModule(gpa, io, address);
2528 defer si.mutex.unlock(io);
2629
......@@ -76,9 +79,8 @@ pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!st
7679 ) catch null,
7780 };
7881}
79pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 {
82pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
8083 _ = si;
81 _ = gpa;
8284 _ = io;
8385 // This function is marked as deprecated; however, it is significantly more
8486 // 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
8789 @ptrFromInt(address),
8890 ) orelse return error.MissingDebugInfo);
8991}
90pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize {
92pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
93 const gpa = std.debug.getDebugInfoAllocator();
9194 const module = try si.findModule(gpa, io, address);
9295 defer si.mutex.unlock(io);
9396 const header: *std.macho.mach_header_64 = @ptrFromInt(module.text_base);
......@@ -107,8 +110,8 @@ pub const UnwindContext = std.debug.Dwarf.SelfUnwinder;
107110/// Unwind a frame using MachO compact unwind info (from `__unwind_info`).
108111/// If the compact encoding can't encode a way to unwind a frame, it will
109112/// defer unwinding to DWARF, in which case `__eh_frame` will be used if available.
110pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize {
111 return unwindFrameInner(si, gpa, io, context) catch |err| switch (err) {
113pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize {
114 return unwindFrameInner(si, io, context) catch |err| switch (err) {
112115 error.InvalidDebugInfo,
113116 error.MissingDebugInfo,
114117 error.UnsupportedDebugInfo,
......@@ -134,7 +137,8 @@ pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContex
134137 => return error.InvalidDebugInfo,
135138 };
136139}
137fn unwindFrameInner(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) !usize {
140fn unwindFrameInner(si: *SelfInfo, io: Io, context: *UnwindContext) !usize {
141 const gpa = std.debug.getDebugInfoAllocator();
138142 const module = try si.findModule(gpa, io, context.pc);
139143 defer si.mutex.unlock(io);
140144
lib/std/debug/SelfInfo/Windows.zig+151-86
......@@ -1,39 +1,51 @@
11mutex: Io.Mutex,
2ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn,
3notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE,
24modules: std.ArrayList(Module),
3module_name_arena: std.heap.ArenaAllocator.State,
45
56pub const init: SelfInfo = .{
67 .mutex = .init,
8 .ntdll_handle = null,
9 .notification_cookie = null,
710 .modules = .empty,
8 .module_name_arena = .{},
911};
10pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
11 for (si.modules.items) |*module| {
12 di: {
13 const di = &(module.di orelse break :di catch break :di);
14 di.deinit(gpa);
12pub fn deinit(si: *SelfInfo, io: Io) void {
13 const gpa = std.debug.getDebugInfoAllocator();
14 if (si.notification_cookie) |cookie| unregister: {
15 switch ((si.getNtdllProc(.LdrUnregisterDllNotification) catch break :unregister)(cookie)) {
16 .SUCCESS => {},
17 else => |status| windows.unexpectedStatus(status) catch break :unregister,
1518 }
1619 }
20 if (si.ntdll_handle) |handle| switch (windows.ntdll.LdrUnloadDll(handle)) {
21 .SUCCESS => {},
22 else => |status| windows.unexpectedStatus(status) catch {},
23 };
24 for (si.modules.items) |*module| module.deinit(gpa, io);
1725 si.modules.deinit(gpa);
18
19 var module_name_arena = si.module_name_arena.promote(gpa);
20 module_name_arena.deinit();
2126}
2227
23pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol {
28pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {
29 const gpa = std.debug.getDebugInfoAllocator();
2430 try si.mutex.lock(io);
2531 defer si.mutex.unlock(io);
2632 const module = try si.findModule(gpa, address);
2733 const di = try module.getDebugInfo(gpa, io);
28 return di.getSymbol(gpa, address - module.base_address);
34 return di.getSymbol(gpa, address - @intFromPtr(module.entry.DllBase));
2935}
30pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 {
36pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
37 const gpa = std.debug.getDebugInfoAllocator();
3138 try si.mutex.lock(io);
3239 defer si.mutex.unlock(io);
3340 const module = try si.findModule(gpa, address);
34 return module.name;
41 return module.name orelse {
42 const name = try std.unicode.wtf16LeToWtf8Alloc(gpa, module.entry.BaseDllName.slice());
43 module.name = name;
44 return name;
45 };
3546}
36pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize {
47pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
48 const gpa = std.debug.getDebugInfoAllocator();
3749 try si.mutex.lock(io);
3850 defer si.mutex.unlock(io);
3951 const module = try si.findModule(gpa, address);
......@@ -141,18 +153,16 @@ pub const UnwindContext = struct {
141153 .history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE),
142154 };
143155 }
144 pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void {
156 pub fn deinit(ctx: *UnwindContext) void {
145157 _ = ctx;
146 _ = gpa;
147158 }
148159 pub fn getFp(ctx: *UnwindContext) usize {
149160 return ctx.cur.getRegs().bp;
150161 }
151162};
152pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize {
163pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize {
153164 _ = si;
154165 _ = io;
155 _ = gpa;
156166
157167 const current_regs = context.cur.getRegs();
158168 var image_base: usize = undefined;
......@@ -188,16 +198,12 @@ pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContex
188198}
189199
190200const Module = struct {
191 base_address: usize,
192 size: u32,
193 name: []const u8,
194 handle: windows.HMODULE,
195
201 entry: *const LDR.DATA_TABLE_ENTRY,
202 name: ?[]const u8,
196203 di: ?(Error!DebugInfo),
197204
198205 const DebugInfo = struct {
199206 arena: std.heap.ArenaAllocator.State,
200 io: Io,
201207 coff_image_base: u64,
202208 mapped_file: ?MappedFile,
203209 dwarf: ?Dwarf,
......@@ -210,14 +216,19 @@ const Module = struct {
210216 section_view: []const u8,
211217 fn deinit(mf: *const MappedFile, io: Io) void {
212218 const process_handle = windows.GetCurrentProcess();
213 assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(mf.section_view.ptr)) == .SUCCESS);
219 switch (windows.ntdll.NtUnmapViewOfSection(
220 process_handle,
221 @constCast(mf.section_view.ptr),
222 )) {
223 .SUCCESS => {},
224 else => |status| windows.unexpectedStatus(status) catch {},
225 }
214226 windows.CloseHandle(mf.section_handle);
215227 mf.file.close(io);
216228 }
217229 };
218230
219 fn deinit(di: *DebugInfo, gpa: Allocator) void {
220 const io = di.io;
231 fn deinit(di: *DebugInfo, gpa: Allocator, io: Io) void {
221232 if (di.dwarf) |*dwarf| dwarf.deinit(gpa);
222233 if (di.pdb) |*pdb| {
223234 pdb.file_reader.file.close(io);
......@@ -262,7 +273,10 @@ const Module = struct {
262273 return .{
263274 .name = pdb.getSymbolName(module, vaddr - coff_section.virtual_address),
264275 .compile_unit_name = fs.path.basename(module.obj_file_name),
265 .source_location = pdb.getLineNumberInfo(module, vaddr - coff_section.virtual_address) catch null,
276 .source_location = pdb.getLineNumberInfo(
277 module,
278 vaddr - coff_section.virtual_address,
279 ) catch null,
266280 };
267281 }
268282 dwarf: {
......@@ -286,13 +300,19 @@ const Module = struct {
286300 }
287301 };
288302
303 fn deinit(module: *Module, gpa: Allocator, io: Io) void {
304 if (module.name) |name| gpa.free(name);
305 if (module.di) |*di_or_err| if (di_or_err.*) |*di| di.deinit(gpa, io) else |_| {};
306 module.* = undefined;
307 }
308
289309 fn getDebugInfo(module: *Module, gpa: Allocator, io: Io) Error!*DebugInfo {
290310 if (module.di == null) module.di = loadDebugInfo(module, gpa, io);
291311 return if (module.di.?) |*di| di else |err| err;
292312 }
293313 fn loadDebugInfo(module: *const Module, gpa: Allocator, io: Io) Error!DebugInfo {
294 const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address);
295 const mapped = mapped_ptr[0..module.size];
314 const mapped_ptr: [*]const u8 = @ptrCast(module.entry.DllBase);
315 const mapped = mapped_ptr[0..module.entry.SizeOfImage];
296316 var coff_obj = coff.Coff.init(mapped, true) catch return error.InvalidDebugInfo;
297317
298318 var arena_instance: std.heap.ArenaAllocator = .init(gpa);
......@@ -304,18 +324,15 @@ const Module = struct {
304324 // a binary is produced with -gdwarf, since the section names are longer than 8 bytes.
305325 const mapped_file: ?DebugInfo.MappedFile = mapped: {
306326 if (!coff_obj.strtabRequired()) break :mapped null;
307 var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
308 name_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present
309 const process_handle = windows.GetCurrentProcess();
310 const len = windows.kernel32.GetModuleFileNameExW(
311 process_handle,
312 module.handle,
313 name_buffer[4..],
314 windows.PATH_MAX_WIDE,
315 );
316 if (len == 0) return error.MissingDebugInfo;
317 const name_w = name_buffer[0 .. len + 4 :0];
318 const coff_file = Io.Threaded.dirOpenFileWtf16(null, name_w, .{}) catch |err| switch (err) {
327 var path_buffer: [4 + windows.PATH_MAX_WIDE]u16 = undefined;
328 path_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present
329 const path_slice = module.entry.FullDllName.slice();
330 @memcpy(path_buffer[4..][0..path_slice.len], path_slice);
331 const coff_file = Io.Threaded.dirOpenFileWtf16(
332 null,
333 path_buffer[0 .. 4 + path_slice.len],
334 .{},
335 ) catch |err| switch (err) {
319336 error.Canceled => |e| return e,
320337 error.Unexpected => |e| return e,
321338 error.FileNotFound => return error.MissingDebugInfo,
......@@ -359,7 +376,8 @@ const Module = struct {
359376 null,
360377 null,
361378 .{ .READONLY = true },
362 // The documentation states that if no AllocationAttribute is specified, then SEC_COMMIT is the default.
379 // The documentation states that if no AllocationAttribute is specified,
380 // then SEC_COMMIT is the default.
363381 // In practice, this isn't the case and specifying 0 will result in INVALID_PARAMETER_6.
364382 .{ .COMMIT = true },
365383 coff_file.handle,
......@@ -368,6 +386,7 @@ const Module = struct {
368386 errdefer windows.CloseHandle(section_handle);
369387 var coff_len: usize = 0;
370388 var section_view_ptr: ?[*]const u8 = null;
389 const process_handle = windows.GetCurrentProcess();
371390 const map_section_rc = windows.ntdll.NtMapViewOfSection(
372391 section_handle,
373392 process_handle,
......@@ -381,7 +400,13 @@ const Module = struct {
381400 .{ .READONLY = true },
382401 );
383402 if (map_section_rc != .SUCCESS) return error.MissingDebugInfo;
384 errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(section_view_ptr.?)) == .SUCCESS);
403 errdefer switch (windows.ntdll.NtUnmapViewOfSection(
404 process_handle,
405 @constCast(section_view_ptr.?),
406 )) {
407 .SUCCESS => {},
408 else => |status| windows.unexpectedStatus(status) catch {},
409 };
385410 const section_view = section_view_ptr.?[0..coff_len];
386411 coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo;
387412 break :mapped .{
......@@ -496,7 +521,6 @@ const Module = struct {
496521
497522 return .{
498523 .arena = arena_instance.state,
499 .io = io,
500524 .coff_image_base = coff_image_base,
501525 .mapped_file = mapped_file,
502526 .dwarf = opt_dwarf,
......@@ -509,52 +533,82 @@ const Module = struct {
509533/// Assumes we already hold `si.mutex`.
510534fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebugInfo, OutOfMemory, Unexpected }!*Module {
511535 for (si.modules.items) |*mod| {
512 if (address >= mod.base_address and address < mod.base_address + mod.size) {
513 return mod;
514 }
536 const base = @intFromPtr(mod.entry.DllBase);
537 if (address >= base and address < base + mod.entry.SizeOfImage) return mod;
515538 }
516
517 // A new module might have been loaded; rebuild the list.
518 {
519 for (si.modules.items) |*mod| {
520 const di = &(mod.di orelse continue catch continue);
521 di.deinit(gpa);
522 }
523 si.modules.clearRetainingCapacity();
524
525 var module_name_arena = si.module_name_arena.promote(gpa);
526 defer si.module_name_arena = module_name_arena.state;
527 _ = module_name_arena.reset(.retain_capacity);
528
529 const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0);
530 if (handle == windows.INVALID_HANDLE_VALUE) {
531 return windows.unexpectedError(windows.GetLastError());
532 }
533 defer windows.CloseHandle(handle);
534 var entry: windows.MODULEENTRY32 = undefined;
535 entry.dwSize = @sizeOf(windows.MODULEENTRY32);
536 var result = windows.kernel32.Module32First(handle, &entry);
537 while (result != 0) : (result = windows.kernel32.Module32Next(handle, &entry)) {
538 try si.modules.append(gpa, .{
539 .base_address = @intFromPtr(entry.modBaseAddr),
540 .size = entry.modBaseSize,
541 .name = try module_name_arena.allocator().dupe(
542 u8,
543 std.mem.sliceTo(&entry.szModule, 0),
544 ),
545 .handle = entry.hModule,
546 .di = null,
547 });
539 try si.modules.ensureUnusedCapacity(gpa, 1);
540 var entry: *LDR.DATA_TABLE_ENTRY = undefined;
541 switch (windows.ntdll.LdrFindEntryForAddress(@ptrFromInt(address), &entry)) {
542 .SUCCESS => {},
543 .DLL_NOT_FOUND => return error.MissingDebugInfo,
544 else => |status| return windows.unexpectedStatus(status),
545 }
546 if (si.notification_cookie == null) {
547 var notification_cookie: LDR.DLL_NOTIFICATION.COOKIE = undefined;
548 switch ((try si.getNtdllProc(.LdrRegisterDllNotification))(
549 .{},
550 &dllNotification,
551 si,
552 &notification_cookie,
553 )) {
554 .SUCCESS => si.notification_cookie = notification_cookie,
555 else => |status| return windows.unexpectedStatus(status),
548556 }
549557 }
558 const mod = si.modules.addOneAssumeCapacity();
559 mod.* = .{ .entry = entry, .name = null, .di = null };
560 return mod;
561}
550562
551 for (si.modules.items) |*mod| {
552 if (address >= mod.base_address and address < mod.base_address + mod.size) {
553 return mod;
563inline fn getNtdllProc(
564 si: *SelfInfo,
565 comptime proc: std.meta.DeclEnum(windows.ntdll),
566) !@TypeOf(&@field(windows.ntdll, @tagName(proc))) {
567 return if (load_dll_notification_procs)
568 @ptrCast(try si.loadNtdllProc(@tagName(proc)))
569 else
570 &@field(windows.ntdll, @tagName(proc));
571}
572fn loadNtdllProc(si: *SelfInfo, name: []const u8) Io.UnexpectedError!*anyopaque {
573 const ntdll_handle = si.ntdll_handle orelse ntdll_handle: {
574 var ntdll_handle: *anyopaque = undefined;
575 switch (windows.ntdll.LdrLoadDll(null, null, &.init(
576 &.{ 'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l' },
577 ), &ntdll_handle)) {
578 .SUCCESS => {},
579 else => |status| return windows.unexpectedStatus(status),
554580 }
581 si.ntdll_handle = ntdll_handle;
582 break :ntdll_handle ntdll_handle;
583 };
584 var proc_addr: *anyopaque = undefined;
585 switch (windows.ntdll.LdrGetProcedureAddress(ntdll_handle, &.init(name), 0, &proc_addr)) {
586 .SUCCESS => {},
587 else => |status| return windows.unexpectedStatus(status),
555588 }
589 return proc_addr;
590}
556591
557 return error.MissingDebugInfo;
592fn dllNotification(
593 reason: LDR.DLL_NOTIFICATION.REASON,
594 data: *const LDR.DLL_NOTIFICATION.DATA,
595 context: ?*anyopaque,
596) callconv(.winapi) void {
597 const si: *SelfInfo = @ptrCast(@alignCast(context));
598 switch (reason) {
599 .LOADED => {},
600 .UNLOADED => {
601 const io = std.Options.debug_io;
602 si.mutex.lockUncancelable(io);
603 defer si.mutex.unlock(io);
604 for (si.modules.items, 0..) |*mod, mod_index| {
605 if (mod.entry.DllBase != data.Unloaded.DllBase) continue;
606 mod.deinit(std.debug.getDebugInfoAllocator(), io);
607 _ = si.modules.swapRemove(mod_index);
608 break;
609 }
610 },
611 }
558612}
559613
560614const std = @import("std");
......@@ -563,12 +617,23 @@ const Allocator = std.mem.Allocator;
563617const Dwarf = std.debug.Dwarf;
564618const Pdb = std.debug.Pdb;
565619const Error = std.debug.SelfInfoError;
566const assert = std.debug.assert;
567620const coff = std.coff;
568621const fs = std.fs;
569622const windows = std.os.windows;
623const LDR = windows.LDR;
570624
571625const builtin = @import("builtin");
572626const native_endian = builtin.target.cpu.arch.endian();
627const load_dll_notification_procs = builtin.abi == .msvc and switch (builtin.zig_backend) {
628 .stage2_c => true,
629 else => switch (builtin.output_mode) {
630 .Exe => false,
631 .Lib => switch (builtin.link_mode) {
632 .static => true,
633 .dynamic => false,
634 },
635 .Obj => true,
636 },
637};
573638
574639const SelfInfo = @This();
lib/std/heap.zig+3-3
......@@ -110,11 +110,11 @@ pub fn defaultQueryPageSize() usize {
110110 break :size @intCast(vm_info.page_size);
111111 },
112112 .windows => {
113 var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
113 var sbi: windows.SYSTEM.BASIC_INFORMATION = undefined;
114114 switch (windows.ntdll.NtQuerySystemInformation(
115 .SystemBasicInformation,
115 .Basic,
116116 &sbi,
117 @sizeOf(windows.SYSTEM_BASIC_INFORMATION),
117 @sizeOf(windows.SYSTEM.BASIC_INFORMATION),
118118 null,
119119 )) {
120120 .SUCCESS => break :size sbi.PageSize,
lib/std/os/windows.zig+569-1126
......@@ -24,6 +24,66 @@ pub const nls = @import("windows/nls.zig");
2424
2525pub const current_process: HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))));
2626
27pub const OBJECT = struct {
28 // ref: um/winternl.h
29
30 pub const ATTRIBUTES = extern struct {
31 Length: ULONG = @sizeOf(ATTRIBUTES),
32 RootDirectory: ?HANDLE = null,
33 ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty),
34 Attributes: Flags = .{},
35 SecurityDescriptor: ?*anyopaque = null,
36 SecurityQualityOfService: ?*anyopaque = null,
37
38 // Valid values for the Attributes field
39 pub const Flags = packed struct(ULONG) {
40 Reserved0: u1 = 0,
41 INHERIT: bool = false,
42 Reserved2: u2 = 0,
43 PERMANENT: bool = false,
44 EXCLUSIVE: bool = false,
45 /// If name-lookup code should ignore the case of the ObjectName member rather than performing an exact-match search.
46 CASE_INSENSITIVE: bool = true,
47 OPENIF: bool = false,
48 OPENLINK: bool = false,
49 KERNEL_HANDLE: bool = false,
50 FORCE_ACCESS_CHECK: bool = false,
51 IGNORE_IMPERSONATED_DEVICEMAP: bool = false,
52 DONT_REPARSE: bool = false,
53 Reserved13: u19 = 0,
54
55 pub const VALID_ATTRIBUTES: ATTRIBUTES = .{
56 .INHERIT = true,
57 .PERMANENT = true,
58 .EXCLUSIVE = true,
59 .CASE_INSENSITIVE = true,
60 .OPENIF = true,
61 .OPENLINK = true,
62 .KERNEL_HANDLE = true,
63 .FORCE_ACCESS_CHECK = true,
64 .IGNORE_IMPERSONATED_DEVICEMAP = true,
65 .DONT_REPARSE = true,
66 };
67 };
68 };
69
70 pub const INFORMATION_CLASS = enum(c_int) {
71 Basic = 0,
72 Name = 1,
73 Type = 2,
74 Types = 3,
75 HandleFlag = 4,
76 Session = 5,
77 _,
78
79 pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".fields.len;
80 };
81
82 pub const NAME_INFORMATION = extern struct {
83 Name: UNICODE_STRING,
84 };
85};
86
2787pub const FILE = struct {
2888 // ref: km/ntddk.h
2989
......@@ -73,6 +133,94 @@ pub const FILE = struct {
73133
74134 // ref: km/ntifs.h
75135
136 pub const NAME_FLAGS = packed struct(UCHAR) {
137 NTFS: bool = false,
138 DOS: bool = false,
139 Reserved2: u5 = 0,
140 UNSPECIFIED: bool = false,
141 };
142
143 pub const NOTIFY = struct {
144 pub const CHANGE = packed struct(ULONG) {
145 FILE_NAME: bool = false,
146 DIR_NAME: bool = false,
147 ATTRIBUTES: bool = false,
148 SIZE: bool = false,
149 LAST_WRITE: bool = false,
150 LAST_ACCESS: bool = false,
151 CREATION: bool = false,
152 EA: bool = false,
153 SECURITY: bool = false,
154 STREAM_NAME: bool = false,
155 STREAM_SIZE: bool = false,
156 STREAM_WRITE: bool = false,
157 Reserved12: u20 = 0,
158 };
159
160 pub const INFORMATION = extern struct {
161 NextEntryOffset: ULONG,
162 Action: ULONG,
163 FileNameLength: ULONG,
164 FileName: [0]WCHAR,
165
166 pub fn fileName(info: *INFORMATION) []WCHAR {
167 const ptr: [*]WCHAR = @ptrCast(&info.FileName);
168 return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
169 }
170 };
171
172 pub const EXTENDED_INFORMATION = extern struct {
173 NextEntryOffset: ULONG,
174 Action: ULONG,
175 CreationTime: LARGE_INTEGER,
176 LastModificationTime: LARGE_INTEGER,
177 LastChangeTime: LARGE_INTEGER,
178 LastAccessTime: LARGE_INTEGER,
179 AllocatedLength: LARGE_INTEGER,
180 FileSize: LARGE_INTEGER,
181 FileAttributes: ATTRIBUTE,
182 u: extern union {
183 ReparsePointTag: ULONG,
184 EaSize: ULONG,
185 },
186 FileId: LARGE_INTEGER,
187 ParentFileId: LARGE_INTEGER,
188 FileNameLength: ULONG,
189 FileName: [0]WCHAR,
190
191 pub fn fileName(info: *INFORMATION) []WCHAR {
192 const ptr: [*]WCHAR = @ptrCast(&info.FileName);
193 return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
194 }
195 };
196
197 pub const FULL_INFORMATION = extern struct {
198 NextEntryOffset: ULONG,
199 Action: ULONG,
200 CreationTime: LARGE_INTEGER,
201 LastModificationTime: LARGE_INTEGER,
202 LastChangeTime: LARGE_INTEGER,
203 LastAccessTime: LARGE_INTEGER,
204 AllocatedLength: LARGE_INTEGER,
205 FileSize: LARGE_INTEGER,
206 FileAttributes: ATTRIBUTE,
207 u: extern union {
208 ReparsePointTag: ULONG,
209 EaSize: ULONG,
210 },
211 FileId: LARGE_INTEGER,
212 ParentFileId: LARGE_INTEGER,
213 FileNameLength: ULONG,
214 FileNameFlags: NAME_FLAGS,
215 FileName: [0]WCHAR,
216
217 pub fn fileName(info: *INFORMATION) []WCHAR {
218 const ptr: [*]WCHAR = @ptrCast(&info.FileName);
219 return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
220 }
221 };
222 };
223
76224 pub const PIPE = struct {
77225 /// Define the `NamedPipeType` flags for `NtCreateNamedPipeFile`
78226 pub const TYPE = packed struct(ULONG) {
......@@ -357,6 +505,7 @@ pub const FILE = struct {
357505 IdAllExtdBothDirectory = 81,
358506 StreamReservation = 82,
359507 MupProvider = 83,
508 _,
360509
361510 pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len;
362511 };
......@@ -647,6 +796,19 @@ pub const FILE = struct {
647796 Mode: MODE,
648797 };
649798 };
799
800 // ref: km/
801};
802
803pub const DIRECTORY = struct {
804 pub const NOTIFY_INFORMATION_CLASS = enum(c_int) {
805 Notify = 1,
806 NotifyExtended = 2,
807 NotifyFull = 3,
808 _,
809
810 pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len;
811 };
650812};
651813
652814pub const CONSOLE = struct {
......@@ -880,141 +1042,229 @@ pub const CONSOLE = struct {
8801042
8811043// ref: km/ntddk.h
8821044
883pub const PROCESSINFOCLASS = enum(c_int) {
884 BasicInformation = 0,
885 QuotaLimits = 1,
886 IoCounters = 2,
887 VmCounters = 3,
888 Times = 4,
889 BasePriority = 5,
890 RaisePriority = 6,
891 DebugPort = 7,
892 ExceptionPort = 8,
893 AccessToken = 9,
894 LdtInformation = 10,
895 LdtSize = 11,
896 DefaultHardErrorMode = 12,
897 IoPortHandlers = 13,
898 PooledUsageAndLimits = 14,
899 WorkingSetWatch = 15,
900 UserModeIOPL = 16,
901 EnableAlignmentFaultFixup = 17,
902 PriorityClass = 18,
903 Wx86Information = 19,
904 HandleCount = 20,
905 AffinityMask = 21,
906 PriorityBoost = 22,
907 DeviceMap = 23,
908 SessionInformation = 24,
909 ForegroundInformation = 25,
910 Wow64Information = 26,
911 ImageFileName = 27,
912 LUIDDeviceMapsEnabled = 28,
913 BreakOnTermination = 29,
914 DebugObjectHandle = 30,
915 DebugFlags = 31,
916 HandleTracing = 32,
917 IoPriority = 33,
918 ExecuteFlags = 34,
919 TlsInformation = 35,
920 Cookie = 36,
921 ImageInformation = 37,
922 CycleTime = 38,
923 PagePriority = 39,
924 InstrumentationCallback = 40,
925 ThreadStackAllocation = 41,
926 WorkingSetWatchEx = 42,
927 ImageFileNameWin32 = 43,
928 ImageFileMapping = 44,
929 AffinityUpdateMode = 45,
930 MemoryAllocationMode = 46,
931 GroupInformation = 47,
932 TokenVirtualizationEnabled = 48,
933 OwnerInformation = 49,
934 WindowInformation = 50,
935 HandleInformation = 51,
936 MitigationPolicy = 52,
937 DynamicFunctionTableInformation = 53,
938 HandleCheckingMode = 54,
939 KeepAliveCount = 55,
940 RevokeFileHandles = 56,
941 WorkingSetControl = 57,
942 HandleTable = 58,
943 CheckStackExtentsMode = 59,
944 CommandLineInformation = 60,
945 ProtectionInformation = 61,
946 MemoryExhaustion = 62,
947 FaultInformation = 63,
948 TelemetryIdInformation = 64,
949 CommitReleaseInformation = 65,
950 Reserved1Information = 66,
951 Reserved2Information = 67,
952 SubsystemProcess = 68,
953 InPrivate = 70,
954 RaiseUMExceptionOnInvalidHandleClose = 71,
955 SubsystemInformation = 75,
956 Win32kSyscallFilterInformation = 79,
957 EnergyTrackingState = 82,
958 NetworkIoCounters = 114,
959 _,
1045pub const SYSTEM = struct {
1046 pub const INFORMATION_CLASS = enum(c_int) {
1047 Basic = 0,
1048 Performance = 2,
1049 TimeOfDay = 3,
1050 Process = 5,
1051 ProcessorPerformance = 8,
1052 Interrupt = 23,
1053 Exception = 33,
1054 RegistryQuota = 37,
1055 Lookaside = 45,
1056 CodeIntegrity = 103,
1057 Policy = 134,
1058 _,
1059 };
1060
1061 pub const BASIC_INFORMATION = extern struct {
1062 Reserved: ULONG,
1063 TimerResolution: ULONG,
1064 PageSize: ULONG,
1065 NumberOfPhysicalPages: ULONG,
1066 LowestPhysicalPageNumber: ULONG,
1067 HighestPhysicalPageNumber: ULONG,
1068 AllocationGranularity: ULONG,
1069 MinimumUserModeAddress: ULONG_PTR,
1070 MaximumUserModeAddress: ULONG_PTR,
1071 ActiveProcessorsAffinityMask: KAFFINITY,
1072 NumberOfProcessors: UCHAR,
1073 };
1074};
1075
1076pub const PROCESS = struct {
1077 pub const INFORMATION = extern struct {
1078 hProcess: HANDLE,
1079 hThread: HANDLE,
1080 dwProcessId: DWORD,
1081 dwThreadId: DWORD,
1082 };
1083
1084 pub const INFOCLASS = enum(c_int) {
1085 BasicInformation = 0,
1086 QuotaLimits = 1,
1087 IoCounters = 2,
1088 VmCounters = 3,
1089 Times = 4,
1090 BasePriority = 5,
1091 RaisePriority = 6,
1092 DebugPort = 7,
1093 ExceptionPort = 8,
1094 AccessToken = 9,
1095 LdtInformation = 10,
1096 LdtSize = 11,
1097 DefaultHardErrorMode = 12,
1098 IoPortHandlers = 13,
1099 PooledUsageAndLimits = 14,
1100 WorkingSetWatch = 15,
1101 UserModeIOPL = 16,
1102 EnableAlignmentFaultFixup = 17,
1103 PriorityClass = 18,
1104 Wx86Information = 19,
1105 HandleCount = 20,
1106 AffinityMask = 21,
1107 PriorityBoost = 22,
1108 DeviceMap = 23,
1109 SessionInformation = 24,
1110 ForegroundInformation = 25,
1111 Wow64Information = 26,
1112 ImageFileName = 27,
1113 LUIDDeviceMapsEnabled = 28,
1114 BreakOnTermination = 29,
1115 DebugObjectHandle = 30,
1116 DebugFlags = 31,
1117 HandleTracing = 32,
1118 IoPriority = 33,
1119 ExecuteFlags = 34,
1120 TlsInformation = 35,
1121 Cookie = 36,
1122 ImageInformation = 37,
1123 CycleTime = 38,
1124 PagePriority = 39,
1125 InstrumentationCallback = 40,
1126 ThreadStackAllocation = 41,
1127 WorkingSetWatchEx = 42,
1128 ImageFileNameWin32 = 43,
1129 ImageFileMapping = 44,
1130 AffinityUpdateMode = 45,
1131 MemoryAllocationMode = 46,
1132 GroupInformation = 47,
1133 TokenVirtualizationEnabled = 48,
1134 OwnerInformation = 49,
1135 WindowInformation = 50,
1136 HandleInformation = 51,
1137 MitigationPolicy = 52,
1138 DynamicFunctionTableInformation = 53,
1139 HandleCheckingMode = 54,
1140 KeepAliveCount = 55,
1141 RevokeFileHandles = 56,
1142 WorkingSetControl = 57,
1143 HandleTable = 58,
1144 CheckStackExtentsMode = 59,
1145 CommandLineInformation = 60,
1146 ProtectionInformation = 61,
1147 MemoryExhaustion = 62,
1148 FaultInformation = 63,
1149 TelemetryIdInformation = 64,
1150 CommitReleaseInformation = 65,
1151 Reserved1Information = 66,
1152 Reserved2Information = 67,
1153 SubsystemProcess = 68,
1154 InPrivate = 70,
1155 RaiseUMExceptionOnInvalidHandleClose = 71,
1156 SubsystemInformation = 75,
1157 Win32kSyscallFilterInformation = 79,
1158 EnergyTrackingState = 82,
1159 NetworkIoCounters = 114,
1160 _,
1161
1162 pub const Max: @typeInfo(@This()).@"enum".tag_type = 117;
1163 };
1164
1165 pub const BASIC_INFORMATION = extern struct {
1166 ExitStatus: NTSTATUS,
1167 PebBaseAddress: *PEB,
1168 AffinityMask: ULONG_PTR,
1169 BasePriority: KPRIORITY,
1170 UniqueProcessId: ULONG_PTR,
1171 InheritedFromUniqueProcessId: ULONG_PTR,
1172 };
1173
1174 pub const VM_COUNTERS = extern struct {
1175 PeakVirtualSize: SIZE_T,
1176 VirtualSize: SIZE_T,
1177 PageFaultCount: ULONG,
1178 PeakWorkingSetSize: SIZE_T,
1179 WorkingSetSize: SIZE_T,
1180 QuotaPeakPagedPoolUsage: SIZE_T,
1181 QuotaPagedPoolUsage: SIZE_T,
1182 QuotaPeakNonPagedPoolUsage: SIZE_T,
1183 QuotaNonPagedPoolUsage: SIZE_T,
1184 PagefileUsage: SIZE_T,
1185 PeakPagefileUsage: SIZE_T,
1186 };
1187};
1188
1189pub const THREAD = struct {
1190 pub const INFOCLASS = enum(c_int) {
1191 BasicInformation = 0,
1192 Times = 1,
1193 Priority = 2,
1194 BasePriority = 3,
1195 AffinityMask = 4,
1196 ImpersonationToken = 5,
1197 DescriptorTableEntry = 6,
1198 EnableAlignmentFaultFixup = 7,
1199 EventPair_Reusable = 8,
1200 QuerySetWin32StartAddress = 9,
1201 ZeroTlsCell = 10,
1202 PerformanceCount = 11,
1203 AmILastThread = 12,
1204 IdealProcessor = 13,
1205 PriorityBoost = 14,
1206 SetTlsArrayAddress = 15,
1207 IsIoPending = 16,
1208 // Windows 2000+ from here
1209 HideFromDebugger = 17,
1210 // Windows XP+ from here
1211 BreakOnTermination = 18,
1212 SwitchLegacyState = 19,
1213 IsTerminated = 20,
1214 // Windows Vista+ from here
1215 LastSystemCall = 21,
1216 IoPriority = 22,
1217 CycleTime = 23,
1218 PagePriority = 24,
1219 ActualBasePriority = 25,
1220 TebInformation = 26,
1221 CSwitchMon = 27,
1222 // Windows 7+ from here
1223 CSwitchPmu = 28,
1224 Wow64Context = 29,
1225 GroupInformation = 30,
1226 UmsInformation = 31,
1227 CounterProfiling = 32,
1228 IdealProcessorEx = 33,
1229 // Windows 8+ from here
1230 CpuAccountingInformation = 34,
1231 // Windows 8.1+ from here
1232 SuspendCount = 35,
1233 // Windows 10+ from here
1234 HeterogeneousCpuPolicy = 36,
1235 ContainerId = 37,
1236 NameInformation = 38,
1237 SelectedCpuSets = 39,
1238 SystemThreadInformation = 40,
1239 ActualGroupAffinity = 41,
1240 DynamicCodePolicyInfo = 42,
1241 SubsystemInformation = 45,
1242 _,
1243
1244 pub const Max: @typeInfo(@This()).@"enum".tag_type = 60;
1245 };
9601246
961 pub const Max: @typeInfo(@This()).@"enum".tag_type = 117;
1247 pub const BASIC_INFORMATION = extern struct {
1248 ExitStatus: NTSTATUS,
1249 TebBaseAddress: PVOID,
1250 ClientId: CLIENT_ID,
1251 AffinityMask: KAFFINITY,
1252 Priority: KPRIORITY,
1253 BasePriority: KPRIORITY,
1254 };
9621255};
9631256
964pub const THREADINFOCLASS = enum(c_int) {
965 BasicInformation = 0,
966 Times = 1,
967 Priority = 2,
968 BasePriority = 3,
969 AffinityMask = 4,
970 ImpersonationToken = 5,
971 DescriptorTableEntry = 6,
972 EnableAlignmentFaultFixup = 7,
973 EventPair_Reusable = 8,
974 QuerySetWin32StartAddress = 9,
975 ZeroTlsCell = 10,
976 PerformanceCount = 11,
977 AmILastThread = 12,
978 IdealProcessor = 13,
979 PriorityBoost = 14,
980 SetTlsArrayAddress = 15,
981 IsIoPending = 16,
982 // Windows 2000+ from here
983 HideFromDebugger = 17,
984 // Windows XP+ from here
985 BreakOnTermination = 18,
986 SwitchLegacyState = 19,
987 IsTerminated = 20,
988 // Windows Vista+ from here
989 LastSystemCall = 21,
990 IoPriority = 22,
991 CycleTime = 23,
992 PagePriority = 24,
993 ActualBasePriority = 25,
994 TebInformation = 26,
995 CSwitchMon = 27,
996 // Windows 7+ from here
997 CSwitchPmu = 28,
998 Wow64Context = 29,
999 GroupInformation = 30,
1000 UmsInformation = 31,
1001 CounterProfiling = 32,
1002 IdealProcessorEx = 33,
1003 // Windows 8+ from here
1004 CpuAccountingInformation = 34,
1005 // Windows 8.1+ from here
1006 SuspendCount = 35,
1007 // Windows 10+ from here
1008 HeterogeneousCpuPolicy = 36,
1009 ContainerId = 37,
1010 NameInformation = 38,
1011 SelectedCpuSets = 39,
1012 SystemThreadInformation = 40,
1013 ActualGroupAffinity = 41,
1014 DynamicCodePolicyInfo = 42,
1015 SubsystemInformation = 45,
1016
1017 pub const Max: @typeInfo(@This()).@"enum".tag_type = 60;
1257pub const MEMORY = struct {
1258 pub const BASIC_INFORMATION = extern struct {
1259 BaseAddress: PVOID,
1260 AllocationBase: PVOID,
1261 AllocationProtect: DWORD,
1262 PartitionId: WORD,
1263 RegionSize: SIZE_T,
1264 State: DWORD,
1265 Protect: DWORD,
1266 Type: DWORD,
1267 };
10181268};
10191269
10201270// ref: km/ntifs.h
......@@ -2560,48 +2810,6 @@ pub fn GetProcessHeap() ?*HEAP {
25602810 return peb().ProcessHeap;
25612811}
25622812
2563// ref: um/winternl.h
2564
2565pub const OBJECT_ATTRIBUTES = extern struct {
2566 Length: ULONG = @sizeOf(OBJECT_ATTRIBUTES),
2567 RootDirectory: ?HANDLE = null,
2568 ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty),
2569 Attributes: ATTRIBUTES = .{},
2570 SecurityDescriptor: ?*anyopaque = null,
2571 SecurityQualityOfService: ?*anyopaque = null,
2572
2573 // Valid values for the Attributes field
2574 pub const ATTRIBUTES = packed struct(ULONG) {
2575 Reserved0: u1 = 0,
2576 INHERIT: bool = false,
2577 Reserved2: u2 = 0,
2578 PERMANENT: bool = false,
2579 EXCLUSIVE: bool = false,
2580 /// If name-lookup code should ignore the case of the ObjectName member rather than performing an exact-match search.
2581 CASE_INSENSITIVE: bool = true,
2582 OPENIF: bool = false,
2583 OPENLINK: bool = false,
2584 KERNEL_HANDLE: bool = false,
2585 FORCE_ACCESS_CHECK: bool = false,
2586 IGNORE_IMPERSONATED_DEVICEMAP: bool = false,
2587 DONT_REPARSE: bool = false,
2588 Reserved13: u19 = 0,
2589
2590 pub const VALID_ATTRIBUTES: ATTRIBUTES = .{
2591 .INHERIT = true,
2592 .PERMANENT = true,
2593 .EXCLUSIVE = true,
2594 .CASE_INSENSITIVE = true,
2595 .OPENIF = true,
2596 .OPENLINK = true,
2597 .KERNEL_HANDLE = true,
2598 .FORCE_ACCESS_CHECK = true,
2599 .IGNORE_IMPERSONATED_DEVICEMAP = true,
2600 .DONT_REPARSE = true,
2601 };
2602 };
2603};
2604
26052813// ref none
26062814
26072815pub fn GetCurrentProcess() HANDLE {
......@@ -2623,297 +2831,13 @@ pub fn GetCurrentThreadId() DWORD {
26232831}
26242832
26252833pub fn GetLastError() Win32Error {
2626 return @enumFromInt(teb().LastErrorValue);
2627}
2628/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
2629/// It implements similar behavior to `DeviceIoControl` and is meant to serve
2630/// as a direct substitute for that call.
2631/// TODO work out if we need to expose other arguments to the underlying syscalls.
2632pub fn DeviceIoControl(
2633 device: HANDLE,
2634 io_control_code: CTL_CODE,
2635 opts: struct {
2636 event: ?HANDLE = null,
2637 apc_routine: ?*const IO_APC_ROUTINE = null,
2638 apc_context: ?*anyopaque = null,
2639 io_status_block: ?*IO_STATUS_BLOCK = null,
2640 in: []const u8 = &.{},
2641 out: []u8 = &.{},
2642 },
2643) NTSTATUS {
2644 var io_status_block: IO_STATUS_BLOCK = undefined;
2645 return switch (io_control_code.DeviceType) {
2646 .FILE_SYSTEM, .NAMED_PIPE => ntdll.NtFsControlFile(
2647 device,
2648 opts.event,
2649 opts.apc_routine,
2650 opts.apc_context,
2651 opts.io_status_block orelse &io_status_block,
2652 io_control_code,
2653 if (opts.in.len > 0) opts.in.ptr else null,
2654 @intCast(opts.in.len),
2655 if (opts.out.len > 0) opts.out.ptr else null,
2656 @intCast(opts.out.len),
2657 ),
2658 else => ntdll.NtDeviceIoControlFile(
2659 device,
2660 opts.event,
2661 opts.apc_routine,
2662 opts.apc_context,
2663 opts.io_status_block orelse &io_status_block,
2664 io_control_code,
2665 if (opts.in.len > 0) opts.in.ptr else null,
2666 @intCast(opts.in.len),
2667 if (opts.out.len > 0) opts.out.ptr else null,
2668 @intCast(opts.out.len),
2669 ),
2670 };
2671}
2672
2673pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
2674 var bytes: DWORD = undefined;
2675 if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @intFromBool(wait)) == 0) {
2676 switch (GetLastError()) {
2677 .IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable,
2678 else => |err| return unexpectedError(err),
2679 }
2680 }
2681 return bytes;
2682}
2683
2684pub const CreateIoCompletionPortError = error{Unexpected};
2685
2686pub fn CreateIoCompletionPort(
2687 file_handle: HANDLE,
2688 existing_completion_port: ?HANDLE,
2689 completion_key: usize,
2690 concurrent_thread_count: DWORD,
2691) CreateIoCompletionPortError!HANDLE {
2692 const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
2693 switch (GetLastError()) {
2694 .INVALID_PARAMETER => unreachable,
2695 else => |err| return unexpectedError(err),
2696 }
2697 };
2698 return handle;
2699}
2700
2701pub const PostQueuedCompletionStatusError = error{Unexpected};
2702
2703pub fn PostQueuedCompletionStatus(
2704 completion_port: HANDLE,
2705 bytes_transferred_count: DWORD,
2706 completion_key: usize,
2707 lpOverlapped: ?*OVERLAPPED,
2708) PostQueuedCompletionStatusError!void {
2709 if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
2710 switch (GetLastError()) {
2711 else => |err| return unexpectedError(err),
2712 }
2713 }
2714}
2715
2716pub const GetQueuedCompletionStatusResult = enum {
2717 Normal,
2718 Aborted,
2719 Canceled,
2720 EOF,
2721 Timeout,
2722};
2723
2724pub fn GetQueuedCompletionStatus(
2725 completion_port: HANDLE,
2726 bytes_transferred_count: *DWORD,
2727 lpCompletionKey: *usize,
2728 lpOverlapped: *?*OVERLAPPED,
2729 dwMilliseconds: DWORD,
2730) GetQueuedCompletionStatusResult {
2731 if (kernel32.GetQueuedCompletionStatus(
2732 completion_port,
2733 bytes_transferred_count,
2734 lpCompletionKey,
2735 lpOverlapped,
2736 dwMilliseconds,
2737 ) == FALSE) {
2738 switch (GetLastError()) {
2739 .ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
2740 .OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Canceled,
2741 .HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
2742 .WAIT_TIMEOUT => return GetQueuedCompletionStatusResult.Timeout,
2743 else => |err| {
2744 if (std.debug.runtime_safety) {
2745 @setEvalBranchQuota(2500);
2746 std.debug.panic("unexpected error: {}\n", .{err});
2747 }
2748 },
2749 }
2750 }
2751 return GetQueuedCompletionStatusResult.Normal;
2752}
2753
2754pub const GetQueuedCompletionStatusError = error{
2755 Aborted,
2756 Canceled,
2757 EOF,
2758 Timeout,
2759} || UnexpectedError;
2760
2761pub fn GetQueuedCompletionStatusEx(
2762 completion_port: HANDLE,
2763 completion_port_entries: []OVERLAPPED_ENTRY,
2764 timeout_ms: ?DWORD,
2765 alertable: bool,
2766) GetQueuedCompletionStatusError!u32 {
2767 var num_entries_removed: u32 = 0;
2768
2769 const success = kernel32.GetQueuedCompletionStatusEx(
2770 completion_port,
2771 completion_port_entries.ptr,
2772 @as(ULONG, @intCast(completion_port_entries.len)),
2773 &num_entries_removed,
2774 timeout_ms orelse INFINITE,
2775 @intFromBool(alertable),
2776 );
2777
2778 if (success == FALSE) {
2779 return switch (GetLastError()) {
2780 .ABANDONED_WAIT_0 => error.Aborted,
2781 .OPERATION_ABORTED => error.Canceled,
2782 .HANDLE_EOF => error.EOF,
2783 .WAIT_TIMEOUT => error.Timeout,
2784 else => |err| unexpectedError(err),
2785 };
2786 }
2787
2788 return num_entries_removed;
2834 return teb().LastErrorValue;
27892835}
27902836
27912837pub fn CloseHandle(hObject: HANDLE) void {
2792 assert(ntdll.NtClose(hObject) == .SUCCESS);
2793}
2794
2795pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
2796 return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen)));
2797}
2798
2799pub fn sendmsg(
2800 s: ws2_32.SOCKET,
2801 msg: *ws2_32.WSAMSG_const,
2802 flags: u32,
2803) i32 {
2804 var bytes_send: DWORD = undefined;
2805 if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) {
2806 return ws2_32.SOCKET_ERROR;
2807 } else {
2808 return @as(i32, @as(u31, @intCast(bytes_send)));
2809 }
2810}
2811
2812pub 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 {
2813 var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = @constCast(buf) };
2814 var bytes_send: DWORD = undefined;
2815 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) {
2816 return ws2_32.SOCKET_ERROR;
2817 } else {
2818 return @as(i32, @as(u31, @intCast(bytes_send)));
2819 }
2820}
2821
2822pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 {
2823 var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = buf };
2824 var bytes_received: DWORD = undefined;
2825 var flags_inout = flags;
2826 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) {
2827 return ws2_32.SOCKET_ERROR;
2828 } else {
2829 return @as(i32, @as(u31, @intCast(bytes_received)));
2830 }
2831}
2832
2833pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 {
2834 return ws2_32.WSAPoll(fds, n, timeout);
2835}
2836
2837pub fn WSAIoctl(
2838 s: ws2_32.SOCKET,
2839 dwIoControlCode: DWORD,
2840 inBuffer: ?[]const u8,
2841 outBuffer: []u8,
2842 overlapped: ?*OVERLAPPED,
2843 completionRoutine: ?ws2_32.LPWSAOVERLAPPED_COMPLETION_ROUTINE,
2844) !DWORD {
2845 var bytes: DWORD = undefined;
2846 switch (ws2_32.WSAIoctl(
2847 s,
2848 dwIoControlCode,
2849 if (inBuffer) |i| i.ptr else null,
2850 if (inBuffer) |i| @as(DWORD, @intCast(i.len)) else 0,
2851 outBuffer.ptr,
2852 @as(DWORD, @intCast(outBuffer.len)),
2853 &bytes,
2854 overlapped,
2855 completionRoutine,
2856 )) {
2857 0 => {},
2858 ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
2859 else => |err| return unexpectedWSAError(err),
2860 },
2861 else => unreachable,
2862 }
2863 return bytes;
2864}
2865
2866const GetModuleFileNameError = error{Unexpected};
2867
2868pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 {
2869 const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len);
2870 if (rc == 0) {
2871 switch (GetLastError()) {
2872 else => |err| return unexpectedError(err),
2873 }
2874 }
2875 return buf_ptr[0..rc :0];
2876}
2877
2878pub const NtAllocateVirtualMemoryError = error{
2879 AccessDenied,
2880 InvalidParameter,
2881 NoMemory,
2882 Unexpected,
2883};
2884
2885pub fn NtAllocateVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, zero_bits: ULONG_PTR, size: ?*SIZE_T, alloc_type: ULONG, protect: ULONG) NtAllocateVirtualMemoryError!void {
2886 return switch (ntdll.NtAllocateVirtualMemory(hProcess, addr, zero_bits, size, alloc_type, protect)) {
2887 .SUCCESS => return,
2888 .ACCESS_DENIED => NtAllocateVirtualMemoryError.AccessDenied,
2889 .INVALID_PARAMETER => NtAllocateVirtualMemoryError.InvalidParameter,
2890 .NO_MEMORY => NtAllocateVirtualMemoryError.NoMemory,
2891 else => |st| unexpectedStatus(st),
2892 };
2893}
2894
2895pub const NtFreeVirtualMemoryError = error{
2896 AccessDenied,
2897 InvalidParameter,
2898 Unexpected,
2899};
2900
2901pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_type: ULONG) NtFreeVirtualMemoryError!void {
2902 // TODO: If the return value is .INVALID_PAGE_PROTECTION, call RtlFlushSecureMemoryCache and try again.
2903 return switch (ntdll.NtFreeVirtualMemory(hProcess, addr, size, free_type)) {
2904 .SUCCESS => return,
2905 .ACCESS_DENIED => NtFreeVirtualMemoryError.AccessDenied,
2906 .INVALID_PARAMETER => NtFreeVirtualMemoryError.InvalidParameter,
2907 else => NtFreeVirtualMemoryError.Unexpected,
2908 };
2909}
2910
2911pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
2912 const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
2913 if (success == FALSE) {
2914 return switch (GetLastError()) {
2915 else => |err| unexpectedError(err),
2916 };
2838 switch (ntdll.NtClose(hObject)) {
2839 .SUCCESS => {},
2840 else => |status| unexpectedStatus(status) catch {},
29172841 }
29182842}
29192843
......@@ -2963,114 +2887,75 @@ pub const CreateProcessFlags = packed struct(u32) {
29632887 create_ignore_system_default: bool = false,
29642888};
29652889
2966pub const LoadLibraryError = error{
2967 FileNotFound,
2968 Unexpected,
2969};
2970
2971pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE {
2972 return kernel32.LoadLibraryW(lpLibFileName) orelse {
2973 switch (GetLastError()) {
2974 .FILE_NOT_FOUND => return error.FileNotFound,
2975 .PATH_NOT_FOUND => return error.FileNotFound,
2976 .MOD_NOT_FOUND => return error.FileNotFound,
2977 else => |err| return unexpectedError(err),
2978 }
2979 };
2980}
2981
2982pub const LoadLibraryFlags = enum(DWORD) {
2983 none = 0,
2984 dont_resolve_dll_references = 0x00000001,
2985 load_ignore_code_authz_level = 0x00000010,
2986 load_library_as_datafile = 0x00000002,
2987 load_library_as_datafile_exclusive = 0x00000040,
2988 load_library_as_image_resource = 0x00000020,
2989 load_library_search_application_dir = 0x00000200,
2990 load_library_search_default_dirs = 0x00001000,
2991 load_library_search_dll_load_dir = 0x00000100,
2992 load_library_search_system32 = 0x00000800,
2993 load_library_search_user_dirs = 0x00000400,
2994 load_with_altered_search_path = 0x00000008,
2995 load_library_require_signed_target = 0x00000080,
2996 load_library_safe_current_dirs = 0x00002000,
2997};
2998
2999pub fn LoadLibraryExW(lpLibFileName: [*:0]const u16, dwFlags: LoadLibraryFlags) LoadLibraryError!HMODULE {
3000 return kernel32.LoadLibraryExW(lpLibFileName, null, @intFromEnum(dwFlags)) orelse {
3001 switch (GetLastError()) {
3002 .FILE_NOT_FOUND => return error.FileNotFound,
3003 .PATH_NOT_FOUND => return error.FileNotFound,
3004 .MOD_NOT_FOUND => return error.FileNotFound,
3005 else => |err| return unexpectedError(err),
3006 }
3007 };
3008}
3009
3010pub fn FreeLibrary(hModule: HMODULE) void {
3011 assert(kernel32.FreeLibrary(hModule) != 0);
3012}
3013
3014pub fn QueryPerformanceFrequency() u64 {
3015 // "On systems that run Windows XP or later, the function will always succeed"
3016 // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency
3017 var result: LARGE_INTEGER = undefined;
3018 assert(ntdll.RtlQueryPerformanceFrequency(&result) != 0);
3019 // The kernel treats this integer as unsigned.
3020 return @as(u64, @bitCast(result));
3021}
3022
3023pub fn QueryPerformanceCounter() u64 {
3024 // "On systems that run Windows XP or later, the function will always succeed"
3025 // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter
3026 var result: LARGE_INTEGER = undefined;
3027 assert(ntdll.RtlQueryPerformanceCounter(&result) != 0);
3028 // The kernel treats this integer as unsigned.
3029 return @as(u64, @bitCast(result));
3030}
3031
3032/// This is a workaround for the C backend until zig has the ability to put
3033/// C code in inline assembly.
3034extern fn zig_thumb_windows_teb() callconv(.c) *anyopaque;
3035extern fn zig_aarch64_windows_teb() callconv(.c) *anyopaque;
3036extern fn zig_x86_windows_teb() callconv(.c) *anyopaque;
3037extern fn zig_x86_64_windows_teb() callconv(.c) *anyopaque;
3038
30392890pub fn teb() *TEB {
3040 return switch (native_arch) {
3041 .thumb => if (builtin.zig_backend == .stage2_c)
3042 @ptrCast(@alignCast(zig_thumb_windows_teb()))
3043 else
3044 asm (
3045 \\ mrc p15, 0, %[ptr], c13, c0, 2
3046 : [ptr] "=r" (-> *TEB),
3047 ),
3048 .aarch64 => if (builtin.zig_backend == .stage2_c)
3049 @ptrCast(@alignCast(zig_aarch64_windows_teb()))
3050 else
3051 asm (
3052 \\ mov %[ptr], x18
3053 : [ptr] "=r" (-> *TEB),
3054 ),
3055 .x86 => if (builtin.zig_backend == .stage2_c)
3056 @ptrCast(@alignCast(zig_x86_windows_teb()))
3057 else
3058 asm (
2891 if (builtin.zig_backend == .stage2_c) return @ptrCast(@alignCast(struct {
2892 /// This is a workaround for the C backend until zig has the ability to put
2893 /// C code in inline assembly.
2894 extern fn zig_windows_teb() callconv(.c) *anyopaque;
2895 }.zig_windows_teb()));
2896 switch (native_arch) {
2897 .thumb => return asm (
2898 \\ mrc p15, 0, %[ptr], c13, c0, 2
2899 : [ptr] "=r" (-> *TEB),
2900 ),
2901 .aarch64 => return asm (
2902 \\ mov %[ptr], x18
2903 : [ptr] "=r" (-> *TEB),
2904 ),
2905 .x86 => {
2906 comptime assert(
2907 @offsetOf(TEB, "NtTib") + @offsetOf(@FieldType(TEB, "NtTib"), "Self") == 0x18,
2908 );
2909 return asm (
30592910 \\ movl %%fs:0x18, %[ptr]
30602911 : [ptr] "=r" (-> *TEB),
3061 ),
3062 .x86_64 => if (builtin.zig_backend == .stage2_c)
3063 @ptrCast(@alignCast(zig_x86_64_windows_teb()))
3064 else
3065 asm (
2912 );
2913 },
2914 .x86_64 => {
2915 comptime assert(
2916 @offsetOf(TEB, "NtTib") + @offsetOf(@FieldType(TEB, "NtTib"), "Self") == 0x30,
2917 );
2918 return asm (
30662919 \\ movq %%gs:0x30, %[ptr]
30672920 : [ptr] "=r" (-> *TEB),
3068 ),
2921 );
2922 },
30692923 else => @compileError("unsupported arch"),
3070 };
2924 }
30712925}
30722926
30732927pub fn peb() *PEB {
2928 if (builtin.zig_backend == .stage2_c) switch (native_arch) {
2929 .x86, .x86_64 => return @ptrCast(@alignCast(struct {
2930 /// This is a workaround for the C backend until zig has the ability to put
2931 /// C code in inline assembly.
2932 extern fn zig_windows_peb() callconv(.c) *anyopaque;
2933 }.zig_windows_peb())),
2934 else => {},
2935 } else switch (native_arch) {
2936 .aarch64 => {
2937 comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
2938 return asm (
2939 \\ ldr %[ptr], [x18, #0x60]
2940 : [ptr] "=r" (-> *PEB),
2941 );
2942 },
2943 .x86 => {
2944 comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30);
2945 return asm (
2946 \\ movl %%fs:0x30, %[ptr]
2947 : [ptr] "=r" (-> *PEB),
2948 );
2949 },
2950 .x86_64 => {
2951 comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
2952 return asm (
2953 \\ movq %%gs:0x60, %[ptr]
2954 : [ptr] "=r" (-> *PEB),
2955 );
2956 },
2957 else => {},
2958 }
30742959 return teb().ProcessEnvironmentBlock;
30752960}
30762961
......@@ -3089,20 +2974,6 @@ pub fn toSysTime(ns: Io.Timestamp) i64 {
30892974 return @as(i64, @intCast(hns)) - std.time.epoch.windows * (std.time.ns_per_s / 100);
30902975}
30912976
3092pub fn fileTimeToNanoSeconds(ft: FILETIME) Io.Timestamp {
3093 const hns = (@as(i64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
3094 return fromSysTime(hns);
3095}
3096
3097/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
3098pub fn nanoSecondsToFileTime(ns: Io.Timestamp) FILETIME {
3099 const adjusted: u64 = @bitCast(toSysTime(ns));
3100 return .{
3101 .dwHighDateTime = @as(u32, @truncate(adjusted >> 32)),
3102 .dwLowDateTime = @as(u32, @truncate(adjusted)),
3103 };
3104}
3105
31062977/// Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a
31072978/// redundant copy of the uppercase data.
31082979pub inline fn toUpperWtf16(c: u16) u16 {
......@@ -3126,7 +2997,7 @@ pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool {
31262997 // endianness for the uppercasing
31272998 const a_c_native = std.mem.littleToNative(u16, a_c);
31282999 const b_c_native = std.mem.littleToNative(u16, b_c);
3129 if (a_c != b_c and nls.upcaseW(a_c_native) != nls.upcaseW(b_c_native)) {
3000 if (a_c != b_c and toUpperWtf16(a_c_native) != toUpperWtf16(b_c_native)) {
31303001 return false;
31313002 }
31323003 }
......@@ -3134,19 +3005,7 @@ pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool {
31343005 }
31353006 // Use RtlEqualUnicodeString on Windows when not in comptime to avoid including a
31363007 // redundant copy of the uppercase data.
3137 const a_bytes = @as(u16, @intCast(a.len * 2));
3138 const a_string: UNICODE_STRING = .{
3139 .Length = a_bytes,
3140 .MaximumLength = a_bytes,
3141 .Buffer = @constCast(a.ptr),
3142 };
3143 const b_bytes = @as(u16, @intCast(b.len * 2));
3144 const b_string: UNICODE_STRING = .{
3145 .Length = b_bytes,
3146 .MaximumLength = b_bytes,
3147 .Buffer = @constCast(b.ptr),
3148 };
3149 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
3008 return ntdll.RtlEqualUnicodeString(&.init(a), &.init(b), TRUE) == TRUE;
31503009}
31513010
31523011/// Compares two WTF-8 strings using the equivalent functionality of
......@@ -3464,13 +3323,10 @@ pub const LONG = i32;
34643323pub const ULONG64 = u64;
34653324pub const ULONGLONG = u64;
34663325pub const LONGLONG = i64;
3467pub const HLOCAL = HANDLE;
34683326pub const LANGID = c_ushort;
34693327pub const COLORREF = DWORD;
34703328
3471pub const WPARAM = usize;
34723329pub const LPARAM = LONG_PTR;
3473pub const LRESULT = LONG_PTR;
34743330
34753331pub const va_list = *opaque {};
34763332
......@@ -3480,6 +3336,40 @@ pub const LPCTSTR = @compileError("Deprecated: choose between `LPCSTR` or `LPCWS
34803336pub const PTSTR = @compileError("Deprecated: choose between `PSTR` or `PWSTR` directly instead.");
34813337pub const PCTSTR = @compileError("Deprecated: choose between `PCSTR` or `PCWSTR` directly instead.");
34823338
3339fn STRING(comptime C: type) type {
3340 return extern struct {
3341 Length: USHORT,
3342 MaximumLength: USHORT,
3343 Buffer: ?[*]C,
3344
3345 pub const empty: @This() = .{ .Length = 0, .MaximumLength = 0, .Buffer = null };
3346
3347 pub fn init(string: []const C) @This() {
3348 const len: USHORT = @intCast(@sizeOf(C) * string.len);
3349 return .{
3350 .Length = len,
3351 .MaximumLength = len,
3352 .Buffer = @constCast(string.ptr),
3353 };
3354 }
3355
3356 pub fn isEmpty(string: *const @This()) bool {
3357 return string.Length == 0;
3358 }
3359
3360 pub fn slice(string: *const @This()) []C {
3361 return if (string.isEmpty()) &.{} else string.Buffer.?[0..@divExact(string.Length, @sizeOf(C))];
3362 }
3363
3364 pub fn sliceZ(string: *const @This()) [:0]C {
3365 assert(string.Length + @sizeOf(C) <= string.MaximumLength);
3366 return string.Buffer.?[0..@divExact(string.Length, @sizeOf(C)) :0];
3367 }
3368 };
3369}
3370pub const ANSI_STRING = STRING(CHAR);
3371pub const UNICODE_STRING = STRING(WCHAR);
3372
34833373pub const TRUE = 1;
34843374pub const FALSE = 0;
34853375
......@@ -3509,111 +3399,14 @@ pub const OVERLAPPED = extern struct {
35093399 hEvent: ?HANDLE,
35103400};
35113401
3512pub const OVERLAPPED_ENTRY = extern struct {
3513 lpCompletionKey: ULONG_PTR,
3514 lpOverlapped: *OVERLAPPED,
3515 Internal: ULONG_PTR,
3516 dwNumberOfBytesTransferred: DWORD,
3517};
3518
35193402pub const MAX_PATH = 260;
35203403
3521pub const FILE_INFO_BY_HANDLE_CLASS = enum(u32) {
3522 FileBasicInfo = 0,
3523 FileStandardInfo = 1,
3524 FileNameInfo = 2,
3525 FileRenameInfo = 3,
3526 FileDispositionInfo = 4,
3527 FileAllocationInfo = 5,
3528 FileEndOfFileInfo = 6,
3529 FileStreamInfo = 7,
3530 FileCompressionInfo = 8,
3531 FileAttributeTagInfo = 9,
3532 FileIdBothDirectoryInfo = 10,
3533 FileIdBothDirectoryRestartInfo = 11,
3534 FileIoPriorityHintInfo = 12,
3535 FileRemoteProtocolInfo = 13,
3536 FileFullDirectoryInfo = 14,
3537 FileFullDirectoryRestartInfo = 15,
3538 FileStorageInfo = 16,
3539 FileAlignmentInfo = 17,
3540 FileIdInfo = 18,
3541 FileIdExtdDirectoryInfo = 19,
3542 FileIdExtdDirectoryRestartInfo = 20,
3543};
3544
3545pub const BY_HANDLE_FILE_INFORMATION = extern struct {
3546 dwFileAttributes: DWORD,
3547 ftCreationTime: FILETIME,
3548 ftLastAccessTime: FILETIME,
3549 ftLastWriteTime: FILETIME,
3550 dwVolumeSerialNumber: DWORD,
3551 nFileSizeHigh: DWORD,
3552 nFileSizeLow: DWORD,
3553 nNumberOfLinks: DWORD,
3554 nFileIndexHigh: DWORD,
3555 nFileIndexLow: DWORD,
3556};
3557
3558pub const FILE_NAME_INFO = extern struct {
3559 FileNameLength: DWORD,
3560 FileName: [1]WCHAR,
3561};
3562
3563/// Return the normalized drive name. This is the default.
3564pub const FILE_NAME_NORMALIZED = 0x0;
3565
3566/// Return the opened file name (not normalized).
3567pub const FILE_NAME_OPENED = 0x8;
3568
3569/// Return the path with the drive letter. This is the default.
3570pub const VOLUME_NAME_DOS = 0x0;
3571
3572/// Return the path with a volume GUID path instead of the drive name.
3573pub const VOLUME_NAME_GUID = 0x1;
3574
3575/// Return the path with no drive information.
3576pub const VOLUME_NAME_NONE = 0x4;
3577
3578/// Return the path with the volume device path.
3579pub const VOLUME_NAME_NT = 0x2;
3580
35813404pub const SECURITY_ATTRIBUTES = extern struct {
35823405 nLength: DWORD,
35833406 lpSecurityDescriptor: ?*anyopaque,
35843407 bInheritHandle: BOOL,
35853408};
35863409
3587pub const PIPE_ACCESS_INBOUND = 0x00000001;
3588pub const PIPE_ACCESS_OUTBOUND = 0x00000002;
3589pub const PIPE_ACCESS_DUPLEX = 0x00000003;
3590
3591pub const PIPE_TYPE_BYTE = 0x00000000;
3592pub const PIPE_TYPE_MESSAGE = 0x00000004;
3593
3594pub const PIPE_READMODE_BYTE = 0x00000000;
3595pub const PIPE_READMODE_MESSAGE = 0x00000002;
3596
3597pub const PIPE_WAIT = 0x00000000;
3598pub const PIPE_NOWAIT = 0x00000001;
3599
3600pub const CREATE_ALWAYS = 2;
3601pub const CREATE_NEW = 1;
3602pub const OPEN_ALWAYS = 4;
3603pub const OPEN_EXISTING = 3;
3604pub const TRUNCATE_EXISTING = 5;
3605
3606// flags for CreateEvent
3607pub const CREATE_EVENT_INITIAL_SET = 0x00000002;
3608pub const CREATE_EVENT_MANUAL_RESET = 0x00000001;
3609
3610pub const PROCESS_INFORMATION = extern struct {
3611 hProcess: HANDLE,
3612 hThread: HANDLE,
3613 dwProcessId: DWORD,
3614 dwThreadId: DWORD,
3615};
3616
36173410pub const STARTUPINFOW = extern struct {
36183411 cb: DWORD,
36193412 lpReserved: ?LPWSTR,
......@@ -3650,50 +3443,7 @@ pub const STARTF_USESHOWWINDOW = 0x00000001;
36503443pub const STARTF_USESIZE = 0x00000002;
36513444pub const STARTF_USESTDHANDLES = 0x00000100;
36523445
3653pub const INFINITE = 4294967295;
3654
3655pub const MAXIMUM_WAIT_OBJECTS = 64;
3656
3657pub const WAIT_ABANDONED = 0x00000080;
3658pub const WAIT_ABANDONED_0 = WAIT_ABANDONED + 0;
3659pub const WAIT_OBJECT_0 = 0x00000000;
3660pub const WAIT_TIMEOUT = 0x00000102;
3661pub const WAIT_FAILED = 0xFFFFFFFF;
3662
3663pub const HANDLE_FLAG_INHERIT = 0x00000001;
3664pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
3665
3666pub const MOVEFILE_COPY_ALLOWED = 2;
3667pub const MOVEFILE_CREATE_HARDLINK = 16;
3668pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4;
3669pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32;
3670pub const MOVEFILE_REPLACE_EXISTING = 1;
3671pub const MOVEFILE_WRITE_THROUGH = 8;
3672
3673pub const FILE_BEGIN = 0;
3674pub const FILE_CURRENT = 1;
3675pub const FILE_END = 2;
3676
3677pub const PTHREAD_START_ROUTINE = *const fn (LPVOID) callconv(.winapi) DWORD;
3678pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
3679
3680pub const WIN32_FIND_DATAW = extern struct {
3681 dwFileAttributes: DWORD,
3682 ftCreationTime: FILETIME,
3683 ftLastAccessTime: FILETIME,
3684 ftLastWriteTime: FILETIME,
3685 nFileSizeHigh: DWORD,
3686 nFileSizeLow: DWORD,
3687 dwReserved0: DWORD,
3688 dwReserved1: DWORD,
3689 cFileName: [260]u16,
3690 cAlternateFileName: [14]u16,
3691};
3692
3693pub const FILETIME = extern struct {
3694 dwLowDateTime: DWORD,
3695 dwHighDateTime: DWORD,
3696};
3446pub const THREAD_START_ROUTINE = fn (LPVOID) callconv(.winapi) DWORD;
36973447
36983448pub const SYSTEM_INFO = extern struct {
36993449 anon1: extern union {
......@@ -3716,7 +3466,6 @@ pub const SYSTEM_INFO = extern struct {
37163466
37173467pub const HRESULT = c_long;
37183468
3719pub const KNOWNFOLDERID = GUID;
37203469pub const GUID = extern struct {
37213470 Data1: u32,
37223471 Data2: u16,
......@@ -3771,75 +3520,11 @@ test GUID {
37713520 );
37723521}
37733522
3774pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
3775
3776pub const KF_FLAG_DEFAULT = 0;
3777pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
3778pub const KF_FLAG_CREATE = 32768;
3779pub const KF_FLAG_DONT_VERIFY = 16384;
3780pub const KF_FLAG_DONT_UNEXPAND = 8192;
3781pub const KF_FLAG_NO_ALIAS = 4096;
3782pub const KF_FLAG_INIT = 2048;
3783pub const KF_FLAG_DEFAULT_PATH = 1024;
3784pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
3785pub const KF_FLAG_SIMPLE_IDLIST = 256;
3786pub const KF_FLAG_ALIAS_ONLY = -2147483648;
3787
3788pub const S_OK = 0;
3789pub const S_FALSE = 0x00000001;
3790pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001)));
3791pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002)));
3792pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003)));
3793pub const E_ABORT = @as(c_long, @bitCast(@as(c_ulong, 0x80004004)));
3794pub const E_FAIL = @as(c_long, @bitCast(@as(c_ulong, 0x80004005)));
3795pub const E_UNEXPECTED = @as(c_long, @bitCast(@as(c_ulong, 0x8000FFFF)));
3796pub const E_ACCESSDENIED = @as(c_long, @bitCast(@as(c_ulong, 0x80070005)));
3797pub const E_HANDLE = @as(c_long, @bitCast(@as(c_ulong, 0x80070006)));
3798pub const E_OUTOFMEMORY = @as(c_long, @bitCast(@as(c_ulong, 0x8007000E)));
3799pub const E_INVALIDARG = @as(c_long, @bitCast(@as(c_ulong, 0x80070057)));
3800
3801pub fn HRESULT_CODE(hr: HRESULT) Win32Error {
3802 return @enumFromInt(hr & 0xFFFF);
3803}
3804
3805pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
3806pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
3807pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
3808pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
3809pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
3810pub const FILE_FLAG_OVERLAPPED = 0x40000000;
3811pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
3812pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
3813pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
3814pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
3815pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
3816
3817pub const RECT = extern struct {
3818 left: LONG,
3819 top: LONG,
3820 right: LONG,
3821 bottom: LONG,
3822};
3823
3824pub const SMALL_RECT = extern struct {
3825 Left: SHORT,
3826 Top: SHORT,
3827 Right: SHORT,
3828 Bottom: SHORT,
3829};
3830
3831pub const POINT = extern struct {
3832 x: LONG,
3833 y: LONG,
3834};
3835
38363523pub const COORD = extern struct {
38373524 X: SHORT,
38383525 Y: SHORT,
38393526};
38403527
3841pub const CREATE_UNICODE_ENVIRONMENT = 1024;
3842
38433528pub const TLS_OUT_OF_INDEXES = 4294967295;
38443529pub const IMAGE_TLS_DIRECTORY = extern struct {
38453530 StartAddressOfRawData: usize,
......@@ -3854,8 +3539,6 @@ pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
38543539
38553540pub const PIMAGE_TLS_CALLBACK = ?*const fn (PVOID, DWORD, PVOID) callconv(.winapi) void;
38563541
3857pub const PROV_RSA_FULL = 1;
3858
38593542pub const REGSAM = ACCESS_MASK;
38603543pub const LSTATUS = LONG;
38613544
......@@ -3872,9 +3555,6 @@ pub const HKEY_CURRENT_CONFIG: HKEY = @ptrFromInt(0x80000005);
38723555pub const HKEY_DYN_DATA: HKEY = @ptrFromInt(0x80000006);
38733556pub const HKEY_CURRENT_USER_LOCAL_SETTINGS: HKEY = @ptrFromInt(0x80000007);
38743557
3875/// Open symbolic link.
3876pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
3877
38783558pub const RTL_QUERY_REGISTRY_TABLE = extern struct {
38793559 QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE,
38803560 Flags: ULONG,
......@@ -3975,38 +3655,6 @@ pub const REG = struct {
39753655 pub const QWORD_LITTLE_ENDIAN: ULONG = 11;
39763656};
39773657
3978pub const FILE_NOTIFY_INFORMATION = extern struct {
3979 NextEntryOffset: DWORD,
3980 Action: DWORD,
3981 FileNameLength: DWORD,
3982 // Flexible array member
3983 // FileName: [1]WCHAR,
3984};
3985
3986pub const FILE_ACTION_ADDED = 0x00000001;
3987pub const FILE_ACTION_REMOVED = 0x00000002;
3988pub const FILE_ACTION_MODIFIED = 0x00000003;
3989pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
3990pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
3991
3992pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?*const fn (DWORD, DWORD, *OVERLAPPED) callconv(.winapi) void;
3993
3994pub const FileNotifyChangeFilter = packed struct(DWORD) {
3995 file_name: bool = false,
3996 dir_name: bool = false,
3997 attributes: bool = false,
3998 size: bool = false,
3999 last_write: bool = false,
4000 last_access: bool = false,
4001 creation: bool = false,
4002 ea: bool = false,
4003 security: bool = false,
4004 stream_name: bool = false,
4005 stream_size: bool = false,
4006 stream_write: bool = false,
4007 _pad: u20 = 0,
4008};
4009
40103658pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
40113659pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
40123660
......@@ -4056,26 +3704,6 @@ pub const RTL_RUN_ONCE = extern struct {
40563704
40573705pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
40583706
4059pub const COINIT = struct {
4060 pub const APARTMENTTHREADED = 2;
4061 pub const MULTITHREADED = 0;
4062 pub const DISABLE_OLE1DDE = 4;
4063 pub const SPEED_OVER_MEMORY = 8;
4064};
4065
4066pub const MEMORY_BASIC_INFORMATION = extern struct {
4067 BaseAddress: PVOID,
4068 AllocationBase: PVOID,
4069 AllocationProtect: DWORD,
4070 PartitionId: WORD,
4071 RegionSize: SIZE_T,
4072 State: DWORD,
4073 Protect: DWORD,
4074 Type: DWORD,
4075};
4076
4077pub const PMEMORY_BASIC_INFORMATION = *MEMORY_BASIC_INFORMATION;
4078
40793707/// > The maximum path of 32,767 characters is approximate, because the "\\?\"
40803708/// > prefix may be expanded to a longer string by the system at run time, and
40813709/// > this expansion applies to the total length.
......@@ -4544,14 +4172,6 @@ pub const UNW_FLAG_EHANDLER = 0x1;
45444172pub const UNW_FLAG_UHANDLER = 0x2;
45454173pub const UNW_FLAG_CHAININFO = 0x4;
45464174
4547pub const UNICODE_STRING = extern struct {
4548 Length: c_ushort,
4549 MaximumLength: c_ushort,
4550 Buffer: ?[*]WCHAR,
4551
4552 pub const empty: UNICODE_STRING = .{ .Length = 0, .MaximumLength = 0, .Buffer = null };
4553};
4554
45554175pub const ACTIVATION_CONTEXT_DATA = opaque {};
45564176pub const ASSEMBLY_STORAGE_MAP = opaque {};
45574177pub const FLS_CALLBACK_INFO = opaque {};
......@@ -4564,15 +4184,6 @@ pub const CLIENT_ID = extern struct {
45644184 UniqueThread: HANDLE,
45654185};
45664186
4567pub const THREAD_BASIC_INFORMATION = extern struct {
4568 ExitStatus: NTSTATUS,
4569 TebBaseAddress: PVOID,
4570 ClientId: CLIENT_ID,
4571 AffinityMask: KAFFINITY,
4572 Priority: KPRIORITY,
4573 BasePriority: KPRIORITY,
4574};
4575
45764187pub const TEB = extern struct {
45774188 NtTib: NT_TIB,
45784189 EnvironmentPointer: PVOID,
......@@ -4580,7 +4191,7 @@ pub const TEB = extern struct {
45804191 ActiveRpcHandle: PVOID,
45814192 ThreadLocalStoragePointer: PVOID,
45824193 ProcessEnvironmentBlock: *PEB,
4583 LastErrorValue: ULONG,
4194 LastErrorValue: Win32Error,
45844195 Reserved2: [399 * @sizeOf(PVOID) - @sizeOf(ULONG)]u8,
45854196 Reserved3: [1952]u8,
45864197 TlsSlots: [64]PVOID,
......@@ -4793,7 +4404,7 @@ pub const PEB = extern struct {
47934404};
47944405
47954406/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.
4796/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module.
4407/// It is essentially the head of three double-linked lists of `LDR.DATA_TABLE_ENTRY` structures which each represent one loaded module.
47974408///
47984409/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
47994410/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm
......@@ -4825,24 +4436,89 @@ pub const PEB_LDR_DATA = extern struct {
48254436 ShutdownThreadId: HANDLE,
48264437};
48274438
4828/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
4829/// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
4830/// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
4831pub const LDR_DATA_TABLE_ENTRY = extern struct {
4832 InLoadOrderLinks: LIST_ENTRY,
4833 InMemoryOrderLinks: LIST_ENTRY,
4834 InInitializationOrderLinks: LIST_ENTRY,
4835 DllBase: PVOID,
4836 EntryPoint: PVOID,
4837 SizeOfImage: ULONG,
4838 FullDllName: UNICODE_STRING,
4839 BaseDllName: UNICODE_STRING,
4840 Reserved5: [3]PVOID,
4841 DUMMYUNIONNAME: extern union {
4842 CheckSum: ULONG,
4843 Reserved6: PVOID,
4844 },
4845 TimeDateStamp: ULONG,
4439pub const LDR = struct {
4440 /// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
4441 /// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
4442 /// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
4443 pub const DATA_TABLE_ENTRY = extern struct {
4444 InLoadOrderLinks: LIST_ENTRY,
4445 InMemoryOrderLinks: LIST_ENTRY,
4446 InInitializationOrderLinks: LIST_ENTRY,
4447 DllBase: PVOID,
4448 EntryPoint: PVOID,
4449 SizeOfImage: ULONG,
4450 FullDllName: UNICODE_STRING,
4451 BaseDllName: UNICODE_STRING,
4452 Reserved5: [3]PVOID,
4453 DUMMYUNIONNAME: extern union {
4454 CheckSum: ULONG,
4455 Reserved6: PVOID,
4456 },
4457 TimeDateStamp: ULONG,
4458 };
4459
4460 pub const DLL_NOTIFICATION = struct {
4461 pub const REASON = enum(ULONG) { LOADED = 1, UNLOADED = 2 };
4462
4463 pub const DATA = extern union {
4464 Loaded: LOADED,
4465 Unloaded: UNLOADED,
4466
4467 pub const LOADED = extern struct {
4468 Flags: REGISTER,
4469 FullDllName: *const UNICODE_STRING,
4470 BaseDllName: *const UNICODE_STRING,
4471 DllBase: PVOID,
4472 SizeOfImage: ULONG,
4473 };
4474
4475 pub const UNLOADED = extern struct {
4476 Flags: REGISTER,
4477 FullDllName: *const UNICODE_STRING,
4478 BaseDllName: *const UNICODE_STRING,
4479 DllBase: PVOID,
4480 SizeOfImage: ULONG,
4481 };
4482 };
4483
4484 pub const COOKIE = *opaque {};
4485
4486 pub const FUNCTION = fn (
4487 NotificationReason: REASON,
4488 NotificationData: *const DATA,
4489 Context: ?PVOID,
4490 ) callconv(.winapi) void;
4491
4492 pub const REGISTER = packed struct(ULONG) {
4493 Reserved0: u32 = 0,
4494 };
4495 };
4496
4497 pub const GET_DLL_HANDLE_EX = packed struct(ULONG) {
4498 UNCHANGED_REFCOUNT: bool = false,
4499 PIN: bool = false,
4500 Reserved2: u30 = 0,
4501 };
4502
4503 pub const GET_PROCEDURE_ADDRESS = packed struct(ULONG) {
4504 DONT_RECORD_FORWARDER: bool = false,
4505 Reserved1: u31 = 0,
4506 };
4507
4508 pub const LOAD = packed struct(ULONG) {
4509 DONT_RESOLVE_DLL_REFERENCES: bool = false,
4510 LIBRARY_AS_DATAFILE: bool = false,
4511 PACKAGED_LIBRARY: bool = false,
4512 WITH_ALTERED_SEARCH_PATH: bool = false,
4513 IGNORE_CODE_AUTHZ_LEVEL: bool = false,
4514 LIBRARY_AS_IMAGE_RESOURCE: bool = false,
4515 LIBRARY_AS_DATAFILE_EXCLUSIVE: bool = false,
4516 LIBRARY_REQUERE_SIGNED_TARGET: bool = false,
4517 LIBRARY_SEARCH_DLL_LOAD_DIR: bool = false,
4518 LIBRARY_SEARCH_USER_DIRS: bool = false,
4519 LIBRARY_SEARCH_SYSTEM32: bool = false,
4520 LIBRARY_SEARCH_DEFAULT_DIRS: bool = false,
4521 };
48464522};
48474523
48484524pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
......@@ -4956,86 +4632,6 @@ pub const MODULEINFO = extern struct {
49564632 EntryPoint: LPVOID,
49574633};
49584634
4959pub const PSAPI_WS_WATCH_INFORMATION = extern struct {
4960 FaultingPc: LPVOID,
4961 FaultingVa: LPVOID,
4962};
4963
4964pub const VM_COUNTERS = extern struct {
4965 PeakVirtualSize: SIZE_T,
4966 VirtualSize: SIZE_T,
4967 PageFaultCount: ULONG,
4968 PeakWorkingSetSize: SIZE_T,
4969 WorkingSetSize: SIZE_T,
4970 QuotaPeakPagedPoolUsage: SIZE_T,
4971 QuotaPagedPoolUsage: SIZE_T,
4972 QuotaPeakNonPagedPoolUsage: SIZE_T,
4973 QuotaNonPagedPoolUsage: SIZE_T,
4974 PagefileUsage: SIZE_T,
4975 PeakPagefileUsage: SIZE_T,
4976};
4977
4978pub const PROCESS_MEMORY_COUNTERS = extern struct {
4979 cb: DWORD,
4980 PageFaultCount: DWORD,
4981 PeakWorkingSetSize: SIZE_T,
4982 WorkingSetSize: SIZE_T,
4983 QuotaPeakPagedPoolUsage: SIZE_T,
4984 QuotaPagedPoolUsage: SIZE_T,
4985 QuotaPeakNonPagedPoolUsage: SIZE_T,
4986 QuotaNonPagedPoolUsage: SIZE_T,
4987 PagefileUsage: SIZE_T,
4988 PeakPagefileUsage: SIZE_T,
4989};
4990
4991pub const PROCESS_MEMORY_COUNTERS_EX = extern struct {
4992 cb: DWORD,
4993 PageFaultCount: DWORD,
4994 PeakWorkingSetSize: SIZE_T,
4995 WorkingSetSize: SIZE_T,
4996 QuotaPeakPagedPoolUsage: SIZE_T,
4997 QuotaPagedPoolUsage: SIZE_T,
4998 QuotaPeakNonPagedPoolUsage: SIZE_T,
4999 QuotaNonPagedPoolUsage: SIZE_T,
5000 PagefileUsage: SIZE_T,
5001 PeakPagefileUsage: SIZE_T,
5002 PrivateUsage: SIZE_T,
5003};
5004
5005pub const PERFORMANCE_INFORMATION = extern struct {
5006 cb: DWORD,
5007 CommitTotal: SIZE_T,
5008 CommitLimit: SIZE_T,
5009 CommitPeak: SIZE_T,
5010 PhysicalTotal: SIZE_T,
5011 PhysicalAvailable: SIZE_T,
5012 SystemCache: SIZE_T,
5013 KernelTotal: SIZE_T,
5014 KernelPaged: SIZE_T,
5015 KernelNonpaged: SIZE_T,
5016 PageSize: SIZE_T,
5017 HandleCount: DWORD,
5018 ProcessCount: DWORD,
5019 ThreadCount: DWORD,
5020};
5021
5022pub const ENUM_PAGE_FILE_INFORMATION = extern struct {
5023 cb: DWORD,
5024 Reserved: DWORD,
5025 TotalSize: SIZE_T,
5026 TotalInUse: SIZE_T,
5027 PeakUsage: SIZE_T,
5028};
5029
5030pub const PENUM_PAGE_FILE_CALLBACKW = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.winapi) BOOL;
5031pub const PENUM_PAGE_FILE_CALLBACKA = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.winapi) BOOL;
5032
5033pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct {
5034 BasicInfo: PSAPI_WS_WATCH_INFORMATION,
5035 FaultingThreadId: ULONG_PTR,
5036 Flags: ULONG_PTR,
5037};
5038
50394635pub const OSVERSIONINFOW = extern struct {
50404636 dwOSVersionInfoSize: ULONG,
50414637 dwMajorVersion: ULONG,
......@@ -5098,20 +4694,6 @@ pub const MOUNTMGR_VOLUME_PATHS = extern struct {
50984694 MultiSz: [1]WCHAR,
50994695};
51004696
5101pub const OBJECT_INFORMATION_CLASS = enum(c_int) {
5102 ObjectBasicInformation = 0,
5103 ObjectNameInformation = 1,
5104 ObjectTypeInformation = 2,
5105 ObjectTypesInformation = 3,
5106 ObjectHandleFlagInformation = 4,
5107 ObjectSessionInformation = 5,
5108 MaxObjectInfoClass,
5109};
5110
5111pub const OBJECT_NAME_INFORMATION = extern struct {
5112 Name: UNICODE_STRING,
5113};
5114
51154697pub const SRWLOCK_INIT = SRWLOCK{};
51164698pub const SRWLOCK = extern struct {
51174699 Ptr: ?PVOID = null,
......@@ -5122,17 +4704,6 @@ pub const CONDITION_VARIABLE = extern struct {
51224704 Ptr: ?PVOID = null,
51234705};
51244706
5125pub const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1;
5126pub const FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2;
5127
5128pub const CTRL_C_EVENT: DWORD = 0;
5129pub const CTRL_BREAK_EVENT: DWORD = 1;
5130pub const CTRL_CLOSE_EVENT: DWORD = 2;
5131pub const CTRL_LOGOFF_EVENT: DWORD = 5;
5132pub const CTRL_SHUTDOWN_EVENT: DWORD = 6;
5133
5134pub const HANDLER_ROUTINE = *const fn (dwCtrlType: DWORD) callconv(.winapi) BOOL;
5135
51364707/// Processor feature enumeration.
51374708pub const PF = enum(DWORD) {
51384709 /// On a Pentium, a floating-point precision error can occur in rare circumstances.
......@@ -5431,72 +5002,13 @@ pub const KUSER_SHARED_DATA = extern struct {
54315002/// Read-only user-mode address for the shared data.
54325003/// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
54335004/// https://msrc-blog.microsoft.com/2022/04/05/randomizing-the-kuser_shared_data-structure-on-windows/
5434pub const SharedUserData: *const KUSER_SHARED_DATA = @as(*const KUSER_SHARED_DATA, @ptrFromInt(0x7FFE0000));
5005pub const SharedUserData: *const KUSER_SHARED_DATA = @ptrFromInt(0x7FFE0000);
54355006
54365007pub fn IsProcessorFeaturePresent(feature: PF) bool {
54375008 if (@intFromEnum(feature) >= PROCESSOR_FEATURE_MAX) return false;
54385009 return SharedUserData.ProcessorFeatures[@intFromEnum(feature)] == 1;
54395010}
54405011
5441pub const TH32CS_SNAPHEAPLIST = 0x00000001;
5442pub const TH32CS_SNAPPROCESS = 0x00000002;
5443pub const TH32CS_SNAPTHREAD = 0x00000004;
5444pub const TH32CS_SNAPMODULE = 0x00000008;
5445pub const TH32CS_SNAPMODULE32 = 0x00000010;
5446pub const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE;
5447pub const TH32CS_INHERIT = 0x80000000;
5448
5449pub const MAX_MODULE_NAME32 = 255;
5450pub const MODULEENTRY32 = extern struct {
5451 dwSize: DWORD,
5452 th32ModuleID: DWORD,
5453 th32ProcessID: DWORD,
5454 GlblcntUsage: DWORD,
5455 ProccntUsage: DWORD,
5456 modBaseAddr: *BYTE,
5457 modBaseSize: DWORD,
5458 hModule: HMODULE,
5459 szModule: [MAX_MODULE_NAME32 + 1]CHAR,
5460 szExePath: [MAX_PATH]CHAR,
5461};
5462
5463pub const SYSTEM_INFORMATION_CLASS = enum(c_int) {
5464 SystemBasicInformation = 0,
5465 SystemPerformanceInformation = 2,
5466 SystemTimeOfDayInformation = 3,
5467 SystemProcessInformation = 5,
5468 SystemProcessorPerformanceInformation = 8,
5469 SystemInterruptInformation = 23,
5470 SystemExceptionInformation = 33,
5471 SystemRegistryQuotaInformation = 37,
5472 SystemLookasideInformation = 45,
5473 SystemCodeIntegrityInformation = 103,
5474 SystemPolicyInformation = 134,
5475};
5476
5477pub const SYSTEM_BASIC_INFORMATION = extern struct {
5478 Reserved: ULONG,
5479 TimerResolution: ULONG,
5480 PageSize: ULONG,
5481 NumberOfPhysicalPages: ULONG,
5482 LowestPhysicalPageNumber: ULONG,
5483 HighestPhysicalPageNumber: ULONG,
5484 AllocationGranularity: ULONG,
5485 MinimumUserModeAddress: ULONG_PTR,
5486 MaximumUserModeAddress: ULONG_PTR,
5487 ActiveProcessorsAffinityMask: KAFFINITY,
5488 NumberOfProcessors: UCHAR,
5489};
5490
5491pub const PROCESS_BASIC_INFORMATION = extern struct {
5492 ExitStatus: NTSTATUS,
5493 PebBaseAddress: *PEB,
5494 AffinityMask: ULONG_PTR,
5495 BasePriority: KPRIORITY,
5496 UniqueProcessId: ULONG_PTR,
5497 InheritedFromUniqueProcessId: ULONG_PTR,
5498};
5499
55005012// https://github.com/reactos/reactos/blob/master/sdk/include/ndk/pstypes.h#L977-L983
55015013pub const KERNEL_USER_TIMES = extern struct {
55025014 CreationTime: LARGE_INTEGER,
......@@ -5505,75 +5017,6 @@ pub const KERNEL_USER_TIMES = extern struct {
55055017 UserTime: LARGE_INTEGER,
55065018};
55075019
5508pub const ReadMemoryError = error{
5509 Unexpected,
5510};
5511
5512pub fn ReadProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []u8) ReadMemoryError![]u8 {
5513 var nread: usize = 0;
5514 switch (ntdll.NtReadVirtualMemory(
5515 handle,
5516 addr,
5517 buffer.ptr,
5518 buffer.len,
5519 &nread,
5520 )) {
5521 .SUCCESS => return buffer[0..nread],
5522 // TODO: map errors
5523 else => |rc| return unexpectedStatus(rc),
5524 }
5525}
5526
5527pub const WriteMemoryError = error{
5528 Unexpected,
5529};
5530
5531pub fn WriteProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []const u8) WriteMemoryError!usize {
5532 var nwritten: usize = 0;
5533 switch (ntdll.NtWriteVirtualMemory(
5534 handle,
5535 addr,
5536 buffer.ptr,
5537 buffer.len,
5538 &nwritten,
5539 )) {
5540 .SUCCESS => return nwritten,
5541 // TODO: map errors
5542 else => |rc| return unexpectedStatus(rc),
5543 }
5544}
5545
5546pub const ProcessBaseAddressError = error{
5547 AccessDenied,
5548 InvalidHandle,
5549 Unexpected,
5550} || ReadMemoryError;
5551
5552/// Returns the base address of the process loaded into memory.
5553pub fn ProcessBaseAddress(handle: HANDLE) ProcessBaseAddressError!HMODULE {
5554 var info: PROCESS_BASIC_INFORMATION = undefined;
5555 var nread: DWORD = 0;
5556 const rc = ntdll.NtQueryInformationProcess(
5557 handle,
5558 .BasicInformation,
5559 &info,
5560 @sizeOf(PROCESS_BASIC_INFORMATION),
5561 &nread,
5562 );
5563 switch (rc) {
5564 .SUCCESS => {},
5565 .ACCESS_DENIED => return error.AccessDenied,
5566 .INVALID_HANDLE => return error.InvalidHandle,
5567 .INVALID_PARAMETER => unreachable,
5568 else => return unexpectedStatus(rc),
5569 }
5570
5571 var peb_buf: [@sizeOf(PEB)]u8 align(@alignOf(PEB)) = undefined;
5572 const peb_out = try ReadProcessMemory(handle, info.PebBaseAddress, &peb_buf);
5573 const ppeb: *const PEB = @ptrCast(@alignCast(peb_out.ptr));
5574 return ppeb.ImageBaseAddress;
5575}
5576
55775020pub fn wtf8ToWtf16Le(wtf16le: []u16, wtf8: []const u8) error{ BadPathName, NameTooLong }!usize {
55785021 // Each u8 in UTF-8/WTF-8 correlates to at most one u16 in UTF-16LE/WTF-16LE.
55795022 if (wtf16le.len < wtf8.len) {
lib/std/os/windows/kernel32.zig+4-197
......@@ -1,154 +1,29 @@
11const std = @import("../../std.zig");
22const windows = std.os.windows;
33
4const ACCESS_MASK = windows.ACCESS_MASK;
54const BOOL = windows.BOOL;
6const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
7const COORD = windows.COORD;
85const DWORD = windows.DWORD;
9const FARPROC = windows.FARPROC;
10const FILETIME = windows.FILETIME;
116const HANDLE = windows.HANDLE;
12const HANDLER_ROUTINE = windows.HANDLER_ROUTINE;
13const HMODULE = windows.HMODULE;
14const LARGE_INTEGER = windows.LARGE_INTEGER;
15const LPCSTR = windows.LPCSTR;
167const LPCVOID = windows.LPCVOID;
178const LPCWSTR = windows.LPCWSTR;
18const LPTHREAD_START_ROUTINE = windows.LPTHREAD_START_ROUTINE;
199const LPVOID = windows.LPVOID;
2010const LPWSTR = windows.LPWSTR;
21const MODULEENTRY32 = windows.MODULEENTRY32;
22const OVERLAPPED = windows.OVERLAPPED;
23const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY;
24const PROCESS_INFORMATION = windows.PROCESS_INFORMATION;
11const PROCESS = windows.PROCESS;
12const THREAD_START_ROUTINE = windows.THREAD_START_ROUTINE;
2513const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;
2614const SIZE_T = windows.SIZE_T;
27const SRWLOCK = windows.SRWLOCK;
2815const STARTUPINFOW = windows.STARTUPINFOW;
29const SYSTEM_INFO = windows.SYSTEM_INFO;
30const UCHAR = windows.UCHAR;
3116const UINT = windows.UINT;
32const ULONG = windows.ULONG;
33const ULONG_PTR = windows.ULONG_PTR;
3417const va_list = windows.va_list;
35const WCHAR = windows.WCHAR;
3618const Win32Error = windows.Win32Error;
37const WORD = windows.WORD;
3819
3920// I/O - Filesystem
4021
41pub extern "kernel32" fn ReadDirectoryChangesW(
42 hDirectory: HANDLE,
43 lpBuffer: [*]align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) u8,
44 nBufferLength: DWORD,
45 bWatchSubtree: BOOL,
46 dwNotifyFilter: windows.FileNotifyChangeFilter,
47 lpBytesReturned: ?*DWORD,
48 lpOverlapped: ?*OVERLAPPED,
49 lpCompletionRoutine: windows.LPOVERLAPPED_COMPLETION_ROUTINE,
50) callconv(.winapi) BOOL;
51
52// TODO: Wrapper around NtCancelIoFile.
53pub extern "kernel32" fn CancelIo(
54 hFile: HANDLE,
55) callconv(.winapi) BOOL;
56
57// TODO: Wrapper around NtSetInformationFile + `FILE_POSITION_INFORMATION`.
58// `FILE_STANDARD_INFORMATION` is also used if dwMoveMethod is `FILE_END`
59pub extern "kernel32" fn SetFilePointerEx(
60 hFile: HANDLE,
61 liDistanceToMove: LARGE_INTEGER,
62 lpNewFilePointer: ?*LARGE_INTEGER,
63 dwMoveMethod: DWORD,
64) callconv(.winapi) BOOL;
65
66// TODO: Wrapper around NtSetInformationFile + `FILE_BASIC_INFORMATION`
67pub extern "kernel32" fn SetFileTime(
68 hFile: HANDLE,
69 lpCreationTime: ?*const FILETIME,
70 lpLastAccessTime: ?*const FILETIME,
71 lpLastWriteTime: ?*const FILETIME,
72) callconv(.winapi) BOOL;
73
74pub extern "kernel32" fn WriteFile(
75 in_hFile: HANDLE,
76 in_lpBuffer: [*]const u8,
77 in_nNumberOfBytesToWrite: DWORD,
78 out_lpNumberOfBytesWritten: ?*DWORD,
79 in_out_lpOverlapped: ?*OVERLAPPED,
80) callconv(.winapi) BOOL;
81
82// TODO: Wrapper around NtSetInformationFile + `FILE_IO_COMPLETION_NOTIFICATION_INFORMATION`.
83pub extern "kernel32" fn SetFileCompletionNotificationModes(
84 FileHandle: HANDLE,
85 Flags: UCHAR,
86) callconv(.winapi) BOOL;
87
88pub extern "kernel32" fn ReadFile(
89 hFile: HANDLE,
90 lpBuffer: LPVOID,
91 nNumberOfBytesToRead: DWORD,
92 lpNumberOfBytesRead: ?*DWORD,
93 lpOverlapped: ?*OVERLAPPED,
94) callconv(.winapi) BOOL;
95
9622pub extern "kernel32" fn GetSystemDirectoryW(
9723 lpBuffer: LPWSTR,
9824 uSize: UINT,
9925) callconv(.winapi) UINT;
10026
101// I/O - Kernel Objects
102
103// TODO: Wrapper around NtRemoveIoCompletion.
104pub extern "kernel32" fn GetQueuedCompletionStatus(
105 CompletionPort: HANDLE,
106 lpNumberOfBytesTransferred: *DWORD,
107 lpCompletionKey: *ULONG_PTR,
108 lpOverlapped: *?*OVERLAPPED,
109 dwMilliseconds: DWORD,
110) callconv(.winapi) BOOL;
111
112// TODO: Wrapper around NtRemoveIoCompletionEx.
113pub extern "kernel32" fn GetQueuedCompletionStatusEx(
114 CompletionPort: HANDLE,
115 lpCompletionPortEntries: [*]OVERLAPPED_ENTRY,
116 ulCount: ULONG,
117 ulNumEntriesRemoved: *ULONG,
118 dwMilliseconds: DWORD,
119 fAlertable: BOOL,
120) callconv(.winapi) BOOL;
121
122// TODO: Wrapper around NtSetIoCompletion with `IoStatus = .SUCCESS`.
123pub extern "kernel32" fn PostQueuedCompletionStatus(
124 CompletionPort: HANDLE,
125 dwNumberOfBytesTransferred: DWORD,
126 dwCompletionKey: ULONG_PTR,
127 lpOverlapped: ?*OVERLAPPED,
128) callconv(.winapi) BOOL;
129
130pub extern "kernel32" fn GetOverlappedResult(
131 hFile: HANDLE,
132 lpOverlapped: *OVERLAPPED,
133 lpNumberOfBytesTransferred: *DWORD,
134 bWait: BOOL,
135) callconv(.winapi) BOOL;
136
137// TODO: Wrapper around NtCreateIoCompletion + NtSetInformationFile with FILE_COMPLETION_INFORMATION.
138// This would be better splitting into two functions.
139pub extern "kernel32" fn CreateIoCompletionPort(
140 FileHandle: HANDLE,
141 ExistingCompletionPort: ?HANDLE,
142 CompletionKey: ULONG_PTR,
143 NumberOfConcurrentThreads: DWORD,
144) callconv(.winapi) ?HANDLE;
145
146// TODO: Wrapper around RtlReportSilentProcessExit + NtTerminateProcess.
147pub extern "kernel32" fn TerminateProcess(
148 hProcess: HANDLE,
149 uExitCode: UINT,
150) callconv(.winapi) BOOL;
151
15227// Process Management
15328
15429pub extern "kernel32" fn CreateProcessW(
......@@ -161,86 +36,21 @@ pub extern "kernel32" fn CreateProcessW(
16136 lpEnvironment: ?[*:0]const u16,
16237 lpCurrentDirectory: ?LPCWSTR,
16338 lpStartupInfo: *STARTUPINFOW,
164 lpProcessInformation: *PROCESS_INFORMATION,
165) callconv(.winapi) BOOL;
166
167// TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`.
168pub extern "kernel32" fn GetExitCodeProcess(
169 hProcess: HANDLE,
170 lpExitCode: *DWORD,
39 lpProcessInformation: *PROCESS.INFORMATION,
17140) callconv(.winapi) BOOL;
17241
173pub extern "kernel32" fn CreateToolhelp32Snapshot(
174 dwFlags: DWORD,
175 th32ProcessID: DWORD,
176) callconv(.winapi) HANDLE;
177
17842// Threading
17943
180// TODO: Already a wrapper for this, see `windows.GetCurrentThreadId`.
181pub extern "kernel32" fn GetCurrentThreadId() callconv(.winapi) DWORD;
182
18344// TODO: CreateRemoteThread with hProcess=NtCurrentProcess().
18445pub extern "kernel32" fn CreateThread(
18546 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
18647 dwStackSize: SIZE_T,
187 lpStartAddress: LPTHREAD_START_ROUTINE,
48 lpStartAddress: *const THREAD_START_ROUTINE,
18849 lpParameter: ?LPVOID,
18950 dwCreationFlags: DWORD,
19051 lpThreadId: ?*DWORD,
19152) callconv(.winapi) ?HANDLE;
19253
193// Code Libraries/Modules
194
195// TODO: Wrapper around LdrGetDllFullName.
196pub extern "kernel32" fn GetModuleFileNameW(
197 hModule: ?HMODULE,
198 lpFilename: [*]WCHAR,
199 nSize: DWORD,
200) callconv(.winapi) DWORD;
201
202extern "kernel32" fn K32GetModuleFileNameExW(
203 hProcess: HANDLE,
204 hModule: ?HMODULE,
205 lpFilename: LPWSTR,
206 nSize: DWORD,
207) callconv(.winapi) DWORD;
208pub const GetModuleFileNameExW = K32GetModuleFileNameExW;
209
210// TODO: Wrapper around ntdll.LdrGetDllHandle, which is a wrapper around LdrGetDllHandleEx
211pub extern "kernel32" fn GetModuleHandleW(
212 lpModuleName: ?LPCWSTR,
213) callconv(.winapi) ?HMODULE;
214
215pub extern "kernel32" fn Module32First(
216 hSnapshot: HANDLE,
217 lpme: *MODULEENTRY32,
218) callconv(.winapi) BOOL;
219
220pub extern "kernel32" fn Module32Next(
221 hSnapshot: HANDLE,
222 lpme: *MODULEENTRY32,
223) callconv(.winapi) BOOL;
224
225pub extern "kernel32" fn LoadLibraryW(
226 lpLibFileName: LPCWSTR,
227) callconv(.winapi) ?HMODULE;
228
229pub extern "kernel32" fn LoadLibraryExW(
230 lpLibFileName: LPCWSTR,
231 hFile: ?HANDLE,
232 dwFlags: DWORD,
233) callconv(.winapi) ?HMODULE;
234
235pub extern "kernel32" fn GetProcAddress(
236 hModule: HMODULE,
237 lpProcName: LPCSTR,
238) callconv(.winapi) ?FARPROC;
239
240pub extern "kernel32" fn FreeLibrary(
241 hModule: HMODULE,
242) callconv(.winapi) BOOL;
243
24454// Error Management
24555
24656pub extern "kernel32" fn FormatMessageW(
......@@ -252,6 +62,3 @@ pub extern "kernel32" fn FormatMessageW(
25262 nSize: DWORD,
25363 Arguments: ?*va_list,
25464) callconv(.winapi) DWORD;
255
256// TODO: Getter for teb().LastErrorValue.
257pub extern "kernel32" fn GetLastError() callconv(.winapi) Win32Error;
lib/std/os/windows/ntdll.zig+175-72
......@@ -2,6 +2,7 @@ const std = @import("../../std.zig");
22const windows = std.os.windows;
33
44const ACCESS_MASK = windows.ACCESS_MASK;
5const ANSI_STRING = windows.ANSI_STRING;
56const BOOL = windows.BOOL;
67const BOOLEAN = windows.BOOLEAN;
78const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
......@@ -9,6 +10,7 @@ const CONTEXT = windows.CONTEXT;
910const CRITICAL_SECTION = windows.CRITICAL_SECTION;
1011const CTL_CODE = windows.CTL_CODE;
1112const CURDIR = windows.CURDIR;
13const DIRECTORY = windows.DIRECTORY;
1214const DWORD = windows.DWORD;
1315const DWORD64 = windows.DWORD64;
1416const ERESOURCE = windows.ERESOURCE;
......@@ -22,18 +24,19 @@ const IO_APC_ROUTINE = windows.IO_APC_ROUTINE;
2224const IO_STATUS_BLOCK = windows.IO_STATUS_BLOCK;
2325const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;
2426const LARGE_INTEGER = windows.LARGE_INTEGER;
27const LDR = windows.LDR;
2528const LOGICAL = windows.LOGICAL;
2629const LONG = windows.LONG;
2730const LPCVOID = windows.LPCVOID;
2831const LPVOID = windows.LPVOID;
2932const MEM = windows.MEM;
3033const NTSTATUS = windows.NTSTATUS;
31const OBJECT_ATTRIBUTES = windows.OBJECT_ATTRIBUTES;
32const OBJECT_INFORMATION_CLASS = windows.OBJECT_INFORMATION_CLASS;
34const OBJECT = windows.OBJECT;
3335const PAGE = windows.PAGE;
3436const PCWSTR = windows.PCWSTR;
35const PROCESSINFOCLASS = windows.PROCESSINFOCLASS;
37const PROCESS = windows.PROCESS;
3638const PVOID = windows.PVOID;
39const PWSTR = windows.PWSTR;
3740const RTL_OSVERSIONINFOW = windows.RTL_OSVERSIONINFOW;
3841const RTL_QUERY_REGISTRY_TABLE = windows.RTL_QUERY_REGISTRY_TABLE;
3942const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION;
......@@ -41,8 +44,8 @@ const SEC = windows.SEC;
4144const SECTION_INHERIT = windows.SECTION_INHERIT;
4245const SIZE_T = windows.SIZE_T;
4346const SRWLOCK = windows.SRWLOCK;
44const SYSTEM_INFORMATION_CLASS = windows.SYSTEM_INFORMATION_CLASS;
45const THREADINFOCLASS = windows.THREADINFOCLASS;
47const SYSTEM = windows.SYSTEM;
48const THREAD = windows.THREAD;
4649const ULONG = windows.ULONG;
4750const ULONG_PTR = windows.ULONG_PTR;
4851const UNICODE_STRING = windows.UNICODE_STRING;
......@@ -91,7 +94,7 @@ pub extern "ntdll" fn RtlCaptureContext(
9194
9295pub extern "ntdll" fn NtSetInformationThread(
9396 ThreadHandle: HANDLE,
94 ThreadInformationClass: THREADINFOCLASS,
97 ThreadInformationClass: THREAD.INFOCLASS,
9598 ThreadInformation: *const anyopaque,
9699 ThreadInformationLength: ULONG,
97100) callconv(.winapi) NTSTATUS;
......@@ -99,7 +102,7 @@ pub extern "ntdll" fn NtSetInformationThread(
99102pub extern "ntdll" fn NtCreateFile(
100103 FileHandle: *HANDLE,
101104 DesiredAccess: ACCESS_MASK,
102 ObjectAttributes: *const OBJECT_ATTRIBUTES,
105 ObjectAttributes: *const OBJECT.ATTRIBUTES,
103106 IoStatusBlock: *IO_STATUS_BLOCK,
104107 AllocationSize: ?*const LARGE_INTEGER,
105108 FileAttributes: FILE.ATTRIBUTE,
......@@ -152,7 +155,7 @@ pub extern "ntdll" fn NtLockFile(
152155pub extern "ntdll" fn NtOpenFile(
153156 FileHandle: *HANDLE,
154157 DesiredAccess: ACCESS_MASK,
155 ObjectAttributes: *const OBJECT_ATTRIBUTES,
158 ObjectAttributes: *const OBJECT.ATTRIBUTES,
156159 IoStatusBlock: *IO_STATUS_BLOCK,
157160 ShareAccess: FILE.SHARE,
158161 OpenOptions: FILE.MODE,
......@@ -233,7 +236,7 @@ pub extern "ntdll" fn NtUnlockFile(
233236
234237pub extern "ntdll" fn NtQueryObject(
235238 Handle: HANDLE,
236 ObjectInformationClass: OBJECT_INFORMATION_CLASS,
239 ObjectInformationClass: OBJECT.INFORMATION_CLASS,
237240 ObjectInformation: ?PVOID,
238241 ObjectInformationLength: ULONG,
239242 ReturnLength: ?*ULONG,
......@@ -246,7 +249,7 @@ pub extern "ntdll" fn NtClose(
246249pub extern "ntdll" fn NtCreateSection(
247250 SectionHandle: *HANDLE,
248251 DesiredAccess: ACCESS_MASK,
249 ObjectAttributes: ?*const OBJECT_ATTRIBUTES,
252 ObjectAttributes: ?*const OBJECT.ATTRIBUTES,
250253 MaximumSize: ?*const LARGE_INTEGER,
251254 SectionPageProtection: PAGE,
252255 AllocationAttributes: SEC,
......@@ -331,7 +334,7 @@ pub extern "ntdll" fn NtWaitForSingleObject(
331334
332335pub extern "ntdll" fn NtQueryInformationProcess(
333336 ProcessHandle: HANDLE,
334 ProcessInformationClass: PROCESSINFOCLASS,
337 ProcessInformationClass: PROCESS.INFOCLASS,
335338 ProcessInformation: *anyopaque,
336339 ProcessInformationLength: ULONG,
337340 ReturnLength: ?*ULONG,
......@@ -339,14 +342,14 @@ pub extern "ntdll" fn NtQueryInformationProcess(
339342
340343pub extern "ntdll" fn NtQueryInformationThread(
341344 ThreadHandle: HANDLE,
342 ThreadInformationClass: THREADINFOCLASS,
345 ThreadInformationClass: THREAD.INFOCLASS,
343346 ThreadInformation: *anyopaque,
344347 ThreadInformationLength: ULONG,
345348 ReturnLength: ?*ULONG,
346349) callconv(.winapi) NTSTATUS;
347350
348351pub extern "ntdll" fn NtQuerySystemInformation(
349 SystemInformationClass: SYSTEM_INFORMATION_CLASS,
352 SystemInformationClass: SYSTEM.INFORMATION_CLASS,
350353 SystemInformation: PVOID,
351354 SystemInformationLength: ULONG,
352355 ReturnLength: ?*ULONG,
......@@ -354,15 +357,99 @@ pub extern "ntdll" fn NtQuerySystemInformation(
354357
355358// ref none
356359
360pub extern "ntdll" fn LdrAddRefDll(
361 Flags: ULONG,
362 DllHandle: PVOID,
363) callconv(.winapi) NTSTATUS;
364pub extern "ntdll" fn LdrLoadDll(
365 DllPath: ?PCWSTR,
366 DllCharacteristics: ?*const ULONG,
367 DllName: *const UNICODE_STRING,
368 DllHandle: *PVOID,
369) callconv(.winapi) NTSTATUS;
370pub extern "ntdll" fn LdrUnloadDll(
371 DllHandle: PVOID,
372) callconv(.winapi) NTSTATUS;
373
374pub extern "ntdll" fn LdrFindEntryForAddress(
375 DllHandle: PVOID,
376 Entry: **LDR.DATA_TABLE_ENTRY,
377) callconv(.winapi) NTSTATUS;
378pub extern "ntdll" fn LdrGetDllFullName(
379 DllHandle: ?PVOID,
380 FullDllName: *UNICODE_STRING,
381) callconv(.winapi) NTSTATUS;
382pub extern "ntdll" fn LdrGetDllPath(
383 DllName: PCWSTR,
384 Flags: LDR.LOAD,
385 DllPath: *PWSTR,
386 SearchPaths: *PWSTR,
387) callconv(.winapi) NTSTATUS;
388
389pub extern "ntdll" fn LdrGetDllHandle(
390 DllPath: ?PCWSTR,
391 DllCharacteristics: ?*const ULONG,
392 DllName: *const UNICODE_STRING,
393 DllHandle: *PVOID,
394) callconv(.winapi) NTSTATUS;
395pub extern "ntdll" fn LdrGetDllHandleByMapping(
396 BaseAddress: PVOID,
397 DllHandle: *PVOID,
398) callconv(.winapi) NTSTATUS;
399pub extern "ntdll" fn LdrGetDllHandleByName(
400 BaseDllName: *const UNICODE_STRING,
401 FullDllName: *const UNICODE_STRING,
402 DllHandle: *PVOID,
403) callconv(.winapi) NTSTATUS;
404pub extern "ntdll" fn LdrGetDllHandleEx(
405 Flags: LDR.GET_DLL_HANDLE_EX,
406 DllPath: ?PCWSTR,
407 DllCharacteristics: ?*const ULONG,
408 DllName: *const UNICODE_STRING,
409 DllHandle: *PVOID,
410) callconv(.winapi) NTSTATUS;
411
412pub extern "ntdll" fn LdrGetProcedureAddress(
413 DllHandle: PVOID,
414 ProcedureName: *const ANSI_STRING,
415 ProcedureNumber: ULONG,
416 ProcedureAddress: *PVOID,
417) callconv(.winapi) NTSTATUS;
418pub extern "ntdll" fn LdrGetProcedureAddressEx(
419 DllHandle: PVOID,
420 ProcedureName: *const ANSI_STRING,
421 ProcedureNumber: ULONG,
422 ProcedureAddress: *PVOID,
423 Flags: LDR.GET_PROCEDURE_ADDRESS,
424) callconv(.winapi) NTSTATUS;
425pub extern "ntdll" fn LdrGetProcedureAddressForCaller(
426 DllHandle: PVOID,
427 ProcedureName: *const ANSI_STRING,
428 ProcedureNumber: ULONG,
429 ProcedureAddress: *PVOID,
430 Flags: LDR.GET_PROCEDURE_ADDRESS,
431 CallerAddress: PVOID,
432) callconv(.winapi) NTSTATUS;
433
434pub extern "ntdll" fn LdrRegisterDllNotification(
435 Flags: LDR.DLL_NOTIFICATION.REGISTER,
436 NotificationFunction: *const LDR.DLL_NOTIFICATION.FUNCTION,
437 Context: ?PVOID,
438 Cookie: *LDR.DLL_NOTIFICATION.COOKIE,
439) callconv(.winapi) NTSTATUS;
440pub extern "ntdll" fn LdrUnregisterDllNotification(
441 Cookie: LDR.DLL_NOTIFICATION.COOKIE,
442) callconv(.winapi) NTSTATUS;
443
357444pub extern "ntdll" fn NtQueryAttributesFile(
358 ObjectAttributes: *const OBJECT_ATTRIBUTES,
445 ObjectAttributes: *const OBJECT.ATTRIBUTES,
359446 FileAttributes: *FILE.BASIC_INFORMATION,
360447) callconv(.winapi) NTSTATUS;
361448
362449pub extern "ntdll" fn NtCreateEvent(
363450 EventHandle: *HANDLE,
364451 DesiredAccess: ACCESS_MASK,
365 ObjectAttributes: ?*const OBJECT_ATTRIBUTES,
452 ObjectAttributes: ?*const OBJECT.ATTRIBUTES,
366453 EventType: EVENT_TYPE,
367454 InitialState: BOOLEAN,
368455) callconv(.winapi) NTSTATUS;
......@@ -374,7 +461,7 @@ pub extern "ntdll" fn NtSetEvent(
374461pub extern "ntdll" fn NtCreateKeyedEvent(
375462 KeyedEventHandle: *HANDLE,
376463 DesiredAccess: ACCESS_MASK,
377 ObjectAttributes: ?*const OBJECT_ATTRIBUTES,
464 ObjectAttributes: ?*const OBJECT.ATTRIBUTES,
378465 Flags: ULONG,
379466) callconv(.winapi) NTSTATUS;
380467pub extern "ntdll" fn NtReleaseKeyedEvent(
......@@ -390,10 +477,57 @@ pub extern "ntdll" fn NtWaitForKeyedEvent(
390477 Timeout: ?*const LARGE_INTEGER,
391478) callconv(.winapi) NTSTATUS;
392479
480pub extern "ntdll" fn NtCancelSynchronousIoFile(
481 ThreadHandle: HANDLE,
482 IoRequestToCancel: ?*IO_STATUS_BLOCK,
483 IoStatusBlock: *IO_STATUS_BLOCK,
484) callconv(.winapi) NTSTATUS;
485pub extern "ntdll" fn NtCancelIoFile(
486 FileHandle: HANDLE,
487 IoStatusBlock: *IO_STATUS_BLOCK,
488) callconv(.winapi) NTSTATUS;
489pub extern "ntdll" fn NtCancelIoFileEx(
490 FileHandle: HANDLE,
491 IoRequestToCancel: *const IO_STATUS_BLOCK,
492 IoStatusBlock: *IO_STATUS_BLOCK,
493) callconv(.winapi) NTSTATUS;
494
495/// This function has been observed to return SUCCESS on timeout on Windows 10
496/// and TIMEOUT on Wine 10.0.
497///
498/// This function has been observed on Windows 11 such that positive interval
499/// is real time, which can cause waits to be interrupted by changing system
500/// time, however negative intervals are not affected by changes to system
501/// time.
502pub extern "ntdll" fn NtDelayExecution(
503 Alertable: BOOLEAN,
504 DelayInterval: *const LARGE_INTEGER,
505) callconv(.winapi) NTSTATUS;
506
507pub extern "ntdll" fn NtNotifyChangeDirectoryFileEx(
508 FileHandle: HANDLE,
509 Event: ?HANDLE,
510 ApcRoutine: ?*const IO_APC_ROUTINE,
511 ApcContext: ?*anyopaque,
512 IoStatusBlock: *IO_STATUS_BLOCK,
513 Buffer: *anyopaque,
514 Length: ULONG,
515 CompletionFilter: FILE.NOTIFY.CHANGE,
516 WatchTree: BOOLEAN,
517 DirectoryNotifyInformationClass: DIRECTORY.NOTIFY_INFORMATION_CLASS,
518) callconv(.winapi) NTSTATUS;
519
520pub extern "ntdll" fn NtOpenThread(
521 ThreadHandle: *HANDLE,
522 DesiredAccess: ACCESS_MASK,
523 ObjectAttributes: *const OBJECT.ATTRIBUTES,
524 ClientId: *const windows.CLIENT_ID,
525) callconv(.winapi) NTSTATUS;
526
393527pub extern "ntdll" fn NtCreateNamedPipeFile(
394528 FileHandle: *HANDLE,
395529 DesiredAccess: ACCESS_MASK,
396 ObjectAttributes: *const OBJECT_ATTRIBUTES,
530 ObjectAttributes: *const OBJECT.ATTRIBUTES,
397531 IoStatusBlock: *IO_STATUS_BLOCK,
398532 ShareAccess: FILE.SHARE,
399533 CreateDisposition: FILE.CREATE_DISPOSITION,
......@@ -437,7 +571,7 @@ pub extern "ntdll" fn NtUnmapViewOfSectionEx(
437571pub extern "ntdll" fn NtOpenKey(
438572 KeyHandle: *HANDLE,
439573 DesiredAccess: ACCESS_MASK,
440 ObjectAttributes: *const OBJECT_ATTRIBUTES,
574 ObjectAttributes: *const OBJECT.ATTRIBUTES,
441575) callconv(.winapi) NTSTATUS;
442576
443577pub extern "ntdll" fn NtQueueApcThread(
......@@ -470,6 +604,19 @@ pub extern "ntdll" fn NtProtectVirtualMemory(
470604 OldAccessProtection: *PAGE,
471605) callconv(.winapi) NTSTATUS;
472606
607pub extern "ntdll" fn NtWaitForAlertByThreadId(
608 Address: ?*const anyopaque,
609 Timeout: ?*const LARGE_INTEGER,
610) callconv(.winapi) NTSTATUS;
611pub extern "ntdll" fn NtAlertThreadByThreadId(ThreadId: DWORD) callconv(.winapi) NTSTATUS;
612pub extern "ntdll" fn NtAlertThread(ThreadHandle: HANDLE) callconv(.winapi) NTSTATUS;
613pub extern "ntdll" fn NtAlertMultipleThreadByThreadId(
614 ThreadIds: [*]const ULONG_PTR,
615 ThreadCount: ULONG,
616 Unknown1: ?*const anyopaque,
617 Unknown2: ?*const anyopaque,
618) callconv(.winapi) NTSTATUS;
619
473620pub extern "ntdll" fn NtYieldExecution() callconv(.winapi) NTSTATUS;
474621
475622pub extern "ntdll" fn RtlAddVectoredExceptionHandler(
......@@ -527,10 +674,6 @@ pub extern "ntdll" fn RtlQueryPerformanceCounter(
527674pub extern "ntdll" fn RtlQueryPerformanceFrequency(
528675 PerformanceFrequency: *LARGE_INTEGER,
529676) callconv(.winapi) BOOL;
530pub extern "ntdll" fn NtQueryPerformanceCounter(
531 PerformanceCounter: *LARGE_INTEGER,
532 PerformanceFrequency: ?*LARGE_INTEGER,
533) callconv(.winapi) NTSTATUS;
534677
535678pub extern "ntdll" fn RtlReAllocateHeap(
536679 HeapHandle: *HEAP,
......@@ -539,8 +682,17 @@ pub extern "ntdll" fn RtlReAllocateHeap(
539682 Size: SIZE_T,
540683) callconv(.winapi) ?PVOID;
541684
685pub extern "ntdll" fn RtlReportSilentProcessExit(
686 ProcessHandle: HANDLE,
687 ExitStatus: NTSTATUS,
688) callconv(.winapi) NTSTATUS;
689pub extern "ntdll" fn NtTerminateProcess(
690 ProcessHandle: ?HANDLE,
691 ExitStatus: NTSTATUS,
692) callconv(.winapi) NTSTATUS;
693
542694pub extern "ntdll" fn RtlSetCurrentDirectory_U(
543 PathName: *UNICODE_STRING,
695 PathName: *const UNICODE_STRING,
544696) callconv(.winapi) NTSTATUS;
545697
546698pub extern "ntdll" fn RtlTryAcquireSRWLockExclusive(
......@@ -572,52 +724,3 @@ pub extern "ntdll" fn RtlWakeConditionVariable(
572724pub extern "ntdll" fn RtlWakeAllConditionVariable(
573725 ConditionVariable: *CONDITION_VARIABLE,
574726) callconv(.winapi) void;
575
576pub extern "ntdll" fn NtWaitForAlertByThreadId(
577 Address: ?*const anyopaque,
578 Timeout: ?*const LARGE_INTEGER,
579) callconv(.winapi) NTSTATUS;
580pub extern "ntdll" fn NtAlertThreadByThreadId(ThreadId: DWORD) callconv(.winapi) NTSTATUS;
581pub extern "ntdll" fn NtAlertThread(ThreadHandle: HANDLE) callconv(.winapi) NTSTATUS;
582pub extern "ntdll" fn NtAlertMultipleThreadByThreadId(
583 ThreadIds: [*]const ULONG_PTR,
584 ThreadCount: ULONG,
585 Unknown1: ?*const anyopaque,
586 Unknown2: ?*const anyopaque,
587) callconv(.winapi) NTSTATUS;
588
589pub extern "ntdll" fn NtOpenThread(
590 ThreadHandle: *HANDLE,
591 DesiredAccess: ACCESS_MASK,
592 ObjectAttributes: *const OBJECT_ATTRIBUTES,
593 ClientId: *const windows.CLIENT_ID,
594) callconv(.winapi) NTSTATUS;
595
596pub extern "ntdll" fn NtCancelSynchronousIoFile(
597 ThreadHandle: HANDLE,
598 IoRequestToCancel: ?*IO_STATUS_BLOCK,
599 IoStatusBlock: *IO_STATUS_BLOCK,
600) callconv(.winapi) NTSTATUS;
601
602/// This function has been observed to return SUCCESS on timeout on Windows 10
603/// and TIMEOUT on Wine 10.0.
604///
605/// This function has been observed on Windows 11 such that positive interval
606/// is real time, which can cause waits to be interrupted by changing system
607/// time, however negative intervals are not affected by changes to system
608/// time.
609pub extern "ntdll" fn NtDelayExecution(
610 Alertable: BOOLEAN,
611 DelayInterval: *const LARGE_INTEGER,
612) callconv(.winapi) NTSTATUS;
613
614pub extern "ntdll" fn NtCancelIoFile(
615 FileHandle: HANDLE,
616 IoStatusBlock: *IO_STATUS_BLOCK,
617) callconv(.winapi) NTSTATUS;
618
619pub extern "ntdll" fn NtCancelIoFileEx(
620 FileHandle: HANDLE,
621 IoRequestToCancel: *const IO_STATUS_BLOCK,
622 IoStatusBlock: *IO_STATUS_BLOCK,
623) callconv(.winapi) NTSTATUS;
lib/std/os/windows/win32error.zig+1-1
......@@ -1,5 +1,5 @@
11/// Codes are from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d
2pub const Win32Error = enum(u16) {
2pub const Win32Error = enum(u32) {
33 /// The operation completed successfully.
44 SUCCESS = 0,
55 /// Incorrect function.
lib/std/process.zig+4-4
......@@ -243,7 +243,7 @@ pub fn getBaseAddress() usize {
243243 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
244244 return @intFromPtr(&std.c._mh_execute_header);
245245 },
246 .windows => return @intFromPtr(windows.kernel32.GetModuleHandleW(null)),
246 .windows => return @intFromPtr(windows.peb().ImageBaseAddress),
247247 else => @compileError("Unsupported OS"),
248248 }
249249}
......@@ -606,11 +606,11 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
606606 return @as(u64, @bitCast(physmem));
607607 },
608608 .windows => {
609 var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
609 var sbi: windows.SYSTEM.BASIC_INFORMATION = undefined;
610610 const rc = windows.ntdll.NtQuerySystemInformation(
611 .SystemBasicInformation,
611 .Basic,
612612 &sbi,
613 @sizeOf(windows.SYSTEM_BASIC_INFORMATION),
613 @sizeOf(windows.SYSTEM.BASIC_INFORMATION),
614614 null,
615615 );
616616 if (rc != .SUCCESS) {
lib/std/process/Child.zig+1-1
......@@ -86,7 +86,7 @@ pub const ResourceUsageStatistics = struct {
8686 .visionos,
8787 .watchos,
8888 => @as(?std.posix.rusage, null),
89 .windows => @as(?std.os.windows.VM_COUNTERS, null),
89 .windows => @as(?std.os.windows.PROCESS.VM_COUNTERS, null),
9090 else => {},
9191 };
9292};
lib/std/start.zig+5-9
......@@ -473,10 +473,9 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
473473 std.Thread.maybeAttachSignalStack();
474474 std.debug.maybeEnableSegfaultHandler();
475475
476 const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
477 const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
478
479 std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, .global));
476 std.os.windows.ntdll.RtlExitUserProcess(
477 callMain(std.os.windows.peb().ProcessParameters.CommandLine.slice(), .global),
478 );
480479}
481480
482481fn 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
647646 // values in their intended encoding from the PEB instead.
648647 std.Thread.maybeAttachSignalStack();
649648 std.debug.maybeEnableSegfaultHandler();
650 const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
651 const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
652 return callMain(cmd_line_w, .global);
649 return callMain(std.os.windows.peb().ProcessParameters.CommandLine.slice(), .global);
653650 },
654651 else => {},
655652 }
......@@ -776,8 +773,7 @@ fn call_wWinMain() std.os.windows.INT {
776773 // - With STARTF_USESHOWWINDOW unset:
777774 // - nShowCmd is always SW_SHOWDEFAULT
778775 const SW_SHOWDEFAULT = 10;
779 const STARTF_USESHOWWINDOW = 1;
780 if (peb.ProcessParameters.dwFlags & STARTF_USESHOWWINDOW != 0) {
776 if (peb.ProcessParameters.dwFlags & std.os.windows.STARTF_USESHOWWINDOW != 0) {
781777 break :nShowCmd @truncate(peb.ProcessParameters.dwShowWindow);
782778 }
783779 break :nShowCmd SW_SHOWDEFAULT;
lib/std/zig/system/windows.zig+5-5
......@@ -100,11 +100,11 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
100100 REG.MULTI_SZ,
101101 => {
102102 comptime assert(@sizeOf(std.os.windows.UNICODE_STRING) % 2 == 0);
103 const unicode = @as(*std.os.windows.UNICODE_STRING, @ptrCast(&tmp_bufs[i]));
103 const unicode: *std.os.windows.UNICODE_STRING = @ptrCast(&tmp_bufs[i]);
104104 unicode.* = .{
105105 .Length = 0,
106106 .MaximumLength = max_value_len - @sizeOf(std.os.windows.UNICODE_STRING),
107 .Buffer = @as([*]u16, @ptrCast(tmp_bufs[i][@sizeOf(std.os.windows.UNICODE_STRING)..])),
107 .Buffer = @ptrCast(tmp_bufs[i][@sizeOf(std.os.windows.UNICODE_STRING)..]),
108108 };
109109 break :blk unicode;
110110 },
......@@ -159,8 +159,8 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
159159 REG.MULTI_SZ,
160160 => {
161161 var buf = @field(args, field.name).value_buf;
162 const entry = @as(*align(1) const std.os.windows.UNICODE_STRING, @ptrCast(table[i + 1].EntryContext));
163 const len = try std.unicode.utf16LeToUtf8(buf, entry.Buffer.?[0 .. entry.Length / 2]);
162 const entry: *const std.os.windows.UNICODE_STRING = @ptrCast(table[i + 1].EntryContext);
163 const len = try std.unicode.utf16LeToUtf8(buf, entry.slice());
164164 buf[len] = 0;
165165 },
166166
......@@ -168,7 +168,7 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
168168 REG.DWORD_BIG_ENDIAN,
169169 REG.QWORD,
170170 => {
171 const entry = @as([*]align(1) const u8, @ptrCast(table[i + 1].EntryContext));
171 const entry: [*]const u8 = @ptrCast(table[i + 1].EntryContext);
172172 switch (@field(args, field.name).value_type) {
173173 REG.DWORD, REG.DWORD_BIG_ENDIAN => {
174174 @memcpy(@field(args, field.name).value_buf[0..4], entry[0..4]);
lib/zig.h+24-4
......@@ -4148,7 +4148,7 @@ static inline void zig_msvc_atomic_store_i128(zig_i128 volatile* obj, zig_i128 a
41484148
41494149#if defined(zig_thumb)
41504150
4151static inline void* zig_thumb_windows_teb(void) {
4151static inline void* zig_windows_teb(void) {
41524152 void* teb = 0;
41534153#if defined(zig_msvc)
41544154 teb = (void*)_MoveFromCoprocessor(15, 0, 13, 0, 2);
......@@ -4160,7 +4160,7 @@ static inline void* zig_thumb_windows_teb(void) {
41604160
41614161#elif defined(zig_aarch64)
41624162
4163static inline void* zig_aarch64_windows_teb(void) {
4163static inline void* zig_windows_teb(void) {
41644164 void* teb = 0;
41654165#if defined(zig_msvc)
41664166 teb = (void*)__readx18qword(0x0);
......@@ -4172,7 +4172,7 @@ static inline void* zig_aarch64_windows_teb(void) {
41724172
41734173#elif defined(zig_x86_32)
41744174
4175static inline void* zig_x86_windows_teb(void) {
4175static inline void* zig_windows_teb(void) {
41764176 void* teb = 0;
41774177#if defined(zig_msvc)
41784178 teb = (void*)__readfsdword(0x18);
......@@ -4182,9 +4182,19 @@ static inline void* zig_x86_windows_teb(void) {
41824182 return teb;
41834183}
41844184
4185static inline void* zig_windows_peb(void) {
4186 void* peb = 0;
4187#if defined(zig_msvc)
4188 peb = (void*)__readfsdword(0x30);
4189#elif defined(zig_gnuc_asm)
4190 __asm__ ("movl %%fs:0x30, %[ptr]" : [ptr] "=r" (peb));
4191#endif
4192 return peb;
4193}
4194
41854195#elif defined(zig_x86_64)
41864196
4187static inline void* zig_x86_64_windows_teb(void) {
4197static inline void* zig_windows_teb(void) {
41884198 void* teb = 0;
41894199#if defined(zig_msvc)
41904200 teb = (void*)__readgsqword(0x30);
......@@ -4194,6 +4204,16 @@ static inline void* zig_x86_64_windows_teb(void) {
41944204 return teb;
41954205}
41964206
4207static inline void* zig_windows_peb(void) {
4208 void* peb = 0;
4209#if defined(zig_msvc)
4210 peb = (void*)__readgsqword(0x60);
4211#elif defined(zig_gnuc_asm)
4212 __asm__ ("movq %%gs:0x60, %[ptr]" : [ptr] "=r" (peb));
4213#endif
4214 return peb;
4215}
4216
41974217#endif
41984218
41994219#if defined(zig_loongarch)
stage1/zig.h+24-4
......@@ -4148,7 +4148,7 @@ static inline void zig_msvc_atomic_store_i128(zig_i128 volatile* obj, zig_i128 a
41484148
41494149#if defined(zig_thumb)
41504150
4151static inline void* zig_thumb_windows_teb(void) {
4151static inline void* zig_windows_teb(void) {
41524152 void* teb = 0;
41534153#if defined(zig_msvc)
41544154 teb = (void*)_MoveFromCoprocessor(15, 0, 13, 0, 2);
......@@ -4160,7 +4160,7 @@ static inline void* zig_thumb_windows_teb(void) {
41604160
41614161#elif defined(zig_aarch64)
41624162
4163static inline void* zig_aarch64_windows_teb(void) {
4163static inline void* zig_windows_teb(void) {
41644164 void* teb = 0;
41654165#if defined(zig_msvc)
41664166 teb = (void*)__readx18qword(0x0);
......@@ -4172,7 +4172,7 @@ static inline void* zig_aarch64_windows_teb(void) {
41724172
41734173#elif defined(zig_x86_32)
41744174
4175static inline void* zig_x86_windows_teb(void) {
4175static inline void* zig_windows_teb(void) {
41764176 void* teb = 0;
41774177#if defined(zig_msvc)
41784178 teb = (void*)__readfsdword(0x18);
......@@ -4182,9 +4182,19 @@ static inline void* zig_x86_windows_teb(void) {
41824182 return teb;
41834183}
41844184
4185static inline void* zig_windows_peb(void) {
4186 void* peb = 0;
4187#if defined(zig_msvc)
4188 peb = (void*)__readfsdword(0x30);
4189#elif defined(zig_gnuc_asm)
4190 __asm__ ("movl %%fs:0x30, %[ptr]" : [ptr] "=r" (peb));
4191#endif
4192 return peb;
4193}
4194
41854195#elif defined(zig_x86_64)
41864196
4187static inline void* zig_x86_64_windows_teb(void) {
4197static inline void* zig_windows_teb(void) {
41884198 void* teb = 0;
41894199#if defined(zig_msvc)
41904200 teb = (void*)__readgsqword(0x30);
......@@ -4194,6 +4204,16 @@ static inline void* zig_x86_64_windows_teb(void) {
41944204 return teb;
41954205}
41964206
4207static inline void* zig_windows_peb(void) {
4208 void* peb = 0;
4209#if defined(zig_msvc)
4210 peb = (void*)__readgsqword(0x60);
4211#elif defined(zig_gnuc_asm)
4212 __asm__ ("movq %%gs:0x60, %[ptr]" : [ptr] "=r" (peb));
4213#endif
4214 return peb;
4215}
4216
41974217#endif
41984218
41994219#if defined(zig_loongarch)
test/standalone/coff_dwarf/main.zig+3-4
......@@ -4,17 +4,16 @@ const fatal = std.process.fatal;
44extern fn add(a: u32, b: u32, addr: *usize) u32;
55
66pub fn main(init: std.process.Init) void {
7 const gpa = init.gpa;
87 const io = init.io;
98
109 var di: std.debug.SelfInfo = .init;
11 defer di.deinit(gpa);
10 defer di.deinit(io);
1211
1312 var add_addr: usize = undefined;
1413 _ = add(1, 2, &add_addr);
1514
16 const symbol = di.getSymbol(gpa, io, add_addr) catch |err| fatal("failed to get symbol: {t}", .{err});
17 defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
15 const symbol = di.getSymbol(io, add_addr) catch |err| fatal("failed to get symbol: {t}", .{err});
16 defer if (symbol.source_location) |sl| std.debug.getDebugInfoAllocator().free(sl.file_name);
1817
1918 if (symbol.name == null) fatal("failed to resolve symbol name", .{});
2019 if (symbol.compile_unit_name == null) fatal("failed to resolve compile unit", .{});
test/standalone/windows_argv/fuzz.zig+12-6
......@@ -127,7 +127,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO
127127 .hStdOutput = null,
128128 .hStdError = windows.peb().ProcessParameters.hStdError,
129129 };
130 var proc_info: windows.PROCESS_INFORMATION = undefined;
130 var proc_info: windows.PROCESS.INFORMATION = undefined;
131131
132132 if (windows.kernel32.CreateProcessW(
133133 @constCast(verify_path.ptr),
......@@ -141,7 +141,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO
141141 &startup_info,
142142 &proc_info,
143143 ) == 0) {
144 std.process.fatal("kernel32 CreateProcessW failed with {t}", .{windows.kernel32.GetLastError()});
144 std.process.fatal("kernel32 CreateProcessW failed with {t}", .{windows.GetLastError()});
145145 }
146146
147147 windows.CloseHandle(proc_info.hThread);
......@@ -156,9 +156,15 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO
156156 else => |status| return windows.unexpectedStatus(status),
157157 }
158158
159 var exit_code: windows.DWORD = undefined;
160 if (windows.kernel32.GetExitCodeProcess(child_proc, &exit_code) == 0) {
161 return error.UnableToGetExitCode;
159 var info: windows.PROCESS.BASIC_INFORMATION = undefined;
160 switch (windows.ntdll.NtQueryInformationProcess(
161 child_proc,
162 .BasicInformation,
163 &info,
164 @sizeOf(windows.PROCESS.BASIC_INFORMATION),
165 null,
166 )) {
167 .SUCCESS => return @intFromEnum(info.ExitStatus),
168 else => return error.UnableToGetExitCode,
162169 }
163 return exit_code;
164170}
test/standalone/windows_argv/lib.zig+3-3
......@@ -16,9 +16,9 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {
1616 defer arena_state.deinit();
1717 const allocator = arena_state.allocator();
1818
19 const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
20 const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
21 const raw_args: std.process.Args = .{ .vector = cmd_line_w };
19 const raw_args: std.process.Args = .{
20 .vector = std.os.windows.peb().ProcessParameters.CommandLine.slice(),
21 };
2222 const args = try raw_args.toSlice(allocator);
2323 var wtf8_buf = std.array_list.Managed(u8).init(allocator);
2424